Vue框架(一)-Vue3原理篇

103 阅读4分钟

Vue3原理篇

Vue的设计概述

Vue的设计思路

  • 声明式
  • 数据驱动
  • 渐进式

Vue解决的问题

  • 高效性
    • 编译器,编译模板
    • 数据驱动
      • 响应式
    • 自动更新视图
      • 运行时runtime-core
  • 复用性、可维护性
    • composition api
    • mixin
    • 指令
  • 可扩展性、跨平台
    • 浏览器平台运行时runtime-dom
    • 自定义渲染器API

Vue源码的整体架构

1. 源码学习方法

  • 找到一条主线
    • 思考如何运行起来
    • 数据何时变
    • 如何更新
  • 画思维导图
  • 提出问题,去源码找答案
  • 造一个轮子
  • 写出一篇文章

2. 初始化流程分析

  • 创建应用实例:获取渲染器renderer,renderer是含createApp方法的对象,调用renderer创建实例
  • 创建根节点的vnode:传入的有template模板时,会执行compile编译模板为render函数
  • 执行render函数,将生成的vnode传递给path函数,转换为dom
  • 追加到宿主元素

注:

  1. 挂载都做了什么?

答:将传入组件数据和状态转换为dom,并追加到宿主元素

  1. 初始化方法变化及原因?

答:变化:

  • 函数方式创建实例
  • 实例方法
  • 简化API,一致性增强

原因:

  • ts支持更好
  • 避免全局污染
  • 简化
  • 增强一致性
  • 利于tree shaking

3. 更新流程分析

  • 更新机制建立
  • 数据更新,set方法调用trigger函数
  • 将副作用函数添加到更新队列queueJob里
  • promise异步执行更新队列queueFlush
  • 循环执行相关副作用函数

Mine-Vue设计与实现

1. 视图初始化

  • 应用程序实例创建
    • createApp()返回App实例对象,实现一个mount()
  • 挂载
    • 实例的mount方法传入的组件渲染结果追加到宿主元素
export function createApp(rootComponent) {
    // 接受根组件,返回App实例
    return {
        mount(selector) {
            // 1.获取宿主
            const container = document.querySelector(selector)
            // 2. 渲染视图
            const el = rootComponent.render.call(rootComponent.data())
            // 3. 追加到宿主
            container.appendChild(el)
        }
    }
}

2. 渲染器实现

将传入的组件转换为Dom

  • 渲染器工厂
    • createRenderer
    • 所属runtime-core
  • 渲染器实例
    • render
    • createApp
// runtime-dom
let renderer

// dom平台特有的节点操作
const rendererOptions = {
    querySelector(selector) {
        document.querySelector(selector)
    },
    insert(child, parent, anchor) {
        // anchor设为null, 则为appendChild
        parent.insertBefore(child, anchor || null)
    },
    setElementText(el, text) {
        el.textContent = text
    }
}

// 确保renderer单例
function ensureRenderer() {
    return renderer || (renderer = createRenderer(rendererOptions))
}

export function createApp(rootComponent) {    
    return ensureRenderer().createApp(rootComponent)
}

// runtime-core
// ------------- custom renderer api -------------
export function createRenderer(options) {
    // 渲染组件内容
    const render = () => {
        // 1.获取宿主
        const container = options.querySelector(selector)
        // 2. 渲染视图
        const el = rootComponent.render.call(rootComponent.data())
        // 3. 追加到宿主
        options.insert(el, container)
    }
    
    // 返回渲染器实例
    return {
        render,
        createApp: createAppAPI(options)
    }
}

export function createAppAPI(render) {
    return function createApp(rootComponent) {
        const app = {
            mount(selector) {
                render(rootComponent)
            }
        }
        
        return app
    }
}

3. 数据响应式实现

  • reactive()
    • new Proxy()
  • ref()
    • get/set
let activeEffect

export function effect(fn) {
    activeEffect = fn
}

