# fed-e-task-03-01
**Repository Path**: learning-summary/fed-e-task-03-01
## Basic Information
- **Project Name**: fed-e-task-03-01
- **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-12-21
- **Last Updated**: 2021-01-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 一、简答题
### 1、当我们点击按钮的时候动态给 data 增加的成员是否是响应式数据,如果不是的话,如何把新增成员设置成响应式数据,它的内部原理是什么。
```js
let vm = new Vue({
el: '#el'
data: {
o: 'object',
dog: {}
},
method: {
clickHandler () {
// 该 name 属性是否是响应式的
this.dog.name = 'Trump'
}
}
})
```
> 不是响应式数据,需要通过Vue.set或者this.$set来设置成响应式数据,其内部的原理其实还是通过Observer进行数据劫持和响应
### 2、请简述 Diff 算法的执行过程
Diff算法主要是调用path的过程
**真正主要的还是pathVnode和updateChildren方法**
**path调用过程中判断是相同节点后会调用pathVnode方法**
分为不是文本节点的情况和是文本节点的情况
- 不是文本节点
- 旧oldCh和新ch子节点都存在,两个子节点没指向同一个地址引用,就调用updateChildren方法
- 只有新ch存在,就往elm上添加新ch节点
- 只有旧oldCh存在就,从elm上移除旧oldCh节点
- 只有旧oldCh的text存在就将elm设置为''
- 文本节点并且不相等的情况下
- 如果旧oldCh存在就从elm上移除旧oldCh,然后给elm设置成新的vnode.text
**最后是updateChildren方法**
主要分为主要阶段
第一阶段对比阶段
- 旧开始节点和新开始节点的对比
- 旧结束节点和新开始节点的对比
- 旧开始节点和新结束节点的对比
- 新开始节点和旧结束节点的对比
- 都不满足的话就查找相同key的节点,如果找到了就用找打的插入旧开始DOm之前,如果没找到就创建dom元素,然后插入旧dom开始之前
对比完成后更新节点,最后是收尾工作
旧节点或者新节点没有遍历完的时候,就会进行收尾
- 如果新节点有多余,就把剩余的新节点插入到旧节点右边
- 若干旧节点有多余,就删除掉多余的节点
## 二、编程题
#### 1、模拟 VueRouter 的 hash 模式的实现,实现思路和 History 模式类似,把 URL 中的 # 后面的内容作为路由的地址,可以通过 hashchange 事件监听路由地址的变化。
```js
let _Vue = null
export default class VueRouter {
constructor (options) {
// 实例化router的options
this.options = options
// 路由与组件的映射关系对象
this.routerMap = {}
// 创建相响应式的current属性, 当前的路由名字
this.data = _Vue.observable({
current: this.initCurrentHash()
})
}
static install (Vue) {
// 1.判断有没有安装vue-router
if (this.install.installed) return
this.install.installed = true
// 2.挂载在全局
_Vue = Vue
// 3.将实例化的router挂载vue原型上 这里可以获取到vue实例
Vue.mixin({
beforeCreate () {
if (this.$options.router) {
// 存在即代表是new Vue实例化的
Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
// 初始化方法
init () {
this.createRouteMap()
this.initComponent(_Vue)
this.initEvent()
}
// 创建路由的映射关系
createRouteMap () {
this.options.routes.forEach(route => {
this.routerMap[route.path] = route.component
})
}
// 初始化组件
initComponent (Vue) {
const self = this
Vue.component('router-link', {
props: {
to: String
},
methods: {
clickEvent (e) {
window.location.hash = this.to
self.data.current = this.to
e.preventDefault()
}
},
render (h) {
return h('a', {
attrs: {
to: this.to
},
on: {
click: this.clickEvent
}
}, this.$slots.default)
}
})
Vue.component('router-view', {
render (h) {
const component = self.routerMap[self.data.current]
return h(component)
}
})
}
// 初始化hash
initCurrentHash () {
// 保证刷新还是当前页面
window.location.hash = window.location.hash || '#/'
return window.location.hash.slice(1)
}
// 初始化事件
initEvent () {
window.addEventListener('hashchange', () => {
this.data.current = location.hash.slice(1)
})
}
}
}
}
('hashchange', () => {
this.data.current = location.hash.slice(1)
})
}
}
```
#### 2、在模拟 Vue.js 响应式源码的基础上实现 v-html 指令,以及 v-on 指令。
主要代码,参考本地目录/code/03-vue基本结构/js/compiler.js
```js
// 根据不同的指令调取不同的方法方便维护
update (node, key, attrName) {
let eventType
if (attrName.includes('on:')) {
let arrtNameArr = attrName.split(':')
attrName = arrtNameArr[0]
eventType = arrtNameArr[1]
}
let fn = this[attrName + 'Updater']
fn && fn.call(this, node, this.vm[key], key, eventType)
}
// 处理v-html
htmlUpdater (node, value, key) {
node.innerHTML = value
new Watcher(this.vm, key, (newValue) => {
node.innerHTML = newValue
})
}
// 处理v-on事件,举例v-on:click="clickEvent"
onUpdater(node, value, key, eventType) {
// 如果事件类型不存在就return掉
if(!eventType) return
if(typeof value !== 'function') return
node.addEventListener(eventType, value)
new Watcher(this.vm, key, (newValue) => {
// node.removeEventListener(eventType, value)
node.addEventListener(eventType, newValue)
})
}
```
#### 3、参考 Snabbdom 提供的电影列表的示例,利用Snabbdom 实现类似的效果,如图:

**index.html**
```html