从vue源码看props

前言

平时写vue的时候知道props有很多种用法,今天我们来看看vue内部是怎么处理props中那么多的用法的。

vue提供的props的用法

1. 数组形式

props: [‘name‘, ‘value‘]

2. 对象形式

对象形式内部也提供了三种写法:

props: {
    // 基础的类型检查
    name: String,
    // 多个可能的类型
    value: [String, Number],
    // 对象形式
    id: {
        type: Number,
        required: true
    }
}

props实现的原理

function normalizeProps (options: Object, vm: ?Component) {
  const props = options.props
  if (!props) return
  const res = {}
  let i, val, name
  if (Array.isArray(props)) {
    ...
  } else if (isPlainObject(props)) {
    ...
  } else if (process.env.NODE_ENV !== ‘production‘) {
    ...
  }
  options.props = res
}

normalizeProps函数就是vue实际处理props的地方,从函数名的翻译我们可以看出该函数的功能就是标准化props的值。该函数主要分成3部分:① 从options对象中获取props的值并且定义一个res空对象;②几个if ... else,分别根据props值的不同类型来处理res对象;③ 用处理后的res对象覆盖原来options对象的props属性的值。

接下来看看那几个if ... else的代码:

if (Array.isArray(props)) {
    i = props.length
    while (i--) {
      val = props[i]
      if (typeof val === ‘string‘) {
        name = camelize(val)
        res[name] = { type: null }
      } else if (process.env.NODE_ENV !== ‘production‘) {
        warn(‘props must be strings when using array syntax.‘)
      }
    }
  }

这个代码实际就是处理props的值为数组的情况,例如: props: [‘name‘, ‘value‘]。使用while遍历该数组,如果数组内元素的类型不是字符串并且不是生产环境,那么就抛错:‘props的值类型为数组时,数组里面的元素的类型就必须是字符串’。如果是字符串的情况下,使用camelize函数处理一下val的值,并且赋值给name变量。这里的camelize函数的实际作用就是将‘-‘转换为驼峰。camelize函数具体的实现方式在后面分析。然后在res对象上面添加一个为name变量的属性,该属性的值为空对象 { type: null }

props: [‘name‘, ‘value‘]这种写法经过上面的处理后就会变成了下面这样:

props: {
    name: {
        type: null
    },
    value: {
        type: null
    }
}

接下来看看下面这个else if(isPlainObject(props)),这里的isPlainObject函数实际就是返回props的值是否为objectisPlainObject函数的具体实现我们也在后面分析。

else if (isPlainObject(props)) {
   for (const key in props) {
     val = props[key]
     name = camelize(key)
     res[name] = isPlainObject(val)
       ? val
       : { type: val }
   }
 }

使用for...in遍历props对象,和上面一样使用camelize函数将‘-‘转换为驼峰。这里有个三目运算:

res[name] = isPlainObject(val) ? val : { type: val }

判断了一下val如果是object,那么在res对象上面添加一个为name变量的属性,并且将该属性的值设置为val。这个其实就是处理下面这种props的写法:

props: {
   // 对象形式
   id: {
       type: Number,
       required: true
   }
}

如果val不是object,那么也在res对象上面添加一个为name变量的属性,并且将该属性的值设置为{ type: val }。这个其实就是处理下面这种props的写法:

props: {
    // 基础的类型检查
    name: String,
    // 多个可能的类型
    value: [String, Number],
}

经过处理后props会变成了下面这样:

props: {
    name: {
        type: String
    },
    value: {
        type: [String, Number]
    }
}

所以不管我们使用vue提供的props哪种写法,最终vue都会帮我们转换成下面这种类型:

props: {
    name: {
        ...,
        type: ‘类型‘
    }
}

接下来看看上面提到的util函数isPlainObject,先把源码贴出来。

const _toString = Object.prototype.toString

export function isPlainObject (obj: any): boolean {
  return _toString.call(obj) === ‘[object Object]‘
}

其实Object.prototype.toString.call(obj)的值为obj对象的类型。例如:

Object.prototype.toString.call({a: 1})      // [object Object]
Object.prototype.toString.call(new Date)    // [object Date]
Object.prototype.toString.call([1])         // [object Array]
Object.prototype.toString.call(null)        // [object Null]

接下来看看上面提到的util函数camelize,还是先把源码贴出来。

export function cached<F: Function> (fn: F): F {
  const cache = Object.create(null)
  return (function cachedFn (str: string) {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }: any)
}

const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ‘‘)
})

这里定义了两个函数,分别是cachedcamelize,其中camelize就是我们上面调用的,cached是在camelize函数内部调用的。

我们先来看看camelize函数,其实camelize函数就是执行cached后返回的一个函数。调用cached时传入了一个箭头函数,箭头函数内部是调用了正则的replace方法,将传入的str变量中匹配/-(\w)/g的变成大写字母,并且返回replace后的值。(也就是将-转换成驼峰)。

