学习vue源码(19)四探生命周期之初始化props(1)

1 props 传值的设置

根据上面的场景设置,testb 是一个子组件,接收一个 props(child-name)

然后 根组件 A 把 自身的 parentName 绑定到 子组件的属性 child-name 上

2 props 父传子前

父组件的模板 会被解析成一个 模板渲染函数

(function() {

with(this){

return _c(‘div’,{staticClass:“a”},[

_c(‘testb’,{attrs:{“child-name”:parentName}})

],1)

}

})

这段代码需要解释下

其实,这是属于代码生成器的部分,感兴趣的可以看学习vue源码(9)手写代码生成器

1、_c 是渲染组件的函数,_c(‘testb’) 表示渲染 testb 这个子组件

2、因为 with 的作用是,绑定大括号内代码的 变量访问作用域

3、这是一个匿名自执行函数,会在后面执行

简化上面的函数,做个例子测试一下

3 .props 开始赋值

之后,模板函数会被执行,执行时会绑定 父组件为作用域

所以渲染函数内部所有的变量,都会从父组件对象 上去获取

绑定了父作用域之后, parentName 自然会从父组件获取,类似这样

{ attrs: { child-name: parentVm.parentName } }

函数执行了,内部的 _c(‘testb’) 第一个执行,然后传入了 赋值后的 attrs

父组件赋值之后的 attrs 就是下面这样

{ attrs: { child-name: “我是父组件” } }

此时,父组件就正式 利用 props 把 parentName 传给了 子组件的props child-name

4 .子组件保存 props

_c(‘testb’,{attrs:{“child-name”:parentName}})

子组件拿到父组件赋值过后的 attr

而 attrs 包含 普通属性 和 props,所以需要 筛选出 props,然后保存起来

5 .子组件 设置响应式 props

props 会被 保存到 实例的 _props 中,并且 会逐一复制到 实例上,并且每一个属性会被设置为 响应式 的

你看到的,每一个 实例都会有 一个 _props 的同时,也会把属性直接放在 实例上。

对于props还有个问题:就是我们写的时候可是是数组形式,也是对象形式,这是为什么呢?

  1. 数组形式

props: [‘name’, ‘value’]

  1. 对象形式

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

props: {

// 基础的类型检查

name: String,

// 多个可能的类型

value: [String, Number],

// 对象形式

id: {

type: Number,

required: true

}

}

其实 vue初始化时,会把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: ‘类型’

}

}

明白了这些之后,看开头我们提出的initProps函数,就容易理解的多了

initProps

如果觉得有点难理解,可以先跟着代码后面的解释,逐行明白每一行代码的作用。

function initProps (vm: Component, propsOptions: Object) {

const propsData = vm.$options.propsData || {}

const props = vm._props = {}

const keys = vm.$options._propKeys = []

const isRoot = !vm.$parent

if (!isRoot) {

toggleObserving(false)

}

for (const key in propsOptions) {

//

keys.push(key)

const value = validateProp(key, propsOptions, propsData, vm)

// 非生产环境下进行检查和提示

if (process.env.NODE_ENV !== ‘production’) {

//

const hyphenatedKey = hyphenate(key)

//

if (isReservedAttribute(hyphenatedKey) ||

config.isReservedAttr(hyphenatedKey)) {

warn(

"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.,

vm

)

}

defineReactive(props, key, value, () => {

if (vm.$parent && !isUpdatingChildComponent) {

warn(

Avoid mutating a prop directly since the value will be +

overwritten whenever the parent component re-renders. +

Instead, use a data or computed property based on the prop's +

value. Prop being mutated: "${key}",

vm

)

}

})

} else {

defineReactive(props, key, value)

}

if (!(key in vm)) {

proxy(vm, _props, key)

}

}

// 开启观察状态标识

toggleObserving(true)

}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-zRLNUoXi-1715232378845)]

[外链图片转存中…(img-2HpNN5Fy-1715232378845)]

[外链图片转存中…(img-yG5smHSg-1715232378846)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值