目标:手写迷你版Vue
一:使用rollup打包,打包后的代码体积更小,更适合写框架源码的打包
npm i rollup -D
二:安装babel相关的包,以及实现静态服务,设置环境变量的包
npm i @babel/core @babel/preset-env rollup-plugin-babel roullup-plugin-serve cross-env -D
三:包的相关介绍
- rollup (打包工具)
- @babel/core(用babel核心模块)
- @babel/preset-env(babel将高级语法转成低级语法)
- rollup-plugin-serve(实现静态服务)
- cross-env(设置环境变量)
- rollup-plugin-babel(桥梁)
四:根目录书写rollup.config.js
import babel from 'rollup-plugin-babel'; import serve from 'rollup-plugin-serve'; export default { input:'./src/index.js', // 以哪个文件作为打包的入口 output:{ file:'dist/umd/vue.js', // 出口路径 name:'Vue', // 指定打包后全局变量的名字 format: 'umd', // 统一模块规范 sourcemap:true, // es6-> es5 开启源码调试 可以找到源代码的报错位置 }, plugins:[ // 使用的插件 babel({ exclude:"node_modules/**" }), process.env.ENV === 'development'"htmlcode">{ "name": "vue_souce", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build:dev": "rollup -c", "serve": "cross-env ENV=development rollup -c -w" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.5", "cross-env": "^7.0.2", "rollup": "^2.6.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-serve": "^1.0.1" } }五:新建index.html(public/index.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="/UploadFiles/2021-04-02/vue.js">六:编写Vue入口:index.js
// Vue的核心代码 只是Vue的一个声明 import {initMixin} from './init'; function Vue(options){ // 进行Vue的初始化操作 this._init(options); } // 通过引入文件的方式 给Vue原型上添加方法 initMixin(Vue); // 给Vue原型上添加一个_init方法 export default Vue七:编写初始化操作 init.js
import {initState} from './state' // 在原型上添加一个init方法 export function initMixin(Vue){ // 初始化流程 Vue.prototype._init = function (options) { // 数据的劫持 const vm = this; // vue中使用 this.$options 指代的就是用户传递的属性 vm.$options = options; // 初始化状态 initState(vm); // 分割代码 } }八:初始化数据
import {observe} from './observer/index.js' export function initState(vm){ const opts = vm.$options; // vue的数据来源 属性 方法 数据 计算属性 watch if(opts.props){ initProps(vm); } if(opts.methods){ initMethod(vm); } if(opts.data){ initData(vm); } if(opts.computed){ initComputed(vm); } if(opts.watch){ initWatch(vm); } } function initProps(){} function initMethod() {} function initData(vm){ // 数据初始化工作 let data = vm.$options.data; // 用户传递的data data = vm._data = typeof data === 'function'"htmlcode">// 把data中的数据 都使用Object.defineProperty重新定义 es5 // Object.defineProperty 不能兼容ie8 及以下 vue2 无法兼容ie8版本 import { isObject } from '../util/index' // 后续我可以知道它是不是一个已经观察了的数据 __ob__ class Observer{ constructor(value){ // 仅仅是初始化的操作 // vue如果数据的层次过多 需要递归的去解析对象中的属性,依次增加set和get方法 // 对数组监控 this.walk(value); // 对对象进行观测 } walk(data){ let keys = Object.keys(data); // [name,age,address] // 如果这个data 不可配置 直接return keys.forEach((key)=>{ defineReactive(data,key,data[key]); }) } } function defineReactive(data,key,value){ observe(value); // 递归实现深度检测 Object.defineProperty(data,key,{ configurable:true, enumerable:false, get(){ // 获取值的时候做一些操作 return value; }, set(newValue){ // 也可以做一些操作 if(newValue === value) return; observe(newValue); // 继续劫持用户设置的值,因为有可能用户设置的值是一个对象 value = newValue; } }); } export function observe(data) { let isObj = isObject(data); if (!isObj) { return } return new Observer(data); // 用来观测数据 }十:编写工具类文件,存放校验对象
/** * * @param {*} data 当前数据是不是对象 */ export function isObject(data) { return typeof data === 'object' && data !== null; }总结:
1 创建Vue构造函数,接收所有所有参数options
2 分类初始化options,本章主要处理data,让data上的引用类型的数据通过Object.definePrototy 变成响应式的,初始化是有循序的,先初始化props 然后初始化method 然后初始化data computed watch3 核心如下
function defineReactive(data,key,value){ observe(value); // 递归实现深度检测 Object.defineProperty(data,key,{ configurable:true, enumerable:false, get(){ // 获取值的时候做一些操作 return value; }, set(newValue){ // 也可以做一些操作 if(newValue === value) return; observe(newValue); // 继续劫持用户设置的值,因为有可能用户设置的值是一个对象 value = newValue; } }); }最新资源