# fed-e-task-03-02 **Repository Path**: frontend_site/fed-e-task-03-02 ## Basic Information - **Project Name**: fed-e-task-03-02 - **Description**: Vue.js 源码分析(响应式、虚拟 DOM、模板编译和组件化); - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-27 - **Last Updated**: 2023-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: Vue ## README Vue.js 源码剖析-响应式原理、虚拟 DOM、模板编译和组件化 ## 一、简答题 ### 1、请简述 Vue 首次渲染的过程。 1. new Vue({}),实例化Vue; 2. this._init(),处理传入参数,包括对数据进行响应式处理; 3. this.$mount(),开始挂载工作,判断生成render属性,执行mountComponent函数; 4. mountComponent,定义updateComponent函数,new Watcher(vm, updateComponent, noop, {}); 5. this._render(),生成 vnode节点,作为 this._update函数参数; 6. this._update(this._render(), hydrating),通过createElm函数生成真实dom元素,并插入到el元素中; ### 2、请简述 Vue 响应式原理。 响应式原理:通过`Object.defineProperty`,结合发布订阅设计模式,实现响应式;具体实现:通过`Object.defineProperty`,将`data`,`Dep`、`Watcher`、更新视图操作,整合为一个整体; 1. 传入数据(data)后,递归数据,对data上的每个属性(除数组),通过`Object.defineProperty`进行`getter`、`setter`设置,在`Object.defineProperty`前,通过`new Dep()`,创建dep实例,与属性一一对应; 2. 在`getter`函数中,判断`Dep.target`(Wacher)是否存在,若存在则往`dep.subs`数组中添加`Dep.target`; 3. 在`setter`函数中,调用`dep.notify`函数,即遍历`dep.subs`数组,通知watcher实例,调用`new Watcher`时作为参数传入的更新视图函数,则再次修改`data`属性时,会触发`setter`函数; 4. 在首次渲染页面时,首先通过`new Watcher`,创建watcher实例,并将watcher挂载到`Dep.target`上,之后通过`vnode`创建真实`dom`节点并挂载到页面时,会触发`getter`函数,此时`Dep.target`存在,往`dep.subs`中添加`Dep.target`; 5. 之后再次修改数据时,则通过触发`setter`函数,达到数据响应式的效果; ### 3、请简述虚拟 DOM 中 Key 的作用和好处。 1. 作用:以便它能够跟踪每个节点的身份,在进行比较的时候,会基于 key 的变化重新排列元素顺序。从而重用和重新排序现有元素,并且会移除 key 不存在的元素。方便让 vnode 在 diff 的过程中找到对应的节点,然后成功复用; 2. 好处:可以减少 dom 的操作,减少 diff 和渲染所需要的时间,提升性能; ### 4、请简述 Vue 中模板编译的过程。 1. 以`compileToFunctions`函数作为入口,传入template与参数对象; 2. 在`baseCompile`函数中,首先通过`parse`函数,将template转化为ast,即抽象语法树,再通过`optimize`函数对其进行优化,即标记处静态节点,静态根节点,最后通过`generate`函数将ast生成字符串形式的`render`函数与`staticRenderFns`函数数组,`staticRenderFns`便于使用缓存作为优化方式; 3. 最后通过`new Function(params)`的方式,将`render`、`staticRenderFns`转化为函数,并合并为对象返回; ### 5、Vue computed 实现逻辑 ```js // file: core/instance/state.js // 将开发者定义的 getter 函数,挂载到 watcher.getter 上, // 在 watcher.get 中执行,并将 Dep.target 赋值为当前 watcher 对象 watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) // 通过该方法,重写 getter 函数 defineComputed(vm, key, userDef) sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef) function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { // new Watcher, watcher.update() 将 watcher.dirty 修改为 true if (watcher.dirty) { // 调用该函数, 最后会将 watcher.dirty 修改为 false watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } } } // file: core/observer/watcher.js /** * Subscriber interface. * Will be called when a dependency changes. */ update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { // vue异步渲染逻辑入口 queueWatcher(this) } } /** * Evaluate the value of the watcher. * This only gets called for lazy watchers. */ evaluate () { // 执行 watcher.getter 函数,并将 Dep.target 赋值为当前 watcher 对象 this.value = this.get() this.dirty = false } ```