当前位置:   article > 正文

Vue3源码 学习

vue3源码

Vue3源码系列文章目录

目录

Vue3源码系列文章目录

目录

目录

Vue3源码系列文章目录

前言

一、vue3中createApp()实例是如何创建的,实例是什么样子的?

        1、以todoMvc为入口,进行断点调试

        2、 其调用了工厂函数ensureRenderer()内部的createApp函数, 进入ensureRenderer

         3、ensureRenderer()工厂函数内容据说是vue3中最多的,纠结过程容易迷路,此处直接跳转到该工厂函数返回的结果, 

 4、找到createAppApi()

5、工厂函数创建的app是这个样子

 二、vue3中app.mount(), 挂载都做了什么?

1、创建节点vnode

2、创建render函数并执行render函数, 第一步生成vnode传递给patch函数转换成dom,然后将其添加到宿主上

 挂载时做了: 将传入的组件数据和状态转换成DOM,并追加到宿主元素上

三、patch状态更新流程

1、从状态更新处打断点

 2、单点进入后进入更新状态counter   此处应该是counter = this.counter += 1  会先走get 拿值, 然后走set赋值

3、 单点产看更新,最终走trigger通过!hadkey判断其走trigger Add(添加), 还是走trigger Set(更新)

 4、单点进入trigger Set

 5、走triggerEffect / triggerEffects

 6、走triggerEffect后进入effect.schedule查看其如何更新

 7、单点进入,  激活响应式, 此处想看断点应该打在componentUpdateFn

 8、最后走patch更新 对比prevTree和nextTree哪里不同然后去更新

四、Vue3 Composition Api 探究

1、Composition Api包括:

2、结合reactive、生命周期钩子、属性和上下文进行体验

五、Vue3 ReractiveApi探究



前言

学习源码的笔记,以及vue3源码学习过程的问题、思路、总结。知识是容易遗忘的,让其留下痕迹,方便以后回顾学习。


一、vue3中createApp()实例是如何创建的,实例是什么样子的?

        1、以todoMvc为入口,进行断点调试

        2、 其调用了工厂函数ensureRenderer()内部的createApp函数, 进入ensureRenderer

         3、ensureRenderer()工厂函数内容据说是vue3中最多的,纠结过程容易迷路,此处直接跳转到该工厂函数返回的结果, 

 4、找到createAppApi()

