v0.0.5 总结一波。完善功能,优化代码逻辑,修复bug
功能点:
- 当v-for存在同级子节点时,patch函数无法正常工作
- 完善生命周期钩子函数功能
- v-model 指令会转换为props[value]和@input事件
- 支持数组类型响应
当v-for存在同级子节点时,patch函数无法正常工作
修改render.js中的createElement
js
// ...
// 在解析子节点时,增加isArray判断,然后合并数组
children.forEach(child => {
if (typeof child === 'string') {
arr.push({ vm, tag: 'text', text: child })
} else if (Array.isArray(child)) {
arr.push.apply(arr, child) // 利用apply的数组参数将两个数组合并
} else {
arr.push(child)
}
})
// ...完善生命周期钩子函数功能
- beforeCreate 此时仅初始化了上下文生命周期,事件,渲染函数,vue组件中的属性还无法通过this直接进行访问,只能通过this.$options访问
js
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')- created 初始化injections、state、provide,此时vue组件的相关数据已经处理完毕,可直接通过this获取prop,methods,data,注意,此时真实DOM还未挂载
js
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
function initState(vm){
vm._watchers = []
const opts = vm.$options
opts.props && initProps(vm, opts.props)
opts.methods && initMethods(vm, opts.methods)
opts.data ? initData(vm) : observe(vm._data = {}, true /* asRootData */)
opts.computed && initComputed(vm, opts.computed)
opts.watch && opts.watch !== nativeWatch && initWatch(vm, opts.watch)
}beforeMount 在mountComponent函数中触发,组件挂载前,render函数执行前
mounted 在mountComponent函数中触发,组件挂载后,render函数执行后,由于微任务的执行顺序影响,在此处有时无法直接获取到el,因此需要借助$nextTick。
beforeUpdate 在每个渲染Watcher(isRenderWatcher===true)的before钩子函数中,当每个组件执行渲染更新前触发
js
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);- updated 在scheduler.js中,每一轮监听响应变化,nextTick时,刷新监听队列中的每个监听器,遍历每个vm的渲染Watcher
js
function callUpdatedHooks (queue) {
var i = queue.length;
while (i--) {
var watcher = queue[i];
var vm = watcher.vm;
if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'updated');
}
}
}- beforeDestroy 组件销毁之前触发
js
Vue.prototype.$destroy = function(){
...
callHook(vm, 'beforeDestroy');
...
}- destroyed 组件销毁后触发
js
Vue.prototype.$destroy = function(){
...
const vm = this
vm.$parent.remove(vm)
vm._watcher && vm._watcher.teardown()
let i = vm._watchers.length;
while (i--) {
vm._watchers[i].teardown();
}
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--;
}
vm._isDestroyed = true
vm.__patch__(vm._vnode, null)
callHook(vm, 'destroyed');
...
}activated (使用keep-alive时有用) 重新激活时
deactivated (使用keep-alive时有用) 解除激活状态
v-model 指令会转换为props[value]和@input事件
下面是经过模板编译的虚拟vnode配置数据
js
{
directives: [{
name: "model",
rawName: "v-model",
value: (_vm.todo),
expression: "todo"
}],
domProps: {
"value": (_vm.todo)
},
on: {
"input": function($event) {
if ($event.target.composing) {
return;
}
// 通过监听input事件,将target.value赋值给_vm.todo
_vm.todo = $event.target.value
}
}
}支持数组类型响应
我们之前都是通过this.countList = [...this.countList, this.count], 这种重新赋值的方式来改写数组的,这样写是为了触发data的响应,这样写弊端也很明显,因此我们需要更细粒度的控制数组的响应。
由于Object.defineProperty无法支持未定义的属性,数组的索引也是一样的逻辑 因此我们需要重写数组的push,slice等方法,来增加或移除对应属性的响应。
