源码地址:gitee源码地址
一.使用Rollup搭建开发环境
1.什么是Rollup?
Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码, rollup.js更专注于Javascript类库打包 (开发应用时使用webpack,开发库时使用Rollup)
2.环境搭建
安装rollup环境
npm install @babel/preset-env @babel/core rollup rollup-plugin-babel rollup-plugin-serve cross-env -D
rollup.config.js文件编写
import babel from 'rollup-plugin-babel';
import serve from 'rollup-plugin-serve';
export default {
input: './src/index.js',
output: {
format: 'umd', // 模块化类型
file: 'dist/umd/vue.js',
name: 'Vue', // 打包后的全局变量的名字
sourcemap: true
},
plugins: [
babel({
exclude: 'node_modules/**'
}),
process.env.ENV === 'development'?serve({
open: true,
openPage: '/public/index.html',
port: 3000,
contentBase: ''
}):null
]
}
配置.babelrc文件
{
"presets": [
"@babel/preset-env"
]
}
执行脚本配置
"scripts": {
"build:dev": "rollup -c",
"serve": "cross-env ENV=development rollup -c -w"
}
二.Vue响应式原理
导出vue
构造函数
import {initMixin} from './init';
function Vue(options) {
this._init(options);
}
initMixin(Vue); // 给原型上新增_init方法
export default Vue;
init
方法中初始化vue
状态
import {initState} from './state';
export function initMixin(Vue){
Vue.prototype._init = function (options) {
const vm = this;
vm.$options = options
// 初始化状态
initState(vm)
}
}
根据不同属性进行初始化操作
export function initState(vm){
const opts = vm.$options;
if(opts.props){
initProps(vm);
}
if(opts.methods){
initMethod(vm);
}
if(opts.data){
// 初始化data
initData(vm);
}
if(opts.computed){
initComputed(vm);
}
if(opts.watch){
initWatch(vm);
}
}
function initProps(){}
function initMethod(){}
function initData(){}
function initComputed(){}
function initWatch(){}
1.初始化数据
import {observe} from './observer/index.js'
function initData(vm){
let data = vm.$options.data;
data = vm._data = typeof data === 'function' ? data.call(vm) : data;
observe(data);
}
2.递归属性劫持
class Observer { // 观测值
constructor(value){
this.walk(value);
}
walk(data){ // 让对象上的所有属性依次进行观测
let keys = Object.keys(data);
for(let i = 0; i < keys.length; i++){
let key = keys[i];
let value = data[key];
defineReactive(data,key,value);
}
}
}
function defineReactive(data,key,value){
observe(value);
Object.defineProperty(data,key,{
get(){
return value
},
set(newValue){
if(newValue == value) return;
observe(newValue);
value = newValue
}
})
}
export function observe(data) {
if(typeof data !== 'object' || data == null){
return;
}
return new Observer(data);
}
3.数组方法的劫持
import {arrayMethods} from './array';
class Observer { // 观测值
constructor(value){
if(Array.isArray(value)){
value.__proto__ = arrayMethods; // 重写数组原型方法
this.observeArray(value);
}else{
this.walk(value);
}
}
observeArray(value){
for(let i = 0 ; i < value.length ;i ++){
observe(value[i]);
}
}
}
重写数组原型方法
//! 重写数组方法,进行数组函数劫持
// 获取原来的数组方法
let oldArrayProtoMethods = Array.prototype;
export let ArrMethods = Object.create(oldArrayProtoMethods);
let methods = ['push', 'pop', 'unshift', 'shift', 'splice','reverse','sort'];
methods.forEach((item) => {
ArrMethods[item] = function (...args) {
let result = oldArrayProtoMethods[item].apply(this, args);
let inserted;
switch (item) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.splice(2);
break;
}
let ob = this.__ob__;
if (inserted) {
ob.observerArray(inserted); // 对我们的添加的对象进行劫持
}
ob.dep.notify();
return result;
};
});
增加__ob__属性
class Observer {
constructor(value){
Object.defineProperty(value,'__ob__',{
enumerable:false,
configurable:false,
value:this
});
// ...
}
}
给所有响应式数据增加标识,并且可以在响应式上获取
Observer
实例上的方法
4.数据代理
//! Vue2 对data进行初始化
function initData(vm) {
//> 会有两种初始化方式 1. 对象 2. 函数
let data = vm.$options.data;
data = vm._data = typeof data === "function" ? data.call(vm) : data;
//> 将data上的数据全部代理到vm上
for (const key in data) {
if (Object.hasOwnProperty.call(data, key)) {
proxy(vm, "_data", key);
}
}
//> 对数据data进行劫持
observer(data);
}
function proxy(vm, score, key) {
Object.defineProperty(vm, key, {
get() {
return vm[score][key];
},
set(newValue) {
vm[score][key] = newValue;
},
});
}