开始之前,需要初始化一个webpack环境
1、进入项目目录执行初始化
npm init -y
2、下载webpack依赖
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
5、vue/index.js
import Vue from '../vue'
6、vue/index.js
function Vue() {
}
export default Vue;
7、package.json
{
"name": "vue2_",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"html-webpack-plugin": "^4.5.0",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
}
}
8、webpack.config.js
const path = require('path'),
htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 项目入口文件
entry: './src/index.js',
// 项目打包输出文件配置
output: {
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
devtool: 'source-map',
resolve: {
modules: [path.resolve(__dirname,'',path.resolve(__dirname,'node_modules'))]
},
plugins: [
new htmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html')
})
]
}
启动,一个最简易的webpack环境已经搭建完成
实现vue数据劫持
入口文件 src/index.js
import Vue from '../vue'
let vm = new Vue({
el: '#app',
data(){
return {
title: '零三',
numArr: [1,2,3],
strArr: ['vue','react','html'],
objArr: [
{
name: 'mike',
hobby: ['足球','篮球','code'],
skill: [
{
text: '吹牛皮',
lv: 3
},
{
text: '撩妹',
lv: 0
}
]
}
]
}
}
})
// 直接获取
// console.log('-----vm.title-----')
// console.log(vm.title)
// 先获取vm.objArr再取值[0]获取hobby
// console.log('-----vm.objArr[0].hobby-----')
// console.log(vm.objArr[0].hobby)
// 原始数据,不会被观察
// console.log('-----vm.objArr[0].hobby[0]-----')
// console.log(vm.objArr[0].hobby[0])
// console.log('-----vm.objArr[0].skill.push( {\n' +
// ' text: \'吃吃吃\',\n' +
// ' lv: 6\n' +
// '})-----')
// console.log(vm.objArr[0].skill.push( {
// text: '吃吃吃',
// lv: 6
// }))
// console.log(vm.objArr[0].skill)
console.log('-----vm.objArr[0].skill.splice(1,0)-----')
console.log(vm.objArr[0].skill.splice( 1,1))
console.log(vm.objArr[0].skill)
vue/index.js
import {initState} from './init'
function Vue(options) {
this._init(options)
}
// vue的初始化
Vue.prototype._init = function (options) {
var vm = this;
vm.$options = options;
initState(vm);
}
export default Vue;
vue/init.js
import proxyData from "./proxy";
import observe from "./observe";
function initState(vm) {
var options = vm.$options;
if (options.data) {
initData(vm);
}
}
function initData(vm) {
var data = vm.$options.data;
// 判断data是对象还是方法,如果是方法,取他的返回值并且把this指向vm,如果是对象,那不用处理,如果啥也不是就默认给空对象
// 并且再声明一个_data用于代理
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
for (var key in data){
proxyData(vm,'_data', key)
}
observe(vm._data)
}
export {
initState
}
vue/observe.js
// 观察者
import Observer from "./observer";
function observe(data) {
// 不是对象,或者为null 不进行观察
if (typeof data !== 'object' || data === null) {
return
}
return new Observer(data);
}
export default observe;
vue/observeArr.js
// 观察数组每一项
import observe from "./observe";
function observeArr(arr) {
for (var i = 0; i < arr.length; i++) {
observe(arr[i])
}
}
export default observeArr;
vue/observer.js
import defineReactiveData from './reacitive'
import {arrMethods} from './array';
import observeArr from "./observeArr";
function Observer(data) {
if (Array.isArray(data)) {
data.__proto__ = arrMethods;
// data可能还包含数组,使用递归观察
observeArr(data);
} else {
// 观察对象
this.walk(data)
}
}
Observer.prototype.walk = function (data) {
var keys = Object.keys(data);
for (var i = 0; i < keys.length; i++) {
var key = keys[i],
value = data[key];
defineReactiveData(data, key, value);
}
}
export default Observer;
vue/proxy.js
// 代理vm._data
function proxyData(vm,target,key) {
Object.defineProperty(vm,key,{
get() {
// 如访问的是vm.xxx 那么 get方法返回的会是vm._data.xxx
return vm[target][key];
},
set(v) {
// 如设置的是vm.xxx=v 那么 set方法会执行vm._data.xxx = v
vm[target][key] = v
}
})
}
export default proxyData;
vue/reactive.js
// 处理响应式
import observe from "./observe";
function defineReactiveData(data, key, value) {
// value可能还会是个数组 对象 继续判断观察
observe(value);
Object.defineProperty(data, key, {
get() {
console.log('响应式数据获取',value)
return value
},
set(v) {
if (value === v) {
return
}
console.log('响应式数据设置',v)
// 设置的内容可能也是一个数组或者对象,仍然需要继续判断观察
observe(v);
value = v;
}
})
}
export default defineReactiveData;
vue/config.js
// 定义哪些是会改变原数组的方法
var ARR_METHODS = [
'push',
'pop',
'shift',
'unshift',
'slice',
'sort',
'reserve'
]
export {
ARR_METHODS
}
vue/array.js
import {ARR_METHODS} from './config'
import observeArr from "./observeArr";
var originArrMethods = Array.prototype,
arrMethods = Object.create(originArrMethods);
ARR_METHODS.map(function (method) {
arrMethods[method] = function () {
// 将类数组转换成数组
var args = Array.prototype.slice.call(arguments),
rt = originArrMethods[method].apply(this, args),
newArr;
switch (method) {
case 'push':
case 'unshift':
newArr = args;
break;
case 'splice':
newArr = args.slice(2);//拿到添加的那一项
break;
default:
break;
}
newArr && observeArr(newArr);
return rt;
}
});
export {
arrMethods
}
实现对象以及数组的观察,对数组的方法处理后新增元素进行观察,并且对数组对象进行深度观察
src/index.js进行测试运行