目标:手写迷你版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'?serve({
open:true,
openPage:'/public/index.html', // 默认打开html的路径
port:3000,
contentBase:''
}):null
]
}
配置package.josn
{
"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="/dist/umd/vue.js"></script>
<script>
let vm = new Vue({
el:'#app',
// 随便给些数据
data(){
return {
name:'张三',
age:11,
address:{
number:0,
name:'李四'
}}
},
})
vm._data.address = {a:1};
console.log(vm._data)
</script>
</body>
</html>
六:编写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'?data.call(vm):data;
// 对象劫持 用户改变了数据 我希望可以得到通知 =》 刷新页面
// MVVM模式 数据变化可以驱动视图变化
// Object.defineProperty () 给属性增加get方法和set方法
observe(data); // 响应式原理
}
function initComputed(){}
function initWatch(){}
九:书写核心监听功能
// 把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 watch
3 核心如下
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;
}
});
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。