源码调试
- git clone github.com/vuejs/core.…
- npm install pnpm -g
- pnpm install
- pnpm dev
- 可在packages/vue目录下,创建文件调试
vue/core 使用pnpm包管理工具(pnpm安装速度更快,依赖安全性更强)
示例:
<div id="app">{{title}}</div>
<script src="../../dist/vue.global.js"></script>
<script>
const { createApp } = Vue
createApp({
setup() {
return {
title: 'hello vue3'
}
}
}).mount('#app')
初始化过程
createApp
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
return app
}) as CreateAppFunction<Element>
获取renderer
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {
const {
insert: hostInsert,
remove: hostRemove,
patchProp: hostPatchProp,
createElement: hostCreateElement,
createText: hostCreateText,
createComment: hostCreateComment,
setText: hostSetText,
setElementText: hostSetElementText,
parentNode: hostParentNode,
nextSibling: hostNextSibling,
setScopeId: hostSetScopeId = NOOP,
insertStaticContent: hostInsertStaticContent
} = options
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
if (n1 === n2) {
return
}
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
unmount(n1, parentComponent, parentSuspense, true)
n1 = null
}
if (n2.patchFlag === PatchFlags.BAIL) {
optimized = false
n2.dynamicChildren = null
}
const { type, ref, shapeFlag } = n2
switch (type) {
case Text:
processText(n1, n2, container, anchor)
break
case Fragment:
break
default:
if(shapeFlag & ShapeFlags.COMPONENT) {
setupComponent()
setupRenderEffect()
}
break
}
}
function setupStatefulComponent(instance: ComponentInternalInstance,
isSSR: boolean) {
const Component = instance.type
if(Component.setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
}else {
}
}
const render: RootRenderFunction = (vnode, container, isSVG) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
flushPreFlushCbs()
flushPostFlushCbs()
container._vnode = vnode
}
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}
createAppAPI
export function createAppAPI<HostElement>(
render: RootRenderFunction<HostElement>,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {
const context = createAppContext()
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
vnode.appContext = context
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG)
}
isMounted = true
app._container = rootContainer
return getExposeProxy(vnode.component!) || vnode.component!.proxy
} else if (__DEV__) {}
},
})
}
}
总结
graph TD
获取渲染器 --> createApp --> mount --> render --> patch --> mountComponent --> setupStatefulComponent & setupRenderEffect
手写简易初始化过程
<div id="app"></div>
<script>
const createAppAPI = render => {
return rootComponent => {
const app = {
mount: container => {
const vnode = {
tag: rootComponent
}
render(vnode, container)
}
}
return app
}
}
const createRenderer = options => {
const patch = (v1, v2, container) => {
const rootComponent = v2.tag
const ctx = rootComponent.data()
const vnode = rootComponent.render.call(ctx)
const child = options.createElement(vnode.tag)
const parent = options.querySelector(container)
if (typeof vnode.children === 'string') {
child.textContent = vnode.children
options.insert(parent, child)
} else {
}
}
const render = (vnode, container) => {
patch(container._vnode || null, vnode, container)
container._vnode = vnode
}
return {
render,
createApp: createAppAPI(render)
}
}
const renderer = createRenderer({
createElement: tag => {
return document.createElement(tag)
},
querySelector: sel => {
return document.querySelector(sel)
},
insert: (parent, child, anchor = null) => {
parent.insertBefore(child, anchor)
}
})
const createApp = rootComponent => {
return renderer.createApp(rootComponent)
}
createApp({
data() {
return {
name: 'test'
}
},
render() {
return {
tag: 'h2',
children: this.name
}
}
}).mount('#app')
</script>