5、工厂函数创建的app是这个样子

  1. export function createAppAPI<HostElement>(
  2. render: RootRenderFunction<HostElement>,
  3. hydrate?: RootHydrateFunction
  4. ): CreateAppFunction<HostElement> {
  5. return function createApp(rootComponent, rootProps = null) {
  6. if (!isFunction(rootComponent)) {
  7. rootComponent = { ...rootComponent }
  8. }
  9. if (rootProps != null && !isObject(rootProps)) {
  10. __DEV__ && warn(`root props passed to app.mount() must be an object.`)
  11. rootProps = null
  12. }
  13. const context = createAppContext()
  14. const installedPlugins = new Set()
  15. let isMounted = false
  16. const app: App = (context.app = {
  17. _uid: uid++,
  18. _component: rootComponent as ConcreteComponent,
  19. _props: rootProps,
  20. _container: null,
  21. _context: context,
  22. _instance: null,
  23. version,
  24. get config() {
  25. return context.config
  26. },
  27. set config(v) {
  28. if (__DEV__) {
  29. warn(
  30. `app.config cannot be replaced. Modify individual options instead.`
  31. )
  32. }
  33. },
  34. use(plugin: Plugin, ...options: any[]) {
  35. if (installedPlugins.has(plugin)) {
  36. __DEV__ && warn(`Plugin has already been applied to target app.`)
  37. } else if (plugin && isFunction(plugin.install)) {
  38. installedPlugins.add(plugin)
  39. plugin.install(app, ...options)
  40. } else if (isFunction(plugin)) {
  41. installedPlugins.add(plugin)
  42. plugin(app, ...options)
  43. } else if (__DEV__) {
  44. warn(
  45. `A plugin must either be a function or an object with an "install" ` +
  46. `function.`
  47. )
  48. }
  49. return app
  50. },
  51. mixin(mixin: ComponentOptions) {
  52. if (__FEATURE_OPTIONS_API__) {
  53. if (!context.mixins.includes(mixin)) {
  54. context.mixins.push(mixin)
  55. } else if (__DEV__) {
  56. warn(
  57. 'Mixin has already been applied to target app' +
  58. (mixin.name ? `: ${mixin.name}` : '')
  59. )
  60. }
  61. } else if (__DEV__) {
  62. warn('Mixins are only available in builds supporting Options API')
  63. }
  64. return app
  65. },
  66. component(name: string, component?: Component): any {
  67. if (__DEV__) {
  68. validateComponentName(name, context.config)
  69. }
  70. if (!component) {
  71. return context.components[name]
  72. }
  73. if (__DEV__ && context.components[name]) {
  74. warn(`Component "${name}" has already been registered in target app.`)
  75. }
  76. context.components[name] = component
  77. return app
  78. },
  79. directive(name: string, directive?: Directive) {
  80. if (__DEV__) {
  81. validateDirectiveName(name)
  82. }
  83. if (!directive) {
  84. return context.directives[name] as any
  85. }
  86. if (__DEV__ && context.directives[name]) {
  87. warn(`Directive "${name}" has already been registered in target app.`)
  88. }
  89. context.directives[name] = directive
  90. return app
  91. },
  92. mount(
  93. rootContainer: HostElement,
  94. isHydrate?: boolean,
  95. isSVG?: boolean
  96. ): any {
  97. if (!isMounted) {
  98. // #5571
  99. if (__DEV__ && (rootContainer as any).__vue_app__) {
  100. warn(
  101. `There is already an app instance mounted on the host container.\n` +
  102. ` If you want to mount another app on the same host container,` +
  103. ` you need to unmount the previous app by calling \`app.unmount()\` first.`
  104. )
  105. }
  106. const vnode = createVNode(
  107. rootComponent as ConcreteComponent,
  108. rootProps
  109. )
  110. // store app context on the root VNode.
  111. // this will be set on the root instance on initial mount.
  112. vnode.appContext = context
  113. // HMR root reload
  114. if (__DEV__) {
  115. context.reload = () => {
  116. render(cloneVNode(vnode), rootContainer, isSVG)
  117. }
  118. }
  119. if (isHydrate && hydrate) {
  120. hydrate(vnode as VNode<Node, Element>, rootContainer as any)
  121. } else {
  122. render(vnode, rootContainer, isSVG)
  123. }
  124. isMounted = true
  125. app._container = rootContainer
  126. // for devtools and telemetry
  127. ;(rootContainer as any).__vue_app__ = app
  128. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  129. app._instance = vnode.component
  130. devtoolsInitApp(app, version)
  131. }
  132. return getExposeProxy(vnode.component!) || vnode.component!.proxy
  133. } else if (__DEV__) {
  134. warn(
  135. `App has already been mounted.\n` +
  136. `If you want to remount the same app, move your app creation logic ` +
  137. `into a factory function and create fresh app instances for each ` +
  138. `mount - e.g. \`const createMyApp = () => createApp(App)\``
  139. )
  140. }
  141. },
  142. unmount() {
  143. if (isMounted) {
  144. render(null, app._container)
  145. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  146. app._instance = null
  147. devtoolsUnmountApp(app)
  148. }
  149. delete app._container.__vue_app__
  150. } else if (__DEV__) {
  151. warn(`Cannot unmount an app that is not mounted.`)
  152. }
  153. },
  154. provide(key, value) {
  155. if (__DEV__ && (key as string | symbol) in context.provides) {
  156. warn(
  157. `App already provides property with key "${String(key)}". ` +
  158. `It will be overwritten with the new value.`
  159. )
  160. }
  161. context.provides[key as string | symbol] = value
  162. return app
  163. }
  164. })
  165. if (__COMPAT__) {
  166. installAppCompatProperties(app, context, render)
  167. }
  168. return app
  169. }
  170. }

 二、vue3中app.mount(), 挂载都做了什么?

1、创建节点vnode

2、创建render函数并执行render函数, 第一步生成vnode传递给patch函数转换成dom,然后将其添加到宿主上

 - 

 app.mount('#app')

 挂载时做了: 将传入的组件数据和状态转换成DOM,并追加到宿主元素上

三、patch状态更新流程

以下面代码为例子

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <h1>vue3 更新流程</h1>
  12. <p>{{counter}}</p>
  13. <comp></comp>
  14. </div>
  15. <script src="../dist/vue.global.js"></script>
  16. <script>
  17. // vue2: new Vue({}).$mount()
  18. // 变化1: 函数创建实例
  19. // vue3: createApp({})
  20. const app = Vue.createApp({
  21. // render() {
  22. // return Vue.h('div', {
  23. // myprop, title, onClick
  24. // })
  25. // }
  26. data() {
  27. return {
  28. counter: 1
  29. }
  30. },
  31. mounted() {
  32. setInterval(() => {
  33. this.counter++
  34. }, 1000)
  35. }
  36. })
  37. // 变化2: 实例方法
  38. app.component('comp', {
  39. template: '<div>comp</div>'
  40. })
  41. // 变化3: 挂载mount
  42. app.mount('#app')
  43. </script>
  44. </body>
  45. </html>

1、从状态更新处打断点

 2、单点进入后进入更新状态counter   此处应该是counter = this.counter += 1  会先走get 拿值, 然后走set赋值

 

 此处data是proxy对象, key是counter

3、 单点产看更新,最终走trigger通过!hadkey判断其走trigger Add(添加), 还是走trigger Set(更新)

 4、单点进入trigger Set

进入后进入

 5、走triggerEffect / triggerEffects

 

 6、走triggerEffect后进入effect.schedule查看其如何更新

 7、单点进入,  激活响应式, 此处想看断点应该打在componentUpdateFn

更新走componentUpdateFn所以断点要提前打在componentUpdateFn的elese update上

 启动queueFlush()只启动一次就行了, 说是启动一个异步任务

 

