最近都在写 vue 相关的文章, 感兴趣的可以看回我之前写的.
文章链接
「Vue 实践」项目升级 vue-cli3 的正确姿势
「从源码中学习」Vue 源码中的 JS 骚操作
求一份深圳的内推
目前本人在准备跳槽, 希望各位大佬和 HR 小姐姐可以内推一份靠谱的深圳前端岗位!
微信: huab119
邮箱: 454274033@qq.com
正文: 吃下这条鱼 - props 初始化
initProps 是如何运行的:
1.normalizeProps: initProps 之前的规范化数据
normalizeProps 的代码有点长, 这里只列举经过规范化后的 prop 类型和结果
1.1 字符串
- props: ["data"]
- // 规范化后
- props: {
- data:{
- type: null
- }
- }
1.2 对象
- props: {
- data1: {
- type: String,
- default: ''
- }
- data2: Number,
- }
- // 规范化后
- props: {
- data1: {
- type: String,
- default: ''
- },
- data2: {
- type: Number
- },
- }
2.initProps: 处理 props
源码分析如下:
- 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 (!isRoot && !isUpdatingChildComponent) {
- warn(
- `Avoid mutating a prop direct....`,
- vm
- )
- }
- })
- } else {
- defineReactive(props, key, value)
- }
- if (!(key in vm)) {
- proxy(vm, `_props`, key)
- }
- }
- toggleObserving(true)
- }
2.1 常量定义
- const propsData = vm.$options.propsData || {}
- const props = vm._props = {}
- const keys = vm.$options._propKeys = []
- const isRoot = !vm.$parent
propsData: 存储着传递进来的 props 的值
props: 引用 vm._props`, 并初始化为 {}
keys: 在 vm.$options 上添加 _propKeys 属性
isRoot: 判断是否存在 vm.$parent, 若无则为根节点
2.2 条件判断及循环
- if (!isRoot) {
- toggleObserving(false)
- }
- for (const key in propsOptions) {
- // 省略...
- }
- toggleObserving(true)
!isRoot: 若当前实例非根节点, 关闭 toggleObserving
toggleObserving: 可以理解为数据观测的开关
for...in : 遍历 propsOptions
2.2.1: 遍历 propsOptions 做什么
- 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 (!isRoot && !isUpdatingChildComponent) {
- warn(
- `Avoid mutating a prop directly since the value will be ` +
- `overwri tten 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)
- }
- }
划重点:
propsOptions 即 opts.props
key 就是每个 prop 的名字
此时进入循环:
- keys.push(key)
- const value = validateProp(key, propsOptions, propsData, vm)
将 key 添加到
vm.$options._propKeys
value: 用 validateProp 校验是否为预期的类型值, 然后返回相应 prop 值 (或 default 值)
2.2.2: 接着进入 if...else:
这里注释一下:
- if (process.env.NODE_ENV !== 'production') {
- // 驼峰转连字符
- const hyphenatedKey = hyphenate(key)
- // 校验 prop 是否为内置的属性
- // 内置属性: key,ref,slot,slot-scope,is
- 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 (!isRoot && !isUpdatingChildComponent) {
- warn(
- `Avoid mutating a prop directly since the value will be ` +
- `overwri tten 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 (process.env.NODE_ENV !== 'production') {
- // 驼峰转连字符
- // 校验 prop 是否为内置的属性
- // 内置属性: key,ref,slot,slot-scope,is
- // 若是内置, 弹出警告
- defineReactive(props, key, value, () => {
- // 子组件直接修改属性时 弹出警告
- } else {
- defineReactive(props, key, value)
- }
工具函数: 「从源码中学习」Vue 源码中的 JS 骚操作
2.2.3: defineReactive: 最终处理
defineReactive(props, key, value)
defineReactive 是老熟人了, 但这里要注意一点: 先前 toggleObserving(false), 关闭了观测的开关, 所以 defineReactive 中调用 observe, 是一个无效调用.
此时到这里, 可以得出一个结论
props 是通过 defineReactive 定义的, 此时虽然是响应式数据, 但没有进行深度定义.
即, 父组件传给子组件 props 后, 子组件不必再重复观测 props
2.2.4 toggleObserving(true)`: 最后打开观测开关
toggleObserving(true)
重新打开观测开关, 避免影响后续代码执行.
感悟: 相比分析源码, 理解后写成博客更难. 用文字讲清楚一件事可比敲代码难多了.
来源: https://juejin.im/post/5c88e669f265da2d8f47792a