Vue3 源码解读系列(十四)——内置组件

内置组件

问题:内置组件为什么不需要引入?

答:内置组件默认是全局引入的。

<Teleport>

定义

/*** Teleport 组件定义*/
const Teleport = {__isTeleport: true,// 组件创建和更新process(nl, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals) {if (n1 == null) {// 创建逻辑} else {// 更新逻辑}},// 组件删除remove(vnode, { r: remove, o: { remove: hostRemove } }) {// 删除逻辑},move: moveTeleport,hydrate: hydrateTeleport
}

创建、更新

/*** 组件创建、更新*/
const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, optimized = false) => {// 如果存在新旧节点,且新旧节点类型不同,则销毁旧节点if (n1 && !isSameVNodeType(nl, n2)) { }// 不同节点采用对应的处理方式const { type, shapeFlag } = n2switch (type) {// 处理文本节点case Text:break// 处理注释节点case Comment:break// 处理静态节点case Static:break// 处理 Fragment 元素case Fragment:breakdefault:// 处理普通 DOM 元素if (shapeFlag & 1/* ELEMENT */) { }// 处理组件else if (shapeFlag & 6/* COMPONENT*/) { }// 处理 TELEPORTelse if (shapeFlag & 64/* TELEPORT */) {type.process(nl, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);}// 处理 SUSPENSEelse if (shapeFlag & 128/* SUSPENSE */) { }}
}/*** Teleport 组件创建、更新*/
function process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals) {const { mc: mountChildren, pc: patchChildren, pbc: patchBlockChildren, o: { insert, querySelector, createText, createComment } } = internalsconst disabled = isTeleportDisabled(n2.props)const { shapeFlag, children } = n2// 创建 Teleportif (n1 == null) {// 1、向主视图里插入节点(在非生产环境下插入注释节点,在生产环境下插入空白文本节点)const placeholder = (n2.el = (process.env.NODE_ENV !== 'production') ? createComment('teleport start') : createText(''))const mainAnchor = (n2.anchor = (process.env.NODE_ENV !== 'production') ? createComment('teleport end') : createText(''))insert(placeholder, container, anchor)insert(mainAnchor, container, anchor)// 2、获取目标移动的 DOM 节点const target = (n2.target = resolveTarget(n2.props, querySelector))const targetAnchor = (n2.targetAnchor = createText(''))// 存在则添加目标节点if (target) {insert(targetAnchor, target)}// 不存在则报警告else if ((process.env.NODE_ENV !== 'production')) {warn(/* ... */)}// 3、往目标节点插入 Teleport 组件的子节点const mount = (container, anchor) => {// 子节点为数组,遍历挂载子节点if (shapeFlag & 16/* ARRAY_CHILDREN */) {mountChildren(children, container, anchor, parentComponent, parentSuspense, isSVG, optimized)}}// disabled 情况就在原先的位置挂载if (disabled) {mount(container, mainAnchor)}// 挂载到 target 的位置else if (target) {mount(target, targetAnchor)}}// 更新 Teleportelse {// 1、更新子节点n2.el = nl.elconst mainAnchor = (n2.anchor = nl.anchor)const target = (n2.target = n1.target)const targetAnchor = (n2.targetAnchor = nl.targetAnchor)const wasDisabled = isTeleportDisabled(n1.props) // 之前是不是 disabled 状态const currentContainer = wasDisabled ? container : targetconst currentAnchor = wasDisabled ? mainAnchor : targetAnchor// 优化更新if (n2.dynamicChildren) {patchBlockChildren(n1.dynamicChildren, n2.dynamicChildren, currentContainer, parentComponent, parentSuspense, isSVG)if (n2.shapeFlag & 16/* ARRAY_CHILDREN */) {const oldChildren = n1.childrenconst children = n2.childrenfor (let i = 0; i < children.length; i++) {if (!children[i].el) {children[i].el = oldChildren[i].el}}}}// 组件全量比对else if (!optimized) {patchChildren(n1, n2, currentContainer, currentAnchor, parentComponent, parentSuspense, isSVG)}// 2、处理 disabled 属性以及 to 属性的变化情况// 新节点不显示,旧节点显示(把子节点移动回主容器)if (disabled) {if (!wasDisabled) {moveTeleport(n2, container, mainAnchor, internals, 1/* TOGGLE */)}}// 新节点展示else {// 目标元素改变if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {const nextTarget = (n2.target = resolveTarget(n2.props, querySelector))// 存在新的目标节点,则移动到新的目标元素if (nextTarget) {moveTeleport(n2, nextTarget, null, internals, 0/* TARGET_CHANGE */)}// 不存在新的目标节点,且在非生产环境下则报警告else if ((process.env.NODE_ENV !== 'production')) {warn(/* ... */)}}// 新节点展示,旧节点不展示(把子节点移动到目标元素位置)else if (wasDisabled) {moveTeleport(n2, target, targetAnchor, internals, 1/* ROGGLE */)}}}
}

