秋招斩获所有互联网大厂面经之前端

1
flash和js通过什么类如何交互?
ExternalInterface
 
 

-----------------------------------

2
call和apply:
apply传入的是一个{{c1::参数数组}}。
- call传入的则是直接的{{c1::参数列表}}
apply 数组
call 列表
 
 

-----------------------------------

3
路由懒加载、异步加载、React 文件加载有办法解决吗?
1、react-loadable:将js 拆分成若干个chunk.js
2、react-router 创建一个异步组件 AsyncComponent,然后使用AsyncComponent动态导入组件。
 
 

-----------------------------------

4
为什么浏览器要设置跨域的防范
假如我是A网的管理员,登录了我的账号,然后B网站被攻击了,被植入了一个iframe,里面调用了我通过POST删除用户的接口,这样我只要访问了B网站被攻击的页面,就会删除用户,所以为了避免敏感信息接口被攻击,需要设计为跨域。
 
 

-----------------------------------

5
image标签把href写成b.com,为什么不收到跨域的限制?为什么只对AJAX做限制,对图片不做限制?图片地址也可以写一个api接口,为什么不做限制呢?
1、图片是GET请求,我们设计API时不应用GET做敏感事情。
2、支持跨域的标签还有:image、iframe、link、script
 
 

-----------------------------------

6
浏览器怎么知道允不允许跨域?我如果根据header的话,那就已经请求了一次,怎么解决?

  • 如果同源,浏览器发送数据请求,否则发送跨域请求
  • 浏览器收到跨域请求后,返回自身配置的是否含有Access-control-Allow-origin字段
  • 浏览器收到这个header后,匹配看是否允许,允许的话则发送数据请求。
 
 

-----------------------------------

7
Promise直接resolve,和setTimeout 0,谁先执行?为什么?
  • Promise先执行
  • 主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
  • Promise.resolve() 是微任务,本轮事件循环结束时执行
  • setTimeout(fn,0)是宏任务,在下一轮“事件循环”event loop开始时执行
 
 

-----------------------------------

8
__proto__知道吗?原型链,声明一个object a, 然后让 a.__proto__.b,js原来是面向过程的,但是es6/es7又支持了class,原来不支持class,是怎么模拟的?

  • prototype是函数才有的属性,__proto__是每个对象都有的属性
  • 对象的__proto__等于生成这个对象的函数的prototype
  • ES6类的底层还是通过原型链构造函数去创建
 
 

-----------------------------------

9
React现在官方推荐使用Function组件,之前是class,他们两个有什么区别?
  • function组件没有state状态,删除了生命周期,速度会很快
  • class组件适合有状态组件
 
 

-----------------------------------

10
解释性语言一行一行执行怎么支持类的
1、解释型是说不需要编译,每次执行的时候,再去转为机器语言运行。
2、而是否面向对象,说的是语言的模式,是面向过程还是面向对象的。所以这俩没什么关联,是不同的分法
 
 

-----------------------------------

11
JS的数据类型有哪些?
① JS有八种数据类型:Number、String、Boolean、Null、undefined、object、symbol、bigInt
② 其中除了object之外都是基本类型,而引用类型分为:Object、Array、RegExp、Date、Function。
③ 基本类型和引用类型有四点区别:
1、基本类型保存在栈中,引用类型保存在堆中。
2、基本类型按值访问,引用类型按引用访问。
3、基本类型的值不能改变,引用类型的值可以改变。
4、基本类型的比较是值的比较,引用类型的比较是引用的比较。
 
 

-----------------------------------

12
函数式编程
函数式编程的本质
1、函数式编程中的函数指的不是计算机里面的函数,而是数学中的函数,即自变量的映射
2、只要输入是确定的,输出就是确定的
函数式编程的好处
1、函数式编程引用透明,没有副作用。
2、函数不依赖外部状态,这样写的代码不容易出错。
3、不共享状态就不会造成资源争夺,也不需要锁,也不会出现死锁。
 
 

-----------------------------------

13
Babel/Webpack 分别讲一下,在项目中用到过吗?
1、Babel 是一个JS编译器,用来转换最新的JS语法,比如把ES6, ES7等语法转化成ES5语法。提供了一些插件转换最新的 api,如 babel-pilofill, 基于 core-js 和 regenerator. 也支持 React 的 JSX 语法。
2、Webpack 是一个打包工具,打包js文件,css文件,图片,html等等,可以分析文件结构,确定文件依赖,合并压缩js,加入hash,最终生成项目文件。
3、在使用过的飞冰框架ice-js和微信小程序框架taro中都有应用
 
 

-----------------------------------

14
浏览器渲染的过程
  1. HTML 解析成 DOM 树
  2. CSS 构造成 CSS 对象模型
  3. DOM 和 CSS 模型合并为渲染树,渲染树每个元素的内容都是计算好的 layout.
  4. 将渲染树的节点绘制到屏幕上。
 
 

-----------------------------------

15
重排、重绘、合成
1、重排: 修改几何属性,如宽高度,会重新布局
2、重绘:修改绘制属性,如背景颜色,直接进入绘制阶段。
3、合成:更改一个既不要布局也不要绘制的属性,渲染引擎会跳过布局和绘制,在非主线程上合成,如更改 transform属性。
优化:
1、使用class
2、使用React的虚拟节点
3、避免使用table布局
4、使用硬件加速
5、带动画的元素position:absolute或fixed、不显示的元素使用display: none
 
 

-----------------------------------

16
减少白屏的方式
  1. script放在 body后面
  2. script 加defer属性
  3. 减少 head里的css资源
  4. gzip压缩文件体积
 
 

-----------------------------------

17
React Fiber 了解吗?引入 React Fiber 的好处是什么?
1、React filber 是一种基于浏览器的单线程调度算法,将递归的diff拆分成无数个小任务,可以随时停止与恢复。
2、React fiber优化了虚拟DOM的diff算法,将组件渲染拆分为无数个具有优先级的分片任务,防止浏览器在组件个数较多的时候后陷入假死卡顿状态。
 
 

-----------------------------------

18
HTTP 请求方法
  1. GET、POST、PUT、DELETE、PATCH 
  2.  HEAD、CONECTION、OPTIONS、TRACE
 
 

-----------------------------------

19
HTTP 请求状态码
一共有五类:1是临时响应,2是成功,3是重定向,4是客户端错误,5是服务端错误。
1、100(继续)、101(切换协议)
2、200(成功)、201(成功并创建资源)、204(成功但没有返回内容)
3、301(永久移动)、302(临时移动)、304(服务端已执行 GET, 但是文件未变化)
4、400(错误请求)、401(未授权)、403(禁止访问)、404(未找到)、405(请求方法不支持)、408(请求超时)、410(已经删除)
5、500(服务器内部错误)、501(网关错误)、503(服务不可用)、504(网关超时)
 
 

-----------------------------------

20
Webpack HMR 的原理详细介绍一下
  1. 首先建立 WebSocket 连接
  2. 文件发生变化,Webpack Dev Server 通知应用程序
  3. HMR Runtime 通过 HTTP 请求模块更新清单
  4. script 标签下载模块更新
  5. 插入新模块、进行局部刷新。
 
 

-----------------------------------

21
webpack优化方式,如何提高 webpack 的构建速度
优化方式:https://www.cnblogs.com/wangjiachen666/p/11561186.html
  1. Loader 缩小文件搜索范围
  2. 对编译文件使用缓存
  3. 多线程打包 happypack
  4. image-webpack-loader 压缩图片
  5. 删除无用的 css 样式
  6. CDN 加载资源
 
 

-----------------------------------

22
JS的深拷贝与浅拷贝
① 浅拷贝:Object.assign(obj), ... 展开运算符

② 深拷贝:
1、JSON先stringify再parse.
2、递归实现一个深拷贝:clone函数里面判断类型,如果是基本类型直接返回,如果是object,则定义一个空对象,然后用for key in 的方式递归调用clone来深拷贝每一个属性。还可以用一个Map来避免循环引用。
3、jQuery.extend()
4、lodash.cloneDeep()
 
 

-----------------------------------

23
js判断数据类型的方法,以及他们的优缺点?
① typeof:缺点是基本类型null返回的是object,引用类型中除了function外返回的都是object。
② constructor:缺点是无法判断null和undefined,而且重写prototype后也无法判断。
③ Object.prototype.toString.call(value),默认返回当前对象的class,用中括号括起来的[object + 类名].
 
 

-----------------------------------

24
toString()方法返回的是什么东西?
返回的是当前对象的字符串形式。
 
 

-----------------------------------

25
new 操作发生了什么?如何实现一个 new?
假设构造函数为 Fun,接收参数为 args
① 根据构造函数的原型,创建一个新对象
let obj = Object.create(Fun.prototype)
② 对对象应用参数,改变 this 指向
let result = Fun.apply(obj, args)
③ 如果返回的是 object, 则返回这个 result,否则返回第一步创建的 obj
return typeof reuslt === 'object' ? result : obj
 
整体代码如下:
function myNew (Fun, ...args) {
  let obj = Object.create(Fun.prototype)
  let result = Fun.apply(obj, args)
  return typeof reuslt === 'object' ? result : obj
}
 
 

-----------------------------------

26
函数的防抖与节流
1、函数防抖与节流都可以避免频繁触发事件。
2、函数防抖与节流都可以使用setTimeout实现,目标都是降低执行频率。
3、函数防抖让连续触发的事件只执行最后一次,而函数节流侧重于一段时间内只执行一次。
4、应用:加载更多、输入框输入、避免重复提交。
 
 

-----------------------------------

27
Promise的过程
Promise是一个具有三种状态的状态机:pending、resolved、rejected.初始是pendig,调用resolve变成resolved,调用rejected变成rejected.
 
 

-----------------------------------

28
实现一个Promise.all
1、Promise.all概述:返回一个Promise,参数内所有的Promise都resolved后回调一个结果数组,有一个rejected则会回调失败。
2、实现:
① 输入时一个数组,输出new 一个 Promise.
② 函数内定义一个数组result,来存放最终结果。
③ 返回的Promise内使用for循环,把每一个Promise.then的结果放入result,如果发生了reject则直接reject,如果结果数组的长度等于输入数组的长度,则resolve(result).
function isPromise(obj) {
    return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';  
}

const myPromiseAll = (arr)=>{
    let result = [];
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < arr.length;i++){
            if(isPromise(arr[i])){
                arr[i].then((data)=>{
                    result[i] = data;
                    if(result.length === arr.length){
                        resolve(result)
                    }
                },reject)
            }else{
                result[i] = arr[i];
            }
        }    
    })
}
 

-----------------------------------

29
前端框架的MVVM模式
1、VM视图模型层通过接口从后台model层请求数据,然后VM层与view层实现数据的双向绑定。
2、优点:促进了前后端分离,提升了前端开发效率。
 
 

-----------------------------------

30
VUE里面的数据劫持+发布订阅模式,实现双向数据绑定。
vue.js 是采用数据劫持结合发布者-订阅者模式的方式实现MVVM的,VUE 2 使用的是Object.defineProperty()。
实现过程:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的更新函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并初始化模板数据以及订阅器。
 
 

-----------------------------------

31
CSS 相对于父元素垂直居中
1、display:inline-block的时候可以vertical-align:middle
2、用display:flex和align-items: center;
3、inline-height 和父元素高度一样。
4、transform: translateY(-50%)
 
 

-----------------------------------

32
BFC
① BFC 概念:块格式化上下文,是一个独立的布局环境。
② 适合类型:根元素、浮动元素、绝对定位元素、display: inline-block/flex/inline-flex/table-cell、overflow: 非visible。
 作用:清除浮动、BFC内部外边距折叠、不同的BFC可以避免外边距折叠。
 
 

-----------------------------------

33
响应式布局
使用百分比、rem、vw、flex、媒体查询五种方式
① 使用百分比
② 使用媒体查询对不同分辨率设置不同css样式
③ 使用 rem:相对于html元素的font-size值
④ 使用 vw
⑤ 使用 flex
 
 

-----------------------------------

34
304 协商缓存
① 200是强缓存,直接从缓存中取。304是协商缓存,通过服务器告知缓存是否可用。
② 服务器上的资源不是一成不变的,如果我们还访问本地缓存,那么对用户来说,那就相当于资源没有更新,用户看到的还是旧的资源。
 
 

-----------------------------------

35
跨域的解决方案
① 服务器返回一个header:Access-Control-Allow-Origin
② jsonp跨域:利用script标签的src属性
③ 代理跨域
④ 设置 document.domain 解决一级域名相同,二级域名不同的情况。
 
其他的方式:
① hash 跨域通信:A 里面通过 iframe 嵌入了一个 B,然后 A 修改 B 的 url hash, B 里面通过 onhashchange 监听到 hash 变化。
② postMessage:窗口 A 中 window.postMessage(数据,来源), 窗口 B 中 window.addEventListener('message', event) 接收。
 
 

-----------------------------------

36
CSS 优先级
!important > 内联样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
 
 

-----------------------------------

37
cookie的属性,前端怎么设置cookie,可以设置cookie的哪些属性
① cookie 属性:expires 过期时间、domain/path 域名和路径、secure 安全发送、httpOnly 只允许http访问,不允许 js 访问。
② 前端设置cookie 的方法:document.cookie=,一次只设置一个,多个需要多次。
③ 前端可以设置的 cookie:httpOnly不能设置,secure 在 https 时才能设置,其他都可以。
 
 

-----------------------------------

38
XSS攻击
① 概念:XSS 中文是跨站脚本攻击,指的是攻击者向网页植入恶意代码,用户浏览器会被控制或者导致隐私泄露。
② 分类: 分为反射型(如放置一个恶意跳转链接)、存储型(通过发表评论向数据库注入js代码,然后所有浏览该帖子的浏览器都会执行这段代码)、基于DOM (通过恶意脚本修改页面的 DOM 结构)
③ 防范:HttpOnly 防止截取 Cookie、输入检查、输出检查。
 
 

-----------------------------------

39
为什么第二个参数是[]时,只会在组件mount时执行
useEffect 源码里面有一个if,说如果没有依赖,或者依赖发生变化,则执行callback,而如果数组为空,依赖一直不变化,callback不会二次执行。
 
 

-----------------------------------

