源码版本:2.7.16
src
|-- compiler # 包含与模板编译相关的代码。它将模板字符串转换成渲染函数 render。
|-- core # 核心代码
|-- instance # Vue.js 实例的构造函数和原型方法
|-- observer # 响应式系统实现,负责数据变化侦测
|-- vdom # 虚拟 dom 的创建和更新算法
|-- components # 内置组件,如<keep-alive>
|-- global-api # 全局 API 方法,如 Vue.component
|-- platforms # 不同平台的入口,包括 web 和 weex
|-- shared # 整个项目共用的工具函数或辅助函数
|-- types # 包含 TypeScript 类型定义的目录
查看 package.json
"scripts": {
"dev": "rollup -w -c scripts/config.js --environment TARGET:full-dev"
}
执行 npm run dev
会进入 scripts/config.js 的打包配置,进入 config.js 找到 full-dev :
'full-dev': {
entry: resolve('web/entry-runtime-with-compiler.ts'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
可以看到打包入口文件在 web/entry-runtime-with-compiler.ts :
从这边开始源码入手:
import Vue from './runtime-with-compiler'
import * as vca from 'v3'
import { extend } from 'shared/util'
extend(Vue, vca)
import { effect } from 'v3/reactivity/effect'
Vue.effect = effect
export default Vue
源码路径:src/core/instance/index.ts
import { eventsMixin } from "./events";
import { initMixin } from "./init";
import { lifecycleMixin } from "./lifecycle";
import { renderMixin } from "./render";
import { stateMixin } from "./state";
// Vue 的构造函数
function Vue(options) {
this._init(options);
}
//@ts-expect-error Vue has function type
initMixin(Vue);
//@ts-expect-error Vue has function type
stateMixin(Vue);
//@ts-expect-error Vue has function type
eventsMixin(Vue);
//@ts-expect-error Vue has function type
lifecycleMixin(Vue);
//@ts-expect-error Vue has function type
renderMixin(Vue);
export default Vue;
initMixin 函数负责向 Vue 的原型添加一个 _init 方法,这个方法是Vue实例的私有方法,并且是 Vue 实例初始化过程的起点
源码位置:src/core/instance/init.ts
export function initMixin(Vue: typeof Component) {
Vue.prototype._init = function (options?: Record<string, any>) {
const vm: Component = this
vm._uid = uid++ // 设置一个唯一的标识符 _uid,用于标识组件实例
vm._isVue = true // 这是一个标记,用来简单判断这是一个 Vue 实例
// avoid instances from being observed
vm.__v_skip = true // 设置一个标志 __v_skip,避免 Vue 实例被响应式系统观察
// 创建一个效果范围 _scope,并设置其父作用域为 undefined
vm._scope = new EffectScope(true /* detached */)
vm._scope.parent = undefined
vm._scope._vm = true
// 合并传入的 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 as any)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor as any),
options || {},
vm
)
}
// _self 属性也被引用到实例自身,目的是使得实例可以在内部作用域中访问到自己。
vm._self = vm
initLifecycle(vm) // 初始化实例的生命周期
initEvents(vm) // 初始化实例的事件系统
initRender(vm) // 初始化渲染函数
// callHook 调用 'beforeCreate' 生命周期钩子。
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
// initInjections 初始化 inject 选项(注入依赖)
initInjections(vm) // resolve injections before data/props
// 初始化实例的状态,包括 data, props, computed 等。
initState(vm)
// initProvide 处理 provide 选项(提供依赖)
initProvide(vm) // resolve provide after data/props
// callHook 再次被调用以触发 'created' 生命周期钩子
callHook(vm, 'created')
// 如果 vm.$options.el 存在,那么调用 vm.$mount 来挂载实例
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
stateMixin 函数的作用是给 Vue 实例的原型添加一些与状态相关的实例方法,即操作数据和观察者的方法。这包括 $data 和 $props 访问器属性,以及用于观察和修改响应式数据的实例方法 $set、 $delete 和 $watch
源码位置:src/core/instance/state.ts
export function stateMixin(Vue: typeof Component) {
// flow somehow has problems with directly declared definition object
// when using Object.defineProperty, so we have to procedurally build up
// the object here.
const dataDef: any = {}
dataDef.get = function () {
return this._data
}
const propsDef: any = {}
propsDef.get = function () {
return this._props
}
// 响应式系统中代理了实例数据对象 data 和 props 对象的存取器属性。
// 这些属性是只读的,提供了访问组件数据和传入属性的一致化接口。
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
// 实例方法 $set 允许你向响应式对象添加一个属性,并确保这个新属性也是响应式的
Vue.prototype.$set = set
// 实例方法 $delete 允许你从响应式对象中删除一个属性。
// 这和 delete 操作符的使用相比,$delete 还会触发视图更新
Vue.prototype.$delete = del
//set 和 del 是 Vue 的全局 API 方法,提供了 $set 和 $delete 的具体实现
// $watch 用于观察 Vue 实例中的数据变更。该方法返回一个取消观察函数,用于停止触发回调
// 这个方法接收观察的数据表达式或函数作为第一个参数,以及当被观察的数据发生变化时的回调函数作为第二个参数。
Vue.prototype.$watch = function (
expOrFn: string | (() => any),
cb: any,
options?: Record<string, any>
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`
pushTarget()
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
return function unwatchFn() {
watcher.teardown()
}
}
}
eventsMixin 函数负责向 Vue 的原型添加事件相关的方法。这些方法允许组件实例监听和触发自定义事件,是 Vue 组件通信机制的核心部分。
源码位置: src/core/instance/events.ts
eventsMixin 的基本结构:
export function eventsMixin (Vue: typeof Component) {
// 用于监听当前实例上的自定义事件。事件可以由‘vm.emit` 触发。回调函数会接收所有传入事件触发函数的额外参数。
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
// ...
}
// 监听一个自定义事件,但只触发一次,一旦触发之后,监听器会被移除
Vue.prototype.$once = function (event: string, fn: Function): Component {
// ...
}
// 移除自定义事件监听器。
// 如果没有提供参数,则移除所有的事件监听器;
// 如果只提供了事件,则移除该事件所有的监听器;
// 如果同时提供了事件与回调,则只移除这个回调的监听器。
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
// ...
}
// 触发当前实例上的事件。附加参数都会传给监听器回调
Vue.prototype.$emit = function (event: string): Component {
// ...
}
}
lifecycleMixin 函数的作用是向 Vue 的原型中添加与组件生命周期相关的方法。这些方法主要用于在组件的不同生命周期阶段上触发相关的生命周期钩子
源码位置:src/core/instance/lifecycle.ts
lifecycleMixin 的基本结构:
export function lifecycleMixin (Vue: typeof Component) {
// 这个方法用来根据虚拟 DOM 树来更新 DOM。
// 它是响应系统检测到数据变化后调用渲染函数生成新的虚拟 DOM 树,并与旧的虚拟 DOM 树进行比对之后的更新处理函数
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean): void {
// ...更新逻辑
}
// 这个方法会迫使组件重新渲染。
// 通常,你不需要调用这个方法,因为 Vue 的响应系统会在数据变化时自动进行更新。但是,在一些特殊情况下,可能需要此方法来强制组件重新渲染。
Vue.prototype.$forceUpdate = function (): void {
// ...强制更新逻辑
}
// 用于完全销毁一个实例,清理它与其他实例的连接,解绑它的全部指令及事件监听器。
Vue.prototype.$destroy = function (): void {
// ...销毁逻辑
}
}
renderMixin 函数在 Vue.js 源代码中的作用是将与渲染相关的一些方法和属性混入到 Vue 的原型中。这些方法和属性是渲染过程中使用的,包括生成虚拟 DOM 和更新组件实例的渲染
源码位置: src/core/instance/render.ts
renderMixin 的基本结构:
export function renderMixin (Vue: Class<Component>) {
// install runtime convenience helpers
Vue.prototype._o = markOnce
Vue.prototype._n = toNumber
Vue.prototype._s = toString
// ... more helper methods
// 将 $nextTick 方法添加到原型
// 提供一个在下次 DOM 更新循环结束后执行延迟回调的方法。
// 这在修改数据之后,你想基于更新后的 DOM 状态来执行操作时非常有用。
Vue.prototype.$nextTick = function(fn: Function): void {
return nextTick(fn, this)
}
// 这是一个内部方法,用来生成组件的虚拟 DOM 树(Virtual DOM Tree)
// 它会调用开发者编写的 render 函数(或者编译时由模板转换而成的 render 函数)来生成虚拟节点(VNodes)。这个过程是响应式系统追踪依赖关系的一部分,并且当数据变更时会触发组件的重新渲染。
Vue.prototype._render = function(): VNode {
// 生成 VNode 的逻辑
}
}