We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
REM布局基于html的font-size来动态地计算元素的尺寸,而EM是基于父元素的font-size来动态地计算元素的尺寸
websocket的特点: 双向通信 websocket建立在TCP之上,在握手阶段使用了http协议 数据格式比较轻量,性能开销小,通信高效 可以发送文本,也可以发送二进制数据 没有同源限制 协议标识符是ws,如果加密则为wss
websocket的特点:
双向通信
websocket建立在TCP之上,在握手阶段使用了http协议
数据格式比较轻量,性能开销小,通信高效
可以发送文本,也可以发送二进制数据
没有同源限制
协议标识符是ws,如果加密则为wss
解析HTML,生成DOM树,自上而下,任何内部样式和脚本都会阻塞DOM树的构建 解析CSS,生成CSSOM树 将解析的DOM树和CSSOM树关联生成渲染树render Tree 进入布局阶段,为DOM树的每个节点分配在屏幕上出现的确切坐标,还是渲染树 绘制阶段,离开渲染引擎,由UI Backend对渲染树的每个节点进行绘制,呈现出界面
React的diff算法策略 DOM节点跨层级的移动操作特别少,可以忽略 拥有相同类的两个组件会有相同的树结构,拥有不同类的两个组件会有不同的树结构 对于同一层级的一组子节点,它们可以通过id区分 对于上述三个策略,React分别对tree diff, component diff, element diff 进行优化 tree diff: 两棵树只会对同一层次的节点进行比较。当遇到跨层级移动操作时,会先删掉原来的元素,再添加新的元素 component diff: 如果是同一类型的组件,按照原策略继续比较 Virtual DOM 树即可。 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切知道这点,那么就可以节省大量的 diff 运算时间。因此,React允许用户通过shouldComponentUpdate()来判断该组件是否需要进行diff算法分析,但是如果调用了forceUpdate方法,shouldComponentUpdate则失效。 **element diff: ** 当节点处于同一层级时,diff 提供了 3 种节点操作,分别为 INSERT_MARKUP (插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)。 总结: React的高效得益于其Virtual DOM+React diff的体系。diff算法并非react独创,react只是在传统diff算法做了优化。但因为其优化,将diff算法的时间复杂度一下子从O(n^3)降到O(n)。 React diff的三大策略: Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。 对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。 在开发组件时,保持稳定的 DOM 结构会有助于性能的提升。 在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作。 key的存在是为了提升diff效率,但未必一定就可以提升性能,记住简单列表渲染情况下,不加key要比加key的性能更好。 懂得借助react diff的特性去解决我们实际开发中的一系列问题。
React的diff算法策略
对于上述三个策略,React分别对tree diff, component diff, element diff 进行优化
tree diff:
两棵树只会对同一层次的节点进行比较。当遇到跨层级移动操作时,会先删掉原来的元素,再添加新的元素
component diff:
**element diff: **
当节点处于同一层级时,diff 提供了 3 种节点操作,分别为 INSERT_MARKUP (插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)。
总结:
react中性能主要消耗在update阶段的diff算法,因此性能优化也主要针对diff算法 减少setState的触发,无论数据处理多么复杂,保证最后只调用一次setState 父组件优化,父组件的render必然会触发子组件进入update阶段,此时可以用shouldComponentUpdate方法来判断子组件是否应该更新,或者直接使用PureComponent 禁止使用forceUpdate方法,该方法调用后会直接进入componentWillUpdate阶段,无法被拦截 避免在shouldComponentUpdate中做复杂的操作 合理设计state,不需要渲染的state,尽量使用实例成员变量 不需要渲染的props,合理使用context机制,或公共模块(比如一个单例服务)变量来替换 在diff算法中,不使用跨层级操作DOM的操作 对于条件渲染多个节点时,尽量采用隐藏等方式切换节点,而不是替换节点 避免避免将后面的子节点移动到前面的操作,当节点数量多时会有一定的性能问题
react中性能主要消耗在update阶段的diff算法,因此性能优化也主要针对diff算法
减少setState的触发,无论数据处理多么复杂,保证最后只调用一次setState
父组件优化,父组件的render必然会触发子组件进入update阶段,此时可以用shouldComponentUpdate方法来判断子组件是否应该更新,或者直接使用PureComponent
禁止使用forceUpdate方法,该方法调用后会直接进入componentWillUpdate阶段,无法被拦截
避免在shouldComponentUpdate中做复杂的操作
合理设计state,不需要渲染的state,尽量使用实例成员变量
不需要渲染的props,合理使用context机制,或公共模块(比如一个单例服务)变量来替换
在diff算法中,不使用跨层级操作DOM的操作
对于条件渲染多个节点时,尽量采用隐藏等方式切换节点,而不是替换节点
避免避免将后面的子节点移动到前面的操作,当节点数量多时会有一定的性能问题
官方文档说明: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 Vue的更新机制: Vue是异步执行DOM更新的 事件循环: 第一个tick,即数据更新: 首先修改数据,这是同步任务。同一事件循环的所有同步任务都在主线程上执行,形成一个执行栈。此时还未涉及到DOM Vue开启一个异步队列,并缓存在此事件循环中发生的所有数据变化。如果同一个watcher被多次触发,只会被推入队列一次 第二个tick,即DOM更新: 同步任务执行完毕,开始执行异步watcher队列的任务,更新DOM。Vue在内部尝试对异步队列使用Promise.then和MessageChannel方法,如果环境不支持,会采用setTimeout(fn, 0)代替 第三个tick: 通过Vue.nextTick获取到改变后的DOM
官方文档说明:
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
Vue的更新机制:
Vue是异步执行DOM更新的
事件循环:
第一个tick,即数据更新:
第二个tick,即DOM更新:
同步任务执行完毕,开始执行异步watcher队列的任务,更新DOM。Vue在内部尝试对异步队列使用Promise.then和MessageChannel方法,如果环境不支持,会采用setTimeout(fn, 0)代替
第三个tick:
通过Vue.nextTick获取到改变后的DOM
EventBus实际上是一个key-value形式的map,key为函数方法名,value为函数本身 简单实现 function EventBus() { this.map = new Map(); } EventBus.prototype.on = function (key, func) { if (!this.map.get(key)) { this.map.set(key, [func]); } else { const tempArray = this.map.get(key); tempArray.push(func); } } EventBus.prototype.emit = function (key) { const tempArray = this.map.get(key); tempArray.forEach(function (item) { item(); }) } function A() { console.log('A'); } function B() { console.log('B'); } var eventBus = new EventBus(); eventBus.on('myevent', A); eventBus.on('myevent', B); eventBus.emit('myevent');
EventBus实际上是一个key-value形式的map,key为函数方法名,value为函数本身
简单实现
function EventBus() { this.map = new Map(); } EventBus.prototype.on = function (key, func) { if (!this.map.get(key)) { this.map.set(key, [func]); } else { const tempArray = this.map.get(key); tempArray.push(func); } } EventBus.prototype.emit = function (key) { const tempArray = this.map.get(key); tempArray.forEach(function (item) { item(); }) } function A() { console.log('A'); } function B() { console.log('B'); } var eventBus = new EventBus(); eventBus.on('myevent', A); eventBus.on('myevent', B); eventBus.emit('myevent');
本质是个状态机,在内部有两个数组分别保存着成功和失败的回调,来实现异步,其实是使用了设计模式中的观察者模式。
原型链继承 缺点:包含引用类型的原型属性会被所有实例共享,创建子类时,不能向超类的构造函数传递参数 构造函数继承 缺点:无法避免函数复用的问题,因为所有的方法都在构造函数中定义 组合继承 使用原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承 缺点:会调用两次超类的构造函数 原型式继承 Object.create,基于已有对象创建新的对象,并添加自己需要的属性 缺点:包含引用类型的原型属性会被所有实例共享 寄生式继承 创建一个用于封装过程的函数,该函数在内部以某种方式来增强对象,最后返回这个对象 寄生组合式继承
原型链继承
缺点:包含引用类型的原型属性会被所有实例共享,创建子类时,不能向超类的构造函数传递参数
构造函数继承
缺点:无法避免函数复用的问题,因为所有的方法都在构造函数中定义
组合继承
使用原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承
缺点:会调用两次超类的构造函数
原型式继承
Object.create,基于已有对象创建新的对象,并添加自己需要的属性
缺点:包含引用类型的原型属性会被所有实例共享
寄生式继承
创建一个用于封装过程的函数,该函数在内部以某种方式来增强对象,最后返回这个对象
寄生组合式继承
在父节点上绑定相应的事件,子节点上触发的事件,通过冒泡机制到达父节点进行监听 可以大量节省内存占用,同时对新增加的节点不需要再次绑定事件
在父节点上绑定相应的事件,子节点上触发的事件,通过冒泡机制到达父节点进行监听
可以大量节省内存占用,同时对新增加的节点不需要再次绑定事件
The text was updated successfully, but these errors were encountered:
No branches or pull requests
1.REM布局,原理
2.websocket的原理
3.页面DOM渲染过程
4.React中diff算法的过程
5.React优化
6.Vue中的nextTick是做什么的,用法是什么
7.如何实现一个EventBus
8.ES6的Promise是什么,给一个大概的描述
9.JS继承方式有哪几种
10.DOM事件委托的概念
The text was updated successfully, but these errors were encountered: