惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy & Cybersecurity Law Blog
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
B
Blog RSS Feed
罗磊的独立博客
V
V2EX
V
Visual Studio Blog
博客园 - 叶小钗
W
WeLiveSecurity
小众软件
小众软件
K
Kaspersky official blog
美团技术团队
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
Martin Fowler
Martin Fowler
Recorded Future
Recorded Future
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
Engineering at Meta
Engineering at Meta
Security Latest
Security Latest
Microsoft Azure Blog
Microsoft Azure Blog
V
Vulnerabilities – Threatpost
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
Help Net Security
Help Net Security
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Forbes - Security
Forbes - Security
M
MIT News - Artificial intelligence
H
Hacker News: Front Page
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
Spread Privacy
Spread Privacy
The Register - Security
The Register - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
量子位
Google Online Security Blog
Google Online Security Blog
PCI Perspectives
PCI Perspectives
The Last Watchdog
The Last Watchdog
AI
AI
N
News | PayPal Newsroom
D
DataBreaches.Net
Cloudbric
Cloudbric
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog

Aimee's Blog

微服务与服务拆分:何时拆、怎么拆 异步与事件驱动架构:把协作从「打电话」改成「发消息」 高可用设计:怎么让系统尽量不宕机 可扩展性设计:怎么让系统加机器就能扛更多 缓存架构:多级缓存怎么搭 高并发三板斧:限流、熔断、降级 架构设计到底在设计什么 —— 从单体到微服务的演进 服务成本账:一个服务一个月烧多少钱 可观测性:线上出问题怎么查 API 设计:好接口长什么样 消息队列:为什么要 MQ,以及丢失、重复、顺序怎么破 查询优化:慢查询怎么治 —— 索引 + 预聚合 缓存:为什么快、三大坑、和数据库怎么保持一致 存储与基建选型 —— 传统业务 vs AI 大模型时代 权限系统怎么设计?—— 从 RBAC 到 Google Zanzibar 鉴权怎么落地?—— 常见业务场景的方案选型与避坑 JWT、OAuth、Token 刷新 —— 一个全栈工程师的鉴权入门笔记 我把跑了五年的旧博客升级到了 3.0 大模型 AIGC 满天飞 推荐一些学习和资料网站 2024 冒个泡 冒个泡 阿里云新机器安装 自定义配置 vue 的 webpack.config.js 从输入url到页面展示 React API 使用demo 欢迎加入 aimee前端技术交流群 github actions 自动化部署 很久以前的美食美荟 无穷之路纪录片还不错哦 vue 模板编译和组件化 vue2 响应式原理 vue2 虚拟DOM原理 vue2.0 router及简易实现 防抖和节流 Proxy 和 Object.defineProperty git rebase
vue2 源码解析-初始化
Aimee · 2021-12-28 · via Aimee's Blog

源码目录结构

src
  ├─compiler   编译相关
  ├─core       Vue核心库
  ├─platforms  平台相关代码
  ├─server     SSR,服务端渲染
  ├─sfc        .vue 文件编译为 js 对象
  └─shared     公共的代码

Vue 的不同构建版本

  • 完整版:同时包含编辑器和运行时的版本
  • 编辑器:用来将模板字符串编译称为JavaScript渲染函数的代码,体积大、效率低。
  • 运行时:用来创建Vue实例、渲染并处理虚拟DOM等的代码,体积小、效率高。基本上就是除去编译器的代码。比完整版体积少30%左右。
    • *.vue文件中的模板是在构建时预编译的,最终打包后的结果不需要编译器,只需要运行时版本即可
  • UMD
  • CommonJs
  • ES Module

从入口开始

  • src/platform/web/entry-runtime-with-compiler.js

Vue 的构造函数在哪里

  • src/platform/web/entry-runtime-with-compiler.js 中引用了 './runtime/index'
  • src/platform/web/runtime/index.js
    • 设置 Vue.config
    • 设置平台相关的指令和组件
      • 指令 v-model、v-show
      • 组件 transition、transition-group
    • 设置平台相关的 patch 方法(打补丁方法,对比新旧的 VNode)
    • 设置 $mount 方法,挂载 DOM
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
  • src/platform/web/runtime/index.js 中引用了 'core/index'
  • src/core/index.js
    • 定义了 Vue 的静态方法
    • initGlobalAPI(Vue)
      • extend
      • mixin
      • use
  • src/core/index.js 中引用了 './instance/index'
  • src/core/instance/index.js
    • 定义了Vue的构造函数
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ){
    warn('Vue is a constructor and should be called with the `new` keyword')
	}
	// 调用 _init() 方法
  this._init(options)
}
// 注册 vm 的 _init() 方法,初始化 vm 
initMixin(Vue)
// 注册 vm 的 $data/$props/$set/$delete/$watch 
stateMixin(Vue)
// 初始化事件相关方法
// $on/$once/$off/$emit
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy 
lifecycleMixin(Vue)
// 混入 render
// $nextTick/_render
renderMixin(Vue)

四个导出 Vue 的模块

  • src/platforms/web/entry-runtime-with-compiler.js
    • web 平台相关的入口
    • 重写了平台相关的 $mount() 方法
    • 注册了 Vue.compile() 方法,传递一个 HTML 字符串返回 render 函数
  • src/platforms/web/runtime/index.js
    • web 平台相关
    • 注册和平台相关的全局指令:v-model、v-show
    • 注册和平台相关的全局组件: v-transition、v-transition-group
    • 全局方法:
      • patch:把虚拟 DOM 转换成真实 DOM
      • $mount:挂载方法
  • src/core/index.js
    • 与平台无关
    • 设置了 Vue 的静态方法,initGlobalAPI(Vue)
  • src/core/instance/index.js
    • 与平台无关
    • 定义了构造函数,调用了 this._init(options) 方法
    • 给 Vue 中混入了常用的实例成员

