资讯专栏INFORMATION COLUMN

Vue源码解析(一): 创建vue程序的背后发生了什么

nevermind / 1807人阅读

摘要:主要大纲从方法看全局配置寻根问祖的构造函数的出生地先来一段最常见的代码上面已经创建了一个应用程序从上面很容易就看出来是一个构造器,是用这个构造器构造出来的实例化对象,实例化的时候传入了参数,参数中包括和上述延伸了个问题构造器是什么模样可以

主要大纲:

从initGlobalAPI方法看Vue.config全局配置

寻根问祖-Vue的构造函数的出生地

先来一段最常见的vue代码demo

{{ message }}
// js var vm = new Vue({ el: "#app", data: { message: ‘hello vue" } })

上面已经创建了一个vue应用程序;从上面很容易就看出来 Vue是一个构造器,vm是用这个构造器构造出来的实例化对象,实例化的时候传入了参数,参数中包括el和data
上述延伸了3个问题:

Vue 构造器是什么模样?

Vm可以使用的方法,即vue的开放API都在源码里面怎么实现的?

我们传入构造方法内的参数发生了什么

这些问题是我们解锁vue源码的最开始的步骤,所以我们不妨通过vue源码的入口开始寻找这些源码的实现

在源码的src/platforms/web下面放着不同版本的构建entry文件,这些文件中导出export的Vue,都是从src/core/instance/index这个文件import过来的,
我们先看下入口文件能带给我们什么答案:

// src/core/instance/index
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)

这个入口文件做了3件事:

引用了 ./instance/index, 暴露了vue的来源,即构造器

调用initGlobalAPI方法,将vue传进去, 给vue拓展了全局静态方法

将vue暴露出去

这个入口文件的意义,是在暴露vue之前,给vue通过initGlobalAPI方法给vue拓展了全局静态方法,对应Vue的外部API是 Vue.config,包含了Vue的全局配置,

Vue.config.silent // 日志与警告
Vue.config.errorHandler // 这个处理函数被调用的时候,可以获取错误信息和Vue实例
Vue.config.devtools // 配置是否允许 vue-devtools 检查代码
…..
从initGlobalAPI方法看Vue.config全局配置

initGlobalAPI方法定义了configDef对象,它的getter方法会的属性值是config,setter方法给出警告不允许修改。最后在vue上添加了config属性,属性描述返回configDef对象

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) // 添加config属性

除此之外,还定义了util属性,但是并没有暴露到外面,也并不建议外部去使用

寻根问祖-Vue的构造函数的出生地

了解了构造函数,也就知道了new vue()的时候发生了什么
下面这段代码就是Vue的构造方法,我们可以直观的看出vue构造器是使用ES5的Function去实现类,是因为可以通过prototype往vue原型上拓展很多方法,把这些方法拆分到不同的文件/模块下,这样更有利于代码的维护,与协同开发
比如在这个文件中,可以看到把Vue当作一个参数传进下面的**Mixin方法中,这些方法都是通过接收vue,在它的prototype上面定义一些功能的;

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’)
            //   vue必须是new vue()的实例化对象
           }
        console.log("options", options)

        this._init(options) // 调用内部_init方法
}
initMixin(Vue) // 在created生命周期函数之前的操作
stateMixin(Vue) // 利用 definedProperty 进行静态数据的订阅发布
eventsMixin(Vue) // 实例事件流的注入, 利用的是订阅发布模式的事件流构造
lifecycleMixin(Vue) // 
renderMixin(Vue) // 实现 _render 渲染虚拟dom
export default Vue

这个构造函数的最核心点,就是this._init(options)

在此处打断点,可以看到参数options传进来的就是外面我们实例化时传入的参数el 和 data

new Vue({
    el: "#app",
      data: {
        message: ‘hello vue"    
      }
})

这个_init方法出自initMixin 函数
看完这个函数,我们梳理出整个初始化阶段源码的几个重要的节点

初始化options参数进行合并配置

初始化生命周期

初始化时间系统