40
原型
① 是什么:prototype属性是一个指针,指向一个对象。
② 用途:它包含该类型所有实例共享的属性和方法,
③ proto和prototype的关系:每个实例对象的__proto__都指向这个构造函数/类的prototype属性。
 
 

-----------------------------------

41
闭包
① 闭包简单来说就是函数嵌套函数,详细说就是闭包可以让一个函数访问并操作其声明时的作用域中的变量和函数,并且,即使声明时的作用域消失了,也可以调用
② 内部函数引用来外部函数的变量,从而导致垃圾回收机制没有把当前变量回收掉,这样的操作带来了内存泄漏的影响。
例子:
```
function A() {
    let data = ['1']
    return function () {
        console.log(data)
    }
}
let a = A()
// 此时的 data 仍在引用,没有被回收
```javascript
 
 

-----------------------------------

42
原型链
查找方法的时候会先在自身属性上查找,如果没有这个属性,就会去__proto__中查找,一层层向上直到查找到顶层对象Object,这个查找的过程就是原型链。
 
 

-----------------------------------

43
vue-router
vue-router是vuex.js官方的路由管理器,包含<router-link> 导航、<router-view>视图组件、<keep-alive> 组件。
 
 

-----------------------------------

44
HTTP
① 中文超文本传输协议,无连接无状态,基于TCP/IP,分为请求和响应两个部分。
② 报文包括状态行、头部、空行、数据四个部分。
状态行
(请求)请求方法、URL、HTTP 版本
(响应)状态码、HTTP 版本
头部
(两者都有)Date, Connection, Cache-Control, Content-Type, Content-Length 等
(请求)Host, Accept, Accept-Encoding, Accept-Language 等
(响应)Accept-Range, Location 等
 
 

-----------------------------------

45
说一说SessionStorage和localStorage还有cookie
① cookie在http中传输,其他两个仅在本地保存。
② 大小:cookie不超过4k,其他两个能到5M
③ 有效性:sessionStorage关闭浏览器销毁,localStorage 本地持久存储,cookie 过期之前都有效。
④ 作用域:sessionStorage 不共享,localStorage和cookie 同源共享,
 
 

-----------------------------------

46
前端优化
① HTTP 请求:减少次数、CSS Sprites、JS / CSS 压缩、网页  GZIP、CDN 托管、图片懒加载、AJAX 异步加载、减少 Cookie 传输。
② 页面操作:CSS 放在 head, script 放在 body 后面,innerHTML 代替 DOM 操作,使用 class 而不是 style,避免使用 table 布局。
③ JS 方面:避免使用 eval, 减少全局变量使用,避免 with 语句,避免循环引用,字符串使用 join 连接。
 
 

-----------------------------------

