- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
Vue.createApp(App).mount('#app')执行过程
1.创建app入口
2.render函数
3.返回app
mount
App组件的挂载和渲染过程
patch:进行diff算法.
处理组件节点.
调用mountComponent挂载组件
初始化component,对组件的数据进行赋值和操作
调用设置和渲染的副作用函数
调用了副作用函数effect()
renderComponentRoot,对template进行渲染
把subTree子树的VNode挂载到container上,调用patch()
处理DOM元素,比如div/button/span(每个子树subtree)
如果还有子元素,进行patch之后,进行挂载
11.12.13. 处理子节点,进行挂载
来到runtime-dom>src>index.ts文件
// 创建app入口
export const createApp = ((...args) => {
// 创建app使用渲染器ensureRenderer()
// 渲染器有返回createApp属性,createApp调用之后返回app
const app = ensureRenderer().createApp(...args)
if (__DEV__) {
injectNativeTagCheck(app)
injectCompilerOptionsCheck(app)
}
// 从app取出mount
const { mount } = app
// 将app.mount函数进行重写,函数原本传入属性,而我们传入‘#app’字符串
// 重写mount函数是为了跨平台
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// normalizeContainer通过querySelector获取到container对象
const container = normalizeContainer(containerOrSelector)
if (!container) return
const component = app._component
if (!isFunction(component) && !component.render && !component.template) {
// __UNSAFE__
// Reason: potential execution of JS expressions in in-DOM template.
// The user must make sure the in-DOM template is trusted. If it's
// rendered by the server, the template should not contain any user data.
// 原因:在 DOM 模板中可能执行 JS 表达式。
// 用户必须确保 DOM 模板是受信任的。如果是
// 由服务器呈现,模板不应包含任何用户数据。
component.template = container.innerHTML
// 2.x compat check
if (__COMPAT__ && __DEV__) {
for (let i = 0; i < container.attributes.length; i++) {
const attr = container.attributes[i]
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
compatUtils.warnDeprecation(
DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
null
)
break
}
}
}
}
// clear content before mounting
// 先清空container中的原本内容
container.innerHTML = ''
const proxy = mount(container, false, container instanceof SVGElement)
if (container instanceof Element) {
container.removeAttribute('v-cloak')
container.setAttribute('data-v-app', '')
}
return proxy
}
return app
}) as CreateAppFunction<Element>
来到runtime-core>src>renderer.ts文件
// render函数传入baseCreatoRenderer()的返回值中
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)
}
flushPostFlushCbs()
container._vnode = vnode
}
const internals: RendererInternals = {
p: patch,
um: unmount,
m: move,
r: remove,
mt: mountComponent,
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
n: getNextHostNode,
o: options
}
let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
if (createHydrationFns) {
;[hydrate, hydrateNode] = createHydrationFns(
internals as RendererInternals<Node, Element>
)
}
// baseCreatoRenderer()返回 createApp
return {
render,
hydrate,//ssr服务端错误
// createAppAPI函数使用了柯里化
createApp: createAppAPI(render, hydrate)
}
}
来到runtime-core>src>apiCreateApp.ts文件
// createApp最终返回createAppAPI() 返回app对象
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
// 返回createApp
return function createApp(rootComponent, rootProps = null) {
if (!isFunction(rootComponent)) {
rootComponent = { ...rootComponent }
}
if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null
}
const context = createAppContext()
const installedPlugins = new Set()
let isMounted = false
// app对象 app.mixin.directive.comoinebt 创建一个app对象
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
version,
get config() {
return context.config
},
set config(v) {
if (__DEV__) {
warn(
`app.config cannot be replaced. Modify individual options instead.`
)
}
},
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
__DEV__ && warn(`Plugin has already been applied to target app.`)
} else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin)
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
installedPlugins.add(plugin)
plugin(app, ...options)
} else if (__DEV__) {
warn(
`A plugin must either be a function or an object with an "install" ` +
`function.`
)
}
return app
},
mixin(mixin: ComponentOptions) {
if (__FEATURE_OPTIONS_API__) {
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
} else if (__DEV__) {
warn(
'Mixin has already been applied to target app' +
(mixin.name ? `: ${mixin.name}` : '')
)
}
} else if (__DEV__) {
warn('Mixins are only available in builds supporting Options API')
}
return app
},
component(name: string, component?: Component): any {
if (__DEV__) {
validateComponentName(name, context.config)
}
if (!component) {
return context.components[name]
}
if (__DEV__ && context.components[name]) {
warn(`Component "${name}" has already been registered in target app.`)
}
context.components[name] = component
return app
},
directive(name: string, directive?: Directive) {
if (__DEV__) {
validateDirectiveName(name)
}
if (!directive) {
return context.directives[name] as any
}
if (__DEV__ && context.directives[name]) {
warn(`Directive "${name}" has already been registered in target app.`)
}
context.directives[name] = directive
return app
},
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
// #5571
if (__DEV__ && (rootContainer as any).__vue_app__) {
warn(
`There is already an app instance mounted on the host container.\n` +
` If you want to mount another app on the same host container,` +
` you need to unmount the previous app by calling \`app.unmount()\` first.`
)
}
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
// HMR root reload
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
// for devtools and telemetry
;(rootContainer as any).__vue_app__ = app
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = vnode.component
devtoolsInitApp(app, version)
}
return getExposeProxy(vnode.component!) || vnode.component!.proxy
} else if (__DEV__) {
warn(
`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``
)
}
},
unmount() {
if (isMounted) {
render(null, app._container)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = null
devtoolsUnmountApp(app)
}
delete app._container.__vue_app__
} else if (__DEV__) {
warn(`Cannot unmount an app that is not mounted.`)
}
},
provide(key, value) {
if (__DEV__ && (key as string | symbol) in context.provides) {
warn(
`App already provides property with key "${String(key)}". ` +
`It will be overwritten with the new value.`
)
}
context.provides[key as string | symbol] = value
return app
}
})
if (__COMPAT__) {
installAppCompatProperties(app, context, render)
}
return app
}
}
来到runtime-core>src>renderer.ts文件
// 如果还有子元素,进行pathch之后,进行挂载
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
isSVG && type !== 'foreignObject',
slotScopeIds,
optimized
)
const mountChildren: MountChildrenFn = (
children,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
start = 0
) => {
// 12.对子节点进行遍历
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
? cloneIfMounted(children[i] as VNode)
: normalizeVNode(children[i]))
// 13。遍历后进行挂载
patch(
null,
child,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
}
//片段只能有数组子级 因为它们要么由编译器生成,要么隐式创建 从数组。
// 拿到fragment的所有根组件,拿到子树元素,进行挂载
mountChildren(
// n2.children所有的子元素
n2.children as VNodeArrayChildren,
container,
fragmentEndAnchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
创建流程图:
大致分为13步,具体如下:
// 注意:此闭包中的函数应使用 'const xxx = () => {}'
// 样式,以防止被小写器内联。
// patch:进行diff算法,crateApp->vnode->element
const patch: PatchFn = (
n1,//旧的vnode,n1为空,进行挂载
n2,//新的vnode,n2与n1不同时,进行patch(diff算法)
container,//patch或mount之后,将vnode挂载到container上
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
} else if (shapeFlag & ShapeFlags.COMPONENT) {
// 处理组件节点,vnode-><HelloWorld> 按组件进行处理
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
进入processComponent
// 处理组件类型节点
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
// 调用mountComponent挂载组件
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
// 初始化component,对组件的数据进行赋值和操作
// setup组件实例,对组件的props/emits/slots/data进行初始化
setupComponent(instance)//执行完之后组件啥都有了
if (__DEV__) {
endMeasure(instance, `init`)
}
}
// setup() is async. This component relies on async logic to be resolved
// before proceeding
// setup() 是异步的。此组件依赖于要解析的异步逻辑
// 在继续之前
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect)
// Give it a placeholder if this is not hydration
// TODO handle self-defined fallback
if (!initialVNode.el) {
const placeholder = (instance.subTree = createVNode(Comment))
processCommentNode(null, placeholder, container!, anchor)
}
return
}
// 调用设置和渲染的副作用函数
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
副作用函数
// 副作用函数
const setupRenderEffect: SetupRenderEffectFn = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
// 调用了副作用函数effect()他来着reactive响应式系统,使用响应式数据时,默认进行依赖收集,进行再次执行
const update: SchedulerJob = (instance.update = () => effect.run())
update.id = instance.uid
// allowRecurse
// #1801, #2043 component render effects should allow recursive updates
//组件渲染效果应允许递归更新
toggleRecurse(instance, true)
if (__DEV__) {
effect.onTrack = instance.rtc
? e => invokeArrayFns(instance.rtc!, e)
: void 0
effect.onTrigger = instance.rtg
? e => invokeArrayFns(instance.rtg!, e)
: void 0
update.ownerInstance = instance
}
// 7. renderComponentRoot,对template进行渲染
// subTree是template里面的子树,子树被<Fragment>包裹
instance.subTree = renderComponentRoot(instance)
if (__DEV__) {
endMeasure(instance, `render`)
}
if (__DEV__) {
startMeasure(instance, `hydrate`)
}
// 8. 把subTree子树的VNode挂载到container上,调用patch()
patch(
null,//因为是挂载所以是null
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
// 9. 处理DOM元素,比如div/button/span(每个子树subtree)
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
进入processElement
// 如果template的根元素是div
const processElement = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
isSVG = isSVG || (n2.type as string) === 'svg'
if (n1 == null) {
// 调用mountElement进行挂载元素
mountElement(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else {
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
// 如果还有子元素,进行pathch之后,进行挂载
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
isSVG && type !== 'foreignObject',
slotScopeIds,
optimized
)
}
// 11.挂载子节点
const mountChildren: MountChildrenFn = (
children,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
start = 0
) => {
// 12.对子节点进行遍历
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
? cloneIfMounted(children[i] as VNode)
: normalizeVNode(children[i]))
// 13。遍历后进行挂载
patch(
null,
child,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
}
最后给大家找了个流程图方便大家理解:
**如果大家觉得还不错,下方公z号👇,来跟作者一起学习吧! **
引言 深拷贝是指创建一个新对象,该对象的值与原始对象完全相同,但在内存中具有不同的地址。这意味着如果您对原始对象进行更改,则不会影响到复制的对象 常见的C#常见的深拷贝方式有以下4类:
人工智能是一种未来性的技术,目前正在致力于研究自己的一套工具。一系列的进展在过去的几年中发生了:无事故驾驶超过300000英里并在三个州合法行驶迎来了自动驾驶的一个里程碑;IBM Waston击败了
非法律建议。 欧盟《人工智能法案》 (EU AI Act) 是全球首部全面的人工智能立法,现已正式生效,它将影响我们开发和使用人工智能的方式——包括在开源社区中的实践。如果您是一位开源开发
我已经阅读了所有 HERE Maps API 文档,但找不到答案。 HERE实时流量REST API输出中的XML标签是什么意思? 有谁知道如何解释这个输出(我在我的请求中使用了接近参数)? 最佳答
我的 iPad 应用程序工作正常,我将其留在现场进行测试,但现在崩溃了[保存时?] 这是崩溃日志, Incident Identifier: 80FC6810-9604-4EBA-A982-2009A
我的程序需要 qsort 的功能才能运行,但到目前为止还没有完成它的工作。 我实际上是在对单个字符值的数组进行排序,以便将它们分组,这样我就可以遍历数组并确定每个属性的计数。我的问题是 qsort 返
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我正在尝试使用 AVR 代码对 Arduino Uno 进行编程,因为我不被允许在 9 月份开始的高级项目中使用 Arduino 库。我找到了数据表,让数字引脚正常工作,然后尝试通过 USB 串行连接
我遇到了多次崩溃,似乎 native iOS 方法正在从第三方库调用函数。这是一个例子: Thread: Unknown Name (Crashed) 0 libsystem_kernel.d
我理解如何按照 Dijkstra 算法的解释找到从头到尾的最短路径,但我不明白的是解释。在这里,从图中的图形来看,从 A 到 E 添加到我已知集合的顺序是 A,C,B,D,F,H,G,E 我没有得到的
我正在查看一些 Django 源代码并遇到了 this . encoding = property(lambda self: self.file.encoding) 究竟是做什么的? 最佳答案 其他两
Sentry 提供了很好的图表来显示消息频率,但关于它们实际显示的内容的信息很少。 这些信息是每分钟吗? 5分钟? 15分钟?小时? 最佳答案 此图表按分钟显示。这是负责存储该图数据的模型。 http
我对 JavaScript 和 Uniswap 还很陌生。我正在使用 Uniswap V3 从 DAI/USDC 池中获取价格。我的“主要”功能如下所示: async function main()
我正在尝试弄清楚我下载的 Chrome 扩展程序是如何工作的(这是骗子用来窃取 CS:GO 元素的东西,并不重要...)。我想知道使用什么电子邮件地址(或使用什么其他通信方式)来提交被钓鱼的数据。 这
引言 今天同事问了我一个问题, System.Windows.Forms.Timer 是前台线程还是后台线程,我当时想的是它是跟着UI线程一起结束的,应该是前台线程吧? 我确实没有仔
我需要一些使用 scipy.stats.t.interval() 函数的帮助 http://docs.scipy.org/doc/scipy/reference/generated/scipy.sta
当我在 Oracle 查询计划中看到类似的内容时: HASH JOIN TABLE1 TABLE2 这两个表中的哪一个是 hashed ? Oracle 文档指的是通常被散列的“较小”
我想知道 GridSearchCV 返回的分数与按如下方式计算的 R2 指标之间的差异。在其他情况下,我收到的网格搜索分数非常负(同样适用于 cross_val_score),我将不胜感激解释它是什么
本文分享自华为云社区《 多主创新,让云数据库性能更卓越 》,作者: GaussDB 数据库。 华为《Taurus MM: bringing multi-master to the clou
我真的需要一些帮助来破译这个崩溃报告: Process: Farm Hand [616] Path: /Applications/Farm
我是一名优秀的程序员,十分优秀!