if(opts.props) initProps(vm,opts.props);
if(opts.methods) initMethods(vm,opts.methods);
if(opts.data){
initData(vm);
}else{
observe(vm._data = {},true/asRootData/);
}
if(opts.computed) initComputed(vm,opts.computed);
if(opts.watch && opts.watch !== nativeWatch){
initWatch(vm,opts.watch);
}
1、首先在vm上新增一个属性_watchers,用来保存当前组件中所有的watcher实例。无论是使用vm.$watch注册的watcher还是使用watch选项添加的watcher实例,都会添加到vm._watchers中。
2、可以通过vm._watchers得到当前Vue.js实例中所注册的所有watcher实例,并将它们一次卸载。
3、用户在实例化Vue.js时使用了哪些状态,哪些状态就需要被初始化,没有用到的状态则不用初始化。例如,用户只使用了data,那么只需要初始化data即可。
4、初始化的顺序其实是精心安排的。先初始化props,后初始化data,这样就可以在data中使用props中的数据了。在watch中既可以观察props,也可以观察data,因为它是最后被初始化的。
5、初始化状态可以分为5个子项,分别是初始化props、初始化methods、初始化data、初始化computed和初始化watch。
开始之前,我提出问题
1、父组件 怎么传值给 子组件的 props
2、子组件如何读取props
这一节,我们带着问题去开始我们的讲解
明白这三个问题,那么相信你也就理解了 props 的工作原理
场景设置
现在我们有一个这样的 根组件 A 和 他的 子组件 testb
根组件A 会 把 parentName 传给 子组件 testb 的 props
根组件A 也是 组件testb 的 父组件
new Vue({
el:“.a”,
name:“A”,
components:{
testb:{
props:{
childName:“”
},
template: ‘
父组件传入的 props 的值 {{childName}}
’,}
},
data(){
return {
parentName:“我是父组件”
}
},
})
按照上面的例子,开始我们的问题解析
父组件怎么传值给子组件的 props
这部分内容是 props 的重中之重,必须理解
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还有个问题:就是我们写的时候可是是数组形式,也是对象形式,这是为什么呢?
- 数组形式
props: [‘name’, ‘value’]
- 对象形式
对象形式内部也提供了三种写法:
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]
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。
效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-cw5cD3yd-1713778112326)]
[外链图片转存中…(img-eu3isxGN-1713778112326)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-wGeh88DM-1713778112327)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-ObXzYyA7-1713778112327)]
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦,如果有需要前端校招面试题PDF完整版的朋友可以点击这里即可免费获取,包括答案解析。
[外链图片转存中…(img-B9gmgT5G-1713778112327)]