47
说一下你了解的 ES6
① 箭头函数
② 使用反引号 ` 的模板字符串格式化
③ 解构赋值
④ import / export
⑤ Set 数据结构
⑥ ... 展开运算符
⑦ 引入 class 关键字
⑧ Promise
 
 
 

-----------------------------------

48
Promise 的方法
① then / catch / resolve / reject
② all(),传入 Promise 对象的数组,.then() 可以获取到全都 resolve 的结果数组
③ race(), 顾名思义,传入也是 Promise 队型数组,.then() 输出最快的那个。
 
 

-----------------------------------

49
React diff 算法原理
时间复杂度
传统的差异查找算法时间复杂度为O(n^3),React 通过以下三个策略降低到 O(n):
三大策略
① 跨层级的移动操作特别少,可以忽略不计
② 相同类的组件会生成相似的树形结构,不同类的组件生成不同的树形结构。
③ 同一层级的子节点可以通过唯一 id 区分。
具体的做法
具体来说,React 是这样做的:
① 分层:将树形结构按层级分解,只比较同级元素,并且只会匹配类名相同的组件。
② 列表: 给列表结构的每个单元添加唯一的 key 属性,方便比较。
③ dirty: 调用 setState 方法的时候,先标记为 dirty, 所有的标记完成后,检查所有标记为 dirty 的组件重新绘制。
④ 选择性子树渲染:开发者可以重写 shouldComponentUpdate 提高 diff 的性能。
 
 

-----------------------------------

50
webpack 常用配置
① cache: true, 开启编译缓存
② entry:配置入口,app 写 main.js 的相对路径
③ output: 配置输出目录,有 path, filename, publicPath.
④ module.rules: 配置 loader
⑤ plugins: 配置插件
⑥ externals: 外部引入,不会打包合并。
 
 

-----------------------------------

51
使用过哪些 loader
  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • image-loader:加载并且压缩图片
  • babel-loader:把 ES67 转换成 ES5, jsx 转换成 js
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等功能
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
 
 

-----------------------------------

52
对 babel 的了解
① Babel 是一个JS编译器,用来转换最新的JS语法,比如把ES6, ES7等语法转化成ES5语法。提供了一些插件转换最新的 api,如 babel-pilofill, 基于 core-js 和 regenerator. 也支持 React 的 JSX 语法。
② babel 的原理:
  • 解析: 将代码(其实就是字符串)转换成 AST( 抽象语法树)
  • 转换: 访问 AST 的节点进行变换操作生成新的 AST
  • 生成: 以新的 AST 为基础生成代码
 
 

-----------------------------------

53
异步加载的方法、defer 和 async 的区别?
  • 动态脚本加载
  • defer: html 解析后按加载顺序执行
  • async: 加载完后立即执行,与加载顺序无关
 
 

-----------------------------------

54
CSS 水平居中
① margin: auto,只能水平居中,不支持浮动元素和绝对定位元素。
② text-align: center,只能图片、按钮、文字等行内元素(display: inline / inline-block)
③ 使用表格,或者table-cell模拟单元格
④ 绝对定位直接设置四个位置。
⑤ 浮动元素定位到 50%,然后里面的元素再相对定位拉回来 50%
⑥ flex, justify-content: center
⑦ left: 50%; transform: translateX(-50%)
 
 

-----------------------------------

55
解释一下什么是 Event Loop?
概念
中文是事件循环,Javascript 代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列来搞定另外一些代码的执行,整个执行的过程我们称为事件循环。
任务队列
任务队列又分为宏任务和微任务,宏任务和微任务的组成如下:
① 宏任务:script, setTimeout, setInterval, setImmediate, I/O, UI render
② 微任务:process.nextTick, Promise, Async/Await, MutationObserver.
事件循环的执行顺序
① 首先执行同步代码
② 检查是否有异步代码需要执行
③ 执行所有微任务,如果有必要则渲染页面
④ 开始下一轮事件循环,执行宏任务中的异步代码,如 setTimeout
 
 

-----------------------------------

56
0.1 + 0.2 不等于 0.3 的解决办法
① 原因:JS 采用 IEEE 754 双精度标准,用二进制存储的小数是无限循环的,JS 采取的标准会对数字进行裁剪。
② 解决:自己转换为整数后运算,或者使用原生的解决方案:
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
 
 

-----------------------------------

57
垃圾回收
① 回收方式:标记清除、引用计数
② 内存泄漏:意外的全局变量、闭包泄漏、DOM 被清理后仍有引用、被遗忘的定时器。
 
 

-----------------------------------

58
DOM 的事件流?事件的触发过程是怎样的?
① 概念:用户与浏览器当前页面的交互过程称为事件流。
② 三个阶段捕获阶段(从最顶层元素传递到目标元素的父元素)、目标阶段(事件到达目标元素,阻止冒泡的话就会终止)、冒泡阶段(从目标元素父元素向上传递到顶层元素)
③ 顺序:捕获顺序:window --> document --> html --> body --> 中间元素 --> 目标元素, 冒泡顺序相反。
④ 阻止冒泡: stopPropagation.
 
 

-----------------------------------

59
instanceof 原理
① 功能:可以正确判断对象的类型
② 原理:判断对象的原型链上能不能找到类型的 prototype
③ 实现:通过迭代 left.__proto__,看原型链上能否找到类型的 prototype
function myInstanceOf(left, right) {
  let type = right.prototype
  left = left.__proto__
  while (left) {
    if (left == type) {
      return true
    }
    left = left.__proto__
  }
  return false
}
 
 

-----------------------------------

60
GET 与 POST 的区别
① 回退:POST 回退时会提示再次请求,GET 不会
② 缓存:GET 主动缓存,POST 不会
③ 参数:GET 的参数暴露在 URL 上不安全,GET 长度有限制,GET 只允许 ASCII 字符。
④ 特性:GET 安全幂等,POST 非安全非幂等。安全指的是不引起服务器变化,幂等指的是请求多次和请求一次结果相同。
 
 

-----------------------------------

61
缓存的分类,强缓存、协商缓存、跟缓存有关的 HTTP 头部有哪些?
① 过程:浏览器请求资源 --> 服务器返回资源,并通过响应头告知缓存策略 --> 浏览器根据响应头决定是否缓存资源 --> 浏览器再次访问检查是否读取本地缓存。
② 分类
  • 强缓存:直接使用缓存,HTTP 头有(Expires: 服务器过期时间,Cache-Control: 相对过期时间,在这个时间内不会去服务器请求)
  • 协商缓存:浏览器与服务器协商,如果资源没有变化,直接返回 304 Not Modified, 否则返回 200 Ok。过程是之前服务器返回一个 Last-Modified 上次修改时间,然后第二次请求时浏览器发送 If-Modified-Since 携带这个时间。更高优先级的是服务器返回一个 ETag 作为 hash,然后浏览器发送 If-Non-Match 携带这个 hash。
 
 

-----------------------------------

62
webpack与grunt、gulp的不同?
① grunt 和 gulp 早期流行,现在 webpack 相对来说是主流
② grunt 和 gulp 是基于任务和流的,找到一个文件做一系列的链式操作。
③ webpack 是基于入口的,自动地递归解析入口所需要加载的文件,然后使用不同的 Loader 处理不同的文件,使用 Plugin 拓展 webpack 功能。
 
 

-----------------------------------

63
webpack 常见的 Plugin
① define-plugin:定义环境变量
② commons-chunk-plugin:提取公共代码
③ uglifyjs-webpack-plugin:通过`UglifyJS`压缩`ES6`代码
 
 

-----------------------------------

64
webpack 的工作流程
① 初始化参数:合并配置文件和 shell 中的参数
② 开始编译:初始化 Compile 对象,加载插件,执行 run 方法开始编译
③ 确定入口:根据 entry 找出所有入口文件
④ 编译模块:从入口出发,调用 loader 编译模块,并对它的依赖进行递归编译,就得到了翻译后的内容和依赖关系。
⑤ 输出资源:根据依赖关系组装成多个 chunk,然后根据配置的路径和文件名写入到文件系统。
 
 

-----------------------------------

65
编写loader或plugin的思路
  • Loader:单一原则,每个 Loader 只做一种工作,拿到源文件处理后返回,也可以通过 callback 返回。
  • Plugin: 监听事件,在合适的时机通过 webpack 提供的 API 改变输出结果。
 
 

-----------------------------------

66
如何用 webpack 优化前端性能
  • 压缩代码:删除多余的代码、注释、简化代码的写法。
  • CDN 加速:将静态资源修改为 CDN 路径
  • 删除死代码:将永远不会走到的代码删除掉。
  • 提取公共代码
 
 

-----------------------------------

67
清除浮动的方法
① 使用额外标签:在下面加一个空 div,然后设置 clear: both
② 使用BFC: 设置 overflow: hidden;
③ 使用 after 伪元素:不需要单独加标签,但是为了支持 IE67,增加一个 *zoom: 1.
 
 

-----------------------------------

68
DOM 事件级别
① 0 级:只能注册一个,如 element.onclick = function () {}
② 1 级:没有
③ 2 级:通用的方法,支持多个,如 element.addEventListener('click', function () {})
④ 3级:是对 2 级的拓展,增加了事件类型,如  keyup
 
 

-----------------------------------

69
event 对象的常见应用
① 阻止默认事件:preventDefault
② 阻止冒泡:stopPropagation
③ 当前绑定事件的元素:currentTarget
④ 当前被点击的元素:target
⑤ 阻止调用相同事件的其他监听器:stopImmediatePropagation
 
 

-----------------------------------

70
事件模型
三种事件模型:
① DOM 0 事件模型:事件不会传播,绑定有两种:html 代码中直接绑定、通过 btn.onclick 绑定,移除设置成 null。
② IE 事件模型:有两个阶段:处理阶段和冒泡阶段,监听用 attachEvent, 移除用 detachEvent。
③ DOM 2 事件模型:捕获阶段、处理阶段、冒泡阶段,通过 addEventListener 和 removeEventListener。
 
 

-----------------------------------

71
如何实现继承?
① 组合继承
直接修改子类的 prototype 为新的父类实例。
Child.prototype = new Parent()
② 寄生组合继承
修改子类的 prototype 为 Object.create 创建一个新 Parent.prototype,然后再修改子类 prototype 的 constructor 为子类本身,避免调用父类的构造函数。
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
 
 

-----------------------------------

72
如何判断 this 指向?
核心:谁调用,this 就是谁
步骤
① 如果是箭头函数,则this就指向包裹第一层它的函数
② 如果是通过 bind / call / apply, 则指向第一个参数
③ 如果是普通函数,直接调用的话,则指向 window,如果是 obj.foo() 调用,则指向 obj,如果是通过 new 实例化,则 this 固定在实例上。
 
 

-----------------------------------

73
递归实现深拷贝
deepClone 函数:
① 如果是正则表达式、日期,通过构造函数返回一个新的。
② 如果是基本数据类型(空或者typeof 不是 object),直接返回。
③ 如果缓存 map 中有,直接返回
④ 通过 obj.constructor() 创建一个新实例 t,并放在 map 中 obj --> t
⑤ 通过 for key in 和 hasOwnProperty 判断拥有这个属性,然后递归调用 deepClone 进行拷贝。
function deepClone(obj, map = new WeakMap()) {
    // 正则表达式、日期,返回一个新建的
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);
      // 基本数据类型(null 或者 typeof 返回的不是 object的)
    if (obj == null || typeof obj != 'object') return obj;
    // map 中存放一个缓存,避免循环引用
    if (map.has(obj)) {
        return map.get(obj);
    }
    // 首先使用 constructor() 新建一个实例
    let t = new obj.constructor();
    // 然后把这个实例加入到缓存中
    map.set(obj, t);
    // for key in 的方式循环迭代这个对象的键
    for (let key in obj) {
        // 如果这个对象有这个属性的话,递归调用本身进行深拷贝
        if (obj.hasOwnProperty(key)) {
            // 传入的是一个子对象,和当前的 map 缓存
            t[key] = deepClone(obj[key], map);
        }
    }
    return t;
}
 

-----------------------------------

74
map / filter / reduce
① map 遍历数组生成数组,三个参数:元素、索引、原数组.
[1, 2, 3].map(v => v + 1) // -> [2, 3, 4]

② filter 和 map 格式一样,功能是返回符合条件的元素数组

③ reduce 接收两个参数,第一个是**含有结果和当前元素的回调函数**,第二个是结果的初始值,功能是将数组转换为一个值。

const arr = [1, 2, 3]
const sum = arr.reduce((res, current) => res + current, 0)
console.log(sum) // 6
 
 

-----------------------------------

75
async / await 特点、优缺点、原理
① 概念一个函数如果加上 async ,那么就会返回一个 Promise,看起来是将函数返回值使用 Promise.resolve() 包装了一下。只能配合 await 使用。
② 优点:解决回调地狱,代码逻辑清晰。
③ 缺点:如果异步代码没有依赖性却使用了 await,会导致性能的降低。如发起多个请求,而这几个请求之间没有联系,就不需要 await
④ 原理
语法糖:async 就是 Generator 的语法糖,将星号替换成 async,将 yield 替换成 await。看起来是将函数返回值使用 Promise.resolve() 包装了一下,而 await 就是内部 then 的语法糖。
原理:函数执行时,一旦遇到await就会返回,异步操作完成,并且调用栈清空,就会再接着执行函数体后面的语句
 
 

-----------------------------------

76
Generator
① 概念:生成器是 ES6 标准引入的,看着像一个函数,function 后面加星号,可以通过 yield 和 return 返回多个值。
② 例子
function* Generator() {
  yield '1'
  yield '2'
  return '3'
}
// 实例化一个 Generator
let generator = Generator()
// 连续调用四次 next() 方法
console.log(generator.next()) // {value: 1, done: false}
console.log(generator.next()) // {value: 2, done: false}
console.log(generator.next()) // {value: 3, done: false}
console.log(generator.next()) // {value: undefiend, done: true}
 
 

-----------------------------------

77
JS 单线程的好处
① 如果 JS 执行的时候 UI 线程还在工作,渲染过程变得不安全。
② 单线程运行能够节约内存、节约上下文切换时间。
③ 单线程的话就不需要考虑各种线程同步、互斥、死锁等问题了。
 
 

-----------------------------------

78
null 和 undefined 的区别
大多数语言只有一个表示空的值,而 Javascript 却有两个,这是为什么呢?
① null 表示没有对象,即此处不应该有值。
② undefined 表示缺少值,此处应该有,但是却没有定义。
 
 

-----------------------------------

79
首屏加载优化
① loading 提示:在 js 执行期间加入用户提示,使用 html-webpack-plugin 插件,可以在文件中插入 loading 图。
② 服务器渲染:服务器直接返回完整的 DOM 结构,使用 prerender-spa-plugin 插件,原理是本地模拟浏览器环境,然后返回预先解析好的 HTML.
③ 开启 HTTP2: HTTP2 采用二进制分帧的方式通信,并支持多路复用、头部压缩、服务器推送。
④ 开启浏览器缓存:强缓存的头部为 Expires / Cache-Control, 协商缓存的头部为 Last-Modified / If-Not-Modified-Since、ETag / If-Non-Match。可以使用 commons-chunk-plugin 对不经常变化的第三方库进行抽取。
⑤ 骨架屏:使用骨架屏,Antd 有默认的 Skeleton
⑥ 动态加载:polifill 动态加载、ES6 动态加载、路由动态加载(React.lazy()、React-Loadable、AsyncComponent 动态组件)
 
 

-----------------------------------

80
如何避免重绘或者重排?
① 通过改变 class 的方式集中改变样式。
② 使用 DocumentFragment 批量操作后插入,只触发一次重排。
③ will-change: transform 提升为合成层。
 
 

-----------------------------------

81
前端如何实现即时通讯?
① 短轮询:每隔一段时间就发出一个请求。兼容性强,延迟高。
② comet:基于 AJAX 的长轮询、基于 iframe 的长连接。
③ SSE: 服务端推送事件,请求不能复用。
④ Websocket: 基于 TCP,双向实时通信,但是使用复杂度高,不兼容低版本浏览器。
 
我自己使用过 MQTT,是基于 Websocket 的。一方面与硬件直接通信,另一方面曾在小程序中实现实时聊天。
 
 

-----------------------------------

82
HTTP2相对于HTTP1.x有什么优势和特点
① 二进制分帧:使用二进制而不是文本传输数据,解析更加高效。
② 头部压缩:只发送差异头部。
 服务器推送:主动推送其他资源,不需要浏览器请求。
④ 多路复用:TCP 连接可以复用。
 
 

-----------------------------------

83
source map
① 概念:代码压缩与转换后,如果报错,很难找到在原文件中的位置,而 source map 能够解决这个问题。source map 是一个存储位置信息的文件,转换后的代码的每一个位置都对应着转换前的位置。
② 启用:最后一行加上一个注释
//@ sourceMappingURL=路径
③ 生成:使用 Google 的 Closure 编译器
 
 

-----------------------------------

84
Virtual DOM
① 概念:本质上是一个 javascript 对象,是一个节点树,最初是 真实 DOM 的副本,React 的渲染函数从组件中创建一个节点树,用户操作引起数据模型变化时,根据这种变化更新树。
② 原理:三步,第一步数据发生变化时,重新渲染 虚拟 DOM。第二步计算前后两个 DOM 之间的差异。第三步使用这种差异对真实 DOM 执行最小化渲染.
③ 优点:在 js 和真实 DOM 中间增加了一个缓存,因此它的更新快、DOM 操作简单、内存消耗少。
 
 

-----------------------------------

85
React 生命周期
三个阶段:初始渲染阶段、更新阶段、卸载阶段
初始化阶段:
  • componentWillMount(): 在渲染之前执行。
  • Render: 渲染组件
  • componentDidMount(): 仅在第一次渲染后执行。
运行阶段
  • componentWillReceiveProps(): 当从父类接收到 props 并且在调用另一个渲染器之前调用。 
  • shouldComponentUpdate(): 根据特定条件返回 true 或 false。如果希望更新组件,请返回true, 否则返回 false。默认情况下,它返回 false。
  • componentWillUpdate(): 在 DOM 中进行渲染之前调用。
  • componentDidUpdate(): 在渲染发生后立即调用。
销毁阶段
  • componentWillUnmount(): 从 DOM 卸载组件后调用。用于清理内存空间。
最新版的生命周期
把三个 will 给废弃了,前面加上了大写的 UNSAVE_
componentWillMount() 、componentWillReceiveProps() 、componentWillUpdate()
 
 

-----------------------------------

86
高阶组件 HOC
① 概念:是重用组件逻辑的高级方法,本质上是一个函数,接收一个组件并返回一个新的组件。
② 方式
函数参数称为 ComponentClass.
属性代理:返回新组件,render 里面渲染 ComponentClass,传递 props。
反向继承:直接返回一个继承 ComponentClass 的新类。
渲染劫持:在高阶组件中可以控制 ComponentClass 的 render 渲染输出,因此可以劫持渲染
 
 

-----------------------------------

87
说一下你对 Redux 的理解
概念
Redux 是 JavaScript 中的一个应用状态管理框架,整个应用的状态存储在单个 Store 的状态树里。
三大原则
① 单一事实来源:整个应用的状态存储在单个 store 的状态树里。
② 状态是只读的:改变状态的唯一方法是去触发一个动作。
③ 使用纯函数进行更改:纯函数指的是输出仅取决于输入的函数,相同的输入导致相同的输出。
核心概念
① Store: 存放数据的地方,只有一个
② State: 包含所有数据
③ Action: View 发起的动作,表示 State 应该发生某种变化
④ Action Creator: 创建 Action 的函数
⑤ Reducer:收到 Action 计算出新 State 的过程。接收 Action 和当前 State,返回一个新的 State。
⑥ dispatch: 是 View 发起 Action 的唯一方法
⑦ Provider: 封装整个应用
⑧ connect:连接 React 和 Redux,将 state 和 action 通过 props 的方式传递到组件中。
工作流程
① 用户在 View 通过 dispatch 发起 Action
② Store 调用 Reducer,计算出新的 State
③ Store 监听 State 变化,更新 View
优点
① 结果的可预测性
② 可维护性,易于测试
③ 服务端渲染,只需要将服务器上创建的 store 传到客户端即可
④ 开发工具、社区生态良好
 
 

-----------------------------------

88
Redux 和 Flux 的不同点
① Redux 状态和更改逻辑是分开的,Flux 状态和更改逻辑是在一块的。
② Redux 只有一个 Store,Flux 有多个Store
③ Redux 状态是不可变的,Flux 状态是可变的
④ Redux 容器组件是有联系的 connect,Flux 是组件订阅 Store。
“Redux 容器组件是有联系的” 指的是 “connect 连接 Provider 及容器组件和UI组件”。
 

-----------------------------------

89
调用 setState 之后发生了什么
① 将传入的参数对象和当前状态合并,触发调和过程,这个调和过程指的是:UI 和内存中的虚拟 DOM 保持同步的过程
② 根据新的状态构建状态树
③ 对比前后状态树的差异,根据差异进行最小化渲染。
调和过程:UI 和内存中的虚拟 DOM 保持同步的过程
 

-----------------------------------

90
React的请求应该放在哪个⽣命周期中?
① 先上结论:应该放在 componentDidMount
② 原因分析:有的观点认为在componentWillMount中可以提前进⾏异步请求,避免⽩屏,其实这个观点是有问题的。React 渲染⼀个组件时,它不会等待componentWillMount,而是会继续 render,而且在服务器渲染时,fetch data会执行两次,同时经过React Filber优化后,可能会执行多次。
 
 

-----------------------------------

91
setState到底是异步还是同步?
① 先上结论:有时候同步,有时候异步。
② setState只在合成事件和钩⼦函数中是“异步”的,在原⽣事件和setTimeout中都是同步的。
③ setState的“异步”并不是说内部由异步代码实现,其实本身执⾏的过程和代码都是同步的,只是合成事件和钩⼦函数的调⽤顺序在更新之前,导致在合成事件和钩⼦函数中没法⽴⻢拿到更新后的值,形成了所谓的“异步”,当然可以通过第⼆个参数setState(partialState, callback)中的callback 拿到更新后的结果。
④ setState的批量更新优化也是建⽴在“异步”(合成事件、钩⼦函数)之上的,在原⽣事件和setTimeout中不会批量更新,在“异步”中如果对同⼀个值进⾏多次setStatesetState的批量更新策略会对其进⾏覆盖,取最后⼀次的执⾏,如果是同时setState多个不同的值,在更新时会合并进行批量更新。
 
 

-----------------------------------

92
React 组件通信如何实现
① 父子通信:props
② 子父通信:props + 回调
③ 兄弟通信:用父节点做中转
④ 跨层级:Context: React.createContext()
⑤ 发布订阅模式:引入 event 模块
⑥ 全局状态管理工具:Redux / Mobx
 
 

-----------------------------------

93
Redux 和 MobX 的区别
① Redux 是单一 Store,mobx 是多个 Store
② Redux 状态不能直接修改,只能使用纯函数返回一个新的状态,mobx 可以直接修改。
③ Redux 是函数式编程的思维,mobx 是面向对象的思维。
④ Redux 调试方便,mobx 调试困难,结果难以预测。
 
 

-----------------------------------

94
Redux 如何进行异步操作
① 直接请求:直接在 componentDidMount中请求,但是难以管理
② 借助异步中间件redux-thunkredux-saga
③ 优缺点
- redux-thunk: 一个请求需要大量代码,耦合严重,很多功能需要自己封装。
- redux-saga:异步操作单独放在 saga.js 中,很多功能可以直接使用。
 
 

-----------------------------------

95
React 有哪些性能优化的手段?
① 使用纯组件
② 使用 React.memo 进行组件记忆
③ 使用 shouleComponentUpdate 选择性渲染子树。
④ 避免 componentWillMount()中的异步请求
⑤ 迭代时候增加唯一 key
⑥ 使用其他基本的前端优化措施。
 
 

-----------------------------------

96
MVC 和 MVVM 的区别
① 更新数据的思想不同:MVC 是 Controller 把 Model 的数据赋值给 View,而 MVVM 是 VM 层和 V 层建立双向数据绑定。
② 可重用性:MVC 中的 V 和 C 连接过于紧密,妨碍了独立重用,MVVM 的 VM 可以很方便重用。
 
 

-----------------------------------

97
MVC
MVC: 是 Model View Controller 的缩写,包含
① Model:模型层,负责在数据库中读写数据
② View:视图层,处理渲染逻辑
③ Controller:控制器,M V之间通信的桥梁,从 View 层读取输入发送到 Model 层,以及将 Model 层的数据写入 View 层。
 
 

-----------------------------------

98
三栏布局,高度已知,左右两栏固定,中间自适应
① absolute 实现:三个都是绝对布局,设置好绝对位置,简单快捷但是脱离文档流。
② float 实现:按照 left right center 的顺序放置,然后 left 左浮动,right 右浮动,兼容性好但是脱离文档流,节点顺序出错。
③ flex 实现:父元素 flex 布局,左右固定宽度,中间 flex: 1
④ table 实现:父元素 display: table-cell,  左右固定宽度,性能差且单元格高度要保持一致。
⑤ grid 实现:父元素 display: grid,然后设置 grid-template-columns: 左宽度 auto 右宽度,但是兼容性差。
如果高度未知,只能用 flex 和table
三列布局
 

-----------------------------------

99
CSS 动画
三种实现方式:
① transition 渐变动画:property 配置需要变化的属性如宽高度,duration 设置过渡时间,timing-function 速度曲线,如 linear, ease, ease-in, ease-out, ease-in-out, 还可以设置贝塞尔曲线。
② transform 转变动画:rotate 旋转,scale 缩放,translate 移动, skew 倾斜。
③ animation 自定义动画:使用 @keyframes 配置不同百分比的值,其他的还有 name 名称,duration 过渡时间,timing-function 速度曲线,delay 延迟,iteration-count 次数,direction 是否轮流反向播放。
例子:当鼠标指针滑过时,图标在1秒内匀速旋转360度
.close:hover::before{
    -webkit-transform:rotate(360deg);
    transform:rotate(360deg);
    -webkit-transition:-webkit-transform 1s linear;
    transition:transform 1s linear;
}
 

-----------------------------------

100
盒模型,相互转化
① 包括
  • content
  • padding
  • margin
  • border
② 标准模型、IE 模型
  • 标准模型:宽高,box-sizing: content-box
  • IE 模型:包括 border,box-sizing: border-box
 
 

-----------------------------------

101
ES6 箭头函数和普通函数的区别
① 语法更加简洁、清晰
② 箭头函数不会创建自己的 this
③ 箭头函数的 this 指向永远不变,call / apply / bind 也无法改变箭头函数 this 的指向
④ 箭头函数不能作为构造函数使用,因为创建对象需要 this,而箭头函数没有自己的 this
⑤ 箭头函数没有 arguments 对象
⑥ 箭头函数没有 prototype
⑦ 箭头函数不能做 Generator 函数,不能使用 yield 关键字
 
 

-----------------------------------

102
computed和watch的区别
① computed 支持缓存,watch 不支持缓存
② computed 不支持异步,watch 支持异步
③ computed 适合一对一、多对一,watch 适合一对多。
 
 

-----------------------------------

103
websocket 原理
传输层的协议
特点
① 握手阶段采用HTTP协议,默认端口是80和443,建立在TCP协议基础之上,和http协议同属于应用层
② 可以发送文本,也可以发送二进制数据。
③ 没有同源限制,客户端可以与任意服务器通信。
④ 协议标识符是ws(如果加密,为wss),如ws://localhost:8023
过程
① 浏览器发送请求头
  • Connection: Upgrade, Upgrade: websocket,告知服务器使用的是 websocket
  • 一个随机数、协议名称、协议版本
② 服务器返回 101 切换协议,告知收到请求,成功建立连接。
③ 开始全双工通信
 
优缺点
优点:推送功能、减少通信量、减少资源消耗
缺点:不支持低版本浏览器、服务器维护长连接需要成本。
 
 

-----------------------------------

104
css3新增了哪些特性
① 边框:border-radius, box-shadow, border-image
② 背景:background-size, backtround-origin, background-clip
③ 文字:text-shadow, text-wrap, word-break, word-wrap
④ 动画:transform, transition, animation/@keyframes
 
 

-----------------------------------

105
圣杯布局
① 两侧宽度固定,中间宽度自适应的三栏布局。
② 实现:
使用 flex 布局实现
父元素 container 设置 flex 布局,left center right 按顺序排列,设置两端固定宽度,center 设置 flex: 1
使用浮动实现
在父元素上设置了padding-left和padding-right,在给左右两边的内容设置position为relative,通过左移和右移来使得左右两边的内容得以很好的展现
 
 

-----------------------------------

106
html5 新特性
① 语义标签:让开发者更加清晰地构建页面布局。
② 增强型表单:input 增加了一些新输入特性,如 color 选择颜色,date 选择日期,tel 输入电话等
③ 音视频标准:audio 和 video 元素
④ canvas 绘图
⑤ SVG 绘图
⑥ 地理定位:getCurrentPosition()
⑦ 拖放:draggable
⑧ Web Woker:加载一个脚本文件,创建一个独立工作的线程
⑨ Web Storage: localStorage 和 sessionStorage
⑩ WebSocket
 
 

-----------------------------------

107
语义化标签,有哪些,为什么要用语义化标签?
① 概念:语义化标签,顾名思义指的就是让标签拥有自己的含义。
② 有哪些:常见的: header, nav, article, section, aside, footer
③ 为什么使用
  1. 代码结构清晰,方便阅读与合作开发
  2. 方便其他设备解析,如屏幕阅读器
  3. 有利于搜索引擎优化
 
 

-----------------------------------

108
闭包的应用场景
  • 给 setTimeout 的函数传递参数
  • 函数防抖与函数节流
  • 函数柯里化
 
 

-----------------------------------

109
函数柯里化 curry
① 概念:柯里化是一种将使用多个参数的一个函数转换成一系列使用较少参数的函数的技术。比方说原来是五个参数,生成柯里化函数后,五个参数可以多次调用,每次调用的参数数量都可以不一样。
② 实现
函数的 length 大于参数长度的话,返回一个新函数,递归调用 curry,将新参数加入到原来的调用中取,否则的话返回函数的调用。
 
let curry = (fn, ...args) => fn.length > args.length ? (...arguments) => curry(fn, ...args, ...arguments) : fn(...args)
 
 

-----------------------------------

110
Promise 的优缺点
优点:实现链式调用,解决回调地狱。
缺点:
① Promise无法取消,一旦新建就会立即执行
② 不设置回调函数,Promise内部抛出的错误无法反应到外部
③ 当处于pending状态时,无法得知目前处于哪一阶段(刚刚开始还是即将完成)
 
 

-----------------------------------

111
React 类组件和函数组件的区别
① 类组件有 this,函数式组件没有 this
② 类组件有生命周期,函数式组件没有生命周期
③ 类组件有状态 state,函数式组件没有状态
④ 函数式组件的性能比类组件高,因为类组件需要实例化,函数式组件直接返回结果。
 
 

-----------------------------------

112
双飞翼布局
在center这个div中再加了一个div来放置内容,在给这个新的div设置margin-left和margin-right。
  • left、center、right三种都设置左浮动
  • 设置center宽度为100%
  • 设置负边距,left设置负边距为100%,right设置负边距为自身宽度
  • 设置content的margin值为左右两个侧栏留出空间,margin值大小为left和right宽度
 
 

-----------------------------------

113
CSS 加载会造成阻塞吗?
① CSS 不会阻塞 DOM 解析,因为CSSOM 和 DOM 是并行构建的;CSS 会阻塞 DOM 渲染,因为渲染树是两者结合的。
② CSS 不会阻塞 JS 加载,因为两者是并行下载的;CSS 会阻塞 JS 执行,因为有时候通过 JS 获取样式,是需要 CSS 加载完成才可以获取。
 
 

-----------------------------------

114
为什么 JS 会阻塞页面加载?
JS 阻塞 DOM 解析,是因为浏览器将 JS 执行和 DOM 解析设置成互斥的关系,因为如果两者可以并行,渲染可能会出现不可预知的结果,因此是 JS 先执行完毕,然后再渲染 DOM。
 
 

-----------------------------------

115
图片懒加载的实现
除了使用 JQuery,也可以使用原生方式实现图片懒加载,思路为:给 img 增加 data-src 属性,当图片出现在可视区域时,将 data-src 赋值给 src。
判断图片出现在可视区域的方法有两种:
① 图片的 offsetTop 小于可见高度 clientHeight 和滚动条距离顶部的高度 scrollTop 之和
② 图片的 getBoundingClientRect().top 小于这个和。
 
 

-----------------------------------

116
PWA
① 概念:PWA,全称是 Progressive Web Application,中文是渐进式网页应用。PWA 是一套理念,通过技术手段逐渐缩小与本地应用的差距。
② 渐进性:从开发上来讲,是普通网站逐步过渡到 Web 应用;从技术上来讲,是逐步提供更好的新特性支持。是非常缓和的,不像以前一样一开始就要取代 APP 、取代小程序。
③ 原理:使用 Service Worker 解决离线存储和消息推送的问题,在页面和网络之间增加一个拦截器,用来缓存和拦截请求。Service Worker 与 Web Worker 一样,都有“运行在主线程之外”这种思想,不同的是 Service Worker 增加了缓存,以及不和单个页面绑定在一块。
 
 

-----------------------------------

117
useEffect 第二个参数中,数组如果写一个对象,这个对象的属性发生变化,会执行吗?空和空数组有什么区别?
① 如果数组元素为一个对象,对象内容发生变化,不会触发更新,因为 useEffect 里面比较的是地址。
② 如果传递空,会一直调用。如果传递空数组,只会执行一次。
 
 

-----------------------------------

118
React Fiber 的原理和机制
① 为什么需要 Fiber
虽然 React 的 diff 算法是经过优化的,但是它是同步的,如果有大量的节点需要更新, JS 线程的执行时间就会很长,而 JS 线程和 GUI 线程又是互斥的,用户会感到卡顿。Fiber 可以将长时间的同步任务拆分成多个小任务,让浏览器抽身去响应其他事件,等有空了再回来计算,整个渲染过程就会平滑很多。
② 底层的数据结构
之前的数据结构是一棵树,父节点的 children 指向了所有的子节点,Fiber 改成了三个指针:指向第一个子元素的 child,指向下一个兄弟元素 sibling,指向父元素的 return。
这样改造后,就成为了一个链表。
③ 优化的阶段
react 渲染可以分为 reconcile 调度阶段和 commit 渲染阶段。reconcile 阶段是一个从顶向下的递归算法,比较前后两颗树的差异。commit 阶段使用这种差异对真实 DOM 做最小化渲染。fiber 是对 reconciler 调度阶段进行分割,原因是调度阶段渐进地生成 Fiber 树,是可以被打断的,而渲染阶段不能被打断。
④ 工作流程
1. 在 ReactDOM.render() 和 setState 的时候开始创建更新
2. 将创建的更新加入任务队列,等待调度
3. 在 requestIdleCallback 空闲时执行任务
4. 从根节点开始遍历 Fiber Node, 并且构建 WorkInProgress Tree,
5. 生成 effectList
6. 根据 effectList 更新 DOM
 
 

-----------------------------------

119
React 核心流程有哪两个阶段?分别包含哪些生命周期?
reconcile 调度阶段:
废弃的三个 will:
① componentWillMount
② componentWillReceiveProps
③ componentWillUpdate
还有两个:
① shouldComponentUpdate
② getDerivedStateFromProps
commit 渲染阶段:
① componentDidMount
② componentDidUpdate
③ componentWillUnmount
④ getSnapshotBeforeUpdate
 
 

-----------------------------------

120
react 生命周期的废弃,为什么废弃,废弃了哪些,新的生命周期有哪些,怎么替换?
① 废弃的原因:在 Fiber 架构中,调和过程会多次执行 will,因此失去了原有的意义,多次执行的话还会影响性能。
② 废弃的周期:三个 will:componentWillMount、componentWillReceiveProps、componentWillUpdate
③ 新的周期:getDerivedStateFromProps 在 render 之前调用,替换 componentWillReceiveProps,getSnapshotBeforeUpdate 在 render 之后调用,在更新之前从 DOM 中获取一些信息,实现类似 willMount 的效果。
 
 

-----------------------------------

121
React Fiber 任务分割,使用的是哪两个 API?
① requestIdleCallback
② requestAnimationFrame
 
 

-----------------------------------

122
requestAnimationFrame
① 核心思路:是让系统来决定回调函数的执行时机,保证回调函数在屏幕每一次的刷新间隔中只执行一次,在 60 Hz 的刷新率下,每一帧是 16.7 ms
② 调用格式:直接将回调函数作为 rAF 的参数。
③ 优势
  • CPU 节能,setTimeout 会在后台执行,而rAF 页面未激活的时候会暂停,节省 CPU 开销。
  • 函数节流:可以使用 rAF 实现函数节流,将函数作为 rAF 的参数。
 
 

-----------------------------------

123
requestIdleCallback
① 概念:requestIdleCallback 的作用是在浏览器一帧的剩余空闲时间执行优先级相对较低的任务。
② 缺点:内置的 requestIdleCallback FPS 只有 20 Hz,因此 React 自己进行实现,非 DOM 环境下可以使用 setTimeout 模拟,DOM 环境下则配合 requestAnimationFrame 实现一个 requestHostCallback,也就是 requestIdleCallback。
 
 

-----------------------------------

124
如何解析 jsx?
首先使用 babel 编译转化为 React.createElement 的对象形式,然后通过 ReactDOM.render 渲染 DOM 元素。
 
 

-----------------------------------

125
Fiber 的单链表是如何 Diff 得到 change 的呢?
① 双缓冲技术:使用另一颗树 WorkInProgress Tree, 代表要刷新到屏幕的未来状态。
② 构造完毕之后,得到的就是新的 Fiber Tree, 然后喜新厌旧将 current 指针指向 WorkInProgress Tree ,丢掉旧的 Fiber Tree.
 
 

-----------------------------------

126
flex 布局可以设置哪些属性?
flex-direction, flex-wrap, flex-flow, justify-content, align-items, align-center
  • flex-direction 主轴方向:row | row-reverse | column | column-reverse
  • flex-wrap 是否换行:nowrap | wrap | wrap-reverse
  • flex-flow : direction 和 wrap 的简写形式
  • justify-content: 在主轴上的对齐方式: flex-start | flex-end | center | space-between | space-around
  • align-items: 在交叉轴上如何对齐: flex-start | flex-end | center | stretch | baseline
  • align-center 多根轴线的对齐方式,
 
 

-----------------------------------

127
css 的单位
绝对长度
cm, mm, in, pt, pc, px
相对长度
em: 相对于当前元素的尺寸
%: 百分比
rem: 相对于根元素的尺寸
vw: 视图宽度
vh: 视图高度
vmin: vw, vh 较小的那个
vmax: vw, vh 较大的那个
 
 

-----------------------------------

128
层叠上下文
① 概念:是 HTML 元素的三维概念,在一条假想的 z 轴上延伸。
② 创建:默认创建的根元素、需要配合 z-index 的 relative / absolute / fixed, 不需要配合 z-index 的 (opacity 小于 1、transform 不为 none, will-change )
③ 顺序负 z-index --> block --> float --> inline/inline-block --> z-index: auto/0 --> 正 index
 
 

-----------------------------------

129
css 布局
① 浮动布局:float
② 位置布局:position: relative、absolute、fixed、table、grid、flex。
 
 

-----------------------------------

130
CSS 预处理、后处理
预处理:为 css 增加一些编程特性,将 css 作为目标生成文件,有 scss, less, stylus
后处理:对 css 处理,生成 css,如自动处理兼容性问题的 autoprefixer.
 
 

-----------------------------------

131
display 有哪些取值?
  • none 不显示
  • inline 行内元素
  • list-item 列表元素,如 li
  • inherit 继承
  • block / inline-block 块级元素、行内块元素,可以设置宽高度
  • table / inline-table 块级表格
  • flex / inline-flex 弹性布局
 
 

-----------------------------------

132
相邻的两个inline-block节点为什么会出现间隔
① 原因:代码中的回车、换行会被当成文档节点处理,间隔就是空的文档节点导致的。
② 解决
  • 去掉这个文档节点
  • font-size: 父元素 font-size: 0, 子元素设置自己的 font-size
  • margin 负值
  • 父元素 display: table, word-spacing: 0
 
 

-----------------------------------

133
meta viewport 移动端适配有哪些配置项
  • width : 设置viewport 的宽度
  • height: 设置viewport 的高度
  • initial-scale : 设置页面的初始缩放值
  • minimum-scale :允许用户的最小缩放值
  • maximum-scale:允许用户的最大缩放值
  • user-scalable: 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
 
 

-----------------------------------

134
CSS实现宽度自适应100%,宽高16:9的比例的矩形
① 计算高度,宽度 100%,高度为 56.25%
② 设置 width: 100%; height: 0px; padding-bottom: 56.25%;
③ 内容使用 position: absolute.
 
 

-----------------------------------

135
rem布局的优缺点
① 优点:能实现自适应
② 缺点:不支持 IE,相对于 html 根元素判断大小,屏幕变化的时候不是很好控制。
 
 

-----------------------------------

136
如何用一个 div 模拟 textarea
给 div 添加 contenteditable=true
 
 

-----------------------------------

137
移动设备忽略将页面中的数字识别为电话号码的方法
 
 

-----------------------------------

138
实现继承有哪几种方式?
① 构造函数继承
直接 Parent.call(this)
缺点:prototype 上的方法无法继承
② 原型链继承
设置 Child.prototype = new Parent()
缺点:丢失了 constructor、原型链上引用类型的数据会被所有实例共享。
③ 组合继承
同时使用了 Parent.call(this)Child.prototype = new Parent(),并恢复了 constructor: Child.prototype.constructor = Child
缺点:调用了两次 Parent 的构造函数。
④ 寄生组合继承
使用 Object.create(Parent.prototype) 实现原型链的浅拷贝。
暂无缺点。
 
 

-----------------------------------

139
arguments
arguments 是一个对应于参数的类数组对象。除了 length 和索引访问外没有任何 Array 的属性,可以通过 Array.prototype.slice.call(arguments) 转化为标准的数组。
 
 

-----------------------------------

140
JS 作用域和作用域链
作用域:全局作用域、函数作用域
作用域链:在当前作用域没有查到值,就会向上级作用域查,直到全局作用域,这个查找的过程称为作用域链。
 
 

-----------------------------------

141
图片懒加载、预加载
懒加载:到达可视区域将 data-src 赋值给 src
预加载:JS 中通过一个 Image 对象事先设置 src 属性,就会触发预加载,真正使用的时候设置 img 标签的 src,就会直接从缓存中取。
 
 

-----------------------------------

142
网页加载进度条实现
① 使用定时器定时固定的时间,做一个模拟的进度条。
② document.onreadystatechange 监控加载状态,四种状态:未初始化、加载中、可交互、加载完成。
③ 给重要节点增加 JS 代码,改变进度条显示。
④ 实时监控加载情况,显示加载百分比,如很多个图片,给图片注册 onload 事件,每一个加载完成都更新加载进度。
 
 

-----------------------------------

143
如何先冒泡再捕获
根据w3c标准,应先捕获再冒泡。若要实现先冒泡后捕获,给一个元素绑定两个addEventListener,其中一个第三个参数设置为false(即冒泡),另一个第三个参数设置为true(即捕获),调整它们的代码顺序,将设置为false的监听事件放在设置为true的监听事件前面即可。
 
 

-----------------------------------

144
window的onload事件和domcontentloaded
onload:所有的都加载完成了。
DOMContentLoaded: 仅 DOM 加载完成
 
 

-----------------------------------

145
for in / for of / forEach / for 的区别?
for in 和 for of 的区别
  • for ... in : 迭代的是 key
  • for ... of : 迭代的是 value
forEach 和 for 的区别
  • forEach 中的一个参数,每次都是创建的一个新变量
  • forEach 不能 break、continue
  • forEach 一次只能循环一个数组
 
 

-----------------------------------

146
立即执行函数和使用场景
概念
顾名思义,就是函数定义后立即执行,声明并立即执行一个匿名函数。
作用
① 不需要声明,避免污染全局变量。
② 形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
③ 封装临时变量,如 for var 循环,使用立即执行函数能避免一直输出相同的值。
 
 

-----------------------------------

147
数组的常见方法
不改变数组的方法
名称 参数 功能
concat 一个或多个数组 返回连接的结果
slice [开始位置,结束位置) 返回截取的结果
join 字符串 返回用字符串连接数组元素的结果
toString 返回数组内容,逗号隔开
改变数组的方法
名称 参数 功能
reverse 反序
shift 删除并返回第一个元素的值
pop 删除并返回最后一个元素
unshift 元素 在首位加入元素
push 元素 在尾部加入元素
sort 无或者一个回调函数 排序,默认字典序升序,可以通过回调更改排序行为
splice 开始位置,元素数目 从开始位置删除并返回元素
ES6 中的方法
  • forEach, map, filter,reduce, reduceRight
  • every 是否所有的都满足条件
  • some 是否有一个满足条件
  • indexOf, lastIndexOf
 
 

-----------------------------------

148
css-loader 原理
css 转换成 js,被其他 js 引用,同时导出转换前后的类名对应关系。
 
 

-----------------------------------

149
浏览器内核
IE 浏览器的 Trident, 火狐浏览器的 Gecko,Safari 的 Webkit, 谷歌浏览器的 Chromium/Blink
 
 

-----------------------------------

150
模块化:commonJS,es6,cmd,amd
① commonJS: API 是 require, module.exports 或 exports, 模块输出的是一个值的拷贝,运行时同步加载。
② AMD: require.js 实现。中文是异步模块定义,API 是 define, require, 异步加载,但是不能按需加载。
③ CMD: sea.js 实现,能够按需加载。
④ es6: API 为 import /export,输出的是值的引用。
 
 

-----------------------------------

151
小程序和传统的 web 有什么区别呢?
① 大小有限制
② 不能直接操作 DOM
③ 样式不能引入本地资源
④ 提供了丰富的额外功能。
 
 

-----------------------------------

152
小程序的架构?小程序为什么要有很多限制?怎么设计的,怎么实现的?
① 小程序框架分为逻辑层和视图层两部分,逻辑层并非运行在浏览器中,因此 JS 的一些能力无法使用。
② 实现:所有代码打包成一份 JS 文件,IOS 中运行在 JavaScriptCore 上,Android 中运行在 V8 中,开发工具中运行在 NW.JS 中。
 
架构描述:
微信小程序主要分为 逻辑层 和 视图层,以及在他们之下的原生部分。逻辑层主要负责 JS 运行,视图层主要负责页面的渲染,它们之间主要通过 Event 和 Data 进行通信,同时通过 JSBridge 调用原生的 API。这也是以微信小程序为首的大多数小程序的架构
 
 

-----------------------------------

153
能直接用 React 开发小程序吗?它是怎么做的?
Taro, 通过编译的方式,将 jsx 转换成小程序的格式。
 
 

-----------------------------------

154
通过编译运行小程序的方案,有什么缺陷?
JS 是一个动态语言,使用静态的方式去分析是非常复杂的,稍微有点改动,编译可能就会失败。
 
 

-----------------------------------

155
除了编译的方案,还有别的方案在小程序中使用 react 吗?
采用动态模板的方式。如 Remax,是一个通过 react-reconciler 实现的小程序端的渲染器,引入一层 VNode。
 
https://remaxjs.org/guide/implementation-notes
 
① React 在 reconciliation 过程中不是直接去改变 DOM,而先更新 VNode。React 更新完成之后,将组件转换成 VNode 对象。
② 事先构建了一个 页面模板,然后将整个节点数递归遍历出来。
 
 

-----------------------------------

156
antd 大的组件包怎么做按需的构建?实现的原理是怎样的?
babel-plugin-import
将 import { Button} from 'antd' 替换成具体的路径,antd/lib/xxx
 
 

-----------------------------------

157
Redux 最后一步,为什么 Store 变化 View 就变化?具体是怎么变化?Provider 挂了一个 Store,Store 变化了,下面的组件怎么知道自己需要变化了?
① 实现的关键是 Context
② Provider:将 store 通过 Context 传给后代组件,注册对 store 的监听
③ connect:一旦 store 变化就会执行 mapStateToProps 和 mapDispatchToProps 获取最新的 props 后,将其传给子组件。
 
 

-----------------------------------

158
如果说让你做一个很大型的项目,有很多模块,出现样式冲突的问题,有什么解决思路?
CSS Module
 
 

-----------------------------------

159
CSS Module 的原理是怎样的?
① 将类名编译成一个哈希字符串
② css 里面也进行替换
 
 

-----------------------------------

160
react 服务器渲染怎么做的?服务端渲染
怎么做的
核心 api 是 renderToString
① 启动一个 node 服务
② 把 react 根组件用 reanderToString 渲染成字符串返回给前端
③ 前端再渲染一次:恢复生命周期、给组件绑定事件以响应用户操作
 
好处是什么
① SEO 收录更好
② 减少白屏
③ 支持更多后端代码模板的支持
 
 

-----------------------------------

161
如果我想做一个从 A 页面跳转到 B 页面,打一次点,怎么实现?更简单一些,打开 A 页面的时候要直接 alert 一下,怎么实现?进入每一个页面的时候,发一次请求。
① 给 history 对象添加自定义监听事件,接收参数为 location,里面有 pathName
② 给 layout 组件使用 withRoute 变成路由组件,然后在 componentWillReceiveProps 中处理。
 
 

-----------------------------------

162
如果设计一个比较完整成熟的爬虫方案,你大题的思路是怎样的?
引擎、调度器、下载器、爬虫、管道、中间件
 
 

-----------------------------------

163
你觉得小程序这个知识体系有什么缺陷吗?
无法形成一个统一的标准
大小限制
页面栈深度有限制
框架不稳定
社区不开源
 
 

-----------------------------------

164
React Native 大致的原理
① JavaScriptCore 负责执行 JS 代码
② React 用来管理虚拟 DOM
③ Bridges 翻译 React 指令绘制原生组件,同时将原生组件的事件反馈给 React
 
 

-----------------------------------

165
React Native 有什么弊端
平台没有完全统一、升级版本麻烦、并不能减少人力成本、不够稳定、性能赶不上原生
 
 

-----------------------------------

166
微前端是怎样实现的
概念
这是一个普适的物理定律,熵增定律:任何物体在没有接受外界能量的条件下,总是朝着熵增(无序)的方向变化。而表现在前端上面就是,在分离了前后端之后,业务拓展,应用变大,需要拆分成更小的单位。
多种方式
① 通过反向代理分派到对应的应用上。
② 通过路由管理器和应用管理器动态加载子应用。劫持 window.history, 如果是不同的子应用,卸载之前的 bundle,然后加载新的 bundle。
难点关键点
① 多 Bundle 集成:服务端集成、构建时集成、运行时继承(iframe、前端路由等)
② 影响隔离:样式隔离(CSS 预处理 SASS, 模块定义 CSS Module,  CSS in JS)、作用域隔离(ES6, AMD, CMD, CommonJS)
③ 资源复用:基础资源、UI 组件、业务组件
④ 应用间通信:比如使用 iframe 的话,可以用 window.postMessage。其他的话可以在全局派发消息给各个 app。或者使用发布订阅模式 + 单例模式,父工程生成实例传递给子工程。
⑤ 测试:单元测试、功能测试、集成测试。
https://zhuanlan.zhihu.com/p/96464401
 

-----------------------------------

167
前端发展趋势
前端后端化、前端工程化、跨端开发
① 前端后端化:前端全栈化、Serverless
② 前端工程化从项目初始化,到编写代码,到测试发布,会形成一个完整的闭环体系。
③ 跨端开发:小程序技术继续升级、RN、Flutter、SwiftUI 也迅速发展。
 
 

-----------------------------------

168
CSS 组合选择器的优先级
计算 ID 选择器的个数为 a,类选择器、伪类选择器、属性选择器的个数为 b,标签选择器和伪元素选择器的个数为 c,按照 a、b、c 的顺序比较大小,如果相等则使用就近原则。
 
 

-----------------------------------

169
react-router路由有哪些形式?(hash, history)区别是什么?history模式是用的浏览器的哪个对象?有哪些常用方法?
  • HashRouter: 带有 #,通过 hash 值对路由进行控制
  • BrowserRouter: 不带 #,传统的斜杠分割,原理是使用 history 对象,方法有 pushState, replaceState, popState. 
 
 

-----------------------------------

170
redux是怎么做全局数据管理的?组件通过什么api拿到数据?
redux 原理
Provider 通过 Context,将数据直接传递子孙组件。
api
mapStateToProps、mapDispatchToProps
 
 

-----------------------------------

171
context了解吗?
① 概念:通过 context ,直接进行跨层级的组件之间的通信。
② 使用:
生产者 - 消费者设计模式
 const { Provider, Consumer } = React.createContext(defaultvalue)
通过 createContext 获得一个 Provider 和 Consumer,Provider 提供数据,在最外层包裹。Consumer 在 Privider 里面嵌套,通过回调的方式获取数据。
23种设计模式 
Abstract Factory(抽象工厂):提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们的具体的类。
Adapter(适配器):将一个类的接口转换成调用者希望的另外一个接口。适配器模式是的原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
Bridge(桥接):将抽象部分与他的实现部分分离,使他们都可以独立的变化。比如在实体类里面使用聚合,该聚合的属性为抽象的,可以动态改变其真正的实现类,联想spring ioc 的注入。
Builder(建造者):将一个负责对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式比工厂模式多了一个指挥者的角色,比如某商品是需要由不同工厂加工而成的零件拼装而成,在指挥者角色中指挥由不同工厂创建的零件的拼装过程。
Chain of Responsibility(责任链):为解除请求的发送者和接受者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着该链传递请求,直到有一个对象处理他。是消除多个if-else的好方法。
Commond(命令):将一个请求封装成一个对象,该对象里面封装了真正动手的服务类,从而使你可用不同的请求对具体服务进行参数化;对请求排队或记录请求日志,以及支持可消除的操作。
Composite(组件):将对象组合成性结构以表示“部分-整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。重点在于形成一个树形结构。
Decorator(装饰者):动态给一个对象添加一些额外的职责。就拓展功能而言,Decorator模式比生成子类方式更加灵活。可以联想到以前做swing的时候很多时候用到了装饰者模式动态增加功能。
Facade(外观):为子系统中的一组接口提供一个一致的对外操作方法。Facade模式定义了一个高层接口。这个接口使得这一子系统更加容易使用。可以联想到当我们拿到一些开源项目的时候,我们使用了一个类对一系列的操作进行封装,这其实就是外观模式。
Factory Method(工厂):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method 使一个类的实例化延迟到其子类。联想到将原料放入类返回产品(对象)。
Flyweight(蝇量):运用共享技术有效的支持大量细粒度的对象。比如可以使用一个List对一些相同的对象进行封装,然后通过定义好的一系列接口对所有对象进行操作。
Interpreter(解释器):给定一个语言,定义他的文法的一种表示,并定义一个解释器,该接解释器使用该表示来解释语言中的句子。比较少用到,可以联想到一些语言的解析。
Iterator(迭代器):提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该兑现的内部表示。联想到List的迭代器,就是封装了内部的数据结构,其实内部的结构是一个一维数组,Object[]。
Mediator(中介者):提供一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。把*型交互转换为以中介者为核心的中心者交互。
Memento(备忘录):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象回复到保存的状态。一般使用数据库做持久化处理,还未联想到该模式使用用途。
Observer(观察者):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖他的对象都得到通知并自动更新。原理是在被观察者中增加List**存储观察者**,每次被观察者又什么动静时,调用方法,统一调用List里面的观察者的被通知方法。jdk有提供相应api
Prototype(原型):用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。java使用.clone()方法进行对象的克隆。使用该方法时,是通过直接复制二进制数据来创建一个复制的对象,效果比用new 反射快的多。
Proxy(代理模式):为其他对象提供一个代理以控制对这个对象的访问。代理与真正的实体实现同一接口,代理依赖实体类。当用户调用代理类的方法时,可以在上面做一些处理,在调用真正的实体。java提供动态代理api,以前使用动态代理做日志记录,屏蔽,防护等。但是后来spring aop 功能出现,代替了动态代理功能。
Singleton(单例):保证一个类仅有一个实例,并提供一个访问他的全局访问点。全局只有一个对象,有懒加载模式,饿加载模式,还有使用静态内部类加载模式。
State(状态):允许一个对象在其内部状态改变时改变他的行为。对象看起来似乎修改了它的所属的类。实体对象依赖状态,把所有操作都分别封装在具体的状态中,让状态对象帮我干活。
Strategy(策略):定义一系列算法,把他们一个个封装起来,并且使他们可互相替换。本模式使算法可变化可独立于使用他的用户。同样也是使用组合模式,但是状态是封装了实体类的所有操作,而策略只是改变了他具体行事的某一个或几个方法。策略的意图在于算法的封装与动态改变。
Template Method(模版):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。联想,大牛定义父抽象类,把高难度算法写好封装在父抽象类中并定义好算法执行步骤,又菜鸟基础该抽象类,实现那些无聊的逻辑方法。
Visitor(访问者):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。通过别人去访问自己的方法。
https://www.runoob.com/design-pattern/design-pattern-tutorial.html

-----------------------------------

172
异步请求用的多吗
① 回调函数
② Promise
③ async/await
④ Generator/yield
 
 

-----------------------------------

173
Promise 看过实现吗?大概说说?哪些属性?哪些方法?
实现:
① 成员变量:当前值、状态、两个异步任务队列
② 构造函数:一开始就要执行 fn
③ 方法
  • resolve: 如果状态为 pending,则设置值,设置状态为完成,成功队列回调当前值
  • reject: 如果状态为 pending,则设置值,设置状态为失败,失败队列回调当前值
  • then: 如果状态为 pending,将传入参数分别 push 到两个队列;如果状态为完成,第一个参数去处理;如果状态为失败,第二个参数去处理。
方法:
  • then / catch / resolve / reject
  • all
  • race
 
 

-----------------------------------

174
axios用过吗?哪些方法?
  • get: 第一个参数为地址,第二个参数为携带的参数
  • post:第一个参数为地址,第二个参数为 body
  • all:多个请求并发,先 all, 然后.then 中用 spread 确保两个请求都成功
  • 一开始还可以进行配置、创建实例、添加拦截器 interceptors
 
 

-----------------------------------

175
常用DOM操作API
节点查找
  • getElementById 根据 ID 获取元素
  • getElementsByClassName 根据 CLASS 获取元素
  • getElementsByTagName 根据标签获取元素
  • getElementsByName 根据 NAME 获取元素
节点创建
  • createElement 创建元素
  • createTextNode 创建文本节点
  • createDocumentFragement 创建文档碎片,大量操作 DOM时可以减少重排
节点修改
  • appendChild 添加子节点
  • insertBefore 在某节点前添加节点
  • insertAdjacentHTML: 在四个位置添加 HTML 代码
  • removeChild 删除子节点
元素属性
  • setAttribute: 设置属性
  • getAttribute: 获取属性
  • hasAttribute; 是否含有属性
 
 

-----------------------------------

176
有用过前端路由框架吗?vue router或者react router?可以讲讲原理吗?比如页面不刷新的时候切换路由。
原理
① 核心原理:根据不同的路由,去渲染不同的组件。
② 实现:点击按钮后,首先设置window.location.hash = 'xxx', 然后返回不同的组件。这是 hash 路由的切换方式,可以不刷新切换路由。另一种是 window.location.pathname = 'xxx',但是它会刷新页面。替代方式是 window.history.pushState 第三个参数传递 pathname(数据、标题、路径)
不刷新的时候切换路由
  • window.location.hash
  • window.history.pushState()
 
 

-----------------------------------

177
css-loader是做什么的?假如写了一段css,最终是如何在浏览器上渲染的?
① css-loader 的作用:将 css 加载到 js 代码中,但是并不会使用。
② 需要使用 style-loader,将 css-loader 解析后的内容挂载在 html 页面中。
 
 

-----------------------------------

178
写过webpack插件吗
只尝试过 HelloWorld,比方说实现在打包完成后,显示一行 Hello World 消息
① 创建一个 HelloWorldPlugin 函数
② 设置 HelloWorldPlugin.prototype.apply 为一个 function,携带 compile 参数,然后再 compile.plugin('done', 回调函数),在回调函数中写逻辑
③ 将插件加入到配置文件中的 plugins 中
 
 

-----------------------------------

179
tree shaking
tree shaking
tree shaking 直接找出使用的代码,扫描所有 export,找到被 import 的内容添加到最终代码中,而不是传统的先添加再剔除。
 
 

-----------------------------------

180
模块化进化史
① 全局 function 模式:函数直接挂在 window 上,很容易冲突。
② namespace 模式:将函数挂在一个对象上,但是属性不是私有的。
③ 立即执行函数:属性私有,只能通过暴露的方法修改。
④ commonJS: 通过 module.export 和 require,对外暴露一个拷贝。
⑤ ES6: import / export,是一个引用
⑥ 其他的还有:AMD, CMD
  • AMD: require.js 实现。中文是异步模块定义,API 是 define, require, 异步加载,但是不能按需加载。
  • CMD: sea.js 实现,能够按需加载。
 
 

-----------------------------------

181
hook介绍
hook 允许在纯函数中使用 React 的各种能力,包括:
① useState: 使用状态。
② useEffect: 传入空数组,替代 didMount。传入依赖,依赖发生变化时就会执行。
③ useRef: 使用实例对象
④ useContext: 使用跨层级传递数据的 Context
 
 

-----------------------------------

182
Vue生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
 
 

-----------------------------------

183
Vue双向绑定原理, proxy, defineProperty
① 2.0 通过 Object.defineProperty 数据劫持实现,弊端是需要克隆一份对象,需要给每一个属性都设置监听
② 3.0 通过 ES6 的 Proxy 委托代理实现,不需要克隆,而且只需要对整个对象进行代理。
 
 

-----------------------------------

184
auth协议
OAuth
OAuth 简单理解就是一种授权机制,它是在客户端和资源所有者之间的授权层,用来分离两种不同的角色。在资源所有者同意并向客户端颁发令牌后,客户端携带令牌可以访问资源所有者的资源。
token 拥有权限范围,有时效性的,到期自动失效,而且无效修改。
四种方式
① 授权码:如小程序中 wx.login 获取 code,然后 code + secret 换取 accesss_token
② 隐藏式:没有后端,跳过授权码,直接返回令牌
③ 密码:用户名 + 密码换取令牌。
④ 凭证:app_id + app_secret 换取 access_token
 
 

-----------------------------------

185
let const var区别
① 全局变量:var声明的变量会挂载在window上,而let和const声明的变量不会:
② 变量提升:var声明变量存在变量提升,let和const不存在变量提升
③ 作用域:var 是函数作用域,let和const是块级作用域
④ 重新定义:同一作用域下let和const不能声明同名变量,而var可以
 
 

-----------------------------------

186
前端路由实现
① location.hash:当 url 的 hash 值发生变化时,会触发 onhashchange 事件,通过 window.location.hash 可以获取到,然后根据不同的 hash 加载相应的组件。
② history.pushState(数据,标题,路径):通过 history.pushState() 方法设置路由地址,通过 onpopstate 事件监听修改,加载相应的组件。
 
 

-----------------------------------

187
长连接和多路复用的区别
长连接是逐个复用同一个连接,多路复用是同时复用同一个连接。
好比面试,长连接是您辛苦的面完一个又一个,而多路复用是您对我们进行群体面试。
 
 

-----------------------------------

188
css的position有哪些属性值?分别是相对于谁定位的?
① static 没有定位,元素出现在正常的流中。
② relative 相对定位,相对于原来的位置定位。
③ absolute 绝对定位,相对于最近一级不是 static 的父元素定位。
④ fixed 固定定位,相对于窗口或 frame 定位。
⑤ sticky 粘性定位,在指定的位置进行粘性操作。
 
 

-----------------------------------

189
git rebase了解吗
将本地的多次提交合并为一个,以简化提交历史,扁平化提交。
 
 

-----------------------------------

190
git commit -m提交后,message写错了想修改怎么办
git commit --amend
 
 

-----------------------------------

191
为什么url变化页面不会刷新
pushState 只是改变浏览器的历史记录,以及地址栏的显示,但并不会导航至任何页面。
本来就不会刷新,因果关系是,页面发生变化,然后浏览器设置地址栏那个控件的值为新的 url,而不是反过来。
 
 

-----------------------------------

192
使用history模式需要服务器端怎么做配合
如果 URL 匹配不到任何静态资源,则应该返回 index.html 页面
 
 

-----------------------------------

193
ref、useRef
①  ref、useRef 都能获取到一个组件的引用。
② useRef 还能用来跨渲染周期保存数据。
ref 的常规用法 
① ref = 一个回调函数,在回调函数中给变量赋值,记录了当前组件。
② ref = 一个字符串,则可以在 this.refs 中以键的方式访问
同理,useRef 也有这样的常规用法,一般是:
首先通过 const couterRef = useRef(); 创建一个 couterRef, 然后给 Counter 组件的 ref 等于这个 counterRef:
```javascript
const couterRef = useRef()
...