移除

/*** Teleport 组件移除*/
function remove(vnode, { r: remove, o: { remove: hostRemove } }) {const { shapeFlag, children, anchor } = vnode// 移除主视图渲染的锚点(Teleport start 的注释节点)hostRemove(anchor)// 如果子节点为数组,则遍历 Teleport 节点进行移除if (shapeFlag & 16/* ARRAY_CHILDREN */) {for (let i = 0; i < children.length; i++) {remove(children[i])}}
}

<KeepAlive>

<KeepAlive> 实际是一个抽象节点,渲染的是它的第一个子节点。

定义

/*** KeepAlive 组件定义*/
const KeepAliveImpl = {name: `KeepAlive`,__isKeepAlive: true,inheritRef: true,// 用户自定义的配置props: {include: [String, RegExp, Array], // 包含exclude: [String, RegExp, Array], // 不包含max: [String, Number], // 最大缓存个数(默认不限制个数)},setup(props, { slots }) {const cache = new Map()const keys = new Set()let current = nullconst instance = getCurrentlnstance()const parentSuspense = instance.suspenseconst sharedContext = instance.ctxconst { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContextconst storageContainer = createElement('div')// 执行 deactivate 钩子函数sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {const instance = vnode.component// 将该节点添加到 DOM 中move(vnode, container, anchor, 0/* ENTER */, parentSuspense)patch(instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, optimized)queuePostRenderEffect(() => {instance.isDeactivated = falseif (instance.a) {invokeArrayFns(instance.a)}// 执行组件的 onBeforeMount 钩子函数const vnodeHook = vnode.props && vnode.props.onVnodeMountedif (vnodeHook) {invokeVNodeHook(vnodeHook, instance.parent, vnode)}}, parentSuspense)}// 执行 deactivate 钩子函数sharedContext.deactivate = (vnode) => {constinstance = vnode.component// 从 DOM 中移除该节点move(vnode, storageContainer, null, 1/* LEAVE */, parentSuspense)queuePostRenderEffect(() => {if (instance.da) {invokeArrayFns(instance.da)}// 执行组件的 onBeforeUnmount 钩子函数const vnodeHook = vnode.props && vnode.props.onVnodeUnmountedif (vnodeHook) {invokeVNodeHook(vnodeHook, instance.parent, vnode)}instance.isDeactivated = true}, parentSuspense)}function unmount(vnode) {resetShapeFlag(vnode)_unmount(vnode, instance, parentSuspense)}// 删除缓存(LRU 思想)function pruneCache(filter) {cache.forEach((vnode, key) => {const name = getName(vnode.type)if (name && (!filter || !filter(name))) {pruneCacheEntry(key)}})}function pruneCacheEntry(key) {const cached = cacheget(key)if (!current || cached.type !== current.type) {unmount(cached)} else if (current) {resetShapeFlag(current)}cache.delete(key)keys.delete(key)}// 监听用户提供的配置的变化watch(() => [props.include, props.exclude], ([include, exclude]) => {include && pruneCache(name => matches(include, name))exclude && !pruneCache(name => matches(exclude, name))})let pendingCacheKey = nullconst cacheSubtree = () => {if (pendingCacheKey != null) {cache.set(pendingCacheKey, instance.subTree)}}// 在执行 onBeforeMount、onBeforeUpdate 钩子函数时执行 cacheSubtree 来缓存组件onBeforeMount(cacheSubtree)onBeforeUpdate(cacheSubtree)// 在组件卸载时遍历缓存的组件进行卸载onBeforeUnmount(() => {cache.forEach(cached => {const { subTree, suspense } = instanceif (cached.type === subTree.type) {resetShapeFlag(subTree)const da = subTree.component.dada && queuePostRenderEffect(da, suspense)return}unmount(cached)})})// 返回组件渲染函数return () => {pendingCacheKey = nullif (!slots.default) return null// 1、组件渲染// 获取子节点(<KeepAlive>包裹的组件)const children = slots.default()let vnode = children[0]// 因为 <KeepAlive> 只能有一个子组件,因此如果子组件的数量大于 1,则报警告if (children.length > 1) {if ((process.env.NODE_ENV !== 'production')) {warn(/* ... */)}current = nullreturn children} else if (!isVNode(vnode) || !(vnode.shapeFlag & 4/* STATEFUL_COMPONENT */)) {current = nullreturn vnode}const comp = vnode.typeconst name = getName(comp)const { include, exclude, max } = propsif ((include && (!name || !matches(include, name))) || (exclude && name && matches(exclude, name))) {return (current = vnode)}// 2、缓存 vnodeconst key = vnode.key == null ? comp : vnode.keyconst cachedVNode = cache.get(key) // 缓存的 vnodeif (vnode.el) {vnode = cloneVNode(vnode)}pendingCacheKey = key// 存在缓存的 vnodeif (cachedVNode) {vnode.el = cachedVNode.el // vnode 对应的 DOMvnode.component = cachedVNode.component // vnode 对应的组件实例// 更新 vnode 的 shapeFlag,避免 vnode 节点作为新节点被挂载vnode.shapeFlag |= 512/* COMPONENT_KEPT_ALIVE */// 让这个 key 始终新鲜keys.delete(key)keys.add(key)}// 不存在缓存的 vnodeelse {keys.add(key)// 删除最久不用的 key,符合 LRU 思想if (max && keys.size > parselnt(max, 10)) {pruneCacheEntry(keys.values().next().value)}}// 避免 vnode 被卸载vnode.shapeFlag |= 256/* COMPONENT_SHOULD_KEEP_ALIVE */current = vnodereturn vnode}}
}/*** 缓存子树*/
const cacheSubtree = () => {// pendingCacheKey 是在 <KeppAlive> 的 render 函数中才会被赋值,所以 <KeppAlive> 在首次进入 onBeforeMount 钩子函数时不会缓存if (pendingCacheKey != null) {cache.set(pendingCacheKey, instance.subTree)}
}

