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>
项目架构分析
调试
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>
上一篇: C4D怎么建模弹簧摆件并添加贴图效果?