从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 的值是否为 object , isPlainObject 函数的具体实现我们也在后面分析。

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() : '')
})

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

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

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈Vue初学之props的驼峰命名

    在vue的中文官网有这样的说明:HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符.这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名. 重申一次,如果你使用字符串模板,那么这个限制就不存在了. 以以下代码为例: 1.当组件中template及props等使用驼峰式命名,在html中对应的改成短横线命名方式. 2.当组件中template及props等使用字符串模板

  • Vue中的Props(不可变状态)

    组件接受的选项大部分与Vue实例一样,而选项props是组件中非常重要的一个选项.在 Vue 中,父子组件的关系可以总结为 props down, events up.父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息.接下来通过本文给大家介绍Vue中Props,一起看看吧! 单向数据流 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行.这样会防止从子组件意外改变父级组件的状态

  • 解决vue props 拿不到值的问题

    方案一: 在子组件中设置v-if='flag',初始值false,在成功获取数据后设置为true // 子组件 <echarts :datas="conditionStatisticsData" v-if="flag"></echarts> // 成功获取数据后 flag设置成true homeResource.getConditionData().then((res) => { this.flag = true if (res.dat

  • vue组件开发props验证的实现

    使用props 在Vue中父组件向子组件中传送数据是通过props实现的,一个简单的使用props的例子: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue Study</title> </head> <body> <div id="app"> <foo-component :

  • Vue 组件参数校验与非props特性的方法

    子组件接收父组件的参数的时候,props注册接收的参数 props:['count'] 子组件可以对接收的参数校验. 例如规定接收的count参数要求是String props:{ count:String } 如果count是别的类型就会报错 组件的参数校验 组件的参数校验指的是什么呢?你父组件向子组件传递的内容,子组件有权对这个内容进行一些约束,这个约束我们可以把它叫做参数的校验. <div id="root"> <child content="hell

  • Vue中props的使用详解

    props属性是父子组件之间的通信桥梁.何为父子组件?从子组件的观点来看,他的上一级实例或组件即为他的父组件.我们知道,处于安全考虑,组件模板里我们无法直接使用父组件的data数据,使用props这个属性可以将父组件的数据传给子组件. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>props的测试</titl

  • Vue组件中prop属性使用说明实例代码详解

    Prop 的大小写 (camelCase vs kebab-case) HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符.这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名: Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h

  • 简单理解vue中Props属性

    本文实例为大家解析了vue中Props的属性,供大家参考,具体内容如下 使用 Props 传递数据 组件实例的作用域是孤立的.这意味着不能并且不应该在子组件的模板内直接引用父组件的数据.可以使用 props 把数据传给子组件. "prop" 是组件数据的一个字段,期望从父组件传下来.子组件需要显式地用 props 选项 声明 props: Vue.component('child', { // 声明 props props: ['msg'], // prop 可以用在模板内 // 可以

  • Vue props 单向数据流的实现

    1.props通信 注意:DOM模板的驼峰命名props要转为短横分割命名. <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>Vue</title> </head> <body> <div id="app"> <my-component m

  • Vue props用法详解(小结)

    Vue props用法详解 组件接受的选项之一 props 是 Vue 中非常重要的一个选项.父子组件的关系可以总结为: props down, events up 父组件通过 props 向下传递数据给子组件:子组件通过 events 给父组件发送消息. 父子级组件 比如我们需要创建两个组件 parent 和 child.需要保证每个组件可以在相对隔离的环境中书写,这样也能提高组件的可维护性. 这里我们先定义父子两个组件和一个 Vue 对象: var childNode = { templat

  • Vue Prop属性功能与用法实例详解

    本文实例讲述了Vue Prop属性功能与用法.分享给大家供大家参考,具体如下: 1 Prop 的大小写 (camelCase vs kebab-case) HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符.这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名: html: <div id="app-1"> <!-- HTML 中是 keb

随机推荐