此处可以看到调用栈的promise 异步任务  此时跟初始化非常相似,多了一个上一次执行的结果

 可以打到下面nexttree查看 nexttree中的count 加1了

 8、最后走patch更新 对比prevTree和nextTree哪里不同然后去更新

四、Vue3 Composition Api 探究

1、Composition Api包括:

  1. setup
  2. 生命周期钩子
  3. getCurrentInstance
  4. provide/inject

问题1: 执行的时刻? 为什么没有created钩子?

  •         从何看起, 首次执行mount挂载, 调用render函数,render内部 调用patch函数进行dom的渲染与更新, patch内部调用processComponent跟组件初始化开始, 从这开始,

 

  • 进入下一步mountComponent

 

  • 进入后首先创建了实例instance,然后进行组件实例初始化setupComponent(instance)  

 

  • 进入组件初始化列表 , 如果组件有状态,执行初始化过程, 并返回setup选项的返回值

  • 看一下如果组件有状态,是如何处理返回setup的返回值的, 此处可以看到将ctx进行proxy代理, 下面是从组件中拿出创建的setup, 如果有就创建setup上下文将 其存入instance.setupContext

 

  •  查看创建setup上下文, 就可以看到context中为什么有四个属性,且为什么attrs是只读的
  1. export function createSetupContext(
  2. instance: ComponentInternalInstance
  3. ): SetupContext {
  4. // 对外暴露接口
  5. const expose: SetupContext['expose'] = exposed => {
  6. if (__DEV__ && instance.exposed) {
  7. warn(`expose() should be called only once per setup().`)
  8. }
  9. instance.exposed = exposed || {}
  10. }
  11. //组件非属性特性
  12. let attrs: Data
  13. if (__DEV__) {
  14. // We use getters in dev in case libs like test-utils overwrite instance
  15. // properties (overwrites should not be done in prod)
  16. return Object.freeze({
  17. get attrs() {
  18. return attrs || (attrs = createAttrsProxy(instance))
  19. },
  20. get slots() {
  21. return shallowReadonly(instance.slots)
  22. },
  23. get emit() {
  24. return (event: string, ...args: any[]) => instance.emit(event, ...args)
  25. },
  26. expose
  27. })
  28. } else {
  29. //返回的就是setupContext
  30. return {
  31. // 只读的attrs
  32. get attrs() {
  33. return attrs || (attrs = createAttrsProxy(instance))
  34. },
  35. slots: instance.slots,
  36. emit: instance.emit,
  37. expose
  38. }
  39. }
  40. }
  • 继续回到setup, 可以看到setCurrentInstance

  •  下面开始调用setup, 此时传入instance.props, 和setupContext, 这就是为什么setup中有两个参数

 

  1. export function handleSetupResult(
  2. instance: ComponentInternalInstance,
  3. setupResult: unknown,
  4. isSSR: boolean
  5. ) {
  6. // 首先判断返回的结果是不是函数
  7. //如果是函数则作为render函数处理
  8. if (isFunction(setupResult)) {
  9. // setup returned an inline render function
  10. if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
  11. // when the function's name is `ssrRender` (compiled by SFC inline mode),
  12. // set it as ssrRender instead.
  13. instance.ssrRender = setupResult
  14. } else {
  15. instance.render = setupResult as InternalRenderFunction
  16. }
  17. } else if (isObject(setupResult)) {
  18. if (__DEV__ && isVNode(setupResult)) {
  19. warn(
  20. `setup() should not return VNodes directly - ` +
  21. `return a render function instead.`
  22. )
  23. }
  24. // setup returned bindings.
  25. // assuming a render function compiled from template is present.
  26. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  27. instance.devtoolsRawSetupState = setupResult
  28. }
  29. // 如果是对象, 转换setupoResult这个对象为响应式对象
  30. // 将来组件渲染函数中会首先从setupState中获取值
  31. instance.setupState = proxyRefs(setupResult)
  32. if (__DEV__) {
  33. exposeSetupStateOnRenderContext(instance)
  34. }
  35. } else if (__DEV__ && setupResult !== undefined) {
  36. warn(
  37. `setup() should return an object. Received: ${
  38. setupResult === null ? 'null' : typeof setupResult
  39. }`
  40. )
  41. }
  42. // 最后依然执行组件的安装
  43. // 里面主要是处理其他的options api
  44. finishComponentSetup(instance, isSSR)
  45. }
  • 查看finishComponentSetup(instance, isSSR), 可得知为什么不需要created()

回答: 执行时刻beforeCreate之类的传统生命周期钩子,实际上setup函数执行的时候,组件实例已经创建了,所以setup中处理beforeCreate和created是没有意义的。

问题2: 传入setup参数中的props和ctx从何而来? 又是什么?

问题3: 如果和data这些数据发生冲突,他们能共存吗,Vue3处理时的行为?

setup优先级更高一些,两者可以共存, 为什么setup优先级更高,如何处理的?

2、结合reactive、生命周期钩子、属性和上下文进行体验

五、Vue3 ReractiveApi探究

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/283896
推荐阅读
相关标签
  

闽ICP备14008679号