You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ps. 以上思路是简化版,直接把observer按数据层级关系组织。而源码中是单独用了一个binding类来组织watcher的层级关系的。事件触发后,在observer中传播到顶层获得一个变化数据的key(比如user.name.abc),再用这个路径从binding的根开始定位到对应的user.name.abc,watcher存放在这个binding对象中。在这种策略中,只有最顶层的observer被监听了,子observer只负责把事件传播到顶层而已。
vm._self=vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm,'beforeCreate')initInjections(vm)// resolve injections before data/propsinitState(vm)initProvide(vm)// resolve provide after data/propscallHook(vm,'created')if(vm.$options.el){vm.$mount(vm.$options.el)}
vue1 小粒度更新,精确追踪到数据变化所影响的dom变化,精确更新变化的dom
具体实现为,维护 observer watcher directive 三个类
observer负责监听数据变化,并派发事件,向上层传播事件,维护一个watcher数组
watcher订阅observer,数据变化时执行事件,包括$watch注册的回调函数和视图更新
directive负责建立数据data到dom对象的对应关系,对不同指令应用不同的更新方法,是watcher的其中一种类型
parser 解析类似user.name user[0] user["name"] 这样的expression,转换为最终可查找到属性的路径
ps. 以上思路是简化版,直接把observer按数据层级关系组织。而源码中是单独用了一个binding类来组织watcher的层级关系的。事件触发后,在observer中传播到顶层获得一个变化数据的key(比如user.name.abc),再用这个路径从binding的根开始定位到对应的user.name.abc,watcher存放在这个binding对象中。在这种策略中,只有最顶层的observer被监听了,子observer只负责把事件传播到顶层而已。
vue2 以组件粒度为范围,组件内diff式更新,组件层面还是按vue1的方式更新
具体区别体现在,每个组件有了render函数,数据变化时只通知到组件更新,组件更新时会重建全部vnode树,而不是精确更新了(当然到dom层面时还是会做diff,同样表现为精确更新)
好处有:1.render函数可以用js写组件,更灵活
2.跨平台,vue1模板渲染方式依赖浏览器先解析vue模板
3.如果要建立精确的数据--dom对应关系,需要占用大量内存维护directive,vue2可以节约这部分内存
4.小粒度更新需要维护一个变更队列(当数据重复变化时)来避免不必要的dom操作,vue2不要维护这部分
vue的核心部分
$watch
批量更新 通过Object.defineProperty实现
diff算法
关键词:同层级比较 复杂度o(n) 两对头尾指针 加key复用
实现: patch==> 判断sameNode ==> patchNode() ==> 更新text && updateChildren ==> while循环 递归调用patchNode
面试被问到的diff相关问题:
组件与生命周期
父子组件创建顺序
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted ->父mounted
可以看到beforeMount之后才进入子组件的生命周期,但是这个钩子并没有在_init方法中直接调用,需要先走到$mount里。
$mount主要做两件事:
compileToFunctions
方法,把template或el对应的模板编译成render函数(如果是运行时版本,会跳过这一步)mountComponent
方法,也主要做三件事beforeMount
钩子createComponent
为组件创建vnode(占位符);第二个在update的patch过程中,也就是为vnode创建真实dom的时候,识别出vnode为组件占位符后,会调用createComponentInstanceForVnode
创建组件实例,正式进入子组件的生命周期。因为占位vnode的存在,一个组件实例上其实保留了两个vnode的引用,分别是
$vnode
和_vnode
,它们的结构也完全不一样。$vnode
是在render父组件时创建的子组件占位符,所以会保留许多组件相关的引用,data中有组件的hook;_vnode
是在render子组件时创建的普通vnode,对应组件的根元素。$vnode
理论上应该不对应真实dom,但实际上做了一些特殊处理,把componentInsatance
的$el赋值给了$vnode.elm。$vnode
和_vnode
是父子关系,但它们的elm指向相同的dom。$vnode(另外在$options中有个_parentVnode也是指向它)
_vnode,就是普通的li元素
生命周期的应用:
组件更新的传递
prepatch
钩子中的updateChildComponent
方法,将父组件的更新情况传递给子组件。由于子组件的prop也是响应式的,所以同步了父组件data后,如果有变化,会自动进入子组件的update生命周期,父组件就不需要管了。如果没有变化,那么子组件不受影响,也不需要更新。对于含slot children
的组件,则与prop不同,会执行强制更新。参考
简易动态数据绑定
VueComponent的创建过程
component与生命周期
组件更新
The text was updated successfully, but these errors were encountered: