文章内容输出来源:拉勾大前端高薪训练营
1、请简述 Vue 首次渲染的过程。
答:
首先是,定义Vue构造函数,以及静态方法、原型方法。 其次,调用构Vue造函数创建Vue实例:new Vue(VueOptions) Vue构造函数中会调用this._init方法。this._init方法内部会调用【src/platforms/web/entry-runtime-with-compiler.js】文件中的vm.mount方法,增加了把模板编译成render函数【通过compileToFunctions()方法生成】的功能。
【src/platforms/web/runtime/index.js】文件中的vm.$mount方法里面调用了【src/core/instance/lifecycle.js】文件中的mountComponent(this, el)函数。
在mountComponent(this, el)函数中会判断参数中是否有render选项,如果没有但是传入了模板,并且当前环境是开发环境的话会发送警告。之后会触发beforeMount钩子,
定义updateComponent函数【此函数内部会调用vm._render和vm._update分别生成虚拟dom和将虚拟dom转换为真实dom并挂载到dom树上】,
创建Watcher实例,以及触发mounted钩子,最后返回Vue实例vm。其中创建Watcher实例时会传入updateComponent函数,并且Watcher构造函数内部会立即调用get方法触发updateComponent的立即执行。
以上就是Vue首次渲染的过程。
2、请简述 Vue 响应式原理。
答:
Vue响应式的整个过程是:initState() --> initData() --> observe(value)
initState(): Vue的实例方法,用于初始化Vue实例的状态。
initData(): 在initState方法中被调用,用于把data属性注入到Vue实例上。
observe(value): 在initData中调用,用于将data对象转换会响应式对象。定义于【src/core/observer/index.js】文件中,内部处理逻辑是:1/ 判断value是否是对象,如果不是对象直接返回
2/ 判断value对象是否有__ob__,如果有直接返回
3/ 如果没有,创建observer对象
4/ 返回observer对象
Observer对象:定义于【src/core/observer/index.js】文件中。用于给value对象定义不可枚举的__ob__ 属性,记录当前的observer对象。
包含数组的响应式处理和对象的响应式处理。其中数组的响应式处理主要是对那些可以修改原数组的方法(例如push/pop/splice等)进行包装处理,以便这些方法调用时,及时发送通知,更新数组视图。
对象的响应式处理,就是调用walk方法,该方法就是遍历对象的所有属性,然后调用defineReactive方法。
defineReactive方法: 定义于【src/core/observer/index.js】文件中。用于为每一个属性创建dep对象。如果当前属性的值是对象,调用observe(value)方法。
定义getter方法,用于收集依赖并返回属性的值。定义setter方法,用于保存新值,如果新值是对象,调用observe(value);并且调用dep.notify()发送通知。
收集依赖:
1/在watcher对象的get方法中调用pushTarget记录Dep.target属性;
2/访问data中成员的时候收集依赖(此时defineReactive的getter方法会被触发并执行依赖收集)
3/把属性对应的watcher对象添加到dep的subs数组中
4/给childOb收集依赖,目的在于当子对象添加和删除成员时发送通知。
Watcher对象:
dep.notify()会调用watcher对象的update()方法。
queueWatcher()判断watcher是否被处理,若没有的话就添加到queue队列中,同时调用flushSchedulerQueue()
flushSchedulerQueue()方法内部处理逻辑为:1/ 触发beforeUpdate钩子函数
2/ 调用watcher.run()方法, 对于渲染类Watcher,此方法执行过程为:run()-->get()-->getter()-->updateComponent。
3/ 清空上一次的依赖
4/ 触发actived钩子函数
5/ 触发updated钩子函数
3、请简述虚拟 DOM 中 Key 的作用和好处。
答:
Key的作用就是用作虚拟dom的唯一标识。
Key的好处是,能够最大程度的重用该虚拟dom对应的真实dom,从而提高页面渲染性能。常见应用场景:配合v-for指令使用
4、请简述 Vue 中模板编译的过程。
答:
模板编译的关键流程: compileToFunctions(template, ...) --> compile(template, options) --> baseCompile(template.trim(), finalOptions)。
compileToFunctions(template, ...): 先从缓存中加载编译好的render函数,如果缓存中没有,则调用compile(template, options)编译模板。
compile(template, options): 合并options,并将合并后的options作为参数finalOptions,调用baseCompile(template.trim(), finalOptions)
baseCompile(template.trim(), finalOptions):
1/ parse():把template转换成AST tree
2/ optimize(): 标记AST tree中的静态sub trees; 检测到静态子树,设置为静态,不需要在每次重新渲染的时候重新生成节点;patch阶段将跳过静态子树
3/ generate(): 根据AST tree生成字符串形式的js代码
回到compileToFunctions(template, ...)函数继续执行:
1/ 继续把上一步中生成的字符串形式的js代码通过createFunction方法转换为函数。
2/ render和staticRenderFns初始化完毕后会挂载到Vue实例的options对象中对应的属性中。