初始化state,包括data、props、 computed、watcher

export function initMixin (Vue: Class) {

console.log("Vue", Vue)
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid 实例化的uid递增1
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */

...

// 用_isVue来标识当前的实例是个Vue实例,这样做是为了后续被observed
vm._isVue = true    
// 合并配置options,并判断是否是内部Component的options的初始化
if (options && options._isComponent) {
    // 内部
    initInternalComponent(vm, options)
} else {
    // 非内部
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
    )
}
// 在render中将this指向vm._renderProxy
if (process.env.NODE_ENV !== "production") {
    initProxy(vm)
} else {
    vm._renderProxy = vm
}
// expose real self
vm._self = vm
// 初始化生命周期
initLifecycle(vm)    
// 初始化事件注册
initEvents(vm)
// 初始化渲染
initRender(vm)
// 触发回掉函数中的beforeCreate钩子函数
callHook(vm, "beforeCreate")
initInjections(vm) // resolve injections before data/props
// 初始化vm的状态,包括data、props、computed、watcher等
initState(vm)
initProvide(vm) // resolve provide after data/props
// vm已经创建好来,回掉created钩子函数
callHook(vm, "created’)
/* istanbul ignore if */
…
// 将实例进行挂载
if (vm.$options.el) {
    vm.$mount(vm.$options.el)
    }
}

}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109241.html

相关文章

  • Vue自己造个组件轮子,以及实践背后带来思考

    摘要:用造个组件轮子吧闰土大叔如果你掌握了的组件知识,相关的指令事件,花点时间你也可以造出这么个入门级的小轮子。接下来,抛出造轮子实践背后带来的一些思考。以上三部分内容构成了的整个执行过程。 showImg(https://segmentfault.com/img/bV1Tnu?w=754&h=500); 前言 首先,向大家说声抱歉。由于之前的井底之蛙,误认为Vue.js还远没有覆盖到二三线...

    icyfire 评论0 收藏0
  • Vue.js源码(1):Hello World背后

    摘要:构造函数文件路径初始化这里只拿对例子理解最关键的步骤分析。在最后,调用了对数据进行。每个函数之后都会返回一个。就是去实例化指令,将指令和新建的元素在一起,然后将元素替换到中去。 下面的代码会在页面上输出Hello World,但是在这个new Vue()到页面渲染之间,到底发生了什么。这篇文章希望通过最简单的例子,去了解Vue源码过程。这里分析的源码版本是Vue.version = 1...

    jlanglang 评论0 收藏0
  • Vue原理】Component - 白话版

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理白话版从模板上使用到挂载到页面 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    liuyix 评论0 收藏0
  • 太原面经分享:如何在vue面试环节,展示你晋级阿里P6+技术功底?

    摘要:假如你通过阅读源码,掌握了对的实现原理,对生态系统有了充分的认识,那你会在面试环节游刃有余,达到晋级阿里的技术功底,从而提高个人竞争力,面试加分更容易拿。 前言 一年一度紧张刺激的高考开始了,与此同时,我也没闲着,奔走在各大公司的前端面试环节,不断积累着经验,一路升级打怪。 最近两年,太原作为一个准二线城市,各大互联网公司的技术栈也在升级换代,假如你在太原面试前端岗位,而你的技术库里若...

    xiaoqibTn 评论0 收藏0
  • [全网最全 Vue CLI 3 原创合集] 你要这里都有

    摘要:慢慢地,关于的原创学习文章已经写了多篇了会一直放出来,目前篇,因此做一个合集,献给那些对新版本脚手架使用和背后设计感兴趣的同学,都是一步一步去看源码,也给官方提了几次,合进去了几个原创不易,欢迎大家互相转发,期望大家一起快速过度到版本目录 慢慢地,关于 Vue CLI 3 的原创学习文章已经写了 20 多篇了(会一直放出来,目前 23 篇), 因此做一个合集,献给那些对新版本脚手架使用...

    phpmatt 评论0 收藏0

发表评论

0条评论

nevermind

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<