源码笔记(一):前言及总流程概览
前言
Vue
是一套用于构建用户界面的渐进式框架,被设计为可以自底向上逐层应用。本系列不会刻意梳理讲解 API
及 Vue
的用法,官方文档已经有清晰的讲解。
本系列文章作为笔记,用于记录 vue2.x
的构建(含组件)主流程。
概览
根据 vue
构建 demo
的生命周期及其数据变化的更新作为流程线及相关,本系列文章一共分为以下章节:
- 前言及总流程概览
- 源码结构及调试相关介绍
beforeCreate
阶段created
阶段beforeMount
阶段mount
阶段之生成vnode
mount
阶段之生成dom
update
阶段(上)update
阶段(下)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
来看,无论是功能、性能、还是框架设计都远远不如,从中学到的东西很多很多。