Vue2源码梳理:在 import Vue 时干了啥
2023-12-20 07:06:13
分析导入入口
- 在Web应用的 Vue.js 构建过程的 Runtime + Compiler 版本下,我们重点关注这个版本
- 它的入口是 src/platforms/web/entry-runtime-with-compiler.js
1 ) entry-runtime-with-compiler.js 入口文件
-
现在分析一下在
import Vue
的时候,都执行了哪些事情 -
entry-runtime-with-compiler.js
/* @flow */ import config from 'core/config' import { warn, cached } from 'core/util/index' import { mark, measure } from 'core/util/perf' import Vue from './runtime/index' import { query } from './util/index' import { compileToFunctions } from './compiler/index' import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat' const idToTemplate = cached(id => { const el = query(id) return el && el.innerHTML }) const mount = Vue.prototype.$mount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) /* istanbul ignore if */ if (el === document.body || el === document.documentElement) { process.env.NODE_ENV !== 'production' && warn( `Do not mount Vue to <html> or <body> - mount to normal elements instead.` ) return this } const options = this.$options // resolve template/el and convert to render function if (!options.render) { let template = options.template if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template) /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !template) { warn( `Template element not found or is empty: ${options.template}`, this ) } } } else if (template.nodeType) { template = template.innerHTML } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this) } return this } } else if (el) { template = getOuterHTML(el) } if (template) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile') } const { render, staticRenderFns } = compileToFunctions(template, { shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) options.render = render options.staticRenderFns = staticRenderFns /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile end') measure(`vue ${this._name} compile`, 'compile', 'compile end') } } } return mount.call(this, el, hydrating) } /** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */ function getOuterHTML (el: Element): string { if (el.outerHTML) { return el.outerHTML } else { const container = document.createElement('div') container.appendChild(el.cloneNode(true)) return container.innerHTML } } Vue.compile = compileToFunctions export default Vue
-
这里可以看到
export default Vue
最终导出 Vue 对象 -
而一开始的 Vue 的来源:
import Vue from './runtime/index'
-
之后,在其原型上挂载
$mount
方法
2 ) runtime/index 文件
- 我们看下 runtime/index 文件中定义的Vue, 具体位置: src/platforms/web/runtime/index.js
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) } // ... export default Vue
- 它最终也是
export default Vue
, 而这里的 Vue 是从 core/index 而来 - import 之后,它又定义了一些全局配置,挂载在 Vue.config 之上
- 在 Vue 原型上又挂载了
__patch__
方法 和$mount
方法等
- 它最终也是
3 ) core/index 文件
- 我们再进入 core/index,具体位置: src/core/index.js
import Vue from './instance/index' import { initGlobalAPI } from './global-api/index' import { isServerRendering } from 'core/util/env' import { FunctionalRenderContext } from 'core/vdom/create-functional-component' initGlobalAPI(Vue) Object.defineProperty(Vue.prototype, '$isServer', { get: isServerRendering }) Object.defineProperty(Vue.prototype, '$ssrContext', { get () { /* istanbul ignore next */ return this.$vnode && this.$vnode.ssrContext } }) // expose FunctionalRenderContext for ssr runtime helper installation Object.defineProperty(Vue, 'FunctionalRenderContext', { value: FunctionalRenderContext }) Vue.version = '__VERSION__' export default Vue
- 这里的 Vue 一开始 也是从其他地方导入而来的
import Vue from './instance/index'
- 同时,
initGlobalAPI(Vue)
这里初始化了一些 全局的 API- 这里定义了
Vue.config
, 这里是全局config - 这里的 config 定义对应的在vue官网上相关的API文档可供参考
- 还定义了
Vue.util
对象,里面有对应的方法,这里没有写在公共文档上- 这里不建议外部使用,因为里面的方法可能不稳定
- 同时,这里是给 Vue 这个对象本身扩展全局的静态方法
- 定义了
Vue.set
,Vue.delete
,Vue.nextTick
,Vue.options
方法 Vue.options
用于合并方法,里面引用了ASSET_TYPES
- 在
ASSET_TYPES
中又定义了component
,directive
,filter
三个全局方法的枚举
- 定义了
- 在
Vue.options
上又挂载了 _baseVue.options._base = Vue
extend(Vue.options.components, builtInComponents)
这里的 builtInComponents 是内置组件- 里面只有 keep-alive 组件
- 之后又调用了一系列的 init 方法
initUse(Vue)
创建了vue.use
的全局方法initMixin(Vue)
定义了一个全局的mixin
方法initExtend(Vue)
定义了Vue.extend
方法initAssetRegisters(Vue)
定义了上面ASSET_TYPES中枚举的全局 component, directive, filter 方法
- 经过一些列初始化,我们才能在业务代码中使用这些代码
- 可以进入 global-api/index 文件中查看,这里不再赘述, 参考 src/core/global-api/index.js
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) }
- 这里定义了
- 这里的 Vue 一开始 也是从其他地方导入而来的
4)instance/index 文件
- 再进入 instance/index 文件
import { initMixin } from './init' import { stateMixin } from './state' import { renderMixin } from './render' import { eventsMixin } from './events' import { lifecycleMixin } from './lifecycle' import { warn } from '../util/index' 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) } initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default Vue
- 在这里,就找到了 Vue 对象真正的定义的位置
- 最上面,进行环境的判断,并且限制了 必须通过 new 来实例化
- 之后执行
_init()
方法 - 之后,调用了 一系列的 Minxin 的方法
initMixin
源码中,在 Vue 的原型上挂了_init
方法stateMixin
源码中,也是在 Vue中挂载了一些方法,比如:$set
,$delete
,$watch
- … 其他都类似
- 也就是说,每个 Minxin 就是在原型上汇入一些方法
- 这里通过 function 的方式来声明 Vue, 比用 Class 好处在可以方便拆分方法的挂载
- 拆分到不同文件下的好处是,方便代码的管理,有利于代码的维护
- 这是非常值得学习的代码重构方案
5 )总结
- 所以,到现在我们知道 Vue 本身就是一个基于函数实现的类,类上挂载了很多方法和属性
- 对于 Vue 的初始化过程,总结出两个步骤
- 一个是 Vue的定义,也就是最里层的那个 Vue function 的定义
- 另一个是 属性和原型方法,全局方法,静态方法的挂载
文章来源:https://blog.csdn.net/Tyro_java/article/details/135088339
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!