vue Proxy数据代理进行校验部分源码实例解析

 更新时间:2022年01月10日 14:29:42   作者:神奇大叔  
Proxy提供了强大的Javascript元编程,有许多功能,包括运算符重载,对象模拟,简洁而灵活的API创建,对象变化事件,甚至Vue 3背后的内部响应系统提供动力,这篇文章主要给大家介绍了关于vue Proxy数据代理进行校验部分源码解析的相关资料,需要的朋友可以参考下

initProxy

数据拦截的思想除了为构建响应式系统准备,它也可以为数据进行筛选过滤,我们接着往下看初始化的代码,在合并选项后,vue接下来会为vm实例设置一层代理,这层代理可以为vue在模板渲染时进行一层数据筛选

Vue.prototype._init = function(options) {
    // 选项合并
    ...
    {
        // 对vm实例进行一层代理
        initProxy(vm);
    }
    ...
}

initProxy

// 代理函数
var initProxy = function initProxy (vm) {
    //是否支持Proxy
    if (hasProxy) {
        var options = vm.$options;
        //当使用类似webpack这样的打包工具时,通常会使用vue-loader插件进行模板的编译,这个时候options.render是存在的,并且_withStripped的属性也会设置为true(关于编译版本和运行时版本的区别可以参考后面章节),所以此时代理的选项是hasHandler
        //在其他场景下,代理的选项是getHandler
        var handlers = options.render && options.render._withStripped
            ? getHandler
            : hasHandler;
        // 代理vm实例到vm属性_renderProxy
        vm._renderProxy = new Proxy(vm, handlers);
    } else {
        vm._renderProxy = vm;
    }
};

hasProxy

var hasProxy =
      typeof Proxy !== 'undefined' && isNative(Proxy);

hasHandler

var hasHandler = {
    // key in obj或者with作用域时,会触发has的钩子
    has: function has (target, key) {
        ···
    }
};

触发代理

源码中vm._renderProxy的使用出现在Vue实例的_render方法中,Vue.prototype._render是将渲染函数转换成Virtual DOM的方法,这部分是关于实例的挂载和模板引擎的解析

当我们调用render函数时,代理的vm._renderProxy对象便会访问到

而这个render函数就是包装成with的执行语句,在执行with语句的过程中,该作用域下变量的访问都会触发has钩子,这也是模板渲染时之所有会触发代理拦截的原因

之所以会触发数据代理拦截是因为模板中使用了变量,例如<div>{{message}}}

Vue.prototype._render = function () {
    ···
    // 调用vm._renderProxy
    vnode = render.call(vm._renderProxy, vm.$createElement);
}
=========================================
var vm = new Vue({
    el: '#app'     
})
console.log(vm.$options.render)
​​​​​​​//输出, 模板渲染使用with语句
ƒ anonymous() {
    with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(message)+_s(_test))])}
}

数据过滤

通过data选项去设置实例数据,那么这些数据可以随着个人的习惯任意命名吗?显然不是的,如果你使用js的关键字(像Object,Array,NaN)去命名,这是不被允许的。另一方面,Vue源码内部使用了以$,_作为开头的内部变量,所以以$,_开头的变量名也是不被允许的,这就构成了数据过滤监测的前提。

var hasHandler = {
    has: function has (target, key) {
        var has = key in target;
        // isAllowed用来判断模板上出现的变量是否合法。
        var isAllowed = allowedGlobals(key) ||
            (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));
            // _和$开头的变量不允许出现在定义的数据中,因为他是vue内部保留属性的开头。
            
        // 1. warnReservedPrefix: 警告不能以$ _开头的变量
        // 2. warnNonPresent: 警告模板出现的变量在vue实例中未定义
        //has判断是否是target对象中的变量
        if (!has && !isAllowed) {
            if (key in target.$data) { warnReservedPrefix(target, key); }
            else { warnNonPresent(target, key); }
        }
        return has || !isAllowed
    }
};
// 模板中允许出现的非vue实例定义的变量
var allowedGlobals = makeMap(
    'Infinity,undefined,NaN,isFinite,isNaN,' +
    'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
    'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
    'require' // for Webpack/Browserify
);

