Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

  • runtime-core运行时核心模块

    • watch和watcherEffect源码解析
    • scheduler调度器
    • directive自定义指令实现原理
    • provide依赖和inject注入详解
    • 生命周期函数Lifecycle解析
      • 概述
        • 深入了解 createApp
        • runtime-dom中的包装
        • patchProp方法
        • nodeOps
        • runtime-core中的核心实现
        • render方法
        • createAppAPI方法
    • 渲染器renderer源码解析
  • runtime-dom运行时DOM模块

  • 5.18源码学习》
  • runtime-core运行时核心模块
东流
2025-09-15
目录

生命周期函数Lifecycle解析

# 概述

createApp 是使用 vue3 调用的第一个也是必须的入口函数,通过它可以创建一个应用实例,然后我们可以通过返回的实例对象挂载到真实的 DOM 元素上,还可以安装插件等等,一般使用如下

import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
const app = createApp(App); // App为根组件

app.use(createPinia()); // 安装Pinia插件

app.mount("#app"); // 挂载
1
2
3
4
5
6
7
8

# 深入了解 createApp

# runtime-dom中的包装

我们引用的createApp函数定义如下所示,其在node_modules\@vue\runtime-dom

const createApp = (...args) => {
  const app = ensureRenderer().createApp(...args);
  const { mount } = app;
  app.mount = (containerOrSelector) => {
    const container = normalizeContainer(containerOrSelector); // 通过`document.querySelector`查找并返回 dom 元素
    if (!container) return;
    const component = app._component;
    if (
      !shared.isFunction(component) &&
      !component.render &&
      !component.template
    ) {
      component.template = container.innerHTML;
    }
    container.innerHTML = ""; //清空
    const proxy = mount(container, false, resolveRootNamespace(container)); // resolveRootNamespace 判断container是属于svg还是MathML命名空间
    if (container instanceof Element) {
      container.removeAttribute("v-cloak");
      container.setAttribute("data-v-app", "");
    }
    return proxy;
  };
  return app;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

很显然这里只是给app包装了一层,定义了一个mount函数,具体的实现是第一句ensureRenderer().createApp(...args),实际上,这句返回的才是我们所熟知的实例app。通过观察发现,它是调用了runtimeCore的createRenderer方法,参数为rendererOptions,包含patchProp方法和nodeOps

# patchProp方法

patchProp主要作用是根据虚拟 DOM 的变化来更新实际的 DOM 元素的属性,它负责处理添加、删除和更新 DOM 元素的属性,实现 Vue 组件的属性动态更新。而patchProp的操作依据就是参数key的值,这里不过多赘述,详见 key 的更新机制

//伪代码
const patchProp = (
  el,
  key,
  prevValue,
  nextValue,
  namespace,
  prevChildren,
  parentComponent,
  parentSuspense,
  unmountChildren
) => {
  if(key==='class'){
    patchClass()
  }else if(key==='style'){
    patchStyle()
  }else if(isOn(key)){ //key是否是onClick等等
    patchEvent()
  }else if(...){ //主要用于处理设置元素的 CSS 类、排除不应设置为属性的情况,以及正常设置属性的情况
    patchDOMProp()
  }else{
    patchAttr()
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# nodeOps

nodeOps就是基于 DOM 定义了一些 DOM 节点的操作方法,这些方法将在 vue3 运行 DOM 时被使用,主要如下

insert: 将一个子节点插入到父节点中,可以指定插入位置。

remove: 从父节点中移除一个子节点。

createElement: 创建一个新的 DOM 元素节点。

createText: 创建一个包含指定文本的文本节点。

createComment: 创建一个注释节点。

setText: 设置文本节点的文本内容。

setElementText: 设置元素节点的文本内容。

parentNode: 获取节点的父节点。

nextSibling: 获取节点的下一个兄弟节点。

querySelector: 在文档中查找匹配指定选择器的第一个元素。

setScopeId: 设置元素节点的作用域 ID。

insertStaticContent: 插入静态内容到指定位置。

# runtime-core中的核心实现

runtime-core\src\renderer.ts中runtime-core调用createRenderer 函数,会返回一个重载函数baseCreateRenderer,该函数里面定义了许多操作组件的方法,返回如下

return {
  render,
  hydrate, //ssr 渲染不用管
  createApp: createAppAPI(render, hydrate),
};
1
2
3
4
5
# render方法

render顾名思义就是用来渲染节点,这也放在其它文章里重点讲解组件的渲染

const render = (vnode, container, namespace) => {
  if (vnode == null) {
    if (container._vnode) {
      unmount(container._vnode, null, null, true);
    }
  } else {
    patch(
      container._vnode || null,
      vnode,
      container,
      null,
      null,
      null,
      namespace
    );
  }
  if (!isFlushing) {
    isFlushing = true;
    flushPreFlushCbs();
    flushPostFlushCbs();
    isFlushing = false;
  }
  container._vnode = vnode;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# createAppAPI方法

createAppAPI是在runtime-core\src\apiCreateApp.ts中实现的,其伪代码如下。

export function createAppAPI(render) {
  return function createApp(rootComponent, rootProps=null) {

    const context = createAppContext()
    const installedPlugins = new WeakSet()

    const app = {
      _uid: uid++,
      _component: rootComponent, // 即最上面的 App 根组件
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,
      version,
      get config(){},
      set config(){},
      use(plugin,...options){},
      mixin(){},
      component(name,component){},
      mount(){},
      unmount(){},
      provide(){},
      runWithContext(fn){}
    };

    return app;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

首先调用createAppContext定义了上下文环境context,主要用来存放cache数据, 然后声明一个WeakSet集合存储插件,最后定义一个 app 对象并返回。

当我们通过app实例进行挂载mount时,本质上就是调用的上述 app 中的mount方法, 创建完虚拟节点vnode后,会调用render方法是在baseCreateRenderer中实现的,如上。

 mount(rootContainer, isHydrate, namespace) {
        if (!isMounted) {
          const vnode = createVNode(rootComponent, rootProps); //根据rootComponent 创建虚拟DOM
          vnode.appContext = context; //绑定context
          if (namespace === true) {
            namespace = "svg";
          } else if (namespace === false) {
            namespace = void 0;
          }
          if (isHydrate && hydrate) {
            hydrate(vnode, rootContainer); // ssr 不用管
          } else {
            render(vnode, rootContainer, namespace);
          }
          isMounted = true; // 重置挂载标志
          app._container = rootContainer;
          rootContainer.__vue_app__ = app;
          return getExposeProxy(vnode.component) || vnode.component.proxy;
        }
      },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
编辑 (opens new window)
上次更新: 2025/09/15, 10:29:53
provide依赖和inject注入详解
渲染器renderer源码解析

← provide依赖和inject注入详解 渲染器renderer源码解析→

最近更新
01
createApp创建实例解析
09-15
02
渲染器renderer源码解析
09-15
03
provide依赖和inject注入详解
09-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式