# Vue核心原理 - 笔记

主要有两块:

  1. new Vue({ optionAPI })流程,即:Vue Option API代码是如何渲染到最终UI
  2. 响应式原理,即:如何依赖收集的,为什么要用Object.defineProperty/Proxy

涉及到的概念:

  1. 模板编译 & runtime
    • VirtualDOM
    • VNode
    • VDiff
  2. 双向数据绑定
    • 依赖收集

本文主要以vue2.x源码为例,vue3中的核心思想原理依然适用

# 1. new Vue({ optionAPI })流程

问题:下面的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)

涉及到的概念:

  • VirtualDOM、VNode数据结构(VirtualDOM只是概念,VNode才是虚拟DOM的承载)
  • VNode + Diff = patch

# 衍生问题

  1. 为什么要有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
  2. 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
}

# 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
}

自定义依赖者模式:

// 依赖者
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

# Vue调度原则

  1. 先父组件更新,后子组件更新
  2. render Watcher放在最后执行
// 如上案例的调度:
// dep:msg: [WatcherRender(), computedCB(), selfWatcher()]
// dep:msgxxx: [WatcherRender()]

// 最终根据规则依次执行:[computedCB(), selfWatcher(), WatcherRender]