Vue核心原理 - 笔记
  # # Vue核心原理 - 笔记
主要有两块:
new Vue({ optionAPI })流程,即:Vue Option API代码是如何渲染到最终UI响应式原理,即:如何依赖收集的,为什么要用Object.defineProperty/Proxy
涉及到的概念:
- 模板编译 & runtime
- VirtualDOM
 - VNode
 - VDiff
 
 - 双向数据绑定
- 依赖收集
 
 
本文主要以vue2.x源码为例,vue3中的核心思想原理依然适用
# # 1. new Vue({ optionAPI })流程
问题:下面的js代码如何实现从js代码到Web UI上的渲染?
    new Vue({
        el: el,
        render: h => h(App),
    }) // .$mount('#app');
    
 1
2
3
4
5
2
3
4
5
概念流程:new Vue() => Vitrual DOM => DOM
源码流程:template/jsx -> render() -> Vue.prototype._render() -> vnode -> patch() -> vdiff -> ui(DOM API,web:nodeOps)
涉及到的概念:
- VirtualDOM、VNode数据结构(VirtualDOM只是概念,VNode才是虚拟DOM的承载)
 - VNode + Diff = patch
 
# # 衍生问题
- 为什么要有VDOM/VNode?答:1. 找到最小更新DOM操作集 2. 给Web之外的终端UI提供可能(比如Native、webgl)
- 思考:js引擎 / 渲染引擎 (互斥) -> js单线程 (node(libuv),callback,event loop)
 - 浏览器是如何渲染?渲染,js引擎(V8)关系?
 - js -> ui : 假设js多线程? webworker规范: 不能操作dom
 - VNode: tag、children -> leetcode
 
 - Vue2:为什么需要包装一个根节点?答:VDiff算法决定了需要这规则
 
# # 2. 响应式原理
# # 2.1 react状态机模式
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
    }
    
 1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# # 2.2 vue依赖收集模式
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
    }
    
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
自定义依赖者模式:
    // 依赖者
    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
    
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# # Vue调度原则
- 先父组件更新,后子组件更新
 - render Watcher放在最后执行
 
    // 如上案例的调度:
    // dep:msg: [WatcherRender(), computedCB(), selfWatcher()]
    // dep:msgxxx: [WatcherRender()]
    
    // 最终根据规则依次执行:[computedCB(), selfWatcher(), WatcherRender]
    
 1
2
3
4
5
6
2
3
4
5
6
编辑  (opens new window)