Vue实例初始化为渲染函数设置检查源码剖析

目录
  • 引言
  • _renderProxy是干什么的
  • initProxy方法
  • 总结

引言

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

if (__DEV__) {
  initProxy(vm)
} else {
  vm._renderProxy = vm
}

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

_renderProxy是干什么的

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

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

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

initProxy方法

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

//源码文件 src\core\instance\proxy.ts
const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
//...
initProxy = function initProxy(vm) {
  if (hasProxy) {
    // determine which proxy handler to use
    const options = vm.$options
    const handlers =
      options.render && options.render._withStripped ? getHandler : hasHandler
    vm._renderProxy = new Proxy(vm, handlers)
  } else {
    vm._renderProxy = vm
  }
}

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

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

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

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

// 源码文件:src\core\instance\proxy.ts
const getHandler = {
  get(target, key) {
    if (typeof key === 'string' && !(key in target)) {
      if (key in target.$data) warnReservedPrefix(target, key)
      else warnNonPresent(target, key)
    }
    return target[key]
  }
}

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

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

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

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

那么做了什么呢:

// 源码文件:src\core\instance\proxy.ts
const hasHandler = {
  has(target, key) {
    const has = key in target
    const isAllowed =
      allowedGlobals(key) ||
      (typeof key === 'string' &&
        key.charAt(0) === '_' &&
        !(key in target.$data))
    if (!has && !isAllowed) {
      if (key in target.$data) warnReservedPrefix(target, key)
      else warnNonPresent(target, key)
    }
    return has || !isAllowed
  }
}

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

// 源码文件:src\core\instance\proxy.ts
const 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,BigInt,' +
    'require' // for Webpack/Browserify
)

总结

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

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

(0)

