欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue3源码学习

程序员文章站 2022-05-17 20:35:01
...

调试环境搭建

  • 迁出vue3源码:git clone https://github.com/vuejs/vue-next.git
  • 安装依赖: yarn --ignore-scripts
  • 生成sourcemap文件,package.json
"dev":"node scripts/dev.js --sourcemap"
  • 编译 yarn dev

  • 运行 npm run dev

创建vue3项目——初始化

新建packages/vue/examples/01-init.html

<div id="app">
    {{foo}}
</div>
<script src="../dist/vue.global.js"></script>
<script>
    // 初始化
    // vue2: new Vue().$mount//mount挂载
    // vue3: createApp().mount 工厂函数
    const {createApp} = Vue //解构出来
    createApp({
        data(){
            return {
                foo: 'fooo'
            }
        }
    }).mount('#app')
    
</script>

项目架构分析

Vue
compiler-dom
reactivity
runtime-dom
complier-core
runtime-core

调试

F12->Sources->设置断点->Step into next Function call ->Reveal in sidebar

vscode 打开目录: 使用 Command+p 输入目录

打包方式 rollup

scripts/dev.js

// 模块:默认vue
const target
// 打包格式,常见有umd(通用)/cjs(node)/esm(webpack) 在package.json未定义格式时,默认global,完整的格式
const formats
// 源码映射
const sourceMaps

rollup.config.js

// 包路径 packages
const packagesDir
// 默认vue
const packageDir
// 默认入口是packages/vue/src/index.ts
const entryFile
// 不同格式配置 23行
const


vue.global.js 13041行 返回的实例方法 use mount etc.

packages/vue/src/index.ts

export const createApp = ((...args) => {
    // 获取渲染器实例,并调用其createApp
    const app = ensureRender().createApp(...args)
    
})
//行32
function ensureRender(){
    // 单例模式
    return renderer || (renderer = createRenderer<>) 
}
//行389
export function createRenderer<
    HostNode = RendererNode,
    HostElement = RendererElement
    >(options: RendererOptions<HostNode, HostElement>){
    return baseCreateRenderer<HostNode, HostElement>(options)
}

renderer.ts自定义了渲染器

//2236 这就是返回的渲染器
return {
    render, // vnode => dom :虚拟dom转换成真实dom
    hydrate,
    // 获取app实例创建工厂函数
    createApp: createAppAPI(render, hydrate)
}

// 2208 初始化走这里
// 最终转换方法还是patch
patch()

// 446
const patch: PatchFn = {
    n1, // 上次渲染结果,初始化时为null
    n2, // 最新的vnode
}
// 507 初始化默认从组件
processComponent

//1209 初始化挂载组件
mountComponent

//1234 
// 1.创建组件实例
//1259
// 2.安装组件:组件初始化
setupComponent(instance)

//1279
// 安装渲染函数
setupRenderEffect
//1341 副作用effect,组件更新函数添加为副作用函数,将来如果数据发生变化,重新执行组件更新函数
//1361 获取根组件的vnode
//1385 将根组件vnode转换为dom

apiCreateApp.ts

// 114 
export function createAppApI<HostElement>(

// 129 定义app实例,相当于vue2 new app()
const app: App = ()

// 151 
// vue2: Vue.use(VueRouter) 静态方法
// vue3: app.use(VueRouter) 实例调用
use(){},
)

// 216
mount(){
    if(!isMounted){
        // 1.获取整棵树vnode
        const vnode 
              
    }
    
    // 237
    // 2.渲染器传入的render方法将vnode转换
    render(vnode, rootContainer)
}
{{foo}}

手写初始化流程

02-kinit.html

<div id="app">
    {{foo}}
</div>
<script>
    // 4.createAppAPI
    const createAppAPI = (render) => {
        return function createApp(rootComponent){
            // 创建app并返回
            const app = {
                mount(container){
                    // 1.获取vnode,来源于根组件的渲染函数
                    const vnode = {
                        tag: 'h2',
                        props: null,
                        children: rootComponent.data().foo
                    }
                    // 2.执行render
                    render(vnode, container)
                }
            }
            return app
        }
    }
    // 3.createRenderer
    const createRenderer = ({querySelector, createElement, insert}) => {
        // 定义render
        const render = (vnode, container) => {
            // 1.获取宿主元素
            const parent = querySelector(container)
            // 2.创建当前节点
            const child = createElement(vnode.tag)
            // 处理属性和children
            if(typeof vnode.children === 'string') {
                child.innerText = vnode.children
            }else{
                // todo...
            }
            // 3.插入
            insert(child, parent)
        }
        return{
            render,
            createApp:createAppAPI(render)
        }
    }
    // 2.renderer
    const renderer =  createRenderer({
        querySelector(sel) {
            return document.querySelector(sel)
        },
        createElement(tag) {
            return document.createElement(tag)
        },
        insert(child, parent) {
            parent.appendChild(child)
        }
    })
    // 1.声明Vue
	const Vue = {
        createApp(options) {
            return renderer.createApp(options)
        }
    }
    Vue.createApp({
        data(){
            return {
                foo: 'fooo'
            }
        }
    }).mount('#app')
    
</script>

理清嵌套的基本逻辑

自定义渲染器实践

  • 范例:编写一个渲染器把数据用canvas以柱状图形式绘制出来
  • 核心思路是按照自定义的节点操作逻辑处理视图和数据

03-canvasApp.html

<script src="../dist/vue.global.js"></script>
<script>
    const {createRenderer} = Vue
    // 扩展函数,可以创建一个画布并插入到宿主元素中
    createRenderer({
        createElement() {},
        insert() {},
        patchProps() {},            
    })
</script>
相关标签: vue