# 一、简答题
# 1、当我们点击按钮的时候动态给data增加的成员是否是响应式数据,如果不是的话,如果把新增成员设置成响应式数据,它的内部原理是什么?
let vm = new Vue({
el: '#el',
data: {
o: 'object',
dog: {}
},
method: {
clickHandler () {
// 该 name 属性是否是响应式的
this.dog.name = 'Trump'
}
}
})
答案: 点击按钮的时候动态给data增加的成员不是响应式的数据。响应式数据是在new Vue()的时候,创建observer实例的时候数据变为响应式的,通过点击按钮给data动态新增成员只是给vm data添加了一个属性而已,这个时候vue已经实例化过了,新增的属性不会是相应式的。vue中不允许动态添加根级别的相应式数据。我们可以使用 Vue.set(object, propoertyName, value)方法想嵌套对象添加响应式属性。
# 2、请简述 Diff 算法的执行过程
答: diff算法是比较同级节点依次比较,然后在找下一级别的节点比较。 1、首先会对新老节点数组的开始和结尾设置标记索引,遍历过程中移动索引。 在比较新老节点的时候,会依次按照下面几种情况进行比较
- 老节点的开始节点和新开始节点进行比较,如果el和key都相同,会执行patchVnode更新dom,索引往下移动。如果不同则会按照下面条件
- 老节点的结束节点和新节点的结束节点进行比较,如果相同,执行patchVnode更新dom,索引移动。如果不同继续比较
- 老节点开始节点和新结束节点进行比较,如果相同调用patchVnode更新节点,把老开始节点移动最右边,更新索引。如果不同进行下面比较
- 老节点结束节点和新开始节点进行比较,如果相同调用patchVnode更新节点,把老节点对应dom移动到最左边
- 如果上面都不满足,即上面的比较都不相同,那会是新开始节点的key在老节点数组中找相同节点。如果找不到,说明新开始节点是新节点,创建新节点对应dom,插入到dom树中,如果找到了。判断新节点和找到的老节点del选择器是否相同,如果不同说明节点被就该,插入到dom树中,如果相同移动到最左边。 2、当新老节点的所有自己点遍历完,循环结束,循环结束后,然后做后续相应操作。新的有剩余,把剩余插入到右边,老的有剩余,删掉剩余节点。
# 二、编程题
# 1、模拟VueRouter 的 hash 模式的实现,实现思路和 History 模式类似,把 URL 中的 # 后面内容作为路由的地址,可以通过 hashchange 事件监听路由地址的变化。
答:
let _Vue = null
export default class VueRouter {
static install (Vue) {
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
_Vue = Vue
_Vue.mixin({
beforeCreate () {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
constructor (options) {
this.options = options
this.routeMap = {}
this.data = _Vue.observable({
current: '/'
})
}
init () {
this.createRouteMap()
this.initComponents(_Vue)
}
createRouteMap () {
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
console.log(this.routeMap)
}
initComponents (Vue) {
Vue.component('router-link', {
props: {
to: String
},
render (h) {
return h('a', {
attrs: {
href: this.to
}
}, [this.$slots.default])
}
// template: '<a :href="to"><slot></slot></a>'
})
const self = this
Vue.component('router-view', {
render (h) {
const component = self.routeMap[self.data.current]
console.log(component)
return h(component)
}
})
}
}
# 2、在模拟 Vue.js 相应式源码的基础上实现 v-html 指令,以及 v-on 指令。
答 : 代码见code中 mini-vue 项目,js中compiler.js 文件 其中关键代码片段:
class Compiler {
constructor (vm) {
this.el = vm.$el
this.vm = vm
this.compiler(this.el)
}
// 编译模板,处理文本节点和元素节点
compiler (el) {
let childNodes = el.childNodes
Array.from(childNodes).forEach(node => {
// 处理文本节点
if (this.isTextNode(node)) {
this.compileText(node)
}else if (this.isElementNode(node)) {
// 处理元素节点
this.compileElement(node)
}
// 判断node节点是否有子节点,如果有子节点,要递归调用compile
if (node.childNodes && node.childNodes.length) {
this.compiler(node)
}
})
}
// 编译元素节点,处理指令
compileElement (node) {
// console.dir(node)
// 遍历所有的属性节点
Array.from(node.attributes).forEach(attr => {
// 判断是否是指令
let attrName = attr.name
if (this.isDirective(attrName)) {
// v-text --> text
attrName = attrName.substr(2)
let key = attr.value
this.update(node, key, attrName)
}
})
}
update (node, key, attrName) {
let updateFn = this[attrName + 'Updater']
updateFn && updateFn.call(this, node, this.vm[key], key)
}
// 处理 v-text 指令
textUpdater (node, value, key) {
node.textContent = value
new Watcher(this.vm, key, newValue => {
node.textContent = newValue
})
}
// 处理 v-html 指令
htmlUpdater (node, value, key) {
node.innerHTML = value
new Watcher(this.vm, key, newValue => {
node.innerHTML = newValue
})
}
// 处理 v-on 指令
onUpdater (node, value, key) {
console.log(node)
console.log(value)
console.log(key)
node.addEventListener('')
debugger
}
// v-model
modelUpdater (node, value, key) {
node.value = value
new Watcher(this.vm, key, newValue => {
node.value = newValue
})
// 双向绑定
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
// 编译文本节点,处理差值表达式
compileText (node) {
// {{ msg }}
// 正则表达式
let reg = /\{\{( .+?)\}\}/
let value = node.textContent
if (reg.test(value)) {
let key = RegExp.$1.trim() // 获取匹配正则里的值
node.textContent = value.replace(reg, this.vm[key])
// c创建watcher对象,当数据改变 更新视图
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
}
// 判断元素属性是否是指令
isDirective (attrName) {
return attrName.startsWith('v-')
}
// 判断节点是否是文本节点
isTextNode (node) {
return node.nodeType === 3
}
// 判断元素节点是否是元素节点
isElementNode (node) {
return node.nodeType === 1
}
}
# 3、参考 Snabbdom 提供的电影列表的示例,利用 Snabbdom 实现类似的效果,如图:
答: 1、答案在code中,sanbbdom-demo下,见04-cinema.js文件 2、也可在github中访问代码,https://github.com/bootstet/sanbbdom-study (opens new window)
← webpack nodejs高级编程 →