牡丹江做网站建设,平台推广话术,网页设计制作源代码,组建网站 多少钱props
props 的作用#xff1a;允许组件的使用者在外部传递#xff0c;实现各种各样的功能。
初始化 props
初始化 Props 主要做了 3 件事#xff1a;
设置 props 的值验证 props 合法把 props 变为响应式并且添加到组件实例 instance 上
/*** 初始化组件*/
function s…props
props 的作用允许组件的使用者在外部传递实现各种各样的功能。
初始化 props
初始化 Props 主要做了 3 件事
设置 props 的值验证 props 合法把 props 变为响应式并且添加到组件实例 instance 上
/*** 初始化组件*/
function setupComponent(instance, isSSR false) {const { props, children, shapeFlag } instance.vnode// 判断是否是一个有状态的组件const isStateful shapeFlag 4// 初始化 propsinitProps(instance, props, isStateful, isSSR)// 初始化插槽initSlots(instance, children)// 设置有状态的组件实例const setupResult isStateful ? setupStatefulComponent(instance, isSSR) : undefinedreturn setupResult
}/*** 初始化 Props* initProps 主要做了 3 件事* 1、设置 props 的值* 2、验证 props 合法* 3、把 props 变为响应式并且添加到组件实例 instance 上*/
function initProps(instance, rawProps, isStateful, isSSR false) {const props {}const attrs {}def(attrs, InternalObjectKey, 1)// 1、设置 props 的值setFullProps(instance, rawProps, props, attrs)// 2、验证 props 合法非生产环境下执行if ((process.env.NODE_ENV ! production)) {validateProps(props, instance.type)}// 3、把 props 变为响应式并且添加到组件实例 instance 上// 有状态组件响应式处理有状态组件是通过对象方式定义的组件if (isStateful) {instance.props isSSR ? props : shallowReactive(props)}// 函数式组件处理else {if (!instance.type.props) {instance.props attrs} else {instance.props props}}// 普通属性赋值instance.attrs attrs
}设置 props 的值
/*** 设置 props 的值 - 对 props 求值然后把求得的值赋值给 props 对象和 attrs 对象中* param {Object} instance - 组件实例* param {Object} rawProps - 原始的 props 值即创建 vnode 过程中传递的 props* param {Object} props - 解析后的 props 数据* param {Object} attrs - 解析后的普通属性数据* setFullProps 主要做了 3 件事* 1、标准化 props 的配置* 2、遍历 props 数据求值* 3、对需要做转换的 props 求值*/
function setFullProps(instance, rawProps, props, attrs) {// 1、标准化 props 的配置const [options, needCastKeys] normalizePropsOptions(instance.type)// 2、遍历 props 数据求值if (rawProps) {for (const key in rawProps) {const value rawProps[key]// 一些保留的 prop 比如 ref、 key 是不会传递的if (isReservedProp(key)) continue// 连字符形式的 props 也转成驼峰形式let camelKeyif (options hasOwn(options, (camelKey camelize(key)))) {props[camelKey] value}// 非事件派发相关的且不在 props 中定义的普通属性用 attrs 保留else if (!isEmitListener(instance.type, key)) {attrs[key] value}}}// 3、对需要做转换的 props 求值if (needCastKeys) {const rawCurrentProps toRaw(props) // 需要做转换的 propsfor (let i 0; i needCastKeys.length; i) {const key needCastKeys[i]props[key] resolvePropValue(options, rawCurrentProps, key, rawCurrentProps[key])}}
}标准化 props 的配置
/*** 标准化 props 的配置* description 所有形式的 props 最终都会被标准化为对象形式*/
function normalizePropsOptions(comp) {// comp.__props 用于缓存标准化的结果有缓存则直接返回if (comp.__props) {return comp.__props}const raw comp.propsconst normalized {} // 标准化后的 props 定义const needCastKeys [] // 需要转换的 props key// 处理 mixins和 extends 两个特殊的 prop因为它们的作用是扩展组件的定义所以需要对它们定义中的 props 递归执行 normalizePropsOptionslet hasExtends falseif (!shared.isFunction(comp)) {const extendProps (raw) {const [props, keys] normalizePropsOptions(raw)shared.extend(normalized, props)if (keys) {needCastKeys.push(...keys)}}if (comp.extends) {hasExtends trueextendProps(comp.extends)}if (comp.mixins) {hasExtends truecomp.mixins.forEach(extendProps)}}if (!raw !hasExtends) {return (comp._props shared.EMPTY_ARR)}// 处理数组形式的 props 定义如果 props 以数组的形式定义那么每一项一定要是一个字符串if (shared.isArray(raw)) {for (let i 0; i rawlength; i) {// 非字符串项报警告if (!shared.isString(raw[i])) {warn(/* ... */)}// 把字符串变为驼峰形式作为 key并为每一个 key 创建一个空对象作为值const normalizedKey shared.camelize(raw[i])if (validatePropName(normalizedKey)) {normalized[normalizedKey] shared.EMPTY_OBJ}}}// 处理对象形式的 props 定义else if (raw) {if (!shared.isObject(raw)) {warn(/* ... */)}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 booleanlndex getTypelndex(Boolean, prop.type)const stringIndex getTypelndex(String, prop.type)prop[0/* shouldCast */] booleanindex -1prop[1 /* shouldCastTrue */] stringIndex 0 || booleanlndex stringIndex//布尔类型和有默认值的 prop 都需要转换if (booleanIndex -1 || shared.hasOwn(prop, default)) {needCastKeys.push(normalizedKey)}}}}}const normalizedEntry [normalized, needCastKeys]// 缓存标准化结果comp._props normalizedEntry// 返回标准化结果return normalizedEntry
}遍历 props 数据求值
// 无额外的函数对需要做转换的 props 求值
/*** 处理 Prop 的值 - 对 Props 求值然后把求得的值赋给 Props 对象的 attrs 对象中*/
function resolvePropValue(options, props, key, value) {const opt options[key]// 针对两种情况做转换if (opt ! null) {const hasDefault hasOwn(opt, default)// 默认值转换当在 prop 中定义了默认值且父组件没有传递 prop 时才取默认值if (hasDefault value undefined) {const defaultValue opt.defaultvalue opt.type ! Function isFunction(defaultValue) ? defaultValue() : defaultValue}// 布尔类型转换if (opt[0 /* shouldCast */]) {// 如果在 prop 中定义了 Boolean 类型且父组件没有传递 prop且没有定义默认值时直接转换为 falseif (!hasOwn(props, key) !hasDefault) {value false}// 其他情况转换为 trueelse if (opt[1 /* shouldCastTrue */] (value || value hyphenate(key))) {value true}}}return value
}验证 props 合法
/*** 验证 props 是否合法*/
function validateProps(props, comp) {const rawValues toRaw(props)const options normalizePropsOptions(comp)[0]// 对标准化后的 props 进行遍历拿到每一个配置 opt然后执行 validateProp 验证for (const key in options) {let opt options[key]if (opt null) continuevalidateProp(key, rawValues[key], opt, !hasOwn(rawValues, key))}
}/*** 验证 prop 是否合法*/
function validateProp(name, value, prop, isAbsent) {const { type, required, validator } prop// 如果配置了 required 但是没有传值则报警告if (required isAbsent) {warn(/* ... */)return}// 虽然没有值但也没有配置 required直接返回if (value null !prop.required) return// 类型检测只要满足其中一种类型就是合法的否则报警告if (type ! null type ! true) {let isValid falseconst types isArray(type) ? type : [type]const expectedTypes []}// 只要指定的类型之一匹配值就有效for (let i 0; i types.length !isValid; i) {const { valid, expectedType } assertType(value, types[i])expectedTypes.push(expectedType || )isValid valid}if (!isValid) {warn(/* ... */)return}// 如果配置了自定义校验器但是不满足校验器的规则则报警告if (validator !validator(value)) {warn(/* ... */)}
}把 props 变为响应式并且添加到组件实例 instance 上
// 无额外函数问题 为什么 instance.props 要变成响应式 因为希望在子组件中监听 props 的变化而进行一些操作。 为什么用 shallowReactive API 因为 props 在更新过程中只会修改最外层属性shallowReactive 就足够了。 更新 props
/*** 更新组件*/
const updateComponent (nl, n2, parentComponent, optimized) {const instance (n2.component nl.component)// 根据新旧子组件 vnode 判断是否需要更新子组件if (shouldUpdateComponent(n1, n2, parentComponent, optimized)) {instance.next n2 // 新的子组件 vnode 赋值给 instance.next// 子组件也可能因为数据变化被添加到更新队列里了移除它们防止对一个子组件重复更新 invalidateJob(instance.update)// 执行子组件的副作用渲染函数instance.update()}// 不需要更新只复制属性else {n2.component n1.componentn2.el n1.el}
}/*** 更新组件 - 初始化渲染副作用*/
const setupRenderEffect (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) {// 创建响应式的副作用渲染函数instance.update effect(function componentEffect() {// 渲染组件if (!instance.isMounted) {// ...}// 更新组件else {let { next, vnode } instance // next 表示新的组件 vnode// 更新组件 vnode 节点信息if (next) {updateComponentPreRender(instance, next, optimized)} else {next vnode}// 渲染新的子树 vnodeconst nextTree renderComponentRoot(instance)// 缓存旧的子树 vnodeconst prevTree instance.subTree// 更新子树 vnodeinstance.subTree nextTree// 组件更新核心逻辑根据新旧子树 vnode 做 patchpatch(prevTree, nextTree,hostParentNode(prevTree.el), // 如果在 teleport 组件中父节点可能已经改变所以容器直接找旧树 DOM 元素的父节点getNextHostNode(prevTree), // 考节点在 fragment 的情况可能改变所以直接找旧树 DOM 元素的下一个节点instance, parentSuspense, isSVG)// 缓存更新后的 DOM 节点next.el nextTree.el}}, prodEffectOptions)
}/*** 更新组件 - 在渲染前做一些操作*/
const updateComponentPreRender (instance, nextVNode, optimized) {nextVNode.component instanceconst prevProps instance.vnode.propsinstance.vnode nextVNodeinstance.next nullupdateProps(instance, nextVNode.props, prevProps, optimized)updateSlots(instance, nextVNode.children)
}/*** 更新 Props - 把父组件渲染时求得的 props 新值更新到子组件实例的 instance.props 中*/
function updateProps(instance, rawProps, rawPrevProps, optimized) {const { props, attrs, vnode: { patchFlag } } instanceconst rawCurrentProps toRaw(props)const [options] normalizePropsOptions(instance.type)if ((optimized || patchFlag 0) !(patchFlag 16/* FULL_PROPS */)) {// 只更新动态 props 节点if (patchFlag 8/* PROPS */) {const propsToUpdate instance.vnode.dynamicPropsfor (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)props[camelizedKey] resolvePropValue(options, rawCurrentProps, camelizedKey, value)}} else {attrs[key] value}}}}else {// 全量 props 更新setFullProps(instance, rawProps, props, attrs)// 因为新的 props 是动态的把那些不在新的 props 中但存在于旧的 props 中的值设置为 undefinedlet kebabKeyfor (const key in rawCurrentProps) {if (!rawProps || (!hasOwn(rawProps, key) ((kebabKey hyphenate(key)) key || !hasOwn(rawProps, kebabKey)))) {if (options) {if (rawPrevProps (rawPrevProps[key] ! undefined || rawPrevProps[kebabKey] ! undefined)) {props[key] resolvePropValue(options, rawProps || EMPTY_OBJ, key, undefined)}} else {delete props[key]}}}}if ((process.env.NODE_ENV production) rawProps) {validateProps(props, instance.type)}
}emit
/*** 自定义事件的派发* param {Object} instance - 执行 $emit 的组件实例* param {string} event - 事件名称* param {...any} args - 事件传递的参数*/
function emit(instance, event, ...args) {const props instance.vnode.props || EMPTY_OBJ// 获取事件名称 - 把传递的 event 首字母大写然后在前面加上 onlet handlerName on${capitalize(event)}// 根据事件名称在 props 中找到对应的回调函数let handler props[handlerName]// 如果没有对应的回调函数 且 事件是以 update: 开头则尝试将事件名改为 - 形式再查找对应的回调函数if (!handler event.startsWith(update:)) {handlerName on${capitalize(hyphenate(event))}handler props[handlerName]}// 如果有对应的回调函数则执行if (handler) {callWithAsyncErrorHandling(handler, instance, 6/* COMPONENT_EVENT_HANDLER */, args)}
}