Vue2 源码分析(一): 入口platforms/web/runtime/index.js & initGlobalAPI
杂记:
Vue 采用Flow 做了静态类型检查,Flow 是类似 Typescript 的静态类型检查工具
Vue 2.6.14版本用了rollup 打包
根据打包配置,我们仅仅看web 端的源码,入口文件在 src/platforms/web/ 下
Vue 以 '_' 开头的变量不会追踪变化
拉取到vue源码后,执行yarn build, 在script/build 下打印所有打包列表:
从打包脚本可以一步一步找到入口文件,我们从 platforms/web/runtime/index.js下分析
入口文件:在Vue原型上绑定了$mount 方法,$mount的具体实现是我们需要取关注的,后续会讲,这里记住是在入口初始化的就好
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser, isChrome } from 'core/util/index'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// 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)
}
// devtools global hook
/* istanbul ignore next */
Vue.nextTick(() => {
if (config.devtools) {
if (devtools) {
devtools.emit('init', Vue)
} else if (process.env.NODE_ENV !== 'production' && isChrome) {
console[console.info ? 'info' : 'log'](
'Download the Vue Devtools extension for a better development experience:\n' +
'https://github.com/vuejs/vue-devtools'
)
}
}
if (process.env.NODE_ENV !== 'production' &&
config.productionTip !== false &&
inBrowser && typeof console !== 'undefined'
) {
console[console.info ? 'info' : 'log'](
`You are running Vue in development mode.\n` +
`Make sure to turn on production mode when deploying for production.\n` +
`See more tips at https://vuejs.org/guide/deployment.html`
)
}
}, 0)
export default Vue
核心代码入口: core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
Vue.version = '__VERSION__'
export default Vue
引入Vue后,调用了initGlobalAPI(Vue),我们先看initGlobalAPI
/* @flow */
// core/global-api/index.js
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
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.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
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
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
initGlobalAPI:
1、在Vue 原型上初始化了config 和 util,可以直接打印Vue.config和Vue.util 查看
- config 就是定义了一些会全局用到的配置
- util 导出了四个方法:
见下图打印:
2、在Vue 原型上定义了 set, del, nextTick 方法
3、在Vue 原型上初始化了options
4、调用 initUse,在Vue 原型上定义use 方法,主要用于vue 使用第三方插件 Vue.use()
5、调用 initMixin:
在Vue 原型上定义mixin 方法,Vue 的extend 和 mixin 属性都会走这个方法去实现合并策略。
-
initMixin 中向Vue.config 上的 optionMergeStrategies 对象上添加了属性以及属性值属性一般就是合并的key,值就是对应该属性的合并策略;
-
每种key 的合并策略:
-
data,provide的合并策略一样: 将当前元素没有的属性合并赋值到data 对象上,如果当前元素有同名的data key,则被合并的会不生效;
-
生命钩子函数会已数组的方式合并;
-
filters, directivs, components 的合并策略比较复杂,在parent 的基础上合并了构造函数中的和当前子元素的; todo: 在mixin 中定义filter,在组件A 中定义同名filter且引用mixin, 按道理会以mixin 生效
-
watch 会以数组的方式合并;
-
props、methods、inject、computed 的合并方式是一样的,同名的以当前元素生效
-
6、调用initExtend ,通过类继承,实现Vue 组件间的继承逻辑
7、调用initAssertType 实现Vue.directive(),Vue.component(),Vue.filter() 方法
Vue2 源码分析(二)# Vue构造函数 & this._init
问题1:vue 源码中的cached 函数作用是什么,为什么要为纯函数创建版本
转载自:https://juejin.cn/post/7034452125312778277