经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
Vue实例初始化为渲染函数设置检查源码剖析
来源:jb51  时间:2022/8/22 18:18:39  对本文有异议

引言

之前的文章提到,Vue实例化时_init方法做了很多处理,其中就有这么一段:

  1. if (__DEV__) {
  2. initProxy(vm)
  3. } else {
  4. vm._renderProxy = vm
  5. }

在生产模式下,_renderProxy直接指向了Vue实例本身,而在开发环境下调用了initProxy方法,那么它究竟是做什么的呢?

_renderProxy是干什么的

通过对_renderProxy进行全局搜索,我们在src\core\instance\render.ts文件中找到了它:

  1. // 源码文件 src\core\instance\render.ts
  2. vnode = render.call(vm._renderProxy, vm.$createElement)

也就是说,_renderProxy是渲染函数render的执行上下文,在生产环境下,执行上下文就是实例本身,而在开发环境下,执行上下文则使用initProxy进行了处理,我们接下来看看它究竟做了什么。

initProxy方法

首先判断了当前环境下Proxy对象是否存在进行了判断:

  1. //源码文件 src\core\instance\proxy.ts
  2. const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
  3. //...
  4. initProxy = function initProxy(vm) {
  5. if (hasProxy) {
  6. // determine which proxy handler to use
  7. const options = vm.$options
  8. const handlers =
  9. options.render && options.render._withStripped ? getHandler : hasHandler
  10. vm._renderProxy = new Proxy(vm, handlers)
  11. } else {
  12. vm._renderProxy = vm
  13. }
  14. }

如果Proxy对象不存在,就放弃治疗,上下文仍为原Vue实例。

而如果Proxy对象存在,则进一步去$options里获取_withStripped属性,如果_withStripped存在,则使用getHandler方法来代理Vue实例,如果不存在,就使用hasHandler来代理实例。

关于Proxy对象的用法,我在这篇文章里提过,简单来说,它可以为一个对象设定代理,用以拦截对象的各种方法。

那么,我们进一步看一下,hasHandlergetHandler都做了什么,首先是比较简单的getHandler:

  1. // 源码文件:src\core\instance\proxy.ts
  2. const getHandler = {
  3. get(target, key) {
  4. if (typeof key === 'string' && !(key in target)) {
  5. if (key in target.$data) warnReservedPrefix(target, key)
  6. else warnNonPresent(target, key)
  7. }
  8. return target[key]
  9. }
  10. }

这个方法拦截了Vue实例对象的getter,也就是说,当获取实例的属性时,就会触发这个方法,在这个方法中,对属性值是否在实例中以及是否在实例的$data中进行了检查,如果不存在则发出相应的警告:

  1. // 源码文件:src\core\instance\proxy.ts
  2. const warnReservedPrefix = (target, key) => {
  3. warn(
  4. `Property "${key}" must be accessed with "$data.${key}" because ` +
  5. 'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
  6. 'prevent conflicts with Vue internals. ' +
  7. 'See: https://v2.vuejs.org/v2/api/#data',
  8. target
  9. )
  10. }
  11. const warnNonPresent = (target, key) => {
  12. warn(
  13. `Property or method "${key}" is not defined on the instance but ` +
  14. 'referenced during render. Make sure that this property is reactive, ' +
  15. 'either in the data option, or for class-based components, by ' +
  16. 'initializing the property. ' +
  17. 'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
  18. target
  19. )
  20. }

hasHandler则是对实例对象的in操作符进行拦截,也就是拦截以下操作:

  • 属性查询:foo in proxy
  • 继承属性查询:foo in Object.create(proxy)
  • with 检查: with(proxy) { (foo); }
  • Reflect.has()

那么做了什么呢:

  1. // 源码文件:src\core\instance\proxy.ts
  2. const hasHandler = {
  3. has(target, key) {
  4. const has = key in target
  5. const isAllowed =
  6. allowedGlobals(key) ||
  7. (typeof key === 'string' &&
  8. key.charAt(0) === '_' &&
  9. !(key in target.$data))
  10. if (!has && !isAllowed) {
  11. if (key in target.$data) warnReservedPrefix(target, key)
  12. else warnNonPresent(target, key)
  13. }
  14. return has || !isAllowed
  15. }
  16. }

类似的,依然是对属性是否在实例中存在进行了检查,但是多了一步判断,也就是allowedGlobals,它实际上是一个全局方法列表,当模板中出现了里面的方法名后,不会进行下一个步骤的判断,也就不会因为在Vue实例和$options中找不到这个名字的属性而弹出报错,这些方法你在开发过程中肯定都用过:

  1. // 源码文件:src\core\instance\proxy.ts
  2. const allowedGlobals = makeMap(
  3. 'Infinity,undefined,NaN,isFinite,isNaN,' +
  4. 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
  5. 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
  6. 'require' // for Webpack/Browserify
  7. )

总结

Vue实例化过程中,在开发环境下会调用initProxy方法来包装render函数原本的执行上下文(也就是Vue实例本身),在它的getterin操作符中加入一部分属性的检查,当模板中调用的属性不存在于实例中或不存在于$options中时,及时提出警告。

以上就是Vue实例初始化为渲染函数设置检查源码剖析的详细内容,更多关于Vue实例初始化渲染函数检查的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号