vue2-手写数据劫持

开始之前,需要初始化一个webpack环境

1、进入项目目录执行初始化

npm init -y

2、下载webpack依赖

npm install webpack webpack-cli webpack-dev-server html-webpack-plugin

3、新建相关目录

4、public/index.html

<!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进行测试运行

流程图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值