主要有两块:
new Vue({ optionAPI })流程
,即:Vue Option API代码是如何渲染到最终UI响应式原理
,即:如何依赖收集的,为什么要用Object.defineProperty/Proxy涉及到的概念:
本文主要以vue2.x源码为例,vue3中的核心思想原理依然适用
问题:下面的js代码如何实现从js代码到Web UI上的渲染?
new Vue({
el: el,
render: h => h(App),
}) // .$mount('#app');
概念流程:new Vue() => Vitrual DOM => DOM
源码流程:template/jsx -> render() -> Vue.prototype._render() -> vnode -> patch() -> vdiff -> ui(DOM API,web:nodeOps)
涉及到的概念:
react基于状态机:msg -> this.setState({msg})
-> render() -> vdiff -> ui(DOM)
export default class ReactComp extend React.Component {
state = {msg : 1}
render() {
return <div>{msg}</div>
}
this.setState({msg: 111}) // -> render()
// 状态机模式:msg -> setState -> render() -> vdiff -> ui(DOM)
{msg: 1} -> UI1
{msg: 111} -> UI2
}
vue基于劫持进行自动依赖收集:msg -> this.msg = 111 -> Object.defineProperty
-> render() -> vdiff -> ui(DOM)
源码流程:this.xxx = 1 -> 中间劫持(defineProperty/Proxy) -> getter/setter -> Dep/Watcher -> Watcher(cb): render/computed/this.$watch() -> render() -> UI
// vue2.x proxy: Object.defineProperty
export default {
render() {
return <div>{msg}{msgxxx}</div>
}
data() {
return { msg: 11}
}
computed: {
msgxxx() {
return this.msg + 'hello' // Watcher(() => {return this.msg + 'hello'})
}
}
// 双向数据绑定原理:
Object.defineProperty(data, 'msg', {
getter() {
// 依赖收集 Dep: { msg: [Wathcer(() => render()), ] }
dep(new Watcher(() => render()))
return value
}
setter() {
// 触发更新
dep.notify()
}
})
this.msg = 111 // js语法,更新值 + 收集的Watcher update
}
自定义依赖者模式:
// 依赖者
class Dep {
arr = []
dep(watch) {
arr.push(watch)
}
notify() {
arr.forEach(watch => watch.update())
}
}
class Watcher() {
constructor(cb) {
this.cb = cb
}
update() {
this.cb()
}
}
let dep = new Dep() // msg
dep(new Watcher(() => render())) // render Watcher
dep(new Watcher(() => computed())) // computed Watcher
// 如上案例的调度:
// dep:msg: [WatcherRender(), computedCB(), selfWatcher()]
// dep:msgxxx: [WatcherRender()]
// 最终根据规则依次执行:[computedCB(), selfWatcher(), WatcherRender]