可以先从简版的vue开始,逐步过渡到实际源码。

  • Roolup 和源码相同配置,基本目录结构
  • Vue 数据响应式原理、渐进处理
  • watch和computed
  • Vue 模板编译
  • diff

项目基本配置

vue使用 roolup打包

  1. npm install @babel/preset-env @babel/core rollup rollup-plugin-babel rollup-plugin-serve cross-env -D

配置文件 rollup.config.js

  1. import babel from 'rollup-plugin-babel';
  2. import serve from 'rollup-plugin-serve';
  3. export default {
  4. input: './src/index.js',
  5. output: {
  6. format: 'umd', // 模块化类型
  7. file: 'dist/umd/vue.js',
  8. name: 'Vue', // 打包后的全局变量的名字
  9. sourcemap: true
  10. },
  11. plugins: [
  12. babel({
  13. exclude: 'node_modules/**'
  14. }),
  15. process.env.ENV === 'development'?serve({
  16. open: true,
  17. openPage: '/public/index.html',
  18. port: 3000,
  19. contentBase: ''
  20. }):null
  21. ]
  22. }

babel配置文件 .babelrc

  1. {
  2. "presets": [
  3. "@babel/preset-env"
  4. ]
  5. }

项目启动命令

  1. "scripts": {
  2. "build:dev": "rollup -c",
  3. "serve": "cross-env ENV=development rollup -c -w"
  4. }

Vue基本封装思路

使用 class Vue 会让方法无法被分割,这里采用 function Vue(){} 原型方案,我们可以对 Vue.prototype.xx 进行文件分割。

初始化部分 设置为 initMixin() ,这样代码的风格:

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

其中 init 的具体代码:

  1. import {initState} from './state';
  2. export function initMixin(Vue){
  3. Vue.prototype._init = function (options) {
  4. const vm = this;
  5. vm.$options = options
  6. // 初始化状态
  7. initState(vm)
  8. }
  9. }

后续 initState 时候再去进行 prop method data compouted watch 的初始化,这样代码读起来更清晰,专注。

数据响应式

https://www.yuque.com/xinbao37/vue-source/vue2-reactive

Watcher和Computed

https://www.yuque.com/xinbao37/vue-source/vue2-watch-computed

模板编译

https://www.yuque.com/xinbao37/vue-source/template-compiler

Diff

https://www.yuque.com/xinbao37/vue-source/vue2-diff