vue2 框架运行原理剖析系列(一)之 new Vue()实例化过程到底做了什么!!!

一、vue 基础用法

1.1 引入vue
1.2 使用vue语法编写ui
1.3 实现数据绑定
示例代码如下

<div id="app">
  {{ message }}
</div>
<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>

1.4 代码效果:使用浏览器打开对应的的html文件,则会看到Hello Vue!
在这里插入图片描述

二、源码下载

2.1 在(一)中,我们在script标签中,编写了vue.js 提供的vue 实例化语法,创建了一个vue实例。并将文档中id 为app 下的 {{message}} 文本替换成了实例内的 message变量的真实值,完成渲染。
2.2 那么上面的过程中,vue.js 帮我们完成了哪些工作呢?下面带大家深入源码。
2.3 下载源码后(vue 2.6 源码地址)执行 npm install 安装依赖,安装完毕后,执行 npm run dev 启动项目,其编译的文件 位于 dist/vue.js 文件
在这里插入图片描述

2.4 在example 目录中,vue 提供很多demo ,我们可以将其脚本引入的路径改为经过2.3 编译过的文件,这样我们在调试源码的时候,更改文件后,便会同步到vue.js 脚本,方便调试。
在这里插入图片描述

三、npm run dev 的做了什么

3.1 在 package.json 文件中 ,找到 scripts 字段下的 dev ,可以看到,该命令执行 rollup 工具,并将 scripts/config.js 文件 作为 配置文件,传入 TARGET 值为:web-full-dev
在这里插入图片描述
3.2 在config.js 的 builds 变量中,找到与 web-full-dev 匹配的配置如下:

 // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner

3.3 上面的配置中,以src/platforms/web/entry-runtime-with-compiler.js 作为整个vue.js 框架打包入口,接下来,我们具体分析一下改文件

四、顺藤摸瓜,寻找 vue 实例化具体代码

4.1 在entry-runtime-with-compiler.js 文件中 ,发现以下代码,他重写了 vue原型的 mount 方法,

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
){
	 return mount.call(this, el, hydrating)
}

4.2根据文件引入路径,可以知道 4.1的 Vue 来 自src/platforms/web/runtime/index,这个文件在Vue 原型中定义了mount 方法

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

其中 mountComponent具体分析要到后面文章,本文不具体分析,该方法主要是调用了render函数,渲染UI,如果存在 需要监听的数据,则设置其为可观测对象,当其发生变化时,便会通知Watcher 更新视图。

4.3 根据 4.2 引入Vue 的路径,可以找到src/core/instance/index.js 文件,代码内容如下

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)
}
//以下方法是在实例化vue之前执行,应该是在引入vue.js 的时候执行
console.log("开始执行initMinxin")
initMixin(Vue)//给Vue 的原型挂载 _init 方法 ->_init 做了什么
stateMixin(Vue)//给vue原型挂载$watche $set 和$delete $data $props
eventsMixin(Vue)//给原型$on 
lifecycleMixin(Vue)//给原型挂载 _update方法,这个方法就是视图更新的底层源码
renderMixin(Vue)//给原型挂载 $nextTick、  _render->生成虚拟DOM 的算法

export default Vue

五、Vue 实例化

5.1 在四 中我们找到 函数 Vue ,当我们执行 new Vue() 时,实际上执行的是 Vue 函数,里面执行了_init 方法
5.2 _init 具体代码位置如下
在这里插入图片描述
代码内容如下

Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++
    let startTag, endTag
 
    // a flag to avoid this being observed
    vm._isVue = true
    // merge 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)
    } else {
      debugger
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

5.3 其中 initState 方法 将 实例化传入的参数(data、watch、methods)进行处理,处理后可以通过this 访问到
5.4 在 init 方法中,还会判断是否传入 el 选项,如果传入了,则直接调用 原型的 vm.$mount 方法,该方法就是 指向 entry-runtime-with-compiler.js 的 Vue.prototype.mount ,这个方法就会在后续文章持续讲解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值