当前位置: 首页 > news >正文

Vue2源码梳理:在 import Vue 时干了啥

分析导入入口

  • 在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 functionif (!options.render) {let template = options.templateif (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 = renderoptions.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 = compileToFunctionsexport 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) : undefinedreturn 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 上又挂载了 _base Vue.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) {// configconst configDef = {}configDef.get = () => configif (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 = setVue.delete = delVue.nextTick = nextTickVue.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 = Vueextend(Vue.options.components, builtInComponents)initUse(Vue)initMixin(Vue)initExtend(Vue)initAssetRegisters(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 的定义
    • 另一个是 属性和原型方法,全局方法,静态方法的挂载

相关文章:

  • whisper深入-语者分离
  • 部署智能合约以及 javascript 调用合约函数(Web3项目二实战之三)
  • 每日一题 2828. 判别首字母缩略词(简单)
  • 源码解析8-QSS原理-案例-Qt的qss特殊设置多个子控件的颜色与伪状态
  • 【Python必做100题】之第十九题(海伦公式求三角形面积)
  • 跟我学c++高级篇——C++26反射预览
  • 年终汇报这么写,升值加薪必有你!
  • 一句话分清C/C++声明和定义
  • Https图片链接下载问题
  • 记录| cat查看文件命令的使用
  • 【BEV感知】BEVFormer 融合多视角图形的空间特征和时序特征 ECCV 2022
  • 【jvm从入门到实战】(九) 垃圾回收(2)-垃圾回收器
  • docker在线安装minio
  • HTML5+CSS3小实例:纯CSS实现锚点平滑过渡
  • 【jvm从入门到实战】(十) 实战篇-内存调优
  • 2017届校招提前批面试回顾
  • Android 控件背景颜色处理
  • es6要点
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • HTTP那些事
  • javascript 哈希表
  • markdown编辑器简评
  • oldjun 检测网站的经验
  • python大佬养成计划----difflib模块
  • SAP云平台里Global Account和Sub Account的关系
  • XML已死 ?
  • 初识MongoDB分片
  • 从零开始的无人驾驶 1
  • 对象管理器(defineProperty)学习笔记
  • 猴子数据域名防封接口降低小说被封的风险
  • 前端_面试
  • 前端相关框架总和
  • 我的业余项目总结
  • 原生 js 实现移动端 Touch 滑动反弹
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • # Apache SeaTunnel 究竟是什么?
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • $(selector).each()和$.each()的区别
  • $GOPATH/go.mod exists but should not goland
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (二)linux使用docker容器运行mysql
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (原創) 未来三学期想要修的课 (日記)
  • (转)jQuery 基础
  • .apk 成为历史!
  • .mysql secret在哪_MySQL如何使用索引
  • .NET Core WebAPI中封装Swagger配置
  • .NET 反射的使用
  • .net 无限分类
  • .NET连接数据库方式
  • .NET中的Exception处理(C#)