Vue中Props的实现机制
2021-9-24
| 2023-2-19
0  |  0 分钟
password
Created
Feb 19, 2023 03:36 PM
type
Post
status
Published
date
Sep 24, 2021
slug
summary
Vue中Props的实现机制
tags
Vue
Vue源码
category
源码
icon

初始化过程

Props的初始化是在setupComponent中进行的
 function setupComponent(instance, isSSR = false) {    const { props, children, shapeFlag } = instance.vnode    // 判断是否是一个有状态组件    const isStateful = shapeFlag & 4    // 初始化props    initProps(instance, props, isStateful, isSSR)    // 初始化插槽    initSlots(instance, children)    // 设置有状态的组件实例    const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : undefined    return setupResult  }

initProps 初始化props

  1. 设置props值
  1. 验证合法性(非正式环境)
  1. 转成响应式
 function initProps(instance, rawProps, isStateful, isSSR = false) {    const props = {}    const attrs = {}    def(attrs, InternalObjectKey, 1)    // 设置props的值    setFullProps(instance, rawProps, props, attrs)    // 验证props合法    if ((process.env.NODE_ENV !== 'production')) {      validateProps(props, instance.type)   }    if (isStateful) {      // 有状态组件, 则进行响应式处理      // 主要是针对子组件有使用watch来监听props改变的场景, 通过一层shallowReactive的封装保证了监听的可行性      instance.props = isSSR ? props : shallowReactive(props)   } else {      // 函数式组件处理      if (!instance.type.props) {        instance.props = attrs     } else {        instance.props = props     }   }    instance.attrs = attrs  }

setFullProps 设置props的值

  1. 标准化props的配置, 返回标准后的props, 其中处理了几种不同的写法, 然后递归处理了mixins的内容
  1. 原始props如果有定义, 则进行匹配赋值, 并进行赋值
  1. 对布尔类型和有默认值的prop, 进行转换和赋值
 function setFullProps(instance, rawProps, props, attrs) {    // 标准化props的配置    const [options. needCastKeys] = normalizePropsOptions(instance.type)    if (rawProps) {      for(const key in rawProps) {        const value = rawProps[key]        // 一些保留的props如ref key是不会传递的        if (isReservedProps(key)) {          continue       }        // kebab case to camel case        let camelKey        if (options && hasOwn(options, (camelKey = camelize(key)))) {          props[camelKey] = value       } else if (!isEmitListener(instance.type, key)) {          // 非事件派发相关, 且不再props中定义的普通属性用attrs保留, 如key等          attrs[key] = value       }     }      if (needCastKeys) {        // 需要做转换的props        const rawCurrentProps = toRow(props)        for(ley i = 0; i < needCastKeys.length; i++) {          const key = needCastKeys[i]          props[key] = resolvePropValue(options, rawCurrentProps, key, rawCurrentProps[key])       }     }   }  }

normalizePropsOptions 标准化props的配置

 function normalizePropsOptions(comp) {    // comp.__props用于缓存标准化的结果, 油画村则直接返回    if (comp.__props) {      return comp.__props   }    const raw = comp.props    const normalized = {}    const needCastKeys = []    // 处理mixins和extends这些props    let hasExtends = false    if (!shared.isFunction(comp)) {      const extendsProps = (raw) => {        const [props, keys] = normalizePropsOptions(raw)        shared.extend(normalized, props)        if (keys) needCastKeys.push(...keys)     }      if (comp.extends) {        hasExtends = true        extendProps(comp.extends)     }      if (comp.mixins) {        hasExtends = true        comp.mixins.forEach(extendProps)     }   }    if (!raw && !hasExtends) {      return (comp.__props = shared.EMPTY_ARR)   }    // 数组形式的props定义    // props: ['name', 'nick-name'] => props: { name: {}, nickName: {} }    if (shared.isArray(war)) {      for(let i = 0; i < raw.length; i++) {        const normalizedKey = shared.camelize(war[i])        if (validatePropName(normalizedKey)) {          normalized[normalizedKey] = shared.EMPTY_OBJ       }     }   } else if (raw) {      // 对象形式的定义      // props: { title: String, author: [String, Boolean] } =>      // props: { title: { type: String }, author: { type: [String, Boolean] } }      for (const key in raw) {        const normalizedKey = shared.camelize(key)        if (validatePropName(normalizedKey)) {          const opt = raw[key]          // 标准化prop的定义格式          const prop = (normalized[normalizedKey] = shared.isArray(opt || shared.isFunction(opt) ? { type: opt } : opt))          if (prop) {            const booleanIndex = getTypeIndex(Boolean, prop.type)            const stringIndex = getTypeIndex(String, prop.type)            prop[0] = booleanIndex > -1            // 如果prop的type属性中Boolean存在,String不存在或者Boolean在String前面,此时标记为需要转换成boolean类型            // 如: props: {name: String, intro: [Boolean, String]} =>            // 结果: props: { name: { 0: false, 1: true, type: String }, intro: { 0: true, 1: true, type: [Boolean, String] } }            prop[1] = stringIndex < 0 || booleanIndex < stringIndex            // 布尔类型和有默认值的prop都需要转换            if (booleanIndex > -1 || shared.hasOwn(prop, 'default')) {              needCastKeys.push(normalizedKey)           }         }       }     }   }    // normalized: 转换后的结果    // needCastKeys: 布尔类型, 有默认值的内容, 会存储到keys中, 做后续处理    const normalizedEntry = [normalized, needCastKeys]    // 相当于一次缓存, 后续不再执行normalize的逻辑了    comp.__props = normalizedEntry    return normalizedEntry  }

resolvePropValue 对needCastKeys中的每个key进行值处理

 function resolvePropValue(options, props, key, value) {    const opt = options[key]    if (opt !== null) {      const hasDefault = hasOwn(opt, 'default')      // 默认值处理      if (hasDefault && value === undefined) {        const defaultValue = opt.default        value = opt.type !== Function && isFunction(defaultValue) ? defaultValue() : defaultValue     }      // 布尔类型转换      if (opt[0]) {        if (!hasOwn(props.key) && !hasDefault) {          value = false          // 处理 [Boolean, String]的场景, 如果默认值为'', 此时默认值为true          // 如: test: { type: [Boolean, String], default: '' } => 默认test为true       } else if (opt[1] && (value === '' || value === hyphenate(key))) {          value = true       }     }   }  ]

更新过程

组件重新渲染会触发patch过程, 之后会遍历子节点来递归patch, 遇到组件节点就会触发updateComponent方法, 判断新旧子组件的vnode, 并会发shouldUpdateComponent方法来来确定是否需要更新子组件. 需要更新子组件时, 会将新的子组件赋值给instance.next, 然后触发instance.update()updateComponentPreRender中, 就会通过调用`updateProps 和updateSlots来更新propsslots`了

updateProps

 function updateProps(instance, rawProps, rawPrevProps, optimized) {    const { props, attrs, vnode: { patchFlag } } = instance    const rawCurrentProps = toRow(props)    // 同上, 标准化props配置    const [options] = normalizePropsOptions(instance.type)    // 16: FULL_PROPS    if ((optimized || patchFlag > 0) && !(patchFlag & 16)) {      // 8: PROPS      if (patchFlag & 8) {        // 只更新动态props节点        const propsToUpdate = instance.vnode.dynamicProps        for(let i = 0; i < propsToUpdate.length; i++) {          const key = propsToUpdate[i]          const value = rawProps[key]          if (options) {            if (hasOwn(attrs, key)) {              attrs[key] = value           } else {              const camelizedKey = camelize(key)              propspcamelizedKey[ = resolvePropValue(options, rawCurrentProps, camelizedKey, value)           }         } else {            attrs[key] = value         }       }     }   } else {      // 全量props更新      setFullProps(instance, rawProps, props, attrs)      // 因为新的props是动态的, 吧哪些不再新的props中单在旧props中的值设置为undefined      // ...   }  }
源码
  • Vue
  • Vue源码
  • JavaScript中的异步与EventLoopVue中Slot的实现机制
    目录