Vue中的代理模式

Vue的代理模式

MVVM

在这里插入图片描述

  1. 数据层(Model:应用数据以及逻辑,主要指从后端获取的数据
  2. 视图层(View:页面UI组件,主要由 HTML 和 CSS 来构建
  3. 视图数据模型(ViewModel:数据与视图关联起来,数据和 DOM 已经建立了关联,是响应式的,使编程人员脱离复杂的界面操作
  4. ViewModel主要功能是实现数据双向绑定:数据变化后更新视图,既:model有数据更新时UI组件会响应变化。视图变化后更新数据,界面上如果有input输入框,输入数据时,model中的数据也会更新

代理模式

在这里插入图片描述

  1. 代理模式简述一个类代表另一个类的功能,这种类型的设计模式属于结构型模式
  2. 意图:为其他对象提供一种代理,以控制这个对象的访问
  3. 主要解决:直接访问对象时带来的一些问题。比如远程访问,需要安全控制之类的。
  4. 何时使用:在访问一个类时需要做一些控制和底层需要被调用者的接口完全相同,只是为了做底层被调用者和上端隔离,或者做一些控制。【最佳案例:属性Get/Set】

Vue中代理模式的实现

vue类的部分实现 (myVue.js)

Vue的构造函数

/**
 * Vue类的创建
 * @param {*} options Vue实例中的众多对象(data、method、el)
 */
function Vue(options){
    this._init(options);
}

创建一个 Vue 实例的时候,Vue 构造函数只执行了 this._init(options) 这么一句代码。所以初始化一个 Vue 实例,主要是靠 Vue.prototype._init 这个原型方法。

/**
 * 创建一个方法用于获取Vue对象中的众多属性,并把这个方法赋值给Vue的原型对象_init
 * @param {*} options  Vue实例中的众多对象(data、method、el)
 */
Vue.prototype._init=function(options){
    //此时this指向Vue类
    let vm =this;
    //创建Vue的对象$options,来接受传入的options对象
    vm.$options=options;
    //初始化vue
    initSate(vm);
}
/**
 * 初始化vue
 * @param {*} vm vue对象
 */
function initSate(vm){
    let opt=vm.$options;
    //初始化data对象
    if(opt.data){
        initData(vm);
    }
}

判断data是否为空或者是否是对象类型

/**
 * 初始化Data
 * @param {*} vm vue对象
 */
 function initData(vm) {
    // 获取data
    let data = vm.$options.data
    // vue对象中创建 $data 方法
    // data || {} 表示如果data为null则直接返回{}空对象
    vm.$data = data || {};
    if (typeof data != 'object' || data == null) {
        return;
    }
    else {
        new Observe(data, vm);
    }
}

创建一个监听类用于监听Data的变化,通过对Get/Set属性进行代理来获取或者改变Data中属性的值
在代理Set方法是时要注意判断data中是否存在对象因为set只能对非对象属性进行修改并赋值,如果存在对象类型则通过递归的方式修改该对象类型中属性的值。在该Set方法中实际上会把监听到所改变的值直接展示在视图模型上,这里只是做一个简单的打印输出来展示监听所改变的Value。最后再通过挂载的方式将data中的的变量以(key,value)的方式挂载到Vue的实例上,因此就可以通过Vue的实例来直接访问data中的变量了,这就实现了Vue实例对data的数据代理

/**
 * 监听类,用于监听data的变化
 * @param {*} data 数据对象
 * @param {*} vm Vue对象
 */
 function Observe(data, vm) {
    /**
     * 重构data,并对其进行get/set属性代理
     * @param {*} data 
     * @param {*} vm 
     */
    function restructure(data, vm) {
        let keys = Object.keys(data);
        // 循环遍历每一个key并行进代理化改造 ★核心内容
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            let value = data[key];
            // 先定义vm中的对象,就不会出现后面重复定义的错误
            vm[key] = value;
            // 验证value是否依旧是对象(递归)
            if (typeof value != 'object' || value == null) {
                // 啥都不做
            } else {
                new Observe(value, vm);
            }
            // 给data中的变量配置get/set属性用于监听
            // 或者可以理解成把指定key挂载到data中
            // ★这里其实有问题,userName和user.userName其实是会相互干扰的
            Object.defineProperty(data,key,{
                get:function(){
                    return value;
                },
                set:function(newValue)
                {
                    if(newValue != value)
                    {
                        // 验证newValue是否是对象
                        if(typeof newValue != 'object' || newValue == null)
                        {
                            // 啥都不做
                        }
                        else{
                            new Observe(newValue, vm);
                        }
                        value = newValue;
                        // 这里只是做一个日志输出,如果是一个正常的vue,则应该是更新视图方法
                        console.log(`我现在是【${value}】啦!`);
                    }
                }
            })
            // 直接将data中的变量挂载到vue对象下
            // 注意,如果之前vm中未曾定义过这些属性,若是有两个相同的key,则会出现重复定义属性的错误
            // 解决方案,在之前每次for循环后先通过vm[key] = value;定义一次,那么在这里就不会再次定义了
            // 这和js中xxx.xx的意思一样,如果xxx.xx的xx在xxx中不存在则会定义,存在则只是调用
            // ★这里其实有问题,userName和user.userName其实是会相互干扰的
            Object.defineProperty(vm,key,{
                get:function(){
                    return value;
                },
                set:function(newValue)
                {
                    if(newValue != value)
                    {
                        // 验证newValue是否是对象
                        if(typeof newValue != 'object' || newValue == null)
                        {
                            // 啥都不做
                        }
                        else{
                            new Observe(newValue, vm);
                        }
                        value = newValue;
                        console.log(`我现在是【${value}】啦!`);
                    }
                }
            })
        }

    }
    restructure(data, vm)
}

视图展示

在给data中的属性赋值时其实就是调用了Set方法

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>初始Vue</title>
        <!--引入vue-->
        <!--<script type="text/javascript" src="js/vue.js"></script>-->
        <script src="./myVue.js"></script>
    </head>
    <body>
        <div id="app">
            {{user.age}}
        </div>
        <script>
            var vm=new Vue({
                el:"#app",
                data:{
                    userName:"qq",
                    user:{
                        userName:"qq",
                        age:23
                    }
                }
            })
            setTimeout(function(){
                vm.user.age=18
            },2000)
        </script>
    </body>
</html>

我们可以通过上面Vue实例创建时age的值与通过定时器改变后所监听到的值做比较就说明在Vue 实例中改变属性的值就是调用了Set方法并通过监听的方式来获取改变后的值
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值