最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 手写一下Vue2.0 数据劫持

    正文概述 掘金(梳碧湖的砍柴人)   2021-03-04   519

    一:搭建webpack

    简单的搭建一下webpack的配置。新建一个文件夹,然后init一下。之后新建一个webpack.config.js文件,这是webpack的配置文件。安装一下简单的依赖。

    npm install webpack webpack-cli webpack-dev-server -D
    

    在同级目录下新建一个public/index.html和src/index.js,作为出口文件和入口文件。

    j简单配置一下webpack, 在webpack.config.js文件中:

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        entry: './src/index.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        resolve: { 
            modules: [
            path.resolve(__dirname, ''), path.resolve(__dirname, 'node_modules')        
            ]   
        },
        plugins: [       
            new HtmlWebpackPlugin({            
                template: path.resolve(__dirname, 'public/index.html')        
            })    
        ]
    }
    

    ok,基本配置好webpack就可以开始正题了。

    二:数据劫持

    在v2中,通过new Vue(el, options)的方式,完成vue的实例化。我们需要新建一下vue文件,把数据劫持的方法统一到vue中。

    新建一个vue/index.js,作为数据劫持的入口文件。

    import {initState} from './init.js';
    
    function Vue (options) {
        this._init(options);     // 数据初始化
    }
    Vue.prototype._init = function (options) {
        var vm = options;    // 保存一下实例
        vm.$options = options;   // 实例挂载
        initState(vm);         // 实例初始化
    }
    

    新建一个init.js文件初始化实例:

    初始化的时候注意几个问题: 

    1.  需要分别对computed,watch, data进行处理。

    2. 不要在用户定义的data上直接修改。

    3. 官方指定data为函数,是为了保证组件内部有自己的作用域不会有污染,直接访问data函数是不行的,需要自动执行。data也可以是对象(需要考虑到这个情况)

    4. 这种方式获取data是通过vm._data.xxx 但是在vue中不需要data来获取,所以这里需要拦截重写。

    5. 内部的引用类型需要递归

    function initState (vm) {
        var options = vm.$options;  // 获取options
        if (options.data) {
            initData(vm);  // 因为computed,watch都需要在这里初始化,所以针对data初始化
    };
    
    function initData (vm) {
        var data = vm.$options.data;   // 对data重新赋值,不要改变用户定义的data
        data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
        for (var key in data) {
            proxyData(vm, '_data', key);         // 对data的取值重新赋值
        };
        observe(vm._data);   // 对data内部进行观察
    }
    

    新建一个proxy.js作为代理层:

    function proxyData(vm, target, key) {    
        Object.defineProperty(vm, key, {       
             get () {            
                // 这里做了拦截: vm.xxx => vm._data.xxx            
                return vm[target][key];        
            },        
            set(newValue) {            
                // vm.xxx = yyy  ===> vm._data.title = yyy            
                vm[target][key] = newValue;        
            }    
       }) 
    }
    export default proxyData;
    

    处理好了访问问题,现在需要递归一下data内部元素。obseve(vm._data);

    新建一个observe.js:

    function observe (data) {
        if (typeof data !== 'object' || data = null) return;
        return new Observer(data);   // 如果是应用类型,直接添加一个观察者
    } 
    

    新建一个观察者:observer.js

    function Observer(data) {
        if (Array.isArray(data)) {
            // 处理数组
             data._proto_ = arrMethods;    
        }
        else {
            // 处理对象
            this.walks(data);
        }
    }
    Observer.prototype.walks = function (data) {
        let keys = Object.keys(data);  // 拿到data下面所有的key,并且还是一个数组
        for (var i = 0 ; i < keys.length ; i++) {        
            var key = keys[i];        
            var value = data[key];        
            defineReactiveData(data, key, value);  // 每个重新生成响应式数据   
     }}
    

    新建一个reactive.js          // 处理对象等响应式

    function defineReactiveData (data, key, value) {    
        observe(value);        // 对子元素接着递归。    
        Object.defineProperty(data, key, {        
            get() {            
                return value;        
            },        
            set (newValue) {            
                if (newValue === value) return;            
                value = newValue;        // 触发更改        
            }    
        }
        )
    };
    

    ok,这里处理好了对象的数据劫持,剩余的需要处理数组了

    在V2中采用重写原型上的7种方法,做到数据劫持。

    劫持数组:

    新建一个Array.js文件:

    import {ARR_METHODS} from './config.js';     
        // 7个数组方法的合集
    import observeArr from './observeArr.js';
    var originArrMethods = Array.prototype, 
    arrMethods = Object.create(originArrMethods); 
    ARR_METHODS.map(function (m) {    
        arrMethods[m] = function () {        
            var args = Array.prototype.slice.call(arguments);  // 类数组转为数组        
            var rt = originArrMethods[m].apply(this, args);       
            var newArr;        
            switch (m) {           
                case 'push':            
                case 'ushift':                 
                    newArr = args;            
                case 'splice':                
                    newArr = args.slice(2);                
                    break;            
                default:                 
                    break;        };        
            newArr && observeArr(newArr);        
            return rt;    
            } 
    }); 
        export {    arrMethods }
    

    observeArr(newArr): 数组也可能有嵌套,所以需要对数据进行观察。

    import observe from "./observe";
    function observeArr (arr) {    
        for (var i = 0 ; i < arr.length ; i++) {        
            observe(arr[i]);   // 重新走到了observe上。    
        }
    }
    export default observeArr;
    

    三:总结

     基本流程就是这样的,不仅仅是object.defineProperty对数据进行get和set这么简单。总结一下主要流程:

    (1): 在初始化的时候:保存一下实例,挂载实例。通过initState方法来初始化数据,这里主要是       data数据,也有computed和watch需要处理。

    (2): 调用initData(); 重新赋值data,然后执行data,修改用户获取data属性的写法统一为               this.xxx同时observe(data)

    (3):在observe(data)的时候需要对data进行判断,如果是引用类型需要加上一个观察者              observer,同时在观察者终判断data是为数组还是对象,对象直接重新触发                      object.defineProperty,同时对内部重新observe。如果是数组直接重新7种数组方法,然后对数组内部接着observe。


    下载网 » 手写一下Vue2.0 数据劫持

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元