1.Vue architecture changes
compile-dom —> compile-core
reactivity
runtime-dom —> runtime-core
2,About the package
Vue is packaged with rollup
Run the rollup command with execa library
3. Packaging format:
Global –> umD –> iIFE auto-tuning function
CJS –> NodeJS environment
Es —-> ES6 environment
3,Source code initialization process
0, use (on why it is made this way, different from VUe2)
1. Avoid instance pollution
2, tree shaking optimization (previously many methods mount instances)
3. Better understanding semantically
createApp({
data() {
return {
counter: 1
}
}
})
.use(store)
.use(router)
.mount('#app')
Copy the code
The createApp method is in the Runtime-DOM module
export const createApp = ((. args) = > {
// Get the renderer first
CreateApp is actually called by the renderer
constapp = ensureRenderer().createApp(... args)const { mount } = app
app.mount = (containerOrSelector: Element | string): any= > {
const container = normalizeContainer(containerOrSelector)
if(! container)return
/ /... Compiling templates
const proxy = mount(container)
container.removeAttribute('v-cloak')
container.setAttribute('data-v-app'.' ')
return proxy
}
return app
}) as CreateAppFunction<Element>
Copy the code
2. Renderers
constapp = ensureRenderer().createApp(... args)// This method returns the renderer
ensureRenderer()
/ / ensureRenderer () = > return the renderer | | createRenderer () / / singleton pattern
// If the renderer is created as a global variable, it has a value
/ / 2, createRenderer ({patchProp,... NodeOps}) create renderer (property comparison and replacement, DOM manipulation methods)
createRenderer()
// return baseCreateRenderer ()
/ / 3.baseCreateRenderer({patchProp, ... nodeOps})// Directory location: packages run-time core SRC renderer.ts
// baseCreateRenderer return result
// return ==> {
// render(vnode, container
// Water injection is used for server rendering
// createApp: createAppAPI(render, hydrate) returns a method
/ /}
// 4, createAppAPI(Render, hydrate) return app instance
createAppAPI(render, hydrate)
// Instances include {use, component, mixins, directive, mount, unmount}
// packages\runtime-core\src\apiCreateApp.ts
Copy the code
3, About mount
Location: Vue-next \ Packages \ Runtime-core \ SRC \ apicreateapp.ts (line 227)
We mainly did the following things
Const vNode = createVNode(rootComponent as ConcreteComponent, RootProps) // Get vnode vnode.appContext = context // HMR root reload hot update if (__DEV__) {context.reload = () => { render(cloneVNode(vnode), rootContainer) } } if (isHydrate && hydrate) { hydrate(vnode as VNode<Node, Element>, RootContainer, as any) / / server rendering} else {/ / the browser rendering -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - render (vnode, RootContainer)} isMounted = true app._container = rootContainer // Save the root nodeCopy the code
4, render method
The process (container._vnode) is different depending on whether there is a DOM
const render: RootRenderFunction = (vnode, container) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, Null, null, true)}} else {/ there/here / / parameter 1 / update/parameter 1 does not exist walk mount patch (container. _vnode | | null, vnode. Container)} flushPostFlushCbs() container._vnode = vnode // hang to dom}Copy the code
5. Patch compares virtual DOM updates, including initial rendering
Directory location: Packages \ Runtime-core \ SRC \renderer.ts
const patch: PatchFn = (
n1, // Old virtual node
n2, // New virtual node
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
optimized = false
) = > {
// patching & not the same type, unmount old tree Is not of the same root node type
if(n1 && ! isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense,true)
n1 = null
}
// Get the new node type
const { type, ref, shapeFlag } = n2
switch (type) {
case Text: / / text
processText(n1, n2, container, anchor)
break
case Comment: / / comment
processCommentNode(n1, n2, container, anchor)
break
case Static: / / static
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG)
} else if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
case Fragment: // Non-single root node
processFragment(...)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(...)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
// Initialization goes here
processComponent(...)
} else if(shapeFlag & ShapeFlags.TELEPORT) { ; (typeas typeof TeleportImpl).process(...) // Handle the portal
} else if(__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { ; (typeas typeofSuspenseImpl).process(...) }}// set ref sets ef
if(ref ! =null && parentComponent) {
setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2)
}
}
Copy the code
Initial render steps:
Initialize the Component (processComponent) process (not fragment)
ProcessComponent => mountComponent Mounts the component for the first time
The process of patch components
const processComponent = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: Boolean) => {if (n1 == null) {if (n2.shapeflag & ShapeFlags.COMPONENT_KEPT_ALIVE) {keepalive activation (parentComponent! .ctx as KeepAliveContext).activate()} else {// Initialize mountComponent()}} else {// Update updateComponent(n1, n2, optimized) } }Copy the code
MountComponent simplifies the process
Directory: packages \ runtime – core \ SRC \ the renderer ts
// 1. Create an instance
const instance = createComponentInstance(...) // Create a component instance
/ /...
// 2 component setup is similar to vue2_init merge merge options, // attribute initialization // data responsive // slot processing
setupComponent(instance)
/ /...
// Install render function side effect
setupRenderEffect()
Copy the code
7, the setupComponent (instance)
export function setupComponent( instance: Whether ComponentInternalInstance, isSSR = false / / SSR rendering) = {isInSSRComponentSetup isSSR const {props, children, shapeFlag } = instance.vnode const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT initProps(instance, props, isStateful, isSSR) initSlots(instance, children) const setupResult = isStateful ? SetupStatefulComponent (instance, isSSR) : undefined isInSSRComponentSetup = false return setupResult}Copy the code
function setupStatefulComponent(instance: ComponentInternalInstance, isSSR: boolean) {
const Component = instance.type as ComponentOptions
// 0. create render proxy property access cache
instance.accessCache = Object.create(null)
// 1. create public instance / render proxy
// also mark it raw so it's never observed
// Get the context
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers) // Data responsive
// 2. call setup() handles the setup function
const { setup } = Component / / handle the setup
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
currentInstance = instance
pauseTracking()
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
currentInstance = null
if (isPromise(setupResult)) {
if (isSSR) {
// return the promise so server-renderer can wait on it
return setupResult.then((resolvedResult: unknown) = > {
handleSetupResult(instance, resolvedResult, isSSR)
})
} else if (__FEATURE_SUSPENSE__) {
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
} else if (__DEV__) {
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`)}}else {
handleSetupResult(instance, setupResult, isSSR)
}
} else {
// Setup is not set here
finishComponentSetup(instance, isSSR)
}
}
Copy the code
Both setup and data exist, and the setup value takes precedence
8, finishComponentSetup(instance, isSSR) this is still to be studied
Directory: packages \ runtime – core \ SRC \ component ts
9, setupRenderEffect installation side effects
The initial render and update components are separated
const setupRenderEffect: SetupRenderEffectFn = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) = > {
// Create reactive effect for rendering effect
instance.update = effect(function componentEffect() {
if(! instance.isMounted) {// Unmounted condition
let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode
const { bm, m, parent } = instance
// beforeMount hook life cycle
if (bm) {
invokeArrayFns(bm)
}
// onVnodeBeforeMount
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parent, initialVNode)
}
// 1. Obtain the vNode of the current component
const subTree = (instance.subTree = renderComponentRoot(instance))
if (el && hydrateNode) {
// SSR server rendering
// vnode has adopted host node - perform hydration instead of mount.
hydrateNode(
initialVNode.el as Node,
subTree,
instance,
parentSuspense
)
} else {
// Run patch. If multiple nodes run fragments, run patch
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
initialVNode.el = subTree.el
}
// mounted hook
if (m) {
queuePostRenderEffect(m, parentSuspense)
}
// onVnodeMounted
if ((vnodeHook = props && props.onVnodeMounted)) {
queuePostRenderEffect(() = >{ invokeVNodeHook(vnodeHook! , parent, initialVNode) }, parentSuspense) }// activated hook for keep-alive roots.
// #1742 activated hook must be accessed after first render
// since the hook may be injected by a child keep-alive
const { a } = instance
if (
a &&
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
) {
queuePostRenderEffect(a, parentSuspense)
}
instance.isMounted = true
} else {
// updateComponent
// This is triggered by mutation of component's own state (next: null)
// OR parent calling processComponent (next: VNode)
let { next, bu, u, parent, vnode } = instance
let originNext = next
let vnodeHook: VNodeHook | null | undefined
if (next) {
next.el = vnode.el
updateComponentPreRender(instance, next, optimized)
} else {
next = vnode
}
// beforeUpdate hook
if (bu) {
invokeArrayFns(bu)
}
// onVnodeBeforeUpdate
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
invokeVNodeHook(vnodeHook, parent, next, vnode)
}
const nextTree = renderComponentRoot(instance)
if (__DEV__) {
endMeasure(instance, `render`)}const prevTree = instance.subTree
instance.subTree = nextTree
patch(
prevTree,
nextTree,
// parent may have changed if it's in a teleporthostParentNode(prevTree.el!) ! .// anchor may have changed if it's in a fragment
getNextHostNode(prevTree),
instance,
parentSuspense,
isSVG
)
/ /...
}
Copy the code