手撕Vue源码(二)对象数据劫持

本文详细介绍了在Vue.js中如何进行数据的声明和初始化,包括在HTML中引入Vue.js,声明数据,以及如何通过构造函数和_init方法初始化数据。文章还深入讲解了数据劫持的过程,通过Observer类实现对象属性的劫持,并使用proxy方法解决数据访问的便捷性问题,使得可以直接通过vm.name访问数据。此外,文章还涵盖了如何通过observe函数对数据进行观测,确保数据变化时能够及时响应。
摘要由CSDN通过智能技术生成
  • 我们的vue.js文件编译工作配置好之后,就可以继续写了,首先在html中调用vue,声明好数据,然后一步一步的完善vue中的功能
    • 引入vue.js
      • <script src="./dist/vue.js"></script>

         

      • 声明好数据

        •  <div id="app">{{name}}</div>
              <script>
                  let vm = new Vue({
                      el:'#app',
                      data:{
                          name:'zf'
                      }
                  })
              </script>

           

    • 完善vue
      • 可以看到,声明数据中,是通过新建一个类进行声明,类中包含了两个属性,一个确定要绑定的元素,一个是数据,因此在vuejs中可以更改为:
        • 创建一个构造函数
        • function Vue(options) {
              //options 为传入的选项
              this._init(options)
          }
          
          export default Vue;

          options为用户传进来的参数,_init用来初始化数据

        • 现在开始完善初始化函数

          • 新建一个init.js文件

            • 声明初始化函数,并将_init函数定义在vue原型上

            • 在原型上新添加一个属性用来保存数据($options)

            • 调用初始化状态函数,对每种数据进行初始化

            • import { initState } from "./state";
              
              export function initMixin(Vue){
                  // 在Vue类的原型上添加这个函数
                  Vue.prototype._init = function(options){
                      const vm = this;
                      // 在原型上添加一个属性保存数据
                      vm.$options = options;//
                      //开始对数据进行劫持
                      initState(vm);
                  }
              }

               

            • 初始化数据函数为initData,主要作用

              • 判断其是对象类型还是数组类型,并获取对象类型数据

              • 新建观察者类型,对对象数据进行观察

              • import { observe } from "./observer/index";
                import { isFunction } from "./utils";
                
                export function initState(vm){
                    const opts = vm.$options;
                    if(opts.data){
                        // 数据存在,则劫持数据
                        initData(vm);
                    }
                }
                
                function initData(vm){
                    let data = vm.$options.data;
                    // 判断data是函数还是对象,获取其对象数据
                    data = vm._data =  isFunction(data)?data(vm):data;
                    console.log(vm._data)
                    //观察数据
                    observe(data);
                }

                 

              • 观察者类新建如下:

                • 新建walk函数遍历对象数据的key 和value

                • 调用defineReactive函数对对象属性进行劫持

                  • 第一步便利的value可能也是对象类型,所以要在这个函数开头再次调用观测者类,监测这个value

                  • set函数只是将新值复制给旧值,如果新值是对象类型,则无法劫持,所以需要在这里再次调用观测者类

                  • import { isObject } from "../utils";
                    
                    // 创建观测者
                    class Observer{
                        constructor(data){
                            //对对象中的所有属性进行劫持
                            this.walk(data);
                        }
                        walk(data){
                            Object.keys(data).forEach(key =>{
                              defineReactive(data,key,data[key]);
                            })
                        }
                    }
                    function defineReactive(data,key,value){
                        observe(value)
                        Object.defineProperty(data,key,{
                            get(){
                                return value;
                            },
                            set(newV) {
                                observe(value)
                                value = newV;
                            }
                        })
                    }
                    
                    export function observe(data) {
                        // 判断数据是否为对象
                        if(!isObject(data)){
                            return ;
                        }
                        //创建观测者来观测数据
                        return new Observer(data);
                    }

                     

    • 解决代理问题
      • 根据上述,我们可以通过vm._data.name来访问name属性,但是习惯上,vue直接通过vm.name来访问,因此我们需要在进行数据劫持,将数据放在vue上
        • 要想完成这个逻辑,和之前的劫持一样,只需要在vm上面劫持一下数据
        • function proxy(vm,source,key){
              Object.defineProperty(vm,key,{
                  get(){
                      return vm[source][key];
                  },
                  set(newV){
                      vm[source][key] = newV;
                  }
              })
          }
          
          function initData(vm){
              let data = vm.$options.data;
              // 判断data是函数还是对象,获取其对象数据
              data = vm._data =  isFunction(data)?data(vm):data;
              for( let key in data){
                  proxy(vm,'_data',key);
              }
              //观察数据
              observe(data);
          }

           

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问也去

创作不易,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值