问题:<KeepAlive> 组件是如何注册 activateddeactivated 钩子函数?

答:activated 钩子函数在组件 beforeMount 钩子函数时执行;deactivated 钩子函数在组件 beforeUnmount 钩子函数时执行。

创建

/*** KeepAlive 组件的创建*/
const processComponent = (nl, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {if (n1 == null) {// 处理 KeepAlive 组件if (n2.shapeFlag & 512/* COMPONENT_KEPT ALIVE */) {parentComponent.ctx.activate(n2, container, anchor, isSVG, optimized)}// 挂载组件else {mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)}}// 更新组件else { }
}/*** KeepAlive 缓存组件的创建* 实现:<KeepAlive> 包裹的子组件在其渲染后、下一次更新前会被缓存,缓存后的子组件在下一次渲染的时候直接从缓存中拿到子树 vnode 以及其对应的 DOM 元素直接渲染*/
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {const instance = vnode.componentmove(vnode, container, anchor, 0/* ENTER */, parentSuspense)patch(instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, optimized)queuePostRenderEffect(() => {instance.isDeactivated = falseif (instance.a) {invokeArrayFns(instance.a)}const vnodeHook = vnode.props && vnode.props.onVnodeMountedif (vnodeHook) {invokeVNodeHook(vnodeHook, instance.parent, vnode)}}, parentSuspense)
}

卸载

/*** KeepAlive 组件的卸载*/
const unmount = (vnode, parentComponent, parentSuspense, doRemove = false) => {const { shapeFlag } = vnodeif (shapeFlag & 256/* COMPONENT_SHOULD_KEEP ALIVE */) {parentComponent.ctx.deactivate(vnode)return}// 卸载组件
}/*** KeepAlive 在卸载时执行 onBeforeUnmount 钩子函数*/
onBeforeUnmount(() => {cache.forEach(cached => {const [subTree, suspense] = instanceif (cached.type === subTree.type) {resetShapeFlag(subTree)const da = subTree.component.dada && queuePostRenderEffect(da, suspense)return}unmount(cached)})
})

<Transition>

<Transition> 渲染的是第一个子元素节点。

核心思想:当 <Transition> 包裹的元素插入、删除时,在适当的时机插入这些 CSS 样式。

渲染流程:

  1. 组件的渲染
  2. 钩子函数的执行
  3. 模式的应用

定义

/*** Transition 组件的定义*/
const Transition = (props, { slots }) => h(BaseTransition, (props), slots)/*** Transition 组件的基础配置*/
const BaseTransition = {name: `BaseTransition`,props: {mode: String,appear: Boolean,persisted: Boolean,// enteronBeforeEnter: TransitionHookValidator,onEnter: TransitionHookValidator,onAfterEnter: TransitionHookValidator,onEnterCancelled: TransitionHookValidator,// leaveonBeforeLeave: TransitionHookValidator,onLeave: TransitionHookValidator,onAfterLeave: TransitionHookValidator,onLeaveCancelled: TransitionHookValidator,// appearonBeforeAppear: TransitionHookValidator,onAppear: TransitionHookValidator,onAfterAppear: TransitionHookValidator,onAppearCancelled: TransitionHookValidator,},setup(props, { slots }) {const instance = getCurrentInstance()const state = useTransitionState()let prevTransitionKeyreturn () => {const children = slots.default && getTransitionRawChildren(slots.default(), true)if (!children || !children.length) return// Transition 组件只允许一个子元素节点,多个报警告,提示使用 TransitionGroup 组件if ((process.env.NODE_ENV !== 'production') && children.length > 1) {warn(/* ... */)}// 不需要追踪响应式,所以改成原始值,提升性能const rawProps = toRaw(props)const { mode } = rawProps// 检查 mode 是否合法if ((process.env.NODE_ENV !== 'production') && mode && !['in-out', 'out-in', 'default'].includes(mode)) {warn(/* ... */)}// 获取第一个子元素节点const child = children[0]if (state.isLeaving) return emptyPlaceholder(child)// 处理<transition><keep-alive/></transition>的情况const innerChild = getKeepAliveChild(child)if (!innerChild) {return emptyPlaceholder(child)}// 通过 resolveTransitionHooks 定义组件创建和删除阶段的钩子函数对象const enterHooks = resolveTransitionHooks(innerChild, rawProps, state, instance)// 通过 setTransitionHooks 把钩子函数对象设置到 vnode.transition 上setTransitionHooks(innerChild, enterHooks)const oldChild = instance.subTreeconst oldInnerChild = oldChild && getKeepAliveChild(oldChild)let transitionKeyChanged = falseconst { getTransitionKey } = innerChild.typeif (getTransitionKey) {const key = getTransitionKey()if (prevTransitionKey === undefined) {prevTransitionKey = key} else if (key !== prevTransitionKey) {prevTransitionKey = keytransitionKeyChanged = true}}if (oldInnerChild && oldInnerChild.type !== Comment && (!isSameVNodeType(innerChild, oldlnnerChild) || transitionKeyChanged)) {const leavingHooks = resolveTransitionHooks(oldInnerChild, rawProps, state, instance)// 更新旧树的钩子函数setTransitionHooks(oldInnerChild, leavingHooks)// 在两个视图之间切换if (mode === 'out-in') {state.isLeaving = true// 当离开过渡结束后,再重新渲染组件leavingHooks.afterLeave = () => {state.isLeaving = falseinstance.update()}// 返回空的占位符节点return emptyPlaceholder(children)} else if (mode === 'in-out') {leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => {const leavingVNodesCache = getLeavingNodesForType(state, oldInnerChild)leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild// early removal callbackel._leaveCb = () => {earlyRemove()el._leaveCb = undefineddelete enterHooks.delayedLeave}enterHooks.delayedLeave = delayedLeave}}}return child}}
}

渲染

/*** <Transition> 组件的渲染*/
function resolveTransitionHooks(vnode, props, state, instance) {const { appear, mode, persisted = false, onBeforeEnter, onEnter, onAfterEnter, onEnterCancelled, onBeforeLeave, onLeave, onAfterLeave, onLeaveCancelled, onBeforeAppear, onAppear, onAfterAppear, onAppearCancelled } = propsconst key = String(vnode.key)const leavingVNodesCache = getLeavingNodesForType(state, vnode)const callHook = (hook, args) => {hook && callWithAsyncErrorHandling(hook, instance, 9/* TRANSITION_HOOK */, args)}const hooks = {mode,persisted,// 在 patch 阶段的 mountElement 函数中,在插入元素节点前且存在过渡条件下执行beforeEnter(el) {// 根据 appear 的值和 DOM 是否挂载判断执行 onBefore 还是 onBeforeEnterlet hook = onBeforeEnterif (!state.isMounted) {if (appear) {hook = onBeforeAppear || onBeforeEnter} else return}// el._leaveCb 存在则执行 leave 钩子函数if (el._leaveCb) {el.leaveCb(true/* cancelled */)}const leavingVNode = leavingVNodesCache[key]if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el.leaveCb) {leavingVNode.el.leaveCb()}callHook(hook, [el])},enter(el) {let hook = onEnterlet afterHook = onAfterEnterlet cancelHook = onEnterCancelledif (!state.isMounted) {if (appear) {hook = onAppear || onEnterafterHook = onAfterAppear || onAfterEntercancelHook = onAppearCancelled || onEnterCancelled} else return}let called = falseconst done = (el._enterCb = (cancelled) => {if (called) returncalled = trueif (cancelled) {callHook(cancelHook, [el])} else {callHook(afterHook, [el])}if (hooks.delayedLeave) {hooks.delayedLeave()}el.enterCb = undefined})if (hook) {hook(el, done)if (hook.length <= 1) {done()}} else {done()}},leave(el, remove) {const key = String(vnode.key)if (el._enterCb) {el._enterCb(true/* cancelled */)}if (stateisUnmounting) {return remove()}callHook(onBeforeLeave, [el])let called = falseconst done = (el._leaveCb = (cancelled) => {if (called) returncalled = trueremove()if (cancelled) {callHook(onLeaveCancelled, [el])} else {callHook(onAfterLeave, [el])}el._leaveCb = undefinedif (leavingVNodesCache[key] === vnode) {delete leavingVNodesCache[key]}})leavingVNodesCache[key] = vnodeif (onLeave) {onLeave(el.done)if (onLeave.length <= 1) done()} else {done()}},clone(vnode) {return resolveTransitionHooks(vnode, props, state, instance)}}return hooks
}

钩子函数的执行

/*** 钩子函数的执行 - 对传递的 props 进行封装*/
function resolveTransitionProps(rawProps) {let {name = 'v',type, css = true,duration,enterFromClass = `${name}-enter-from`,enterActiveClass = `${name}-enter-active`,enterToClass = `${name}-enter-to`,appearFromClass = enterFromClass,appearActiveClass = enterActiveClass,appearToClass = enterToClass,leaveFromClass = `${name}-leave-from`,leaveActiveClass = `${name}-leave-active`,leaveToClass = `${name}-leave-to`} = rawPropsconst baseProps = {}for (const key in rawProps) {if (!(key in DOMTransitionPropsValidators)) {baseProps[key] = rawProps[key]}}if (!css) return basePropsconst durations = normalizeDuration(duration)const enterDuration = durations && durations[0]const leaveDuration = durations && durations[1]const {onBeforeEnter,onEnter,onEnterCancelled,onLeave,onLeaveCancelled,onBeforeAppear = onBeforeEnter,onAppear = onEnter,onAppearCancelled = onEnterCancelled} = baseProps// 进入动画结束后执行,移除 DOM 元素的 enterToClass 和 enterActiveClass,然后执行 done 函数进入 onAfterEnter 钩子函数const finishEnter = (el, isAppear, done) => {removeTransitionClass(el, isAppear ? appearToClass : enterToClass)removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass)done && done()}// 离开动画结束后执行,移除 DOM 元素的 leaveToClass 和 leaveActiveClassconst finishLeave = (el, done) => {removeTransitionClass(el, leaveToClass)removeTransitionClass(el, leaveActiveClass)done && done()}// 制作 enter 钩子const makeEnterHook = (isAppear) => {return (el, done) => {const hook = isAppear ? onAppear : onEnterconst resolve = () => finishEnter(el, isAppear, done)hook && hook(el, resolve)nextFrame(() => {removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass)// 添加了 enterToClass 后,浏览器就进入过渡动画了addTransitionClass(el, isAppear ? appearToClass : enterToClass)if (!(hook && hook.length > 1)) {if (enterDuration) {setTimeout(resolve, enterDuration)} else {whenTransitionEnds(el, type, resolve)}}})}}return extend(baseProps, {onBeforeEnter(el) {// 执行 onBeforeEnter 钩子函数onBeforeEnter && onBeforeEnter(el)// 给 DOM 元素添加 enterActiveClass 和 enterFromClassaddTransitionClass(el, enterActiveClass)addTransitionClass(el, enterFromClass)},onBeforeAppear(el) {onBeforeAppear && onBeforeAppear(el)addTransitionClass(el, appearActiveClass)addTransitionClass(el, appearFromClass)},onEnter: makeEnterHook(false),onAppear: makeEnterHook(true),onLeave(el, done) {const resolve = () => finishLeave(el, done)addTransitionClass(el, leaveActiveClass)addTransitionClass(el, leaveFromClass)nextFrame(() => {removeTransitionClass(el, leaveFromClass)// 当添加 leaveToClass 时,浏览器就开始执行离开过渡动画了addTransitionClass(el, leaveToClass)if (!(onLeave && onLeave.length > 1)) {if (leaveDuration) {setTimeout(resolve, leaveDuration)} else {whenTransitionEnds(el, type, resolve)}}})onLeave && onLeave(el, resolve)},onEnterCancelled(el) {finishEnter(el, false)onEnterCancelled && onEnterCancelled(el)},onAppearCancelled(el) {finishEnter(el, true)onAppearCancelled && onAppearCancelled(el)},onLeaveCancelled(el) {finishLeave(el)onLeaveCancelled && onLeaveCancelled(el)}})
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/151668.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

echarts 横向柱状图示例

该示例有如下几个特点&#xff1a; ①实现tooltip自定义样式&#xff08;echarts 实现tooltip提示框样式自定义-CSDN博客&#xff09; ②实现数据过多时滚动展示&#xff08;echarts 数据过多时展示滚动条-CSDN博客&#xff09; ③柱状图首尾展示文字&#xff0c;文字内容嵌入图…

SpringCloud相关

文章目录 Gateway动态路由灰度策略 FeignRibbon SpringCloud五大组件分别对应&#xff08;1&#xff09;服务注册与发现&#xff08;2&#xff09;客服端负载均衡&#xff08;3&#xff09;断路器&#xff08;4&#xff09;服务网关&#xff08;5&#xff09;分布式配置 Gatewa…

力扣刷题第二十六天--二叉树

前言 昨天看总决赛&#xff0c;差距太大&#xff0c;看的没意思&#xff0c;真的是一点变通没有啊。难受&#xff0c;没有写题的状态了。大概率是最后一次看比赛了&#xff0c;青春已复过&#xff0c;白日忽相催。召唤师要和生活对线了。英雄们的语音&#xff0c;台词&#xf…

【LeetCode】1773. 统计匹配检索规则的物品数量

1773. 统计匹配检索规则的物品数量 难度&#xff1a;简单 题目 给你一个数组 items &#xff0c;其中 items[i] [typei, colori, namei] &#xff0c;描述第 i 件物品的类型、颜色以及名称。 另给你一条由两个字符串 ruleKey 和 ruleValue 表示的检索规则。 如果第 i 件物…

thonny的汉字编码是UTF-8,如何才能转为GB2312?

>>> chinese_str "你" >>> gb2312_str chinese_str.encode(GB2312) >>> print(gb2312_str) b\xe4\xbd\xa0 >>> print(chinese_str.encode(GB2312)) b\xe4\xbd\xa0 一个晚上了&#xff0c;就是找不到方法。好在知道问题在哪里…

Android Studio常见问题

Run一直是上次的apk 内存占用太大&#xff0c;导致闪退

R语言——taxize(第二部分)

taxize&#xff08;第二部分&#xff09; 3. taxize 文档中译3.10. classification&#xff08;根据类群ID检索分类阶元层级&#xff09;示例1&#xff1a;传递单个ID值示例2&#xff1a;传递多个ID值示例3&#xff1a;传递单个名称示例4&#xff1a;传递多个名称示例5&#xf…

【C++】传递‘类非静态成员函数’用作回调函数

在C语言中&#xff0c;传递函数指针是非常常见的操作。 在C语言中&#xff0c;使用C语言一致的方法传递全局函数指针&#xff0c;或者传递静态函数指针也很常见。 不过如果遇到想传递非静态成员函数时&#xff0c;可以参考以下示例代码。 #ifndef _WORKER_HPP_ #define _WOR…

详解FreeRTOS:二值信号量和计数信号量(高级篇—2)

目录 1、二值信号量 1.1、二值信号量运行机制 1.2、创建二值信号量 1

2023.11.18 -自用hadoop高可用环境搭建命令

启动hadoop高可用环境 # 1.先恢复快照到高可用环境 # 2.三台服务器启动zookeeper服务 [rootnode1 ~]# zkServer.sh start [rootnode2 ~]# zkServer.sh start [rootnode3 ~]# zkServer.sh start 查看服务状态: [rootnode]# zkServer.sh status 关闭zk服务的命令是: [rootnode]# …

C#入门(5):数组、一维数组,二维数组、交错数组、数组复制

在C#中&#xff0c;数组是一种用于存储相同类型元素的数据结构。数组提供了一种有序、索引访问的方式&#xff0c;使得可以通过索引快速访问和修改数组中的元素。在C#中&#xff0c;主要有一维数组和二维数组两种类型。 一维数组&#xff08;Single-Dimensional Array&#xf…

SpringCloud -Token传递之Feign

目录 方法一 RequestHeader 方法二 使用Feign的Interceptor 步骤一 实现RequestInterceptor接口 步骤二&#xff1a;配置Feign 通常微服务对于用户认证信息解析有两种方案 在 gateway 就解析用户的 token 然后路由的时候把 userId 等相关信息添加到 header 中传递下去。在…

YOLOv5 配置C2模块构造新模型

&#x1f368; 本文为[&#x1f517;365天深度学习训练营学习记录博客 &#x1f366; 参考文章&#xff1a;365天深度学习训练营 &#x1f356; 原作者&#xff1a;[K同学啊] &#x1f680; 文章来源&#xff1a;[K同学的学习圈子](https://www.yuque.com/mingtian-fkmxf/zxwb4…

【Linux】Linux下的基础IO

❤️前言 大家好&#xff01;今天这篇博客和大家聊一聊关于Linux下的基础IO。 正文 在阅读本篇博客之前&#xff0c;请大家先回顾一下C语言文件操作的一些方法&#xff0c;这里可以看看我之前记录的一些内容&#xff1a; 【C语言】C语言成长之路之文件操作_MO_lion的博客-CSD…

mysql使用--表达式和函数

1.表达式 如&#xff1a;11&#xff0c;一般包含操作数&#xff0c;运算符。 _1.操作数 MYSQL中最常用的操作数有以下几种 (1).常数 (2).列名&#xff0c;针对某个具体的表&#xff0c;它的列名可被当作表达式的一部分 (3).函数调用 一个函数用于完成某个特定的功能。比如NOW()…

【PyQt小知识 - 3】: QComboBox下拉框内容的设置和更新、默认值的设置、值和下标的获取

QComboBox 内容的设置和更新 from PyQt5.QtWidgets import * import sysapp QApplication(sys.argv)mainwindow QMainWindow() mainwindow.resize(200, 200) # 设置下拉框 comboBox QComboBox(mainwindow) comboBox.addItems([上, 中, 下])button QPushButton(更新, main…

Colab跑项目

这里写目录标题 Colab文件目录路径显示更改colab当前工作文件夹Colab挂载谷歌云盘colab使用命令&#xff08;从这开始看&#xff0c;前面no zuo no die)最紧要&#xff0c;首先&#xff0c;修改笔记本设置使用启用gpu![在这里插入图片描述](https://img-blog.csdnimg.cn/591a6c…

NAS层协议栈学习笔记

NAS(Non-Access Stratum)是无线网络中非接入层及包括移动性管理(MM)和会话管理(SM)协议 &#xff0c;在5G(NR)系统中连接管理(Connection Management)用于建立和释放UE与AMF之间的控制面(CP)信令连接。 5G中移动性管理是通过NAS信令在UE与核心网之间进行交互的&#xff0c;连接…

SpringBoot——静态资源及原理

优质博文&#xff1a;IT-BLOG-CN 一、使用 SpringBoot 的步骤 【1】创建SpringBoot应用&#xff0c;选中自己需要的模块。 【2】SpringBoot已经默认将这些场景配置好&#xff0c;只需要在配置文件中指定少量配置就可以运行起来。 【3】编写业务逻辑代码。 二、自动配置原理 …

面试鸭 - 专注于面试刷题的网站

网上面试题有很多&#xff0c;但此套面试题真实、原创、高频&#xff0c;全网最强。 题目涵盖大中小公司&#xff0c;真实靠谱&#xff0c;有频率和难度的标记&#xff0c;助你成为Offer收割机。 面试鸭地址&#xff1a;https://mianshiya.skyofit.com/ 本套题是我原创&…