再来看看cached函数,该函数传入的变量其实就是camelize那里的箭头函数,首先定义了一个cache空对象,然后直接返回了cachedFn函数。我们在外部调用camelize(key)时,其实就是执行了这里的了cachedFn函数,str的值就是传入的key的值。很明显这里是一个闭包,可以在外部调用camelize函数的时候可以修改或者读取这里定义的cache对象的值。获取cache对象中keystr变量值的属性值赋值给hit变量。如果有hit变量的值,那么就直接返回hit的值,如果没有就执行camelize传入的箭头函数,并且将箭头函数的返回值赋值给catche对象的str属性。如果下次调用camelize函数时传入了相同的str,那么就不会执行箭头函数,直接返回闭包中的cache对象的str属性的值。这里是性能优化的一种手段。

例如:第一次调用 camelize(‘name‘)后,cache对象的值就变成了{name: ‘name‘}。然后在其他地方再次调用 camelize(‘name‘)时再次执行cachedFn函数,此时hit变量的值为‘name‘。直接返回hit变量的值,不会执行传入的箭头函数。

原文地址:https://www.cnblogs.com/heavenYJJ/p/10242084.html

时间: 2024-11-07 19:17:30

从vue源码看props的相关文章

从vue源码看Vue.set()和this.$set()

前言 最近死磕了一段时间vue源码,想想觉得还是要输出点东西,我们先来从Vue提供的Vue.set()和this.$set()这两个api看看它内部是怎么实现的. Vue.set()和this.$set()应用的场景 平时做项目的时候难免不会对数组或者对象进行这样的骚操作操作,结果发现,咦~~,他喵的,怎么页面没有重新渲染. const vueInstance = new Vue({ data: { arr: [1, 2], obj1: { a: 3 } } }); vueInstance.$d

从Vue.js源码看异步更新DOM策略及nextTick

写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/answershuto/learnVue.在学习过程中,为Vue加上了中文的注释https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以对其他想学习Vue源码的小伙伴有所帮助.可能会有理解存在偏差的地方,欢迎提issue指出,共同学

Vue源码后记-钩子函数

vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方. 本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过一下~ 想到什么写什么了,这节就简单说说钩子函数吧! vue中的钩子函数主要包含初始化的beforeCreated/created,Virtual Dom更新期间的beforeUpdate/updated,页面渲染期间的beforeMount/mounted,组件销毁期间的beforeDestroy

Vue源码思维导图-------------Vue 初始化

上一节看完<Vue源码思维导图-------------Vue 构造函数.原型.静态属性和方法>,这节将会以new Vue()为入口,大体看下 this._init()要做的事情. 1 function Vue (options) { 2 if (process.env.NODE_ENV !== 'production' && 3 !(this instanceof Vue) 4 ) { 5 warn('Vue is a constructor and should be ca

vue源码之响应式数据

分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$watchapi. 所以决定看一下vue的源码, 了解vue是如何实现响应式数据. 本文叙事方式为树藤摸瓜, 顺着看源码的逻辑走一遍, 查看的vue的版本为2.5.2. 目的 明确调查方向才能直至目标, 先说一下目标行为: vue中的数据改变, 视图层面就能获得到通知并进行渲染. $watchapi监

读 vue 源码一 (为什么this.message能够访问data里面的message)

12月离职后,打算在年后再找工作了,最近陆陆续续的看了黄轶老师的vue源码解析,趁着还有几天过年时间记录一下. 目标:vue如何实现通过this.key,就能直接访问data,props,methods,里面的key. 源码:当我们在项目中new Vue实例后会执行Vue构造函数的_init方法,并执行一些混入 function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vu

[Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串

本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsunlei/javascri

Vue源码(上篇)

某课网有个488人名币的源码解读视频看不起,只能搜很多得资料慢慢理解,看源码能知道大佬的功能模块是怎么分块写的,怎么复用的,已经vue是怎么实现的 资料来自 vue源码 喜欢唱歌的小狮子 web喵喵喵 Vue.js源码全方位深入解析 恰恰虎的博客 learnVue 最后四集视频 总文件目录 scripts:包含构建相关的脚本和配置文件.作者声明一般开发不需要关注此目录 dist:构建出的不同分发版本,只有发布新版本时才会跟新,开发分支的新特性不会反映在此 packages:包含服务端渲染和模板编

Vue源码之响应式原理(个人向)

浅谈响应式原理 关于响应式原理,其实对于vue这样一个大项目来说肯定有很多细节和优化的地方,在下水平精力有限,不能一一尝试探索,本文仅以将响应式的大致流程个人向的梳理完毕为目的. 对于响应式主要分为三大部分来分析,1.响应式对象:2.依赖收集:3.派发更新. 最后将是个人的分析. 1.响应式对象 (Object.defineProperty) 我们先从初始化数据开始,再介绍几个比较核心的方法. 1.1.initState 文件位置:src/core/instance/state.js 在Vue的