vue3源码学习

调试环境搭建

  • 迁出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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值