vue2.x源码解析(第四部分)

渲染函数的作用域添加代理

if (process.env.NODE_ENV !== 'production') {
  initProxy(vm)
} else {
  vm._renderProxy = vm
}

上面这段代码可以看到在非生产环境下执行了initProxy函数,参数是实例;在生产环境下设置了实例的_renderProxy属性为实例自身,那么先来看看 initProxy函数,这个函数在core/instance/proxy.js文件下。

initProxy = function initProxy (vm) {
    //判断是否支持ES6的Proxy或存在Proxy函数
    const hasProxy =typeof Proxy !== 'undefined' && isNative(Proxy)
    ...
    //支持Proxy
    if (hasProxy) {
      // determine which proxy handler to use
      //缓存合并选项后的vm.$options
      const options = vm.$options
      //_withStripped这个属性只在测试代码中出现过所以一般为false,也就是赋值为hasHandler
      const handlers = options.render && options.render._withStripped
        ? getHandler
        : hasHandler
      //为vm实例添加代理,对以下情况时进行拦截
      /*
        1、属性查询: foo in proxy
        2、继承属性查询: foo in Object.create(proxy)
        3、with 检查: with(proxy) { (foo); }
        4、Reflect.has()
      */    
      vm._renderProxy = new Proxy(vm, handlers)
    } else {
      vm._renderProxy = vm
    }
}

可以看到开头就是一个判断变量hasProxy,其作用是确定该环境是否支持Proxy,当支持情况下继续执行下面的代码: 读取实例的$options属性, 最后为实例添加_renderProxy属性,这样就和生产环境一致了。只不过对当读取_renderProxy的属性时进行了拦截操作,当该属性存在render函数和 render._withStripped属性时,执行的是getHandler拦截函数;否则就是执行hasHandler拦截函数,那么现在来看看这两个拦截函数都干了什么?

hasHandler

// 判断传入的参数是否存在于下面这段字符中
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,' +
    'require' // for Webpack/Browserify
  )
//比如: in运算符的拦截,但对for...in循环不生效
  const hasHandler = {
    //拦截key in target的操作,返回一个布尔值
    has (target, key) {
      const has = key in target //是否存在与目标中
      //判断key名是否存在于allowedGlobals函数中生成的对象中 || (key名是string类型 && 首字母是_)
      const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_')
      //访问了一个没有定义在实例对象上(或原型链上)的属性 && isAllowed为false提示警告
      if (!has && !isAllowed) {
        warnNonPresent(target, key)
      }
      //返回一个布尔值
      return has || !isAllowed
    }
  }

该函数的作用就是判断对象是否具有某个属性。可以看到首先用in操作符判断这个属性是否存在于_renderProxy中,当不存在 && (属性名不为字符串 || 属性名首字母为_) && allowedGlobals(key)false时,提示警告信息'属性或方法没有在实例上定义'

getHandler

//添加读取属性拦截
  const getHandler = {
    get (target, key) {
      //key名是字符串类型 && key不存在于目标中提示警告
      if (typeof key === 'string' && !(key in target)) {
        warnNonPresent(target, key)
      }
      return target[key]
    }
  }

这个比较好理解,就是读取_renderProxy中的属性的时候,当属性名为字符串 && 不存在_renderProxy中时提示警告信息'属性或方法没有在实例上定义'。

一个实例的生成后的一些隐藏所有属性

vm = {
    _renderProxy: vm,
    _self: vm,
    $options: {
        _parentListeners: {},//所有父组件作用于该组件的监听函数集合
    },//用户可配置的属性和一些不可配置的属性
    $parent,//实例的父组件
    $children: [],//子组件集合
    $root,//根组件一般为App
    $refs:{},//存放该组件中所有存在`ref`属性的节点
    _watcher,//该实例渲染函数的数据的订阅者
    _inactive,//表示该组件是否激活, `false`代表激活  `true`或`null`代表未激活 
    _directInactive,//表示`keep-alive`中组件状态的属性,表示是否要激活 比如父组件未激活 那么其也不会激活
    _isMounted,//用于表示该组件是否已挂载
    _isDestroyed,//表示该组件是否已销毁
    _isBeingDestroyed,//表示该组件是否正在销毁中
    _events,//组件的所有监听函数集合
    _hasHookEvent,//父组件是否有监听子组件的生命周期钩子
}

这一章节到这就没了,下一章我们来看下‘初始化数据’做了什么

详细信息可访问https://github.com/maomao93/vue-2.x/tree/master/personal

posted @ 2020-12-21 10:39  疾风_剑豪  阅读(150)  评论(0编辑  收藏  举报