「从源码中学习」完全理解Vue选项Props

最近都在写Vue相关的文章,感兴趣的能够看回我以前写的。前端

做者文章总集

正文:吃下这条鱼🐟 - props初始化

initProps 是如何运行的: vue

1.normalizeProps: initProps 以前的规范化数据

normalizeProps的代码有点长,这里只列举通过规范化后的prop类型和结果vue-cli

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

源码分析以下:bash

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) } } 复制代码

划重点:微信

  • propsOptionsopts.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骚操做post

2.2.3: defineReactive: 最终处理

defineReactive(props, key, value)
复制代码

defineReactive是老熟人了,但这里要注意一点: 先前toggleObserving(false),关闭了观测的开关,因此defineReactive中调用 observe, 是一个无效调用。学习

此时到这里,能够得出一个结论

props 是经过 defineReactive定义的,此时虽然是响应式数据,但没有进行深度定义。

即,父组件传给子组件props后,子组件没必要再重复观测props

2.2.4 toggleObserving(true)`: 最后打开观测开关

toggleObserving(true)
复制代码

从新打开观测开关,避免影响后续代码执行。

感悟:相比分析源码,理解后写成博客更难。用文字讲清楚一件事可比敲代码难多了。

求一份深圳的内推

目前本人在准备跳槽,但愿各位大佬和HR小姐姐能够内推一份靠谱的深圳前端岗位!

  • 微信:huab119
  • 邮箱:454274033@qq.com
相关文章
相关标签/搜索