Vue
2021-03-24 15:04:27 0 举报
AI智能生成
前端面试_Vue
作者其他创作
大纲/内容
Vue 使用
Vue 基本使用
指令、插值
插值、表达式<br>指令、动态属性<br>v-html:有 XSS 风险,会覆盖子组件
computed 和 watch
computed
computed 有缓存,data 不变则不会重新计算<br>
v-model(双向数据绑定)如果绑定 computed 里的计算属性,一定要有 get 和 set,否则会报错
watch
watch 如何深度监听? handler, deep: true
watch 监听引用类型,拿不到 oldVal。因为指针相同,此时已经指向了新的 val
class 和 style
使用动态属性<br>使用驼峰式写法
条件渲染
`
v-if v-else的用法,可使用变量,也可使用 === 表达式
v-if 和 v-show 的区别?<br>v-if 只渲染满足条件的,v-show 元素总是会被渲染,只切换元素的 CSS display 属性;<br>v-if 有更高的切换开销, v-show 有更高的初始渲染开销<br>
v-if 和 v-show 的使用场景<br>一次性或更新不频繁使用v-if,切换比较频繁使用v-show
循环(列表)渲染<br>
如何遍历对象? v-for
key 的重要性,key 不能乱写(如 random 或 index)<br>高效更新虚拟 DOM:如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。<br>
v-for 和 v-if 不能一起使用<br>v-for 的计算优先级高于 v-if,一起使用会先进行 for 循环再分别进行 if 判断
事件
修饰符
事件修饰符
按键修饰符
表单
<ol><li>v-model </li><li>常见表单项 textarea checkbox radio select </li><li>修饰符 lazy number trim</li></ol>
Vue 父子组件如何通讯
props, $emit:<br>props 父组件向子组件 传递一个数据<br>$emit 子组件向父组件 传递一个事件 <br>
Index 父组件
Input 子组件
如何用自定义事件进行 Vue 组件通讯
List 子组件
event 是 vue 的一个实例
event.$emit 触发一个自定义事件
event.$on 绑定一个自定义事件<br>
event.$off 销毁一个自定义事件(及时销毁,否则可能造成内存泄露)
beforeDestory 的使用场景之一:及时解绑自定义事件
Vue 组件生命周期
生命周期图示
挂载阶段:(beforeCreate,created;beforeMount,mounted)<br>更新阶段:(beforeUpdate,updated)<br>销毁阶段:(beforeDestroy,destroyed)
created 和 mounted 的区别?<br>created:vue 的实例已经初始化完成,但页面还没有开始渲染<br>mounted:页面已经渲染完了,大部分操作(ajax 获取信息、绑定事件等)都在 mounted 里面完成<br>
beforeDestroy 里可能要做什么?<br>解除绑定,销毁子组件以及事件监听器<br>例如:自定义事件的绑定要解除;setTimeout 定时任务要销毁;自己绑定的 window 或 document 事件要销毁<br>总之,该销毁的销毁,不要遗留在内存中
Vue 父子组件生命周期调用顺序
挂载阶段:创建初始化实例是从外到内的,但是渲染是从内到外的<br>父 beforeCreate<br>父 created<br>父 beforeMount<br>子 beforeCreate<br>子 created<br>子 beforeMount<br>子 mounted<br>父 mounted<br>
更新阶段:<br>父 beforeUpdate<br>子 beforeUpdate<br>子 updated<br>父 updated<br>
销毁阶段:<br>父 beforeDestroy<br>子 beforeDestroy<br>子 destroyed<br>父 destroyed
面试官会考察哪些 Vue 高级特性
自定义 v-model<br>$nextTick<br>refs<br>slot(插槽)<br>动态、异步组件(很多面试者不知道)<br>keep-alive (缓存、提升性能)<br>mixin(项目复杂、多个组件之间有重复逻辑时使用)
Vue 如何自己实现 v-model
使用场景
如:颜色选择器
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突
Vue 组件更新之后如何获取最新 DOM
$nextTick
使用场景
Vue 组件更新后获取最新 DOM
基本使用
Vue 是异步渲染(React 也是)<br>data 改变之后,DOM 不会立刻渲染<br>$nextTick 会在 DOM 渲染之后被触发,以获取最新 DOM 节点<br>
异步渲染,$nextTick 待 DOM 渲染完成再回调<br>页面渲染时会将 data 的修改进行合并,多次 data 修改只会渲染一次<br>
$refs 获取dom节点:<br>给相应的元素加 ref="name", 然后通过 this.$refs.name 获取到该元素<br>
slot 是什么
基本使用
slot 插槽作用:让父组件可以往子组件中插入一段内容(不一定是字符串,可以是其它的组件,只要是符合 Vue 标准的组件或者标签都可以)<br>如果父组件不插入内容,就会使用子组件 slot 标签中默认内容<br>
作用域插槽
作用域插槽的作用:让父组件可以访问到子组件的数据(不是很常用,但要知道)
具名插槽
Vue 的动态组件是什么
使用场景
需要根据数据动态渲染组件的场景,比如:新闻落地页,图文视频组件的不同排列组合。
基本使用
<component :is="component-name">
Vue 如何异步加载组件
异步组件
使用场景
按需加载,异步加载大组件(Vue 常见性能优化)
基本使用
import CustomVModel from './CustomVModel'<br>这种引入方式为同步加载,打包时也是打一个包出来<br>
import() 函数
异步组件引入方式:<br>直接在 components 中注册<br>FormDemo: () => import('../BaseUse/FormDemo')<br>
异步组件只有在用到的时候才会加载,且会把结果缓存起来供未来重渲染
Vue 如何缓存组件
keep-alive
使用场景
缓存组件<br>频繁切换,不需要重复渲染(如 tab 切换)<br>Vue 常见性能优化<br>
基本使用
如果去掉 keep-alive,切换 tab 时,会先销毁当前组件,再挂载新组件
keep-alive 、v-show、v-if 的区别
v-if:通过销毁 DOM 和重构 DOM 控制显示隐藏 DOM<br>v-show:通过 CSS 的 display 控制显示隐藏 DOM<br>keep-alive:是 Vue 框架层级进行的 js 对象的渲染(一个组件就是一个 js 对象)<br>
Vue 组件如何抽离公用逻辑
mixin
使用场景
多个组件有相同的逻辑,抽离出来<br>
基本使用
mixin 可以添加多个,会自动合起来
mixin 的问题
变量来源不明确,不利于阅读(mixin中的变量或者方法在当前组件是查不到的)
多mixin可能会造成命名冲突(如data变量,方法名称,只要是有名称的都有可能冲突,但是像mounted函数不会冲突,这是vue特有的生命周期,里面的代码会合并
mixin和组件可能出现多对多的关系,复杂度较高(一个组件引多个mixin,多个组件引用一个mixin)
Vue 3 提出的 Composition API 旨在解决这些问题
Vue 高级特性知识点小结
相关面试技巧
可以不太深入,但必须知道<br>熟悉基本用法,了解使用场景<br>结合项目经验<br>
Vuex 知识点串讲
Vuex 使用
面试考点并不多(因为熟悉 Vue 之后,Vuex 没有难度)<br>但基本概念、基本使用和 API 必须要掌握<br>可能会考察 state 的数据结构设计<br>
Vuex 基本概念
state<br>getters<br>action<br>mutation<br>
用于 Vue 组件
dispatch <br>commit<br>mapState<br>mapGetters<br>mapActions<br>mapMutations
Vuex 图示
actions 里面才能做异步操作,常用的就是 ajax 请求后端 API<br>actions 还会整合多个mutations原子操作<br>
mutation 里是原子操作,力求原子、最小、同步
vue-router 知识点串讲
vue-router 使用
面试考点并不多(前提是熟悉 Vue)<br>路由模式(hash、H5 history)<br>路由配置(动态配置、懒加载)<br>
vue-router 路由模式
hash 模式(默认),如 http://abc.com/#/user/10<br>
H5 history 模式,如 http://abc.com/user/20(需要server端支持,因此无特殊要求可选择 hash 模式)
H5 history 模式的配置
vue-router 路由配置
动态配置
懒加载
Vue 使用考点总结
v-if 和 v-show 的区别<br>
v-if 通过销毁 DOM 和重构 DOM 控制显示隐藏 DOM<br>v-show 通过 CSS 的 display 控制显示隐藏 DOM<br>更新不频繁使用 v-if,切换比较频繁使用 v-show
为何 v-for 中要用 key<br>
key 主要用来做 dom diff 算法用的<br>key 需要使用与业务相关的唯一 id,不要使用 index
描述 Vue 组件的生命周期(有父子组件的情况)
Vue 组件如何通讯
1.父子组件,使用属性和触发事件<br>2.组件之间无关或者层级较深时,使用自定义事件<br>3.Vuex通讯
Vue 原理
组件化和 MVVM
组件化
传统组件:只是静态渲染,更新还要依赖于操作 DOM
数据驱动视图:不直接操作 DOM,通过修改数据重新渲染视图(Vue MVVM、React setState)
MVVM:Model-View-ViewModel
View:视图,就是 DOM<br>Model:模型,就是 Vue 组件里的 data,或 Vuex 里的数据<br>ViewModel:View 和 Model 通过 ViewModel 来做关联,像监听事件、监听指令<br>
响应式
组件 data 的数据一旦变化,立刻触发视图的更新
监听 data 变化的核心 API:Object.defineProperty
基本用法
如何深度监听 data 变化
示例
深度监听,递归到底
Vue 如何监听数组变化
示例
重新定义数组原型:先获取数组原型,然后创建一个新的对象指向数组原型(仅是指向,再扩展新的方法不会影响原型)
Object.create 隔离原型链指向
忌:污染全局的 Array 原型
Object.defineProperty 缺点
<ol><li><span style="font-size: inherit;">深度监听,需要递归到底,一次性计算量大</span></li><li><span style="font-size: inherit;">无法监听新增属性/删除属性(Vue.set Vue.delete)</span></li><li><span style="font-size: inherit;">无法原生监听数组,需要特殊处理</span></li></ol>
Vue3.0 启用 Proxy,但 Proxy 兼容性不太好,且无法用 polyfill
vdom 和 diff
虚拟 DOM (Virtual DOM)
vdom 是实现 Vue 和 React 的基石
背景
<ul><li>DOM 操作非常耗费性能</li><li>以前用 jQuery,可以自行控制 DOM 操作的时机,手动调整</li><li>Vue 和 React 是数据驱动视图,如何有效控制 DOM 操作</li></ul>
解决方案
<ul><li>有了一定复杂度,想减少计算次数比较难。DOM 的改变很耗时。</li><li>能不能把计算,更多的转移为 JS 计算? JS 执行速度很快<br></li><li>vdom 用 JS 模拟 DOM 结构,计算出最小的变更,操作 DOM</li></ul>
用 JS 模拟 DOM 结构
snabbdom
<ul><li>简洁强大的 vdom 库,易学易用</li><li>Vue 参考它实现的 vdom 和 diff</li><li>https://github.com/snabbdom/snabbdom</li></ul>
Vue3.0重写了vdom的代码,优化了性能
代码演示
h 函数<br>vnode 数据结构<br>patch 函数
h 函数:接受三个参数(选择器,属性,子节点数组),返回一个 vnode 结构
vnode 数据结构
patch(container, vnode) 初次渲染<br>patch(vnode, newVnode) DOM 更新<br>
vdom 总结
<ul><li>用 JS 模拟 DOM 结构(vnode)</li><li>新旧 vnode 对比,得出最小的更新范围,最后更新 DOM</li><li>数据驱动视图的模式下,有效控制 DOM 操作</li></ul>
diff 算法
diff 算法是 vdom 中最核心、最关键的部分
diff 算法概述
<ul><li>diff 即对比,是一个广泛的概念,如 linux diff 命令、git diff 等</li><li>两个 js 对象也可以做 diff,如 https://github.com/cujojs/jiff</li><li>两棵树做 diff,如这里的 vdom diff</li></ul>
树 diff 的时间复杂度 O(n^3)
遍历 tree1;遍历 tree2;排序<br>1000个节点,要计算1亿次,算法不可用<br>
优化时间复杂度到 O(n)
<ul><li>只比较同一层级,不跨级比较</li><li>tag 不相同,则直接删掉重建,不再深度比较</li><li>tag 和 key,两者都相同,则认为是相同节点,不再深度比较</li></ul>
深入 diff 算法源码
看源码,不用过于关注细节<br>找到核心函数,查看其参数、返回值、主要逻辑
生成 vnode
h 函数<br>
参数:选择器,属性,子节点数组<br>返回值:通过 vnode 函数返回 vnode 结构
vnode 函数
返回 vnode 结构
patch 函数
参数:oldVnode,vnode
第一个参数不是 vnode,则创建一个空的 vnode,关联到 这个 DOM 元素
相同的 vnode(key 和 selector 都相等),执行 patchVnode 函数
如果都没有传 key,则 undefined === undefined true
不相同的 vnode,直接删掉重建
patchVnode 函数
vnode 对比
参数:oldVnode,vnode
设置 vnode.elem,赋值成旧的 elem<br>
<span style="font-size: inherit;">patch 逻辑:</span><br><span style="font-size: inherit;">1. 新 text 无值(children 有值)</span><br><span style="font-size: 12px;"> (1)新旧都有 children --> 执行 updateChildren 函数<br> (2)新 children 有,旧 children 无(旧 text 有)--> 清空 text ,并添加 children<br> (3)新 children 无,旧 children 有 --> 移除 children<br> (4)旧 text 有 --> 清空 text<br></span><span style="font-size: inherit;">2. 新 text 有值(children 无值),且新 text 不等于 旧 text,--> 移除旧 children,并设置新 text</span><br>
updateChildren 函数(key 的重要性)<br>
1. 开始和开始对比,如果命中(sameVnode true),则执行 patchVnode 函数<br>2. 结束和结束对比,同上;<br>3. 开始和结束对比,同上;<br>4. 结束和结束对比,同上;<br>5. 以上都未命中,拿新节点 key ,能否对应上 oldCh 中的某个节点的 key:<br> (1)没对应上 --> 插入新节点<br> (2)对应上了,判断 sel 是否相等(sameVnode 的条件)<br> 1)sel 不相等 --> 插入新节点<br> 2)sel 相等且 key 相等 --> 执行 patchVnode 函数<br><br>以上细节不必深究<br>
模板编译
渲染过程
前端路由
Vue 面试真题演练
Vue 使用
Vue 基本使用
指令、插值
插值、表达式<br>指令、动态属性<br>v-html:有 XSS 风险,会覆盖子组件
computed 和 watch
computed
computed 有缓存,data 不变则不会重新计算<br>
v-model(双向数据绑定)如果绑定 computed 里的计算属性,一定要有 get 和 set,否则会报错
watch
watch 如何深度监听? handler, deep: true
watch 监听引用类型,拿不到 oldVal。因为指针相同,此时已经指向了新的 val
class 和 style
使用动态属性<br>使用驼峰式写法
条件渲染
`
v-if v-else的用法,可使用变量,也可使用 === 表达式
v-if 和 v-show 的区别?<br>v-if 只渲染满足条件的,v-show 元素总是会被渲染,只切换元素的 CSS display 属性;<br>v-if 有更高的切换开销, v-show 有更高的初始渲染开销<br>
v-if 和 v-show 的使用场景<br>一次性或更新不频繁使用v-if,切换比较频繁使用v-show
循环(列表)渲染<br>
如何遍历对象? v-for
key 的重要性,key 不能乱写(如 random 或 index)<br>高效更新虚拟 DOM:如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。<br>
v-for 和 v-if 不能一起使用<br>v-for 的计算优先级高于 v-if,一起使用会先进行 for 循环再分别进行 if 判断
事件
修饰符
事件修饰符
按键修饰符
表单
<ol><li>v-model </li><li>常见表单项 textarea checkbox radio select </li><li>修饰符 lazy number trim</li></ol>
Vue 父子组件如何通讯
props, $emit:<br>props 父组件向子组件 传递一个数据<br>$emit 子组件向父组件 传递一个事件 <br>
Index 父组件
Input 子组件
如何用自定义事件进行 Vue 组件通讯
List 子组件
event 是 vue 的一个实例
event.$emit 触发一个自定义事件
event.$on 绑定一个自定义事件<br>
event.$off 销毁一个自定义事件(及时销毁,否则可能造成内存泄露)
beforeDestory 的使用场景之一:及时解绑自定义事件
Vue 组件生命周期
生命周期图示
挂载阶段:(beforeCreate,created;beforeMount,mounted)<br>更新阶段:(beforeUpdate,updated)<br>销毁阶段:(beforeDestroy,destroyed)
created 和 mounted 的区别?<br>created:vue 的实例已经初始化完成,但页面还没有开始渲染<br>mounted:页面已经渲染完了,大部分操作(ajax 获取信息、绑定事件等)都在 mounted 里面完成<br>
beforeDestroy 里可能要做什么?<br>解除绑定,销毁子组件以及事件监听器<br>例如:自定义事件的绑定要解除;setTimeout 定时任务要销毁;自己绑定的 window 或 document 事件要销毁<br>总之,该销毁的销毁,不要遗留在内存中
Vue 父子组件生命周期调用顺序
挂载阶段:创建初始化实例是从外到内的,但是渲染是从内到外的<br>父 beforeCreate<br>父 created<br>父 beforeMount<br>子 beforeCreate<br>子 created<br>子 beforeMount<br>子 mounted<br>父 mounted<br>
更新阶段:<br>父 beforeUpdate<br>子 beforeUpdate<br>子 updated<br>父 updated<br>
销毁阶段:<br>父 beforeDestroy<br>子 beforeDestroy<br>子 destroyed<br>父 destroyed
面试官会考察哪些 Vue 高级特性
自定义 v-model<br>$nextTick<br>refs<br>slot(插槽)<br>动态、异步组件(很多面试者不知道)<br>keep-alive (缓存、提升性能)<br>mixin(项目复杂、多个组件之间有重复逻辑时使用)
Vue 如何自己实现 v-model
使用场景
如:颜色选择器
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突
Vue 组件更新之后如何获取最新 DOM
$nextTick
使用场景
Vue 组件更新后获取最新 DOM
基本使用
Vue 是异步渲染(React 也是)<br>data 改变之后,DOM 不会立刻渲染<br>$nextTick 会在 DOM 渲染之后被触发,以获取最新 DOM 节点<br>
异步渲染,$nextTick 待 DOM 渲染完成再回调<br>页面渲染时会将 data 的修改进行合并,多次 data 修改只会渲染一次<br>
$refs 获取dom节点:<br>给相应的元素加 ref="name", 然后通过 this.$refs.name 获取到该元素<br>
slot 是什么
基本使用
slot 插槽作用:让父组件可以往子组件中插入一段内容(不一定是字符串,可以是其它的组件,只要是符合 Vue 标准的组件或者标签都可以)<br>如果父组件不插入内容,就会使用子组件 slot 标签中默认内容<br>
作用域插槽
作用域插槽的作用:让父组件可以访问到子组件的数据(不是很常用,但要知道)
具名插槽
Vue 的动态组件是什么
使用场景
需要根据数据动态渲染组件的场景,比如:新闻落地页,图文视频组件的不同排列组合。
基本使用
<component :is="component-name">
Vue 如何异步加载组件
异步组件
使用场景
按需加载,异步加载大组件(Vue 常见性能优化)
基本使用
import CustomVModel from './CustomVModel'<br>这种引入方式为同步加载,打包时也是打一个包出来<br>
import() 函数
异步组件引入方式:<br>直接在 components 中注册<br>FormDemo: () => import('../BaseUse/FormDemo')<br>
异步组件只有在用到的时候才会加载,且会把结果缓存起来供未来重渲染
Vue 如何缓存组件
keep-alive
使用场景
缓存组件<br>频繁切换,不需要重复渲染(如 tab 切换)<br>Vue 常见性能优化<br>
基本使用
如果去掉 keep-alive,切换 tab 时,会先销毁当前组件,再挂载新组件
keep-alive 、v-show、v-if 的区别
v-if:通过销毁 DOM 和重构 DOM 控制显示隐藏 DOM<br>v-show:通过 CSS 的 display 控制显示隐藏 DOM<br>keep-alive:是 Vue 框架层级进行的 js 对象的渲染(一个组件就是一个 js 对象)<br>
Vue 组件如何抽离公用逻辑
mixin
使用场景
多个组件有相同的逻辑,抽离出来<br>
基本使用
mixin 可以添加多个,会自动合起来
mixin 的问题
变量来源不明确,不利于阅读(mixin中的变量或者方法在当前组件是查不到的)
多mixin可能会造成命名冲突(如data变量,方法名称,只要是有名称的都有可能冲突,但是像mounted函数不会冲突,这是vue特有的生命周期,里面的代码会合并
mixin和组件可能出现多对多的关系,复杂度较高(一个组件引多个mixin,多个组件引用一个mixin)
Vue 3 提出的 Composition API 旨在解决这些问题
Vue 高级特性知识点小结
相关面试技巧
可以不太深入,但必须知道<br>熟悉基本用法,了解使用场景<br>结合项目经验<br>
Vuex 知识点串讲
Vuex 使用
面试考点并不多(因为熟悉 Vue 之后,Vuex 没有难度)<br>但基本概念、基本使用和 API 必须要掌握<br>可能会考察 state 的数据结构设计<br>
Vuex 基本概念
state<br>getters<br>action<br>mutation<br>
用于 Vue 组件
dispatch <br>commit<br>mapState<br>mapGetters<br>mapActions<br>mapMutations
Vuex 图示
actions 里面才能做异步操作,常用的就是 ajax 请求后端 API<br>actions 还会整合多个mutations原子操作<br>
mutation 里是原子操作,力求原子、最小、同步
vue-router 知识点串讲
vue-router 使用
面试考点并不多(前提是熟悉 Vue)<br>路由模式(hash、H5 history)<br>路由配置(动态配置、懒加载)<br>
vue-router 路由模式
hash 模式(默认),如 http://abc.com/#/user/10<br>
H5 history 模式,如 http://abc.com/user/20(需要server端支持,因此无特殊要求可选择 hash 模式)
H5 history 模式的配置
vue-router 路由配置
动态配置
懒加载
Vue 使用考点总结
v-if 和 v-show 的区别<br>
v-if 通过销毁 DOM 和重构 DOM 控制显示隐藏 DOM<br>v-show 通过 CSS 的 display 控制显示隐藏 DOM<br>更新不频繁使用 v-if,切换比较频繁使用 v-show
为何 v-for 中要用 key<br>
key 主要用来做 dom diff 算法用的<br>key 需要使用与业务相关的唯一 id,不要使用 index
描述 Vue 组件的生命周期(有父子组件的情况)
Vue 组件如何通讯
1.父子组件,使用属性和触发事件<br>2.组件之间无关或者层级较深时,使用自定义事件<br>3.Vuex通讯
Vue 原理
组件化和 MVVM
组件化
传统组件:只是静态渲染,更新还要依赖于操作 DOM
数据驱动视图:不直接操作 DOM,通过修改数据重新渲染视图(Vue MVVM、React setState)
MVVM:Model-View-ViewModel
View:视图,就是 DOM<br>Model:模型,就是 Vue 组件里的 data,或 Vuex 里的数据<br>ViewModel:View 和 Model 通过 ViewModel 来做关联,像监听事件、监听指令<br>
响应式
组件 data 的数据一旦变化,立刻触发视图的更新
监听 data 变化的核心 API:Object.defineProperty
基本用法
如何深度监听 data 变化
示例
深度监听,递归到底
Vue 如何监听数组变化
示例
重新定义数组原型:先获取数组原型,然后创建一个新的对象指向数组原型(仅是指向,再扩展新的方法不会影响原型)
Object.create 隔离原型链指向
忌:污染全局的 Array 原型
Object.defineProperty 缺点
<ol><li><span style="font-size: inherit;">深度监听,需要递归到底,一次性计算量大</span></li><li><span style="font-size: inherit;">无法监听新增属性/删除属性(Vue.set Vue.delete)</span></li><li><span style="font-size: inherit;">无法原生监听数组,需要特殊处理</span></li></ol>
Vue3.0 启用 Proxy,但 Proxy 兼容性不太好,且无法用 polyfill
vdom 和 diff
虚拟 DOM (Virtual DOM)
vdom 是实现 Vue 和 React 的基石
背景
<ul><li>DOM 操作非常耗费性能</li><li>以前用 jQuery,可以自行控制 DOM 操作的时机,手动调整</li><li>Vue 和 React 是数据驱动视图,如何有效控制 DOM 操作</li></ul>
解决方案
<ul><li>有了一定复杂度,想减少计算次数比较难。DOM 的改变很耗时。</li><li>能不能把计算,更多的转移为 JS 计算? JS 执行速度很快<br></li><li>vdom 用 JS 模拟 DOM 结构,计算出最小的变更,操作 DOM</li></ul>
用 JS 模拟 DOM 结构
snabbdom
<ul><li>简洁强大的 vdom 库,易学易用</li><li>Vue 参考它实现的 vdom 和 diff</li><li>https://github.com/snabbdom/snabbdom</li></ul>
Vue3.0重写了vdom的代码,优化了性能
代码演示
h 函数<br>vnode 数据结构<br>patch 函数
h 函数:接受三个参数(选择器,属性,子节点数组),返回一个 vnode 结构
vnode 数据结构
patch(container, vnode) 初次渲染<br>patch(vnode, newVnode) DOM 更新<br>
vdom 总结
<ul><li>用 JS 模拟 DOM 结构(vnode)</li><li>新旧 vnode 对比,得出最小的更新范围,最后更新 DOM</li><li>数据驱动视图的模式下,有效控制 DOM 操作</li></ul>
diff 算法
diff 算法是 vdom 中最核心、最关键的部分
diff 算法概述
<ul><li>diff 即对比,是一个广泛的概念,如 linux diff 命令、git diff 等</li><li>两个 js 对象也可以做 diff,如 https://github.com/cujojs/jiff</li><li>两棵树做 diff,如这里的 vdom diff</li></ul>
树 diff 的时间复杂度 O(n^3)
遍历 tree1;遍历 tree2;排序<br>1000个节点,要计算1亿次,算法不可用<br>
优化时间复杂度到 O(n)
<ul><li>只比较同一层级,不跨级比较</li><li>tag 不相同,则直接删掉重建,不再深度比较</li><li>tag 和 key,两者都相同,则认为是相同节点,不再深度比较</li></ul>
深入 diff 算法源码
看源码,不用过于关注细节<br>找到核心函数,查看其参数、返回值、主要逻辑
生成 vnode
h 函数<br>
参数:选择器,属性,子节点数组<br>返回值:通过 vnode 函数返回 vnode 结构
vnode 函数
返回 vnode 结构
patch 函数
参数:oldVnode,vnode
第一个参数不是 vnode,则创建一个空的 vnode,关联到 这个 DOM 元素
相同的 vnode(key 和 selector 都相等),执行 patchVnode 函数
如果都没有传 key,则 undefined === undefined true
不相同的 vnode,直接删掉重建
patchVnode 函数
vnode 对比
参数:oldVnode,vnode
设置 vnode.elem,赋值成旧的 elem<br>
<span style="font-size: inherit;">patch 逻辑:</span><br><span style="font-size: inherit;">1. 新 text 无值(children 有值)</span><br><span style="font-size: 12px;"> (1)新旧都有 children --> 执行 updateChildren 函数<br> (2)新 children 有,旧 children 无(旧 text 有)--> 清空 text ,并添加 children<br> (3)新 children 无,旧 children 有 --> 移除 children<br> (4)旧 text 有 --> 清空 text<br></span><span style="font-size: inherit;">2. 新 text 有值(children 无值),且新 text 不等于 旧 text,--> 移除旧 children,并设置新 text</span><br>
updateChildren 函数(key 的重要性)<br>
1. 开始和开始对比,如果命中(sameVnode true),则执行 patchVnode 函数<br>2. 结束和结束对比,同上;<br>3. 开始和结束对比,同上;<br>4. 结束和结束对比,同上;<br>5. 以上都未命中,拿新节点 key ,能否对应上 oldCh 中的某个节点的 key:<br> (1)没对应上 --> 插入新节点<br> (2)对应上了,判断 sel 是否相等(sameVnode 的条件)<br> 1)sel 不相等 --> 插入新节点<br> 2)sel 相等且 key 相等 --> 执行 patchVnode 函数<br><br>以上细节不必深究<br>
模板编译
渲染过程
前端路由
0 条评论
下一页