# Task-03-02 **Repository Path**: fishlyn/task-03-02 ## Basic Information - **Project Name**: Task-03-02 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-24 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Part3 模块二 作业 ## 第一题 **一、请简述 Vue 首次渲染的过程** 答: 1. Vue 首次渲染的时候,会先初始化 Vue,在 Vue 上挂载相应的实例方法和静态方法 ```js // src/core/instance/index.js function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } // 挂载实例方法 initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) // src/core/index.js // 注册 Vue 的静态属性/方法 initGlobalAPI(Vue) // src/platforms/web/runtime/index.js // 初始化了和平台相关的指令和组件 Vue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement extend(Vue.options.directives, platformDirectives) extend(Vue.options.components, platformComponents) Vue.prototype.__patch__ = inBrowser ? patch : noop Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating) } // src/platforms/web/entry-runtime-with-compiler.js // 重写了上个文件的 $mount 方法,增加模板编译的功能 const mount = Vue.prototype.$mount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { ... return mount.call(this, el, hydrating) } ``` 2. 当 new Vue 使用构造函数的时候,会触发 _init 方法,开始渲染 3. _init 方法挂载在 Vue 的原型对象上,用于判断是否是 Vue 实例,还是组件,首次渲染是先渲染 Vue 实例,所以会将用户设置的 options 选项和 Vue 实例的 options 选项合并,然后会进行代理对象的设置以及挂载相应的实例方法,最后调用实例的 $mount 方法进行模板的编译 4. $mount 渲染模板,会先判断是否传入了 render 函数,若传入了则跳过判断,继续执行,若没传入,判断是否传入了 template,若传入了会通过 compileToFunctions() 将 template 转成 render 函数,并挂载到 options,最后执行实例原有的 $mount 方法并返回 5. 以上是带编译器版本的重写了 $mount 方法,为了编译 template,并将结果重新挂载到 options 的 render 函数上,并返回默认的 $mount 执行结果,默认的 $mount 需要重新获取 el 元素,因为可能会直接执行不带编译器版本的,并返回 mountComponent() 6. **mountComponent 方法的作用是将 render 函数执行后返回的虚拟 DOM 转换成真实 DOM** mountComponent 会先判断 render 函数是否存在,template 在运行时版本下会报警告, 之后会触发实例的生命周期钩子函数 beforeMount,挂载之前, 之后才开始定义 updateComponent 方法,目的是将虚拟 DOM 转换成真实 DOM,调用 Vue 实例的 _render 方法执行 options 的 render 方法,并将返回的虚拟 DOM 通过实例的 _update 方法转换成真实 DOM, 然后会调用 Watcher 构造函数执行 updateComponent 方法 最后触发 mounted 方法后,结束挂载 7. Watcher 构造函数的作用是用于统一渲染视图 **二、请简述 Vue 响应式原理** 答: 1. Vue 响应式是将 data 的数据转换成响应式的数据,在初始化 Vue 的时候,调用 initState 初始化 Vue 实例的状态,在该方法内通过调用 initData 方法将 data 属性注入到 Vue 实例上,并通过 observe 方法将 data 转换成响应式的数据 2. observe 方法会先对传入的对象进行判断,判断是否是对象,否则直接返回,判断对象是否含有 \_\_ob\_\_,如果有则是已经做过响应式数据处理了,直接返回,如果都没有,则创建 Observer 对象,并返回 3. Observer 对象会给 data 对象添加不可枚举的 \_\_ob\_\_ 属性,并记录当前的 Observer 对象,并进行数组和对象的响应化处理 4. 数组的响应化处理主要是处理数组的那些会改变原数组的方法,比如 push,sort等方法,在触发这些方法后通过调用 dep 的 notify 去重新渲染,并且遍历新数组的数据,调用 observe 方法将未响应化的数据进行处理 5. 对象的响应化处理,通过调用 walk 方法,遍历对象的数据,并对每个数据调用 defineReactive 6. defineReactive 方法为每一个属性创建了 dep 对象,用于收集依赖,如果属性的值是对象,会递归执行 observe 方法将对象的所有属性转成响应式数据,并且给每个属性添加了 getter/setter,getter 用于依赖收集,setter 用于保存新值,如果新值是对象,则执行 observe,并在触发 setter 的时候将新值通过 dep.notify 更新 7. 在 getter 中收集依赖的过程,会执行 dep 中的 depend 方法,该方法会执行 Dep 实例属性 target 的 addDep 方法,target 的值为 Watcher 对象,而 addDep 方法会执行 dep 的 addSub 方法,将 Watcher 对象添加到 dep 的 subs 数组中,为属性收集依赖,如果属性的值也是对象,则会创建 childOb 对象收集子对象的依赖 8. Watcher 对象,会在 dep 调用 notify 时候触发 watcher 对象的 update 方法,该方法会调用 queueWatcher 方法判断 watcher 是否被处理,如果没有的话会添加到 queue 队列中,并调用 flushSchedulerQueue 方法刷新队列,并调用 watcher.run 方法渲染页面 **三、请简述虚拟 DOM 中 Key 的作用和好处** 答:在 Vue 中,使用 v-for 指令的时候,可以给每个节点设置 key 属性,跟踪每个元素的身份,以便重用和重新排序现有元素 好处也是显而易见的,如果不使用 key,元素会在每一次改变的时候重新渲染成新的元素,而使用了 key,则会表明每个元素的身份,对于未改变的元素,会进行重用,从而减少 DOM 的渲染,并且在使用表单元素单选框的时候,同样可以记录当前选中的元素,从而在新增元素的时候,仍能保持当前的选中状态 **四、请简述 Vue 中模板编译的过程** 答: 1. 模板的编译从函数 compileToFunctions 开始,会先从缓存中加载编译好的 render 函数,如果没有,则调用 compile 函数进行编译 2. compile 函数会先将用户设置的 options 和 Vue 构造函数的 options 进行合并,然后将 template 和合并后的 options 传入 baseCompile 函数进行处理 3. 在函数 baseCompile 内会进行模板编译核心的三件事 1. 执行 parse() ,将 template 转换成 AST 抽象语法树 2. 执行 optimize() ,对抽象语法树进行优化,标记所有的静态根节点,静态根节点不需要每次都进行重绘,patch 的时候会跳过静态根节点 3. 执行 generate(),将抽象语法树转换成字符串格式的 js 代码 4. 将上一步获取的字符串 js 代码转换成函数,通过 createFunction 方法 5. 最后挂载到 Vue 实例的对应的 options 选项上