...
```
然后就能够通过 counterRef.current 访问这个 DOM 对象。
 
useRef 的第二个用法:跨渲染周期保存数据
```javascript
// 首选创建一个 ref
const timerID = useRef()

// 初始的时候,给 timerID.current 设置一个定时器
useEffect(() => {
  timerID.current = setInterval(()=>{
    setCount(count => count + 1);
  }, 1000);
}, []);

//
```
 

-----------------------------------

194
Object.is() 用来做什么?
比较基本类型,值相同。比较引用类型,引用相同。
 
 

-----------------------------------

195
为什么使用 React.Children.map(props.children, () => {}), 而不是直接 props.children.map(() => {})?
因为不能保证 props.children是一个数组,单个的话就是一个对象,多个才是数组,而React.Chilredn.map可以自动处理这两种情况。
 
 

-----------------------------------

196
描述事件在 React 中的处理方式
① 事件处理程序通过合成事件 (SyntheticEvent) 的实例传递,SyntheticEvent 是浏览器原生事件跨浏览器的封装。SyntheticEvent 是池化的,可以通过 e.persist() 对事件进行保留。但是在 React 17 中,移除了事件池,不需要主动保留了。
② React 没有将事件附加在子节点本身,而是通过事件委托模式代理。
 
 

-----------------------------------

197
ES5 / ES6 的 React 有什么区别?
① 组件定义:ES5 使用 React.createClass, ES6 是继承 Component 的一个 class。
② 默认 props: ES5 使用 getDefaultProps, ES6 使用 defaultProps
③ 初始 state: ES5 使用 getInitialState, ES6 使用 this.state = xxx
 
 

-----------------------------------

198
Object.assign() 的作用
① 用法:
 Object.assign(target, ...sources)
② 作用:将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象。
 
 

-----------------------------------

199
Reducer 文件里,对于要返回的结果,要注意什么问题?
需要使用 Object.assign({}, state, ...newState)浅拷贝一份。
 
 

-----------------------------------

200
Vue 和 React 的区别
① 编写方式:Vue 使用 HTML 模板文件,React 完全使用 JS 创建 DOM
② 数据绑定:Vue 是双向绑定,React 是单向流动。
③ 状态:React 中 state 是不能直接改变的,需要使用 setState,而 Vue 中 state 不是必须的,数据主要是在 data 属性里,能直接修改。
 
 

-----------------------------------

201
副作用是什么?
函数式编程将那些与数据计算无关的操作,都称为副作用 side effect。如生成日志、存储数据、改变状态等。
 
 

-----------------------------------

202
HTTP 管线化
管线化后,请求和响应不再是交替的顺序。可以一次性发送多个请求,一次性接收多个响应。只支持 GET 与 HEAD。
 
 

-----------------------------------

203
HTTP 3
让 HTTP 跑在 QUIC 上,而不是 TCP 上,QUIC 的特点有以下几个方面:
① 基于 UDP, 实现快速握手。
② 在 UDP 的基础上,实现了类似 TCP 的超时重传、流量控制、拥塞控制等可靠性措施。
③ 集成了 TLS1.3,减少了握手时间。
④ 多路复用,同一条物理连接上可以有多个独立的逻辑数据流,彻底解决 TCP 中队头阻塞的问题。
 
 

-----------------------------------

204
React.lazy() 用法
用途是动态调用 import(), 封装的是组件的导入行为,需要配合 Suspence 使用,Suspence 有一个 fallback 属性,用来在异步加载的时候,显示一个 Loading
 
 

-----------------------------------

205
PureComponent 的使用、纯组件与普通组件的区别
纯组件的使用
① PureComponent 通过 props 和 state 的浅比较实现 shouldComponentUpdate
② 如果状态更新涉及到一个对象内部的更新,则不应该使用 PureComponent.
 
纯组件与普通组件的区别
① 纯组件在 render 之前会进行浅比较,普通组件不会。
② 也不能滥用纯组件,因为纯组件多了一个浅比较的步骤,滥用反而拖累性能。
 
顺便说一下,React 判断类组件是否需要更新,只有两个地方:
一是看有没有shouldComponentUpdate方法,二就是这里的PureComponent判断
 
 

-----------------------------------

206
useEffect 返回函数的作用是什么?
清除上一次副作用遗留下来的状态。
 
 

-----------------------------------

207
useMemo、useCallback 的用法
① 使用 useMemo 可以避免无用方法的调用:一个函数直接写在 render 中,当重新渲染的时候,就会又触发一次这个函数。而如果使用 useMemo 包裹,则只当依赖变化的时候才会调用,渲染的时候不会主动触发。
② useCallback: 相当于 useMemo 回调函数返回一个函数.
 
 

-----------------------------------

208
TS 都有哪些数据类型?
在 js 基础上,增加了:
  • tuple 元祖
  • enum 枚举
  • any, never 任意值
  • void 没有返回值
 
 

-----------------------------------

209
webpack 的缓存机制,三种 hash?
① hash: 修改任何一个文件,所有文件的 hash 都会改变
② chunkhash: 根据不同的入口文件,生成对应的哈希值。将公共库和程序入口文件分开,只要不改动公共库的代码,就可以保证其哈希值不会受到影响。
③ contenthash: chunkhash 有个问题,css 或 js 改变,与之关联的文件的 hash 也会改变,哪怕其文件内容没变。contenthash 能够解决这个问题。
 
 

-----------------------------------

210
DOM 树的构建过程
① Conversion 转换:将 HTML 内容根据编码转换为字符流。
② Tokenizing 分词: 根据 HTML 规范,将字符流解析为标记。
③ Lexing 语法分析: 将标记转换为对象,并定义属性和规则。
④ DOM 构建: 根据 HTML 标记关系,将对象组成 DOM 树。
 
 

-----------------------------------

211
no-cache 指的是不缓存吗?
不是,no-cache 指的是每次都向服务器验证。no-store 才是不缓存。
 
 

-----------------------------------

212
DNS 预解析
① 自动解析:http 下碰到 a 标签,会自动解析,https 为了安全默认不会。
② 手动解析
  • link 标签:
  • meta 标签,可强制开启 HTTPS 下的预解析:
 
 

-----------------------------------

213
Scope Hoisting
分析模块之间的依赖关系,尽可能地把打包出来的模块合并在一个函数中去,能减少代码量,开启方式是设置 optimization.concatenateModules 为 true
 
 

-----------------------------------

214
Object.create() 作用,实现
作用:创建一个新对象,使用现有的对象来提供新对象的 __proto__。
实现:创建一个空函数,然后函数的 prototype 等于 obj,然后再恢复 constructor,最后返回一个实例化
Object.prototype.myCreate = function (obj) {
  function Fn () {}
  // 这里直接指向 obj
  Fn.prototype = obj
  Fn.prototype.constructor = Fn
  return new Fn()
}
 

-----------------------------------

215
React setState 是同步还是异步?
结论:既可能是同步的,也可能是异步的。
具体来说:在 React 内部机制能检测到的地方,setState 就是异步的;在 React 检测不到的地方,如 setInterval, setTimeout 里,setState 就是同步的。
再细节一点:本身并不异步,只是因为 React 的性能优化机制表现为异步。在 React 的生命周期函数或作用域下为异步,在原生的环境下为同步。
 
 

-----------------------------------

216
nodeJS 的事件循环
事件循环的顺序
  • timers 阶段:这个阶段执行 timer(setTimeout、setInterval)的回调
  • I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
  • idle, prepare 阶段:仅 node 内部使用
  • poll 阶段:获取新的 I/O 事件, 适当的条件下 node 将阻塞在这里
  • check 阶段:执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socket 的 close 事件回调
process.nextTick
这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。
node 与浏览器端的不同
  • Node 端,microtask 在事件循环的各个阶段之间执行
  • 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
 
 

-----------------------------------

217
小程序的优化
① 减少请求次数
② setDate 不要太频繁
③ wxss 能写一行的不写多行
④ 尽量使用缓存、刷新使用局部刷新
⑤ 开启压缩,降低代码量
⑥ 资源控制,清除无用代码、资源
⑦ 分包加载、分包预加载
⑧ 骨架屏
 
 

-----------------------------------

218
小程序的优缺点
优点
① 容易上手
② 兼容性问题相对来说少
③ 自带统计
④ 支持插件开发、云开发
缺点
① 后端调试麻烦
② 模拟器和真机有时不一致,安卓和 iOS 有时不一致
③ native 组件存在遮挡问题
④ 能力上的限制:new Function,eval,Generator,没有 cookie,没有 DOM
 
 

-----------------------------------

219
vue 组件通信
① 父子通信:直接通过 props
② 子父通信:绑定事件,然后 $emit 传值
③ Vuex 通信:通过 Store 通信。
 
 

-----------------------------------

220
TCP长连接和短连接的区别和应用场景是什么?
区别
① 长连接:在一个 TCP 连接上可以连续发送多个数据包,如果没有数据包发送,需要双方发送心跳包保活。
② 短连接:有数据交互时,就建立一个 TCP 连接,数据发送完成后,就断开。
应用场景
① 长连接:频繁读写,点对点,连接数少,如数据库的连接使用长连接。
② 短连接:WEB 服务器,高并发使用短连接,否则的话连接太多。
 
 

-----------------------------------

221
小程序和普通网页开发的区别
① 线程:网页开发 JS 线程和渲染线程是互斥的,小程序是在不同的线程中的。
② DOM: 小程序的逻辑层和渲染层是分开的,没有 DOM API,一些库没法使用。
③ 兼容:网页开发需要兼顾各种浏览器、手机、电脑,小程序开发只需要考虑 Android 和 iOS
④ 开发流程:网页开发只需要自己的服务器即可,小程序开发还需要账号申请、审核、发布等流程。
 
 

-----------------------------------

222
小程序做不了,H5 能做的?
比方说想做一个在线运行 JS 的编辑器,小程序由于不能使用 eval,因此没法直接使用,只能使用服务器中转一下。
 
 

-----------------------------------

223
表单提交规则
分为以下几个步骤:
① 识别所有的成功组件
② 为成功组件创建一个数据集合,包含 name 和 value 的键值对。
③ 按照编码规则,对这些数据进行编码。
④ 提交编码后的数据。
成功组件如:
① 非 disabled
② 选中的 checkbox, radio, select, 选择了文件的 file 上传。
 
 

-----------------------------------

224
小程序的数据共享
① 利用页面的 url 传递
② 利用 localStorage 传递
③ 通过一个全局变量,挂在 getApp() 上
 
 

-----------------------------------

225
DOCTYPE
① 作用:它是用来告知 Web 浏览器页面使用了哪种 HTML 版本
② 版本
  • 1.0: (Strict, Transitional, Frameset)
  • 4.01: (Strict, Transitional, Frameset)
  • 5.0: 只有一种
③ 三种之间的区别
  • Strict: 不允许过时标签,不允许框架集。
  • Transitional: 允许过时标签,不允许框架集
  • Frameset: 允许过时标签,允许框架集
 
 

-----------------------------------

226
伪元素和伪类的区别,都有哪些?
区别:
① 本质:伪类本质上是为了弥补常规选择器的不足,伪元素本质上是创建了一个有内容的虚拟容器。
② CSS3 中冒号的数量不同:伪类是一个,伪元素是两个。
③ 数量:伪类可以有多个,伪元素同时只能使用一个。
伪类有:
① :hover
② :active
③ :firsr-child
④ :visited
伪元素有:
① :first-line
② :first-letter
③ :after
④ :before
单个冒号和两个冒号的区别:
单个冒号是 CSS2 的写法,两个冒号是 CSS3 的写法。
 
 

-----------------------------------

227
媒体查询的原理
窗口的 onresize 事件,得到窗口大小,匹配对应的样式修改。
 
 

-----------------------------------

228
localStorage 的最大存储空间是多少
5Mb,超出会报错:exceeded error
 
 

-----------------------------------

229
async/await 和 Promise 的区别
① async/await 函数前面多了一个 async 关键字
② async/await 更加简洁
③ async/await 可以同时处理同步和异步错误
④ 调试的时候,如果多个 Promise 其中一个出现了错误,堆栈信息很乱很误导人,而 async/await 直接给出行号。
 
 

-----------------------------------

230
Proxy 和 Reflect
Proxy 代理
① 作用:Proxy 就是操作对象的中间媒介,要操作对象的话,需要经过这个媒介同意。
② 例子:首先创建一个 obj,然后 new Proxy(obj, 代理行为),代理行为里面可以写 get 和 set, 则在获取、设置的时候,都会首先经过这样的一层中间代理。
Reflect 反射
① 作用:Reflect 可以当做 object 的工具类来使用。
② 例子
  • 获取属性:Reflect.get(obj, 'key')
  • 设置属性:Reflect.set(obj, 'key', value)
 
 

-----------------------------------

231
小程序的运行环境
环境 逻辑层 视图层
iOS JavaScriptCore WKWebView
Android V8 自研 XWeb 引擎,基于 Mobile Chrome 内核
开发者工具 NW.js Chromium Webview
 
 

-----------------------------------

232
上传多个文件,其中可能会有失败的,使用 Promise.all 的话,失败的话就会直接 reject,怎么让让失败的异步请求不影响其他成功的异步请求?
所有的 Promise 的 catch 中,直接返回一个 Promise.resolve()
 const  p1 =  new Promise(resolve => {
     const a =b;
     resolve(a);
 }).catch(()=>{
     // 直接返回这个
     return Promise.resolve('aaab')
 });
 ​
 const  p2 =  new Promise(resolve => {
     const a =1;
     return resolve(a);
 }).catch(()=>{
     return Promise.resolve('aaa')
 });
  
  
 Promise.all([p1,p2]).then((data)=>{
     console.log('then 成功',data);
 }).catch((err)=>{
     console.log('333');
     console.log('errr',err);
 })
实际上就是新出的 API :Promise.allSettled
其他还有实现:https://www.jianshu.com/p/c5c3c2595c98
 
 

-----------------------------------

233
redux-thunk
作用
实现异步,可以将 thunk 看做 dispatch 方法的封装器,使用 redux-thunk 之前,只能 dispatch 一个 action 对象,使用之后可以 dispatch 一个函数,然后就可以在函数里面做一些逻辑处理工作。
使用
dispatch 里面填一个回调函数,参数还是 dispatch, 然后在这个函数内部进行一步请求,异步请求拿到结果后再 dispatch.
实现
 function createThunkMiddleware(extraArgument) {
   return ({ dispatch, getState }) => next => action => {
     if (typeof action === 'function') {
       return action(dispatch, getState, extraArgument);
     }
 ​
     return next(action);
   };
 }
 ​
 const thunk = createThunkMiddleware();
 thunk.withExtraArgument = createThunkMiddleware;
 ​
 export default thunk;
 
 

-----------------------------------

234
函数式编程的 compose
作用
把一系列的函数,组装生成一个新的函数,并且从后往前,后面函数的执行结果,作为前一个函数的参数。
实现
 // 从右到左
 function compose() {
     var fns = [].slice.call(arguments)
     return function (initialArg) {
         var res = initialArg
         for (var i = fns.length - 1; i > -1; i--) {
             res = fns[i](res)
         }
         return res
     }
 }
 ​
 // 从左到右
 function pipe() {
     var fns = [].slice.call(arguments)
     return function (initialAgr) {
         var res = initialAgr
         for (var i = 0; i < fns.length; i++) {
             res = fns[i](res)
         }
         return res
     }
 }
 ​
 // 测试
 var greet = function (name) { return 'hi:' + name }
 var exclaim = function (statement) { return statement.toUpperCase() + '!' }
 var transform = function (str) { return str.replace(/[dD]/, 'DDDDD') }
 var welcome1 = compose(greet, exclaim, transform)
 var welcome2 = pipe(greet, exclaim, transform)
 console.log(welcome1('dot'))//hi:DDDDDOT!
 console.log(welcome2('dolb'))//HI:DDDDDOLB!
 
 

-----------------------------------

235
怎么配置单页应用?怎么配置多页应用?
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述
 
多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
  • 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
  • 随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
 
 

-----------------------------------

236
 
npm打包时需要注意哪些?如何利用webpack来更好的构建?
① 要支持 CommonJS 模块化规范
② 打包结果应该上传 ES5的,SourceMap 也要一块上传。
③ 包的大小应该尽可能小
④ 不打包所依赖的模块,让用户自己去选择是否安装依赖。
⑤ UI 资源也要打包。
 
基于以上需要注意的问题,我们可以对于webpack配置做以下扩展和优化:
  1. CommonJS模块化规范的解决方案: 设置output.libraryTarget='commonjs2'使输出的代码符合CommonJS2 模块化规范,以供给其它模块导入使用
  2. 输出ES5代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map'输出SourceMap以发布调试。
  3. Npm包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件
  4. 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要打包。
  5. 对于依赖的资源文件打包的解决方案:通过css-loaderextract-text-webpack-plugin来实现
https://blog.csdn.net/duyujian706709149/article/details/97299339
 

-----------------------------------

237
webpack 如何在 vue 项目中实现按需加载?antd 如何实现按需加载?
① 组件库现成的解决方案
名称是:Element 的 babel-plugin-component, AntD 的 babel-plugin-import
原理是:将 import 的路径具体化,比方说 import { Button } from 'antd', 替换成 import { Button } from 'antd/lib/button'
缺点是:每一个按需加载的 chunk 中都单独打包相关的模块,导致每一个 chunk 的体积都比较大。
 
② 使用 webpack 的 externals 配置
原理是:配置之后,webpack 打包时会绕过这些引用
缺点是:会外部单独引入一个 antd.min.js, 大约 1Mb
 
③ 最佳方案:使用 webpack 的 CommonsChunkPlugin 插件
原理是:CommonsChunkPlugin 能提取第三方库和公共模块,避免首屏加载的 bundle 文件或者按需加载的 bundle 文件体积过大。
 
 

-----------------------------------

238
两栏布局、两列布局
① 浮动:两个 div,第一个浮动,设置固定宽度即可。
② 绝对定位:父元素 relative,两个子元素 absolute,然后设置第二个子元素的 left 为左边的宽度。
③ flex 布局:父元素 flex, 左边固定宽度,右边 flex: 1;
 
 

-----------------------------------

239
为什么在有 HTTPS 的情况下,还要使用 RSA 呢
HTTPS 防止中间人攻击,RSA 验证身份,缺一不可。
比如一个 HTTPS 接口,谁都可以调用,但是自己的公钥加密的数据,就可以知道这是我自己的程序在调用接口。
 
 

-----------------------------------

240
Taro 的优缺点
优点
① React 方式组件开发
② 多端开发
缺点:核心就是架构问题
① 和 React DSL 强绑定
② JSX 适配工作量大、社区贡献复杂
③ 使用静态编译的方式去处理动态的 JS 语言,稍微有一点变化,就可能会不适用。
④ React 新特性需要手动对接
⑤ 前端生态无法复用
⑥ 错误栈复杂,而且没有 SourceMap
 
 

-----------------------------------

241
静态编译和动态编译的优缺点,性能测试
优点
① 性能上:同等条件下,编译时做的工作越多,运行时做的工作越少,性能更好。
② 可读性:重编译时,就保证了编译之后代码也具有可行性。
缺点
① 性能:长久来看,硬件性能剩余会越来越多,牺牲一点性能换取更大的开发灵活性会更好。
② 适配:需要适配很多的 JSX
其他缺点:
① 和 React DSL 强绑定
② JSX 适配工作量大、社区贡献复杂
③ 使用静态编译的方式去处理动态的 JS 语言,稍微有一点变化,就可能会不适用。
④ React 新特性需要手动对接
⑤ 前端生态无法复用
⑥ 错误栈复杂,而且没有 SourceMap
性能测试
taro-benchmark
 
 

-----------------------------------

242
在端上运行的程序,它的瓶颈主要在什么地方?
① 内存
② 大小
③ CPU 线程
 
 

-----------------------------------

243
JS 执行和渲染拆开后,有什么好处?双线程有什么好处?
① 性能:UI渲染和 JS 脚本在一个单线程中执行,这就容易导致一些逻辑任务抢占UI渲染的资源
② 安全性:阻止开发者使用一些浏览器提供的开发性接口,诸如跳转页面、操作 DOM、动态执行脚本。
③ 方便基础库 BUG 修改:渲染层和逻辑层是两个线程管理,两个线程各自注入了基础库。可以单独修复其中一个基础库的 BUG。
 
 

-----------------------------------

244
taro 的一些新的变化?背后的原理是什么?Taro 最新的架构?
https://mp.weixin.qq.com/s?__biz=MzU3NDkzMTI3MA==&mid=2247483770&idx=1&sn=ba2cdea5256e1c4e7bb513aa4c837834
新的变化:
  • 没有 DSL 限制:无论是是 React 还是 Vue 技术栈,都能够使用 Taro 开发
  • 模版动态构建:和之前模版通过编译生成的不同,Taro Next 的模版是固定的,然后基于组件的 template,动态 “递归” 渲染整棵 Taro DOM 树。
  • 新特性无缝支持:由于 Taro Next 本质上是将 React/Vue 运行在小程序上,因此,各种新特性也就无缝支持了。
  • 社区贡献更简单:错误栈将和 React/Vue 一致,团队只需要维护核心的 taro-runtime。
  • 基于 Webpack:Taro Next 基于 Webpack 实现了多端的工程化,提供了插件功能。
背后的原理:
① 前端的本质:无论是用什么框架,React 还是 Vue,最终代码都是调用了浏览器那几个 BOM/DOM 的 API
② Taro 的实现
如何动态渲染出 DOM 树
  • 创建一个 taro-runtime 包,实现了一套高效、精简的 BOM/DOM API
  • 通过 Webpack 的 ProvidePlugin 插件,注入到小程序的逻辑层
  • React 精简:React-DOM 含有大量浏览器兼容的代码,去除了这部分的代码。
  • React 三部分核心:核心 react-core, 与虚拟 DOM 有关的 react-reconciler, 与渲染有关的 Renderer,Taro 实现了 taro-react 包,替换掉 Renderer 用来连接 react-reconciler 和 taro-runtime.
  • 具体这样做:在 hostConfig 配置中调用Taro的 api,实现一个创建 DOM 树的 render。
DOM 树如何更新到页面
基于组件模板,动态地递归渲染整棵树。
 
 

-----------------------------------

245
remax 的短板,优缺点?
优点
  • 基于真正 React 开发,无缝使用 Redux/Mobx 等状态管理库。
  • 完全支持 TypeScript,为组件和 API 提供完整的类型支持。
  • 跨平台,使用一套代码同时开发小程序和 H5 应用。
缺点
① 小程序编译后,模板组件引入过多。有个 base.axml。但是支付宝不支持在 template 里引用小程序自定义组件。所以现在所有页面里都包含了所有的 template,可以优化下如果页面没用到小程序自定义组件,那就引用 base.axml
 
② 原生混合开发会编译两次:原生写法和 Remax 写法同时依赖的模块会被打包两次。解决方案:把所有小程序页面和自定义组件的 js 全部作为 entry 放入 webpack 的编译流程,这样公共模块就能抽到 common chunk 里。
 
③ 性能问题:动态模板渲染的方式,将这部分工作放在了用户手机上,对算力的要求更高。
 
https://remaxjs.org/guide/advanced/hybrid
 
 

-----------------------------------

246
云开发与普通开发的优点、缺点
优点:
① 规模经济:利用云计算供应商提供的基础设施,同在单一的企业内开发相比,开发者能够提供更好,更便宜和更可靠的应用
② 配套齐全:直接提供了数据库、存储、云函数、用户鉴别能力
缺点:
① 安全性:数据不在自己手上,可能会丢失数据。
② 数据备份机制:虽然有数据备份接口,但是可能会超出云函数20秒的时间限制,总之还是不灵活。
 
 

-----------------------------------

247
React context,如果有多个 context 怎么使用?
① 使用 React.createContext() 创建两个 contextA, contextB
② 两个 Provider 嵌套,两个 Consumer 嵌套,即 contextA.Provider 点出来之后嵌套。
③ 顺序不重要,要注意语法。
 
 

-----------------------------------

248
函数式组件比类组件有什么优势?
① 代码包大小:编译后代码更少
② 性能:没有生命周期,不需要实例化,性能更好
 
 

-----------------------------------

249
React 组件如何防止无效渲染?
什么时候会渲染?
① state 发生变化
② props 发生变化
阻止无效渲染的方式:
① shouleComponentUpdate
② 使用 useMemo, useCallback 反之
③ 使用生产版本的 React,开发版本有时候会渲染两次。
 
 

-----------------------------------

250
React 性能如何分析?
① 直接肉眼观察某个组件是否闪烁,如果闪烁,说明重复渲染。
② 代码中 log,看是否重复 log
③ 使用最新的 React DevTool 中的 Profiler 进行定量分析,可以查看每个组件渲染时间的火焰图。
 
 

-----------------------------------

251
服务端渲染的过程?为什么前端还需要再渲染一次?
怎么做的
核心 api 是 renderToString
① 启动一个 node 服务 ② 把 react 根组件用 reanderToString 渲染成字符串返回给前端 ③ 前端再渲染一次:恢复生命周期、给组件绑定事件以响应用户操作
好处是什么
① SEO 收录更好
② 减少白屏
③ 支持更多后端代码模板的支持
 
 

-----------------------------------

252
TS 里面的抽象类,抽象类能有抽象属性吗?
抽象类
  • 使用abstract关键字定义抽象类和抽象方法
  • 抽象类不允许被实例化
  • 抽象类中的抽象方法必须被实现
抽象类中可以有抽象属性,如下面的写法,是没有报错的
 
 

-----------------------------------

253
type 和 interface 的区别?
相同点
① 都可以描述一个属性或函数
② 都允许拓展:interface 使用 extends, type 使用 &
不同点
① type 可以声明基本类型、联合类型、元祖类型,interface 不行。
  • 基本类型: string
  • 联合类型:TypeA | TypeB
  • 元祖类型:具体定位到每个位置的类型:[TypeA, TypeB]
② type 可以通过 typeof 获取到实例的类型
③ interface 声明能够直接合并,同时写多个同名的 interface
④ interface 可以被实现,type 不行
https://www.jianshu.com/p/8dff10ce3912
 
 

-----------------------------------

254
Taro 的架构
https://mp.weixin.qq.com/s?__biz=MzU3NDkzMTI3MA==&mid=2247483770&idx=1&sn=ba2cdea5256e1c4e7bb513aa4c837834
① 分为两部分:编译时 和 运行时
  • 编译时主要是将 Taro 代码通过 Babel[11] 转换成 小程序的代码,如:JSWXMLWXSSJSON
  • 运行时:生命周期、事件、data 等部分的处理和对接
② 编译时具体的过程:使用 babel-parser[12] 将 Taro 代码解析成抽象语法树,然后通过 babel-types[13] 对抽象语法树进行一系列修改、转换操作,最后再通过 babel-generate[14] 生成对应的目标代码。
③ 运行时:编译后的代码中,React 的核心 render 方法 没有了。同时代码里增加了 BaseComponent 和 createComponent ,它们是 Taro 运行时的核心。Taro 当前架构只是在开发时遵循了 React 的语法,在代码编译之后实际运行时,和 React 并没有关系
④ 总结
整个 Taro 当前架构的特点是:
  • 重编译时,轻运行时:这从两边代码行数的对比就可见一斑。
  • 编译后代码与 React 无关:Taro 只是在开发时遵循了 React 的语法。
  • 直接使用 Babel 进行编译:这也导致当前 Taro 在工程化和插件方面的羸弱。
其他的 mpvue 架构的特点:
  • 半编译时,半运行时
  • WXML 模板通过编译生成
  • 将 Vue 运行在小程序上,实现 [email protected] 绝大部分特性
  • 基于 wevpack
 
 

-----------------------------------

255
性能优化、audits、LightHouse、刑恩性能测试工具
性能测试工具
以前叫 audits, 现在叫 LightHouse.
有以下几个方面:
① Performance性能
  • First Contentful Paint(FCP):第一次有内容绘制。
  • Time to Interactive (TTI):可交互时间
  • Speed Index (SI):页面内容填充速度。 how quickly the contents of a page are visibly populated.
  • Total Blocking Time (TBT): 总阻塞时间
  • Largest Contentful Paint (LCP): 大内容绘制
  • Cumulative Layout Shift (CLS): 衡量可视窗口内元素的移动
给出建议:移除无用代码、DNS 预解析、压缩代码、
② Accessibility 可访问性
  • 对比度
  • 代码可读性
③ Best Practiveces 最佳实践
  • HTTPS
  • 跨域安全
  • JS 安全
④ SEO 检查
  • 各种标签是否语义化等
⑤ PWA
  • 速度是否够快
 
 

-----------------------------------

256
CDN 原理
https://zhuanlan.zhihu.com/p/147571853?from_voters_page=true
简要来说:
① 本地 DNS 解析,通过 CNAME 转到 CDN 专用的 DNS 服务器
② DNS 服务器返回全局负载均衡的服务器 IP
③ 请求全局负载均衡 IP,返回局部负载均衡 IP
④ 请求局部负载均衡 IP,返回缓存服务器 IP
⑤ 请求缓存服务器 IP
具体来说:
资源上传cdn之后,当用户访问cdn的资源地址之后会经历下面的步骤:
  1. 首先经过本地的dns解析,请求cname指向的那台cdn专用的dns服务器。
  2. dns服务器返回全局负载均衡的服务器ip给用户
  3. 用户请求全局负载均衡服务器,服务器根据ip返回所在区域的负载均衡服务器ip给用户
  4. 用户请求区域负载均衡服务器,负载均衡服务器根据用户ip选择距离近的,并且存在用户所需内容的,负载比较合适的一台缓存服务器ip给用户。当没有对应内容的时候,会去上一级缓存服务器去找,直到找到资源所在的源站服务器,并且缓存在缓存服务器中。用户下一次在请求该资源,就可以就近拿缓存了。
 
 

-----------------------------------

257
前端工程化
四化:模块化、组件化、规范化、自动化
① 模块化:将一个大文件拆分成相互依赖的小文件,再进行统一的拼装和加载。只有这样,才有多人协作的可能。


- JS 模块化:CommonJS --> AMD --> CMD,语言层面上使用 ES6,

  - 1. 用Webpack+Babel将所有模块打包成一个文件同步加载,也可以打成多个chunk异步加载;

    2. 用SystemJS+Babel主要是分模块异步加载;

    3. 用浏览器的

你可能感兴趣的:(前端)