【Vue源码】Vue核心应用之拥抱Vue

263 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

Vue全家桶的系统学习,其中包括Vue源码分析Vue-Router的使用和原理Vuex的用法和原理Vue-ssr 和 一些常见的Vue面试题


参考官网 vue.js官网

Vue全家桶的使用,已经是一个老生常谈的话题了。

具体的使用可以去参考Vue的官网,上面有很详细的用法介绍。

从这篇文章开始,我会详细介绍一下Vue全家桶的使用及其原理

准备工作

我们先要进行前期的准备工作。

首先我们要先创建一个默认的 package.json 文件。

npm init -y

配置文件创建好后,我们就需要使用 rollup 来进行代码编译了。

Rollup环境配置

因为 webpack 太大了,不方便做测试,所以我们会使用 rollup 作为打包工具。

rollup 简单的来说,就是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码。rollup.js 更专注于Javascript类库打包 (开发应用时使用 Webpack,开发库时使用 Rollup)

随后我们来安装 rollup 及其相应的插件。

npm install @babel/preset-env @babel/core rollup rollup-plugin-babel rollup-plugin-serve cross-env -d
  • rollup:我们需要用到的模块打包工具。
  • rollup-plugin-babelbabelrollup 之间互通的插件,用来对ES6代码进行转义与编译。
  • @babel/preset-env / @babel/corebabel 需要用到的关联插件。
  • rollup-plugin-serve:可以用来启动本地服务。
  • cross-env:运行跨平台设置和使用环境变量的脚本。

随后我们来创建一个 rollup.config.js 文件作为它的配置文件。

import babel from "rollup-plugin-babel";
import serve from "rollup-plugin-serve";
export default {
  input: "./src/index.js",
  output: {
    format: "umd", // 模块化类型
    file: "dist/umd/vue.js",
    name: "Vue", // 打包后的全局变量的名字
    sourcemap: true,
  },
  plugins: [
    babel({
      exclude: "node_modules/**",
    }),
    process.env.ENV === "development"
      ? serve({
          open: true,
          openPage: "/public/index.html",
          port: 3000,
          contentBase: "",
        })
      : null,
  ],
};

配置 .babelrc 文件用来处理babel。

{
  "presets": ["@babel/preset-env"]
}

package.json 中进行如下配置。

"scripts": {
  "build:dev": "rollup -c",
  "serve": "cross-env ENV=development rollup -c -w"
}

最终项目结构如下。

|-- vue-test
    |-- .babelrc
    |-- package-lock.json
    |-- package.json
    |-- rollup.congfig.js
    |-- public
    |   |-- index.html
    |-- src
        |-- index.js

这样我们的配置文件就创建完毕了。

初始化流程

使用过Vue后都知道,Vue在作为插件库使用的时候,都是以 option Api选项进行配置)的形式进行创建的。

var vm = new Vue({
    el: '#app',
    data() {
        return {
            a: 1
        }
    }
})

所以我们第一步要实现的,就是将 Vue作为构造函数,并导出

导出构造函数

先来看一下完整的导出代码。

import {initMixin} from './init';

function Vue(options) {
    this._init(options);
}
initMixin(Vue); // 给原型上新增_init方法
export default Vue;

(注:这里我们不采用ES6类的写法,因为它会将类或函数作为一个整体进行编写。我们希望它可以分散到不同的文件中,所以采用ES5构造函数的方式进行定义,这样在结构上看起来会更清晰。)

  1. 将创建的构造函数导出,他会接收一个参数options

    options 其实就是我们传入的属性,包括 eldatamethod 等。

    function Vue(options) {
      console.log(options); // 实例化时传入的"属性"
    }
    export default Vue;
    
  2. 新增 _init方法

    options 接收到了,我们就需要通过传入的参数对 Vue进行初始化

    function Vue(options) {
      this._init(options);
    }
    Vue.prototype._init = function(){
      // ...
    }
    export default Vue;
    

    我们再对其进行 解耦,可以得到一个入口方法,可以使所有文件进行初始化操作

    import {initMixin} from './init';
    
    function Vue(options) {
        this._init(options);
    }
    initMixin(Vue); // 给原型上新增_init方法
    export default Vue;
    

这个文件的最终目的是向 Vue原型上扩展方法

很明显,我们目前就需要对 Vue进行初始化

初始化Vue状态

上一步中,我们创建了 Vue类,并传入了 options参数

这一步我们就要将需要的内容封装成插件。

import {initState} from './state';
export function initMixin(Vue){
    Vue.prototype._init = function (options) {
        const vm  = this; // 获取当前实例
        vm.$options = options
        // 初始化状态
        initState(vm);
    }
}

options 参数被传入了 vm.$options 中,这样我们就可以使用 $options 来进行配置。

initState 就是对状态进行初始化,也就是对数据进行一个初始化的劫持当我改变数据时,视图也会更新)。

根据属性进行初始化操作

**Vue并不完全属于MVVM,它只是参考的MVVM。**其包含的 $ref 就可以说明这个问题,它还是可以操作DOM的。

现在我们就需要对不同的属性进行不同的数据劫持。

export function initState(vm){
    const opts = vm.$options;
    if(opts.props){
        initProps(vm);
    }
    if(opts.methods){
        initMethod(vm);
    }
    if(opts.data){
        // 初始化data
        initData(vm);
    }
    if(opts.computed){
        initComputed(vm);
    }
    if(opts.watch){
        initWatch(vm);
    }
}
function initProps(){}
function initMethod(){}
function initData(){}
function initComputed(){}
function initWatch(){}

根据不同的传入参数,来进行不同的处理。

下面我们就可以对拆分出来的不同属性,进行不同的初始化处理了。

本篇文章由 莫小尚 创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。 您也可以关注我的 个人站点博客园掘金,我会在文章产出后同步上传到这些平台上。 最后感谢您的支持!