Vue 的初始化

静态方法初始化

// 注册 Vue 的静态属性/方法 
initGlobalAPI(Vue)
// src/core/global-api/index.js
// 初始化 Vue.config 对象 Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on // them unless you are aware of the risk.
// 这些工具方法不视作全局API的一部分,除非你已经意识到某些风险,否则不要去依赖他们 
Vue.util = {
  warn,
  extend,
  mergeOptions,
  defineReactive
}
// 静态方法 set/delete/nextTick 
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
// 让一个对象可响应
Vue.observable = <T>(obj: T): T => {
  observe(obj)
return obj }
// 初始化 Vue.options 对象,并给其扩展
// components/directives/filters/_base 
Vue.options = Object.create(null) ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-
object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
// 设置 keep-alive 组件 
extend(Vue.options.components, builtInComponents)
// 注册 Vue.use() 用来注册插件 
initUse(Vue)
// 注册 Vue.mixin() 实现混入 
initMixin(Vue)
// 注册 Vue.extend() 基于传入的 options 返回一个组件的构造函数 
initExtend(Vue)
// 注册 Vue.directive()、 Vue.component()、Vue.filter() 
initAssetRegisters(Vue)

Vue实例初始化

  • 定义 Vue 的构造函数
  • 初始化 Vue 的实例成员
// 此处不用 class 的原因是因为方便,后续给 Vue 实例混入实例成员 
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
){
warn('Vue is a constructor and should be called with the `new`
keyword') }
  this._init(options)
}
// 注册 vm 的 _init() 方法,初始化 vm 
 initMixin(Vue)
// 注册 vm 的 $data/$props/$set/$delete/$watch 
 stateMixin(Vue)
// 初始化事件相关方法
// $on/$once/$off/$emit
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy 
lifecycleMixin(Vue)
// 混入 render
// $nextTick/_render
renderMixin(Vue)
  • initMixin(Vue)
    • 初始化_init()方法
 // 给 Vue 实例增加 _init() 方法
  // 合并 options / 初始化操作  
Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    // 如果是 Vue 实例不需要被 observe
    vm._isVue = true
    // merge options
    // 合并 options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    // vm 的生命周期相关变量初始化
    // $children/$parent/$root/$refs
    initLifecycle(vm)
    // vm 的事件监听初始化, 父组件绑定在当前组件上的事件
    initEvents(vm)
    // vm 的编译render初始化
    // $slots/$scopedSlots/_c/$createElement/$attrs/$listeners
    initRender(vm)
    // beforeCreate 生命钩子的回调
    callHook(vm, 'beforeCreate')
    // 把 inject 的成员注入到 vm 上
    initInjections(vm) // resolve injections before data/props
    // 初始化 vm 的 _props/methods/_data/computed/watch
    initState(vm)
    // 初始化 provide
    initProvide(vm) // resolve provide after data/props
    // created 生命钩子的回调
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }
    // 调用 $mount() 挂载
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

首次渲染过程

  • Vue 初始化,实例成员,静态成员
  • new Vue()
  • this._init()
    • 合并options
    • initLifecycle(vm) 初始化生命周期的相关变量
    • initEvents(vm) 将父组件的事件绑定到当前实例的事件中
    • initRender(vm) 初始化 slots /createElement/ attrs/listeners
    • 执行beforeCreate 钩子
    • initInjections(vm) 把 inject 的成员注入到 vm 上
    • initState(vm) 初始化状态相关的参数,data/props/methods/computed/watch
    • initProvide(vm) 初始化provide
    • 执行created 钩子
    • 如果传递了el 直接执行 $mount()
  • vm.$mount()
    • 编辑版本
      • 重写$mount
      • 如果传递了render 直接执行
      • 如果没有传递render 获取template,生成render 渲染函数
    • 运行时版本
      • 调用mountComponent()
  • mountComponent(this, el)
    • 触发beforeMount钩子
    • 定义updateComponent
      • vm._update(vm._render)
      • vm._render()渲染,虚拟dom
      • vm.update()更新,将VDom 转换成真实dom
    • 创建Watcher实例
      • before 中执行beforeUpdate钩子
    • 触发mounted
    • return vm
  • watcher.get()
    • 创建完watcher 会调用一次get
    • 调用updateComponent()
    • 调用vm._render() 创建VNode
    • 调用vm.update()
      • 调用vm.patch(vm.$el, vnode) 挂载真实dom
      • 记录vm.$el

简述

  • 初始化Vue 静态方法和实例方法
  • 实例化Vue new Vue()
  • 执行this._init() 处理vm 的参数
  • 执行beforeCreate 钩子
  • 创建vm的data、props、methods等
  • 执行created钩子
  • 执行$mount()
    • 编辑版本
      • 将template处理生成render 函数
    • 运行时版本
      • 执行mountComponent()
  • mountComponent()
    • 执行beforeMount 钩子
    • 生成updateComponent 函数
      • vm._update(vm._render)
    • 添加Watcher 实例
    • Watcher中before 参数中执行beforeUpdate钩子
    • Watcher实例化会执行一次get方法 将执行updateComponent函数
  • updateComponent函数中
    • vm._render() 生成虚拟dom并返回vnode
    • vm._update()中有个vm.patch(vm.$el, vnode)将vnode挂载到真实dom中
    • 记录vm.$el 作为下一次对比的旧vnode