Skip to content

源码笔记(一):前言及总流程概览 ​

前言 ​

Vue 是一套用于构建用户界面的渐进式框架,被设计为可以自底向上逐层应用。本系列不会刻意梳理讲解 API 及 Vue 的用法,官方文档已经有清晰的讲解。

本系列文章作为笔记,用于记录 vue2.x 的构建(含组件)主流程。

概览 ​

根据 vue 构建 demo 的生命周期及其数据变化的更新作为流程线及相关,本系列文章一共分为以下章节:

  1. 前言及总流程概览
  2. 源码结构及调试相关介绍
  3. beforeCreate 阶段
  4. created 阶段
  5. beforeMount 阶段
  6. mount 阶段之生成 vnode
  7. mount 阶段之生成 dom
  8. update 阶段(上)
  9. update 阶段(下)
  10. destroyed 阶段

准备工作 ​

npm 安装 ​

bash
yarn add vue
yarn add vue-loader vue-style-loader vue-template-compiler css-loader -D
yarn add @babel/core babel-loader webpack-cli webpack webpack-dev-server html-webpack-plugin -D

代码环境 ​

json
  "dependencies": {
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.4.2",
    "html-webpack-plugin": "^4.0.4",
    "vue-loader": "^15.9.1",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3"
  }

版本不同,源码略微有差异。

demo 文件 ​

本项目 demo 开源在 github,欢迎交流学习。

index.html ​

html
<div id="main">
  <Bpp></Bpp>
  <div v-on:click="plus">a:{{a}},计算属性:{{compute}}</div>
  <App name="one" v-bind:num="a"></App>
  <div v-on:click="hide">====点击让第二个App组件卸载====</div>
  <App name="two" v-if="isShow"></App>
</div>

index.js ​

js
import Vue from 'vue';
import App from './app.vue';
new Vue({
  el: '#main',
  components: {
    App,
    Bpp: () => import('./bpp.vue'),
  },
  data: {
    info: { name: 'korey', age: 28 },
    isShow: true,
  },
  computed: {
    compute() {
      return this.info.age + 1;
    },
  },
  methods: {
    plus() {
      this.info.age++;
    },
    hide() {
      this.isShow = false;
    },
  },
  watch: {
    info: {
      handler(val, oldVal) {
        console.log('变量info变化了:', val, oldVal);
      },
      deep: true,
    },
  },
  beforeCreate() {
    console.log(`vue beforeCreate`);
  },
  created() {
    console.log(`vue created`);
  },
  beforeMount() {
    console.log(`vue beforeMount`);
  },
  mounted() {
    console.log(`vue mounted`);
  },
  beforeUpdate() {
    console.log('vue beforeUpdate');
  },
  updated() {
    console.log('vue updated');
  },
});

app.vue ​

vue
<template>
  <div id="app">
    <div>{{ num ? `num:${num}` : `` }}</div>
    <Child class="example"></Child>
  </div>
</template>

<script>
import Child from './child.vue';
export default {
  name: 'app',
  components: {
    Child,
  },
  props: {
    num: Number,
    name: String,
  },
  beforeCreate() {
    console.log(`App beforeCreate`);
  },
  created() {
    console.log(`App ${this.name} created`);
  },
  beforeMount() {
    console.log(`App ${this.name} beforeMount`);
  },
  mounted() {
    console.log(`App ${this.name} mounted`);
  },
  beforeUpdate() {
    console.log(`App ${this.name} beforeUpdate`);
  },
  updated() {
    console.log(`App ${this.name} updated`);
  },
  beforeDestroy() {
    console.log(`App ${this.name} beforeDestroy`);
  },
  destroyed() {
    console.log(`App ${this.name} destroyed`);
  },
};
</script>

<style>
.example {
  color: red;
}
</style>

bpp.vue ​

vue
<template>
  <div id="bpp">
    <div>{{ message }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: '异步Bpp组件',
    };
  },
  beforeCreate() {
    console.log('async Bpp beforeCreate');
  },
  created() {
    console.log('async Bpp created');
  },
  beforeMount() {
    console.log('async Bpp beforeMount');
  },
  mounted() {
    console.log('async Bpp mounted');
  },
  beforeDestroy() {
    console.log('async Bpp beforeDestroy');
  },
  destroyed() {
    console.log('async Bpp destroyed');
  },
};
</script>

child.vue ​

vue
<template>
  <div id="child">
    <div>Child组件</div>
  </div>
</template>

<script>
export default {
  beforeCreate() {
    console.log('Child beforeCreate');
  },
  created() {
    console.log('Child created');
  },
  beforeMount() {
    console.log('Child beforeMount');
  },
  mounted() {
    console.log('Child mounted');
  },
  beforeDestroy() {
    console.log('Child beforeDestroy');
  },
  destroyed() {
    console.log('Child destroyed');
  },
};
</script>

webpack.config.js ​

js
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  devtool: 'hidden-source-map',
  entry: {
    main: path.resolve(__dirname, './src/index.js'),
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js',
  },
  resolve: {
    extensions: ['.js', '.vue'],
    alias: {
      vue$: 'vue/dist/vue.esm.js',
    },
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: ['vue-loader'],
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: 'src/index.html',
    }),
  ],
};

为什么要写这个系列 ​

从 vue 过去的 1.x 到现在主流的 2.x 到即将发布的 3.x 版本,网上的各种相关教程多不胜数。但纸上得来终觉浅,只有自己亲自来梳理一遍,才更能对 vue 有更深刻的认知。

在这之前曾自己手撸了一个类 vue 使用 proxy 实现一个简单完整的 MVVM 库,如今整体对比 vue 来看,无论是功能、性能、还是框架设计都远远不如,从中学到的东西很多很多。