export function reactive(obj) {
    return new Proxy(obj, {
        get(target, key) {
            const vlaue = Reflect.get(target, key)
            // 依赖跟踪
            track(target, key)
            return value
        },
        set(target, key, value) {
            const result = Reflect.set(target, key, value)
            // 通知更新
            trigger(target, key)
            return result
        },
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            // 通知更新
            trigger(target, key)
            return result
        }
    })
}

const targetMap = new WeakMap()

function track(target, key) {
    if(activeEffect) {
        let depsMap = targetMap.get(target)
        // 首次depsMap不存在
        if(!depsMap) {
            targetMap.set(target, (depsMap = new Map()))
        }
        
        let deps = depsMap.get(key)
        // 首次depsMap不存在
        if(!deps) {
            depsMap.set(key, (deps = new Set()))
        }
        
        // 添加激活的副作用
        deps.add(activeEffect)
    }
}

function trigger(target, key) {
    const depsMap = targetMap.get(target)
    
    if(depsMap) {
        let deps = depsMap.get(key)
        if(deps) {
            deps.forEach(dep => dep())
        }
    }
}
import { reactive } from './reactive'

...
export function createRenderer(options) {
    ...
    const render = () => {
        // 1. 获取宿主
        const container = options.querySelector(selector)
        // 2. 渲染视图
        const observed = reactive(rootComponent.data())
        // 3. 组件更新函数
        const componentUpdateFn = () => {
            const el = rootComponent.render.call(observed)
            options.setElementText(el, '')
            // 3. 追加到宿主
            options.insert(el, container)
        }
        
        effect(componentUpdateFn)
        
        // 初始化
        componentUpdateFn()
        
        if(rootComponent.mounted) {
            rootComponent.mounted.call(observed)
        }
    }
    ...
}
...

4. 虚拟DOM

// vnode.js
export function createVNode(type, props, children) {
    return {
        type, 
        props, 
        children
    }
}
...
export function createRenderer(options) {
    // 渲染组件内容
    const render = (vnode, container) => {
        if(vnode) {
            patch(container._vnode || null, vnode, container)
        }
        
        container._vnode = vnode
    }
  
    
    // 挂载三件事:
    // 1. 组件实例化
    // 2. 状态初始化
    // 3. 副作用安装
    const mountComponent = (initialVNode, container) => {
        // 组件实例化
        const instance = {
            data: {},
            vnode: initialVNode,
            isMounted: false
        }
        
        // 初始化组件状态
        const { data: dataOptions } = instance.vnode.type
        instance.data = reactive(dataOptions())
        
        // 安装渲染函数副作用
        setupRenderEffect(instance, container)
    }
    
    const setupRenderEffect = (instance, container) => {
        // 声明组件更新函数
        const componentUpdateFn = () => {
            if(!instance.isMounted) {
                // 创建阶段
                const { render, isMounted } = instance.vnode.type
                const vnode = render.call(instance.data)
                patch(null, vnode, container)
                
                if(isMounted) {
                    instance.vnode.type.isMounted.call(instance.data)
                }
            } else {
                // 更新阶段
            }
        }
        
        // 建立更新机制
        effect(componentUpdateFn)
        
        // 首次执行组件更新函数
        componentUpdateFn()
    }
    
    const patch = (n1, n2, container) => {
        const { type } = n2

        if(typeof type === 'string') {
            // 原生元素
            processElement(n1, n2, container)
        } else {
            // 组件
            processComponent(n1, n2, container)
        }
    }
    
    const processComponent = (n1, n2, container) => {
        if(n1 == null) {
            mountComponent(n2, container)
        } else {
            
        }
    }
    
    const processElement = (n1, n2, container) => {
        if(n1 == null) {
            mountElement(n2, container)
        } else {
            patchElement(n1, n2)
        }
    }
    
    
    // 返回渲染器实例
    return {
        render,
        createApp: createAppAPI(options)
    }
}

...
export function createAppAPI(render) {
    return function createApp(rootComponent) {
        const app = {
            mount(selector) {
                const vnode = createVNode(rootComponent)
                render(vnode, selector)
            }
        }
        
        return app
    }
}