在浏览器不支持proxy的情况下的数据过滤

在没有经过代理的情况下,使用_开头的变量依旧会 报错,但是它变成了js语言层面的错误。但是这个报错无法在Vue这一层知道错误的详细信息,而这就是能使用Proxy的好处。

在初始化数据阶段,Vue已经为数据进行了一层筛选的代理。具体看initData对数据的代理

有了isReserved的筛选,即使this._data._test存在,我们依旧无法在访问this._test时拿到_test变量

function initData(vm) {
    vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
    if (!isReserved(key)) {
        // 数据代理,用户可直接通过vm实例获取返回data数据
        proxy(vm, "_data", key);
    }
}

function isReserved (str) {
    var c = (str + '').charCodeAt(0);
    // 首字符是$, _的字符串
    return c === 0x24 || c === 0x5F
  }

proxy

function proxy (target, sourceKey, key) {
    sharedPropertyDefinition.get = function proxyGetter () {
        // 当访问this[key]时,会代理访问this._data[key]的值
        return this[sourceKey][key]
    };
    sharedPropertyDefinition.set = function proxySetter (val) {
        this[sourceKey][key] = val;
    };
    Object.defineProperty(target, key, sharedPropertyDefinition);
}

总结 

到此这篇关于vue Proxy数据代理进行校验部分源码解析的文章就介绍到这了,更多相关vue Proxy数据代理校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue中使用iframe嵌入网页,页面可自适应问题

    vue中使用iframe嵌入网页,页面可自适应问题

    这篇文章主要介绍了vue中使用iframe嵌入网页,页面可自适应问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • vue3下watch的使用方法示例

    vue3下watch的使用方法示例

    vue3中的watch是一个组合式的API使用时需要引入,下面这篇文章主要给大家介绍了关于vue3下watch使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • vue阻止重复请求实现示例详解

    vue阻止重复请求实现示例详解

    这篇文章主要为大家介绍了vue阻止重复请求实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • vue实现滑动和滚动效果

    vue实现滑动和滚动效果

    这篇文章主要为大家详细介绍了vue实现滑动和滚动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Vue2响应式系统之嵌套

    Vue2响应式系统之嵌套

    这篇文章主要介绍了Vue响应式系统之嵌套,我们在开发中肯定存在组件嵌套组件的情况,下文将举例说明情况,需要的小伙伴可以参考一下
    2022-04-04
  • vue3中使用vuedraggable实现拖拽el-tree数据分组功能

    vue3中使用vuedraggable实现拖拽el-tree数据分组功能

    这篇文章主要介绍了vue3中使用vuedraggable实现拖拽el-tree数据分组功能,可以实现单个拖拽、双击添加、按住ctrl键实现多个添加,或者按住shift键实现范围添加,添加到框中的数据,还能拖拽排序,需要的朋友可以参考下
    2024-02-02
  • Vue标签属性动态传参并拼接字符串的操作方法

    Vue标签属性动态传参并拼接字符串的操作方法

    这篇文章主要介绍了Vue标签属性动态传参并拼接字符串的操作方法,我们需要根据传入值的类型,在placeholder属性赋值"请输入长度",“请输入宽度”,"请输入厚度"等提示字符,本文通过实例代码介绍的非常详细,需要的朋友参考下吧
    2023-11-11
  • vue中实现div可编辑并插入指定元素与样式

    vue中实现div可编辑并插入指定元素与样式

    这篇文章主要给大家介绍了关于vue中实现div可编辑并插入指定元素与样式的相关资料,文中通过代码以及图文将实现的方法介绍的非常详细,对大家学习或者使用vue具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Vue编译器AST抽象语法树源码分析

    Vue编译器AST抽象语法树源码分析

    这篇文章主要为大家介绍了Vue编译器AST抽象语法树源码分析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • vue自定义穿梭框支持远程滚动加载的实现方法

    vue自定义穿梭框支持远程滚动加载的实现方法

    这篇文章主要介绍了vue自定义穿梭框支持远程滚动加载,iview是全局注入,基本使用原先的类名进行二次创建公共组件,修改基础js实现逻辑,本文结合实例代码介绍的非常详细,需要的朋友可以参考下
    2023-08-08

最新评论