前言

平时写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变量的值,不会执行传入的箭头函数。

最新文章

  1. 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
  2. UDS(ISO14229-2006) 汉译(No.3术语与定义)
  3. 【WPF】FillRule
  4. STL概述
  5. powershell samples
  6. 增加duilib edit控件的提示功能和多种文字颜色
  7. iOS应用的真机调试
  8. 【学习总结】UIGestureRecognizer(手势识别器)
  9. Network view
  10. zookeeper 新手安装指南
  11. go语言获取变量的数据类型
  12. C语言的函数调用过程
  13. XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Siberia
  14. mycat 安装 分表 分库 读写分离
  15. LeetCode(114): 二叉树展开为链表
  16. Spring Security之Remember me详解
  17. 将gitlab中的postgresql数据库开通远程访问
  18. spark使用正则表达式读入多个文件
  19. nginx静态资源缓存策略配置
  20. python(45)内置函数:os.system() 和 os.popen()

热门文章

  1. React browserHistory.push()传参
  2. vue中遇到的问题:Error: Cannot find module &#39;chalk&#39;
  3. office word memo
  4. 用php实现表格
  5. Sublime 个人配置
  6. 放弃antd table,基于React手写一个虚拟滚动的表格
  7. Linux运维跳槽40道面试精华题
  8. web自动化测试python+selenium学习总结----selenium安装、浏览器驱动下载
  9. 怎么让微信下载APK文件包,微信内置浏览器无法打开APP下载链接的解决方案
  10. Vue2.0 入门 安装Vue-cli