相关推荐

  • 详解vue3中渲染函数的非兼容变更

    目录 渲染函数API变更 Render函数参数 渲染函数签名更改 VNode Props 格式化 slot统一 移除$listeners $attrs现在包括class和style 渲染函数API变更 此更改不会影响到<template>用户 h现在全局导入,而非作为参数传递给渲染函数 渲染函数参数更改为在有状态组件和函数组件之间更加一致 vnode现在又一个扁平的prop结构 Render函数参数 // 2.0 渲染函数 export default { render(h) { return

  • Vue渲染函数详解

    前面的话 Vue 推荐在绝大多数情况下使用 template 来创建HTML.然而在一些场景中,真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接近编译器.本文将详细介绍Vue渲染函数 引入 下面是一个例子,如果要实现类似下面的效果.其中,H标签可替换 <h1> <a name="hello-world" href="#hello-world" rel="external nofol

  • Vue监听数据渲染DOM完以后执行某个函数详解

    实例如下: vm.$watch('某data数据',function(val){ vm.$nextTick(function() { 某事件(); }); }) 以上这篇Vue监听数据渲染DOM完以后执行某个函数详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Vue源码分析之Vue实例初始化详解

    这一节主要记录一下:Vue 的初始化过程 以下正式开始: Vue官网的生命周期图示表 重点说一下 new Vue()后的初始化阶段,也就是created之前发生了什么. initLifecycle 阶段 export function initLifecycle (vm: Component) { const options = vm.$options // locate first non-abstract parent let parent = options.parent if (pare

  • 详解vue渲染函数render的使用

    1.什么是render函数? vue通过 template 来创建你的 HTML.但是,在特殊情况下,这种写死的模式无法满足需求,必须需要js的编程能力.此时,需要用render来创建HTML. 比如如下我想要实现如下html: <div id="container"> <h1> <a href="#" rel="external nofollow" rel="external nofollow"

  • 在vue.js渲染完界面之后如何再调用函数

    目录 vue.js渲染完界面后调用函数 1.下面开始介绍下 2.运行之后 3.此时查询官方api文档发现 4.继续查询api文档发现 5.最终把watch和nextTick组合一起 6.运行后发现 vue渲染完成事件 vue里面本身带有两个回调函数 vue.js渲染完界面后调用函数 在使用vue.js框架的时候,有时候会希望在页面渲染完成之后,再执行函数方法来处理初始化相关的操作,如果只处理页面位置.宽或者高时,必须要在页面完全渲染之后才可以,页面没有加载完成之前,获取到的宽高不准确. 使用过j

  • Vue实例初始化为渲染函数设置检查源码剖析

    目录 引言 _renderProxy是干什么的 initProxy方法 总结 引言 之前的文章提到,Vue实例化时_init方法做了很多处理,其中就有这么一段: if (__DEV__) { initProxy(vm) } else { vm._renderProxy = vm } 在生产模式下,_renderProxy直接指向了Vue实例本身,而在开发环境下调用了initProxy方法,那么它究竟是做什么的呢? _renderProxy是干什么的 通过对_renderProxy进行全局搜索,我

  • Vue $nextTick 为什么能获取到最新Dom源码解析

    目录 正文 修改数据之后 update 方法 nextTick 方法里面怎么执行传进去更新方法 正文 <template> <p id='text'>{{text}}</p> <button @click='change'>click</button> </template> <script> export default { data() { return { text: 'hello world' } } method

  • ssm+vue前后端分离框架整合实现(附源码)

    前言 本文针对Spring+SpringMVC+Mybatis后台开发框架(基于maven构建)与vue前端框架(基于webpack构建)的项目整合进行介绍,对于ssm和vue单独项目的搭建不作为本文的重点,而着重介绍两者之间交互的要点. SSM 项目结构 说明 项目有service和web两个子项目组成,web依赖于service,其中web主要是control层内容,service则对应service层,而MyBatis内容放在了service项目中,spring配置文件放在了web项目中.

  • vue使用引用库中的方法附源码

    monaco-editor-vue的官方源码如下 Index.js import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; function noop() { } export { monaco }; export default { name: 'MonacoEditor', props: { diffEditor: { type: Boolean, default: false }, //是否使用diff模式 wid

  • Vue 组件事件触发和监听实现源码解析

    目录 正文 Vue 的事件触发和监听 源码分析 on once emit off 动手实现 总结 正文 通常我们在使用Vue的时候,会使用$emit和$on来触发和监听事件,但是有没有思考是如何实现的呢? 今天带来的是一个微型的事件触发的库,借它们的源码来简单初步了解Vue的事件触发和监听的实现. mitt tiny-emitter mitt使用TypeScript编写,tiny-emitter使用原生ES5编写,两者对比tiny-emitter功能稍微丰富一写,所以直接看tiny-emitte

  • VUE+Element实现增删改查的示例源码

    前言 &最近因为一些原因,没有更博客,昨天老师布置了一个作业,用vue实现增删改查功能,想想这也不难,就做一下试试吧. 因为自己写的样式没有别人做的好,因此我想用现成的UI框架,一直也没用过Element,就干脆趁机学一下吧. 实验步骤 首先引入一下element的css以及js <!-- 引入样式 --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chal

  • 深入PHP购物车模块功能分析(函数讲解,附源码)

    一,购物车概述购物车是为消费者在网上购物中提供一个临时存储商品的地方.其主要功能包括:添加商品.删除商品.更改商品数量.商品金额小计.商品金额总计和清空购物车:还包括生成订单.订单打印.订单预览.提交订单和取消购物等.购物车的操作流程:首先,登录到网站中浏览商品:然后,购买指定的商品,进入购物车页面中,在该页面可以实现更改商品数量.删除商品.清空购物车.继续购物等:最后,填写收货人信息,生成订单,订单打印.预览,提交订单等操作. 二,热点关键技术1,Smarty模块的安装配置smarty是一个使

  • vue实例的选项总结

    一.数据 API:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E6%95%B0%E6%8D%AE 1. data 类型:Object | Function 限制:组件的定义只接受 function 详细:Vue 实例的数据对象.Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化.对象必须是纯粹的对象 (含有零个或多个的 key/value 对).当一个组件被定义,data 必须声明

  • Vue源码解析之Template转化为AST的实现方法

    什么是AST 在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式. Virtual Dom Vue的一个厉害之处就是利用Virtual DOM模拟DOM对象树来优化DOM操作的一种技术或思路. Vue源码中虚拟DOM构建经历 template编译成AST语法树 -> 再转换为render函数 最终返回一个VNode(VNod

  • Vue3源码解析watch函数实例

    目录 引言 一.watch参数类型 1. 选项options 2. 回调cb 3. 数据源source 二.watch函数 三.watch的核心:doWatch 函数 引言 想起上次面试,问了个古老的问题:watch和computed的区别.多少有点感慨,现在已经很少见这种耳熟能详的问题了,网络上八股文不少.今天,我更想分享一下从源码的层面来区别这八竿子打不着的两者.本篇针对watch做分析,下一篇分析computed. 一.watch参数类型 我们知道,vue3里的watch接收三个参数:侦听

随机推荐