阅读 243

【Vue2.x源码学习】第二篇 - Vue 的初始化流程

这是我参与更文挑战的第2天,活动详情查看: 更文挑战

一,前言

上篇,使用 rollup 完成了 Vue2 源码环境的搭建

本篇,介绍 Vue 的初始化流程


二,Vue 简介

以两个概念性问题做简单介绍

1,问题:Vue 是 MVVM 框架吗?

在 Vue 官网上是这样说的:

Vue 是一套用于构建用户界面的渐进式框架,

Vue 并没有完全支持 MVVM 模型,但 Vue 的设计受到了它的启发,

变量名 vm 是 vue model 的缩写,表示 vue 实例
复制代码

所以,严格说 Vue 并不是一个 MVVM 框架

MVVM 模式是仅能够通过视图更改数据,通过数据来更改视图的,

但 Vue 是可以通过 ref 获取 dom 进行操作的

2,问题:Vue 的双向绑定和单向数据流矛盾吗?

当然不矛盾,这是一个理解问题

双向绑定是指数据变了视图会更新;视图变了会影响数据;

单向数据说的是响应式数据,即数据变化后会更新视图;
复制代码

三,使用 Vue

1,Vue Demo

以一个简单的 Vue Demo 为例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
  
<body>
  <!-- 模板 -->
  <div id=app>{{message}}</div>
  <!-- 引入 vue -->
  <script src="./vue.js"></script>
  <script>
    <!-- 初始化 Vue,传入 options 对象 -->
    let vm = new Vue({
      el: '#app',
      // 1,data 是对象
      data: {
        message: 'Hello Vue'
      }
      // 2,data 是函数,返回一个对象
      // data() {
      //   return { message: 'Hello Vue' }
      // }
    });
  </script>
</body>
</html>
复制代码

Vue 初始化时,传入 el 挂载点,data 数据;

初始化完成后,message 成为响应式数据,数据变化更新视图,视图变化影响数据;

2,问题:响应式数据原理

Object.defineProperty 数据劫持(这里也正是 vue2 的性能瓶颈)

3,问题:Vue 中的数据可以是对象吗?

根组件不会被共享,可以是对象也可以是函数

非根组件必须为函数,否则 data 状态会多组件共享


四,Vue 的初始化操作

1,原型方法 _init

在 Vue 原型上扩展一个 _init 方法(原型方法),用于 Vue 的初始化操作

/**
 * vue中所有功能都是通过原型扩展方式添加的
 * @param {*} options 		new Vue时传入的 options 配置对象
 */
function Vue(options){
    this._init(options);	// 调用Vue原型上_init方法
}

// 在Vue原型上扩展一个原型方法_init,进行vue初始化
Vue.prototype._init = function(options) {
  
}

export default Vue;
复制代码

2,重构:原型方法 _init 模块化处理

将用于初始化操作的原型方法 _init 单独抽离形成一个独立模块 initMixin

// src/init.js
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    console.log(options)
  }
}

//------------------------------------------------//
// src/index.js
import { initMixin } from "./init";// 引入initMixin模块

function Vue(options){
    this._init(options);	// 这里调用的是Vue的原型方法
}

// 调用 initMixin 进行 Vue 初始化操作
initMixin(Vue)

export default Vue;
复制代码

3,重构后测试

image.png


4,原型方法 _init 的 this 指向

export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    console.log(vm)	// this == vm 实例
  }
}
复制代码

打印原型方法 _init 的 this,this 指向 vm 实例

image.png


5,vm.$options

为了后续让 vue 中的其他方法也能够轻松获取到外部 new Vue 实例化时传入的 options 对象

干脆将 options 选项挂到 vm 实例上,即 vm.$options

export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;  // this 指向当前 vue 实例
    vm.$options = options; // 将 Vue 实例化时用户传入的 options 暴露到 vm 实例上
  }
}
复制代码

6,initState 方法:状态的初始化

在实际使用中,数据不仅来源于传入的 data,还可能来自 props、watch、computed...

所以,最好能够统一在一个地方,集中进行数据的初始化处理:initState 方法

// src/state.js
export function initState(vm){
    // 获取options:_init 中已将 options 挂载到 vm.$options
    const opts = vm.$options;

    if(opts.data){
        initData(vm);	// data数据的初始化
    }
  	// props 数据的初始化
    // watch 数据的初始化
    // computed 数据的初始化
}

function initData(vm){
    console.log("进入 state.js - initData,数据初始化操作")
}
复制代码

引入并使用 initState

import { initState } from "./state";

export function initMixin(Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    vm.$options = options;

    // new Vue 时,传入 options 选项,包含 el 和 data
    initState(vm);  // 状态的初始化

    if (vm.$options.el) {
      console.log("有el,需要挂载")
    }
  }
}
复制代码

image.png


五,结尾

本篇主要介绍了 Vue 数据的初始化流程,核心的几个点:

  • initMixin
  • vm.$options
  • initState

下一篇,数据劫持:Object.defineProperty

维护日志

  • 20210605:调整文章的目录结构
文章分类
前端
文章标签