# fed-e-task-03-01 **Repository Path**: frontend_site/fed-e-task-03-01 ## Basic Information - **Project Name**: fed-e-task-03-01 - **Description**: 手写 Vue Router、手写响应式实现、虚拟 DOM 和 Diff 算法 - **Primary Language**: JavaScript - **License**: ISC - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-27 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、简答题 1、当我们点击按钮的时候动态给 data 增加的成员是否是响应式数据,如果不是的话,如果把新增成员设置成响应式数据,它的内部原理是什么。 ```javascript let vm = new Vue({ el: '#el' data: { o: 'object', dog: {} }, method: { clickHandler () { // 该 name 属性是否是响应式的 this.dog.name = 'Trump' } } }) 动态给data增加的成员不是响应式数据,响应式对象和响应式数组是指在vue初始化时期,利用Object.defineProperty()方法对其进行监听,这样在修改数据时会及时体现在页面上; 在使用Vue.set/this.$set函数对data动态添加成员时,其函数里再次调用defineReactive(obj,key,val)将成员挂在到data的待挂载对象上,然后手动调用了待挂载对象的dep.notify(),通知相应Watcher更新视图; ``` 2、请简述 Diff 算法的执行过程 1. diff 的过程就是调用名为`patch `的函数,比较新旧节点,一边比较一边给真实的`dom`打补丁; 2. patch 函数接收两个参数 oldVnode 和 Vnode 分别代表新的节点和之前的旧节点,这个函数会比较 oldVnode 和 vnode 是否是相同的, 即函数` sameVnode(oldVnode, vnode)`, 根据这个函数的返回结果分如下两种情况: - true:则执行 `patchVnode`; - false:则用 vnode 替换 oldVnode; 3. `patchVnode`实现逻辑: - 找到对应的真实 dom,设置成el的值; - 判断 vnode 和 oldVnode 是否指向同一个对象,如果是,那么直接 return,不做任何操作; - 如果他们都有文本节点并且不相等,那么将 el 的文本节点设置为 vnode 的文本节点; - 如果 oldVnode 有子节点而 vnode 没有,则删除 el 的子节点; - 如果 oldVnode 没有子节点而 vnode 有,则将 vnode 的子节点真实化之后添加到 el; - 如果两者都有子节点,则执行 `updateChildren `函数比较子节点; 4. updateChildren实现逻辑: - 初始化必要数据,`oldStartIdx、oldEndIdx、newStartIdx、newEndIdx`等; - 进入`while`循环,进行逻辑判断; - 判断新旧节点是否同时存在,若不同时存在,则仅移动对应的下标位置; - 若同时存在,首先对新旧节点的首位-首位、末位-末位、新节点首位-旧节点末位、新节点末位-旧节点首位进行`sameVnode`比较,若找相同节点,则再次执行`patchVnode`,与相应的`dom`节点插入操作; - 上述判断都不满足,则进入最后的判断逻辑;对oldVnode进行key-下标转换,即使进行键值对(key:下标)的方式存储起来`oldKeyToIdx`; `idxInOld = oldKeyToIdx[newStartVnode.key as string];` - 通过对`idxInOld `的判断,若`idxInOld `不存在,则依次进行`dom`节点插入操作,向后移动`newStartIdx`; - 若`idxInOld `存在,`elmToMove = oldCh[idxInOld];`,进行相应`dom`节点插入操作与`patchVnode`; ```javascript if (elmToMove.sel !== newStartVnode.sel) { api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm as Node); } else { patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); oldCh[idxInOld] = undefined as any; api.insertBefore(parentElm, (elmToMove.elm as Node), oldStartVnode.elm as Node); } ``` - 最后结束`while`循环,通过判断`oldStartIdx > oldEndIdx`,进行`addVnodes`或者`removeVnodes`操作; ## 二、编程题 1、模拟 VueRouter 的 hash 模式的实现,实现思路和 History 模式类似,把 URL 中的 # 后面的内容作为路由的地址,可以通过 hashchange 事件监听路由地址的变化。 2、在模拟 Vue.js 响应式源码的基础上实现 v-html 指令,以及 v-on 指令。 3、参考 Snabbdom 提供的电影列表的示例,利用Snabbdom 实现类似的效果,如图