前言

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

从vue源码看props的更多相关文章

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

    前言 最近死磕了一段时间vue源码,想想觉得还是要输出点东西,我们先来从Vue提供的Vue.set()和this.$set()这两个api看看它内部是怎么实现的. Vue.set()和this.$se ...

  2. Vue源码后记-更多options参数(1)

    我是这样计划的,写完这个还写一篇数据变动时,VNode是如何更新的,顺便初探一下diff算法. 至于vue-router.vuex等插件源码,容我缓一波好吧,vue看的有点伤. 其实在之前讲其余内置指 ...

  3. Vue源码后记-其余内置指令(3)

    其实吧,写这些后记我才真正了解到vue源码的精髓,之前的跑源码跟闹着玩一样. go! 之前将AST转换成了render函数,跳出来后,由于仍是字符串,所以调用了makeFunction将其转换成了真正 ...

  4. Vue源码后记-钩子函数

    vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方. 本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过 ...

  5. 【vuejs深入二】vue源码解析之一,基础源码结构和htmlParse解析器

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. vuejs是一个优秀的前端mvvm框架,它的易用性和渐进式的理念可以使每一个前端开发人员感到舒服,感到easy.它内 ...

  6. 【一套代码小程序&Native&Web阶段总结篇】可以这样阅读Vue源码

    前言 前面我们对微信小程序进行了研究:[微信小程序项目实践总结]30分钟从陌生到熟悉 在实际代码过程中我们发现,我们可能又要做H5站又要做小程序同时还要做个APP,这里会造成很大的资源浪费,如果设定一 ...

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

    12月离职后,打算在年后再找工作了,最近陆陆续续的看了黄轶老师的vue源码解析,趁着还有几天过年时间记录一下. 目标:vue如何实现通过this.key,就能直接访问data,props,method ...

  8. vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...

  9. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

随机推荐

  1. stm32高级定时器的应用——spwm

    用过stm32定时器的朋友都知道,定时器的CCR寄存器,可以用来配置PWM的输出,但同样也可以用来配置spwm.废话不多说,直接上代码. 首先,你得考虑一下几个因素: 1.同步调制还是异步调制.  2 ...

  2. Postman接口自动化测试实例

    一.实例背景  在实际业务中,经常会出现让用户输入用户密码进行验证的场景.而为了安全,一般都会先请求后台服务器获取一个随机数做为盐值,然后将盐值和用户输入的密码通过前端的加密算法生成加密后串传给后台服 ...

  3. 解决IE浏览器把application/json响应视为文件并尝试下载

    下面我的解决方案是针对.net MVC的,其他的解决方案也类似,就是把响应的mimeType换成IE浏览器已经拥有的.如application/json换成text/plain #region 退出登 ...

  4. HDFS组件性能调优:数据平衡

    生产系统中什么情况下会添加一个节点呢? 1 增加存储能力 disk 2 增加计算能力 cpu mem 如果增加是的是存储能力,说明存储已接近饱和或者说过段时间就会没有剩余的空间给作业来用.新加的节点存 ...

  5. 获取Type的三种方式

    using System;using UnityEngine; public class Type_Test : MonoBehaviour{    private void Awake()    { ...

  6. SVN的安装和启动SVN的安装

    SVN的安装和启动SVN的安装 i. windows下安装SVN 首先要先下载SVN服务器,下载地址https://www.visualsvn.com/downloads/ 下载软件VisualSVN ...

  7. 第一次安装myeclipse+tomcat经验

    在网上找了很多资料,这里记录一下验证有用的资料,避免以后走弯路 1.安装myeclipse 参考如下URL,亲测有用 https://blog.csdn.net/qingjianduoyun/arti ...

  8. windows之电脑开机出现 this product is covered by one or more of the following prtents

    电脑开机出现 this product is covered by one or more of the following prtents 有次意外断电后就每次都出现这个提示,然后要等检查完才能进入 ...

  9. php通过phpize安装扩展

    //下载libevent扩展文件压缩包(在当前系统哪个目录下载随意) ~# wget http://pecl.php.net/get/libevent-0.1.0.tgz //解压文件 ~# tar ...

  10. 论文速读(Yongchao Xu——【2018】TextField_Learning A Deep Direction Field for Irregular Scene Text)

    Yongchao Xu--[2018]TextField_Learning A Deep Direction Field for Irregular Scene Text Detection 论文 Y ...