从vue源码看props的用法


Posted in Javascript onJanuary 09, 2019

前言

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

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

Javascript 相关文章推荐
ASP.NET jQuery 实例8 (动态添加内容到DropDownList)
Feb 03 Javascript
原生javascript实现图片滚动、延时加载功能
Jan 12 Javascript
jQuery获取上传文件的名称的正则表达式
May 21 Javascript
JS实现在状态栏显示打字效果完整实例
Nov 02 Javascript
浅谈JS原型对象和原型链
Mar 02 Javascript
浅谈JavaScript 浏览器对象
Jun 03 Javascript
angularjs实现下拉列表的选中事件示例
Mar 03 Javascript
详解vue-cil和webpack中本地静态图片的路径问题解决方案
Sep 27 Javascript
jQuery实现的简单前端搜索功能示例
Oct 28 jQuery
npm 更改默认全局路径以及国内镜像的方法
May 16 Javascript
vue-swiper的使用教程
Aug 30 Javascript
简单了解vue.js数组的常用操作
Jun 17 Javascript
浅谈关于JS下大批量异步任务按顺序执行解决方案一点思考
Jan 08 #Javascript
vue-cli2 构建速度优化的实现方法
Jan 08 #Javascript
一个因@click.stop引发的bug的解决
Jan 08 #Javascript
JavaScript学习笔记之图片库案例分析
Jan 08 #Javascript
JavaScript学习笔记之DOM操作实例分析
Jan 08 #Javascript
vue单文件组件lint error自动fix与styleLint报错自动fix详解
Jan 08 #Javascript
说说如何在Vue.js中实现数字输入组件的方法
Jan 08 #Javascript
You might like
PHP在XP下IIS和Apache2服务器上的安装
2006/09/05 PHP
php HandlerSocket的使用
2011/05/02 PHP
PHP 字符串长度判断效率更高的方法
2014/03/02 PHP
Codeigniter上传图片出现“You did not select a file to upload”错误解决办法
2014/06/12 PHP
PHP简单选择排序算法实例
2015/01/26 PHP
PHP在弹框中获取foreach中遍历的id值并传递给地址栏
2017/06/13 PHP
php 替换文章中的图片路径,下载图片到本地服务器的方法
2018/02/06 PHP
javascript 命名规则 变量命名规则
2010/02/25 Javascript
在JavaScript中操作时间之getUTCDate()方法的使用
2015/06/10 Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(一)
2015/12/10 Javascript
深入浅析NodeJs并发异步的回调处理
2015/12/21 NodeJs
jquery mobile开发常见问题分析
2016/01/21 Javascript
jQuery实现邮箱下拉列表自动补全功能
2016/09/08 Javascript
使用微信小程序开发前端【快速入门】
2016/12/05 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
2016/12/17 Javascript
微信小程序使用video组件播放视频功能示例【附源码下载】
2017/12/08 Javascript
微信小程序开发之点击按钮退出小程序的实现方法
2019/04/26 Javascript
javascript获取元素的计算样式
2019/05/24 Javascript
vue中的 $slot 获取插槽的节点实例
2019/11/12 Javascript
vue之debounce属性被移除及处理详解
2019/11/13 Javascript
使用Bootstrap做一个朝代历史表
2019/12/10 Javascript
[43:58]DOTA2上海特级锦标赛C组败者赛 Newbee VS Archon第二局
2016/02/27 DOTA
[01:08:29]DOTA2-DPC中国联赛定级赛 RNG vs Aster BO3第一场 1月9日
2021/03/11 DOTA
Python入门_浅谈数据结构的4种基本类型
2017/05/16 Python
python随机模块random使用方法详解
2020/02/14 Python
Python判断字符串是否为空和null方法实例
2020/04/26 Python
python简单利用字典破解zip文件口令
2020/09/07 Python
HTML5单页面手势滑屏切换原理分析
2017/07/10 HTML / CSS
internal修饰符起什么作用
2013/12/16 面试题
国培教师自我鉴定
2014/02/12 职场文书
运动会演讲稿200字
2014/08/25 职场文书
物业工程部岗位职责
2015/02/11 职场文书
先进个人自荐书
2015/03/06 职场文书
导游词之无锡华莱坞
2019/12/02 职场文书
经典人生语录分享:不畏将来,不念过去,笑对当下
2019/12/12 职场文书
python中出现invalid syntax报错的几种原因分析
2022/02/12 Python