当前位置: 首页 > news >正文

花瓣按照哪个网站做的/西安网站建设推广

花瓣按照哪个网站做的,西安网站建设推广,互动网站制作,龙港做网站目录 备注响应式数据创建ref 和 reactive 核心 作用第一轮的 依赖收集 发生时机setup 阶段 去更改了 响应式数据 会发生依赖收集吗 派发更新派发更新是什么时候 触发的?扩展: setup阶段 响应式数据被修改 会触发组件更新吗 vue 是如何根据派发更新来触发…

目录

    • 备注
    • 响应式数据创建
      • ref 和 reactive 核心 作用
      • 第一轮的 依赖收集 发生时机
      • setup 阶段 去更改了 响应式数据 会发生依赖收集吗
    • 派发更新
      • 派发更新是什么时候 触发的?
        • 扩展: setup阶段 响应式数据被修改 会触发组件更新吗
      • vue 是如何根据派发更新来触发组件的更新渲染的?
      • 组件副作用函数执行时 有多个响应式数据更新 是如何保证组件只会触发一次更新渲染的?
      • 多余的组件依赖 是如何被清理掉的?

备注

本文中 只会涉及到 setup 主流程的 更新 , watch computed 等后面再分析
带着问题,去源码寻找答案。
响应式数据是什么时候创建的?
什么时候进行的依赖收集?
响应式数据更新后 怎么做的派发更新?

本文中 用到的测试用例
一定得 debug 跟着调试看 不然很容易绕晕

 it('should support runtime template compilation', async () => {const container = document.createElement('div')container.classList.add('app')const child = defineComponent({template: `<div><p>{{age}}---{{status?add:'hihi'}}</p></div>`,props:{age:{type: Number,default:20}},data(){return {add: '12',status: true}},mounted() {this.status = falsethis.add = '24'},})const App = {components:{child},beforeMount() {console.log('beforeMount');},data() {return {}},setup() {const count = ref(1)const age = ref('20')const obj  = reactive({name:'ws',address:'usa'})onMounted(()=>{obj.name = 'kd'count.value = 5age.value = '2'})return ()=>{return  h('div',[obj.name,h(child,{age:age.value})])}}}createApp(App).mount(container)await nextTick()expect(container.innerHTML).toBe(`0`)})

响应式数据创建

还记得 之前文章中 初始化 setup 是在哪个阶段执行的吗?

patch
processComponent
mountComponent
 // packages/runtime-dom/src/renderer.tspatch 阶段 组件首次挂载时
// mountComponent 方法1. 先创建 组件 instance 实例
2. 初始化 setup props 等属性
3. 设置并运行带副作用的渲染函数

初始化 setup 时 ,就会创建响应式数据
测试用例中 会先 执行 App 组件中 的setup 函数

 setup() {// 会创建一个 ref 响应式数据const count = ref(1)// 会创建一个 ref 响应式数据const age = ref('20')// 会创建一个 reactive 响应式数据const obj  = reactive({name:'ws',address:'usa'})onMounted(()=>{obj.name = 'kd'count.value = 5age.value = '2'})return ()=>{return  h('div',[obj.name,h(child,{age:age.value})])}}}

ref 和 reactive 核心 作用

先说结论 :
就是 数据 驱动 视图 更新的 桥梁 。依赖收集(getter) 和 派发更新(setter) 都在里面

ref 和 reactive 差别不大(对于基本数据类型 proxy 无法做代理 ,所以vue3 自己利用 class 类中 get set 做的 代理工作 后续 依赖收集 和 派发更新 原理 和 reactive 基本一致 ) 下面 只对 reactive 做分析

  1. 先判断 代理对象 做类型分类
function targetTypeMap(rawType) {switch (rawType) {case 'Object':case 'Array':return TargetType.COMMONcase 'Map':case 'Set':case 'WeakMap':case 'WeakSet':return TargetType.COLLECTIONdefault:return TargetType.INVALID}
}
  1. 根据 不同分类 选择 不同的 getter setter 方法
    只分析下 最常见的 Object Array 代理
export function reactive(target: object) {// if trying to observe a readonly proxy, return the readonly version.if (isReadonly(target)) {return target}return createReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap)
}
。。。。function createReactiveObject(target: Target,isReadonly: boolean,baseHandlers: ProxyHandler<any>,collectionHandlers: ProxyHandler<any>,proxyMap: WeakMap<Target, any>
) {// 省略。。。const proxy = new Proxy(target,targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers)proxyMap.set(target, proxy)return proxy
}

会走到 baseHandlers 也就是 mutableHandlers

  1. mutableHandlers 中 如何 做的 数据代理工作

先看 get 核心 就是 依赖收集 track方法

function createGetter(isReadonly = false, shallow = false) {return function get(target: Target, key: string | symbol, receiver: object) {// 对 ReactiveFlags 的处理部分if (key === ReactiveFlags.IS_REACTIVE) {return !isReadonly} else if (key === ReactiveFlags.IS_READONLY) {return isReadonly} else if (key === ReactiveFlags.IS_SHALLOW) {return shallow} else if (key === ReactiveFlags.RAW &&receiver ===(isReadonly? shallow? shallowReadonlyMap: readonlyMap: shallow? shallowReactiveMap: reactiveMap).get(target)) {return target}const targetIsArray = isArray(target)if (!isReadonly) {// 数组的特殊方法处理if (targetIsArray && hasOwn(arrayInstrumentations, key)) {return Reflect.get(arrayInstrumentations, key, receiver)}// 对象 hasOwnProperty 方法处理if (key === 'hasOwnProperty') {return hasOwnProperty}}// 取值const res = Reflect.get(target, key, receiver)// Symbol Key 不做依赖收集if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {return res}// 进行依赖收集if (!isReadonly) {track(target, TrackOpTypes.GET, key)}// 一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露if (shallow) {return res}if (isRef(res)) {//跳过数组、整数 key 的展开// ref unwrapping - skip unwrap for Array + integer key.return targetIsArray && isIntegerKey(key) ? res : res.value}if (isObject(res)) {// Convert returned value into a proxy as well. we do the isObject check// here to avoid invalid value warning. Also need to lazy access readonly// and reactive here to avoid circular dependency.// 如果res 是 对象 且不是 readonly 就继续处理成 reactivereturn isReadonly ? readonly(res) : reactive(res)}return res}
}

track 依赖收集

export function track(target: object, type: TrackOpTypes, key: unknown) {if (shouldTrack && activeEffect) {let depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, (depsMap = new Map()))}let dep = depsMap.get(key)if (!dep) {depsMap.set(key, (dep = createDep()))}const eventInfo = __DEV__? { effect: activeEffect, target, type, key }: undefined// 将 activeEffect 存入到 dep 同时将 dep[] 存入到 activeEffect 中 deps 属性 上 trackEffects(dep, eventInfo)}
}export function trackEffects(dep: Dep,debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {let shouldTrack = falseif (effectTrackDepth <= maxMarkerBits) {// 如果本轮副作用函数执行过程中已经访问并收集过,则不用再收集该依赖if (!newTracked(dep)) {dep.n |= trackOpBit // set newly tracked 标识本轮已经被收集过shouldTrack = !wasTracked(dep)}} else {// Full cleanup mode. 判断现在有没有activeEffect  有activeEffect才发生依赖收集// activeEffect 每个组件初始化的时候会有一个activeEffect // 这一步的作用 是为了避免多余的依赖收集 例如在setup 创建了 响应式数据 同步 访问 或者 修改 这个数据 这时候 都不会发生 依赖收集。只会在 执行render函数的时候 才发生依赖收集 shouldTrack = !dep.has(activeEffect!)}if (shouldTrack) {dep.add(activeEffect!)activeEffect!.deps.push(dep)}
}

reactiveEffect 核心代码

// 用于记录位于响应上下文中的effect嵌套层次数
let effectTrackDepth = 0
// 二进制位,每一位用于标识当前effect嵌套层级的依赖收集的启用状态
export left trackOpBit = 1
// 表示最大标记的位数
const maxMarkerBits = 30// 当前活跃的 effect
let activeEffect;export class ReactiveEffect {// 用于标识副作用函数是否位于响应式上下文中被执行active = true// 副作用函数持有它所在的所有依赖集合的引用,用于从这些依赖集合删除自身deps = []// 指针为,用于嵌套 effect 执行后动态切换 activeEffectparent = undefined// ...run() {// 若当前 ReactiveEffect 对象脱离响应式上下文// 那么其对应的副作用函数被执行时不会再收集依赖if (!this.active) {return this.fn()}// 缓存是否需要收集依赖let lastShouldTrack = shouldTracktry {// 保存上一个 activeEffect 到当前的 parent 上this.parent = activeEffect// activeEffect 指向当前的 effectactiveEffect = this// shouldTrack 置成 trueshouldTrack = true// 左移操作符 << 将第一个操作数向左移动指定位数// 左边超出的位数将会被清除,右边将会补零。// trackOpBit 是基于 1 左移 effectTrackDepth 位trackOpBit = 1 << ++effectTrackDepth// 如果未超过最大嵌套层数,则执行 initDepMarkersif (effectTrackDepth <= maxMarkerBits) {initDepMarkers(this)} else {cleanupEffect(this)}// 这里执行了 fnreturn this.fn()} finally {if (effectTrackDepth <= maxMarkerBits) {// 用于对曾经跟踪过,但本次副作用函数执行时没有跟踪的依赖采取删除操作。// 新跟踪的 和 本轮跟踪过的都会被保留finalizeDepMarkers(this)}// << --effectTrackDepth 右移动 effectTrackDepth 位trackOpBit = 1 << --effectTrackDepth// 返回上个 activeEffectactiveEffect = this.parent// 返回上个 shouldTrackshouldTrack = lastShouldTrack// 情况本次的 parent 指向this.parent = undefined}}
}

在这里插入图片描述

说明 :

depsMap 中 effect[] 用于 每次 派发更新时候 去执行 effect 数组中的 reactiveEffect (实际调用 reactiveEffect 实例的 run 方法)

reactiveEffect 中 会在执行 run 方法的时候 给 initDepMarkers 方法来 给 deps 数组中每个对象 添加 w 属性 表示 已经收集处理 在 依赖收集中 track----> trackEffects 会给 depsMap 中 dep(这个 dep 和 effect 实例的 deps 中 每一个对象 相对应) 赋值 n (表示 是 新收集的)

export const finalizeDepMarkers = (effect: ReactiveEffect) => {const { deps } = effectif (deps.length) {let ptr = 0for (let i = 0; i < deps.length; i++) {const dep = deps[i]if (wasTracked(dep) && !newTracked(dep)) {// dep 类型是 set<ReativeEffect>dep.delete(effect)} else {deps[ptr++] = dep}// clear bitsdep.w &= ~trackOpBitdep.n &= ~trackOpBit}deps.length = ptr}
}

当 effect.run() 中 注册的fn 函数执行完后 会调用 finalizeDepMarkers 去 删除掉 这一轮 dep 没有被收集到的 effect 避免 多余的 触发更新逻辑

那测试用例来说明
在这里插入图片描述

第一轮的 依赖收集 发生时机

在App 组件 初始化 副作用函数, 会先创建 reactiveEffect 并挂载到 app.instance
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

app 会主动触发 instance.update() 发生第一次 组件挂载 。
之前章节 说明过 组件首次挂载流程
实际调用的是 reactiveEffect.run ----> 执行componentUpdateFn —>render(生成subtree)----> patch ----> processElement

在render 过程中 使用到的响应式数据 就发生依赖收集 。
这时候 app 组件的 这一轮的依赖收集完成 使用到了 obj 和 age
在这里插入图片描述
这时候进行app 组件 processElement 由于存在子组件child 执行 mountChild ----> patch —> child 组件的processComponent child组件 也会和 app 组件 一样 去 初始化 instance 创建 ReactiveEffect 触发update 执行属于 child 的 componentUpdateFn 再 执行 child组件的 render 函数 child组件发生依赖收集

在这里插入图片描述
age status add

本轮的依赖收集全部完成。

总结:
组件的首次依赖收集 发生在 render阶段 顺序是 父组件 setup---->父组件 render ---->子组件 setup 
----> 子组件render

setup 阶段 去更改了 响应式数据 会发生依赖收集吗

例如:
setup(){const age = ref(20)// 这里发生了访问操作const temp = age.valuereturn ()=>{return h('div',[age.value]) }
}这时候会触发响应式数据的 get 操作 
但是由于 没有 activeEffect(这时候 组件还没开始设置副作用函数(SetupRenderEffectFn)所以没有activeEffect) 所以不会发生依赖收集 扩展:
setup(){const age = ref(20)setTimeout(()=>{// 这里发生了访问操作console.log(age.value);  })return ()=>{return h('div',[age.value]) }
}
这时候 也会触发响应式数据的 get 操作 ,也是没有activeEffect(组件已经完成 effect.run 方法了,这时候 activeEffect 已经被置为空) 所以也不会发生依赖收集后续:
在setup函数之后的生命周期(如mounted、updated等钩子函数)中访问响应式数据会触发依赖收集 (后面再分析)

派发更新

派发更新是什么时候 触发的?

上面 组件 挂载完成后,我在mouted 生命周期钩子里面 写的修改响应式数据操作,会触发 setter 下面看看 reactive 的 setter 源码


function createSetter(shallow = false) {return function set(target: object,key: string | symbol,value: unknown,receiver: object): boolean {// 。。。 省略部分逻辑const hadKey =isArray(target) && isIntegerKey(key)? Number(key) < target.length: hasOwn(target, key)const result = Reflect.set(target, key, value, receiver)// 如果target是原型链上的东西,不要触发if (target === toRaw(receiver)) {if (!hadKey) {// 新增操作trigger(target, TriggerOpTypes.ADD, key, value)} else if (hasChanged(value, oldValue)) {// 更新操作trigger(target, TriggerOpTypes.SET, key, value, oldValue)}}return result}
}export function trigger(target: object,type: TriggerOpTypes,key?: unknown,newValue?: unknown,oldValue?: unknown,oldTarget?: Map<unknown, unknown> | Set<unknown>
) {// 根据 target 查到对应的 depsMapconst depsMap = targetMap.get(target)// 不存在depsMap 不触发更新if (!depsMap) {// never been trackedreturn}// 用于 暂存 effectlet deps: (Dep | undefined)[] = []if (type === TriggerOpTypes.CLEAR) {// collection being cleared// trigger all effects for targetdeps = [...depsMap.values()]} else if (key === 'length' && isArray(target)) {const newLength = Number(newValue)depsMap.forEach((dep, key) => {if (key === 'length' || key >= newLength) {deps.push(dep)}})} else {// schedule runs for SET | ADD | DELETEif (key !== void 0) {deps.push(depsMap.get(key))}// also run for iteration key on ADD | DELETE | Map.SETswitch (type) {case TriggerOpTypes.ADD:if (!isArray(target)) {deps.push(depsMap.get(ITERATE_KEY))if (isMap(target)) {deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))}} else if (isIntegerKey(key)) {// new index added to array -> length changesdeps.push(depsMap.get('length'))}breakcase TriggerOpTypes.DELETE:if (!isArray(target)) {deps.push(depsMap.get(ITERATE_KEY))if (isMap(target)) {deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {deps.push(depsMap.get(ITERATE_KEY))}break}}const eventInfo = __DEV__? { target, type, key, newValue, oldValue, oldTarget }: undefined// 最终处理 在这里if (deps.length === 1) {if (deps[0]) {if (__DEV__) {triggerEffects(deps[0], eventInfo)} else {triggerEffects(deps[0])}}} else {const effects: ReactiveEffect[] = []for (const dep of deps) {if (dep) {effects.push(...dep)}}// 下面操作 是为了 去重 保证相同的effect 只会有一个if (__DEV__) {triggerEffects(createDep(effects), eventInfo)} else {triggerEffects(createDep(effects))}}
}export function triggerEffects(dep: Dep | ReactiveEffect[],debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {// spread into array for stabilizationconst effects = isArray(dep) ? dep : [...dep]for (const effect of effects) {if (effect.computed) {triggerEffect(effect, debuggerEventExtraInfo)}}for (const effect of effects) {if (!effect.computed) {triggerEffect(effect, debuggerEventExtraInfo)}}
}function triggerEffect(effect: ReactiveEffect,debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {if (effect !== activeEffect || effect.allowRecurse) {if (__DEV__ && effect.onTrigger) {effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))}// 最终会执行 scheduler 是在 初始化的时候 创建的if (effect.scheduler) {effect.scheduler()} else {effect.run()}}
}//  在SetupRenderEffectFn 阶段中 create reactive effect for renderingconst effect = (instance.effect = new ReactiveEffect(componentUpdateFn,() => queueJob(update),// 这个就是 schedulerinstance.scope // track it in component's effect scope))

总结:当响应式数据被更新 且 对应的 depsMap 不为空 就会触发 组件更新(如何更新 则下个问题给出答案)

扩展: setup阶段 响应式数据被修改 会触发组件更新吗

setup(){const age = ref(20)// 修改操作age.value = 10return ()=>{return h('div',[age.value]) }
}
会触发 setter 操作 由于 depsMap 为空 所以不会发生派发更新

vue 是如何根据派发更新来触发组件的更新渲染的?

在这里插入图片描述
派发更新的核心就是 触发 effect.scheduler(常规的组件写法 就是 会给activeEffect 创建 scheduler)

const effect = (instance.effect = new ReactiveEffect(componentUpdateFn,() => queueJob(update), // effect.scheduleinstance.scope // track it in component's effect scope))

分析下 queueJob


export function queueJob(job: SchedulerJob) {// the dedupe search uses the startIndex argument of Array.includes() 确保不会重复设置 schedule// by default the search index includes the current job that is being run 默认包括正在运行的 schedule// so it cannot recursively trigger itself again. 避免递归触发自身再次运行// if the job is a watch() callback, the search will start with a +1 index to 运行在watch 中 重复运行// allow it recursively trigger itself - it is the user's responsibility to// 确保它不会陷入无限循环// 去重判断if (!queue.length ||!queue.includes(job,isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)) {//添加到队列尾部if (job.id == null) {queue.push(job)} else {// 按照 job id 自增的顺序添加 (一般父组件的id 要小于子组件 保证 父组件永远先于子组件触发更新)// 这个id 是由 instance.uid 决定 就是在初始化组件实例 确定( 具体代码 runtime-core/src/component) 先初始化的 uid(每次创建组件实例 全局 uid会加1) 会小,queue.splice(findInsertionIndex(job.id), 0, job)}queueFlush()}
}// 通过promise.then 创建 微任务(去执行flushjob)
function queueFlush() {if (!isFlushing && !isFlushPending) {isFlushPending = truecurrentFlushPromise = resolvedPromise.then(flushJobs)}
}function flushJobs(seen?: CountMap) {// 是否正在等待执行isFlushPending = false// 正在执行isFlushing = true// 在更新前,重新排序好更新队列 queue 的顺序// 这确保了:// 1. 组件都是从父组件向子组件进行更新的。(因为父组件都在子组件之前创建的// 所以子组件的渲染的 effect 的优先级比较低)// 2. 如果父组件在更新前卸载了组件,这次更新将会被跳过。queue.sort(comparator)try {// 遍历主任务队列,批量执行更新任务for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {const job = queue[flushIndex]if (job && job.active !== false) {if (__DEV__ && check(job)) {continue}// 这个 job 就是 effect.runcallWithErrorHandling(job, null, ErrorCodes.SCHEDULER)}}} finally {// 队列任务执行完,重置队列索引flushIndex = 0// 清空队列queue.length = 0// 执行后置队列任务flushPostFlushCbs(seen)// 重置队列执行状态isFlushing = false// 重置当前微任务为 NullcurrentFlushPromise = null// 如果主任务队列、后置任务队列还有没被清空,就继续递归执行if (queue.length || pendingPostFlushCbs.length) {flushJobs(seen)}}
}

总结:在组件mounted 触发的派发更新 会被收集到 一个微任务执行任务队列中 ,在主流程宏任务 执行完后 就会 去执行 微任务任务队列 开始 触发 执行 job (effect.run — > updateComponentFn)

组件副作用函数执行时 有多个响应式数据更新 是如何保证组件只会触发一次更新渲染的?

有了 上面源码的分析 我们已经可以得出答案 为啥 在一个组件的 副作用函数执行时 多个响应式数据更新 只会触发一次 组件更新
演示代码:

setup(){const num1 = ref(20)const num2 = ref(10)onMounted(()=>{num1.value = 40num1.value = 50num2.value = 100})return ()=>{return h('div',[num1.value+num2.value]) }
}

在 onMounted 中 更新了三次 触发三次 triggerEffect 会有三次 往微任务 放入 update 操作,由于 传入的 job.id 都是同一个 所以 在更新队列中 只会被创建一个更新任务 组件也只会被更新一次

多余的组件依赖 是如何被清理掉的?

组件 再每次 渲染后 会 去 清理 后续没被收集的 effect (对应的是 每个响应式数据 对应的dep(set) 中的 reactiveEffect )

例子:

const child = defineComponent({template: `<div><p>{{age}}---{{status?add:'hihi'}}</p></div>`,props:{age:{type: Number,default:20}},data(){return {add: '12',status: true}},mounted() {this.status = falsethis.add = '24'},})
// mounted 阶段 改变了 status 触发了 组件更新 重新 render 的 时候 会发生新的一轮依赖收集 
// 之前 组件 是有两个 dep 一个 属于 status 一个属于 add 但是,由于新的依赖收集 add 不会被用到 所以 在 effect.run 执行完 后 add 的 dep 会被清除掉 是根据 dep 赋值的 w 和 n 属性 去比较

相关文章:

vue3学习源码笔记(小白入门系列)------ 重点!响应式原理 代码逐行分析

目录 备注响应式数据创建ref 和 reactive 核心 作用第一轮的 依赖收集 发生时机setup 阶段 去更改了 响应式数据 会发生依赖收集吗 派发更新派发更新是什么时候 触发的&#xff1f;扩展&#xff1a; setup阶段 响应式数据被修改 会触发组件更新吗 vue 是如何根据派发更新来触发…...

62、SpringBoot 使用RestTemplate 整合第三方 RESTful 服务

这节的要点&#xff1a; 就是弄两个项目 &#xff0c; 从 端口9090 这个项目&#xff0c;通过 restTemplate&#xff0c; 去访问 端口8080 的项目&#xff0c;并获取8080项目的数据。 ★ RESTful服务包含两方面的含义 1. 自己的应用要暴露一些功能供别人来调用。此时我们是服…...

Linux基本认识

一、Linux基本概念 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统&#xff0c;是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多…...

leetcode top 100 (8)无重复字符的最长子串(滑动窗口

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 package TOP1_10;import java.util.HashMap; import java.…...

我也惊呆了!原来软件开发根本不需要会编码

一、前言 众所周知&#xff0c;完成一个大型的企业级系统&#xff0c;公司往往需要大量的人力做支持后盾&#xff0c;例如需要需求分析师、数据库管理员、前台美工、后台程序员、测试人员等。在快速发展中的企业&#xff0c;尤其是中小企业&#xff0c;都是一个萝卜多个坑&…...

定制化图标——Element UI 组件图标替换指南

本篇博客将介绍如何在使用 Element UI 组件时对原生图标进行定制化替换&#xff0c;提供了适用于满足个性化需求的方法和技巧。 引言 Element UI 是一款基于 Vue.js 的流行 UI 组件库&#xff0c;在前端开发中得到广泛应用。然而&#xff0c;在使用 Element UI 的组件时&#…...

63、SpringBoot---定制 RestTemplate--消息转化器、拦截器

★ 定制RestTemplate 如要对RestTemplate进行自定义设置&#xff0c;Spring Boot也提供了两种主要方式&#xff1a;▲ 局部式&#xff1a;在调用RestTemplateBuilder构建RestTemplate之前&#xff0c;先调用RestTemplateBuilder的方法对其定制&#xff0c;通过这种方式设置的R…...

面试系列 - Redis持久化机制详解

目录 一、Redis 持久化机制 二、混合使用 RDB 和 AOF 三、 RDB(Redis DataBase)详解 四、AOF&#xff08;Append-Only File&#xff09;详解 Redis 是一个内存数据库&#xff0c;为了持久化数据以确保数据不会在服务器重启时丢失&#xff0c;Redis 提供了两种主要的持久化机…...

Ceph入门到精通-存储集群ceph df 用量统计算法说明

3.2.5. Ceph 如何计算数据使用量 used 值反映了使用的实际原始存储量。xxx GB / xxx GB 代表可用的存储&#xff08;其中较小的数字&#xff09;和总存储容量。总容量反映了在复制、克隆或快照前存储数据的大小。因此&#xff0c;实际存储的数据量通常会超过名义上的存储量。这…...

堡垒机的相关介绍

描述 堡垒机&#xff0c;即在一个特定的网络环境下&#xff0c;为了保障网络和数据不受来自外部和内部用户的入侵和破坏&#xff0c;而运用各种技术手段监控和记录运维人员对网络内的服务器、网络设备、安全设备、数据库等设备的操作行为&#xff0c;以便集中报警、及时处理及审…...

无涯教程-JavaScript - INDIRECT函数

描述 INDIRECT函数返回由文本字符串指定的引用。 如果您在Excel公式中键入引用B1,则Excel会理解这引用了单元格B1。但是,Excel无法将文本字符串" B1"理解为引用。因此,如果单元格引用采用文本字符串的形式,则需要使用INDIRECT函数将其转换为实际的单元格引用。 立…...

LiveNVR监控流媒体Onvif/RTSP功能-支持海康摄像头海康NVR通过EHOME协议ISUP协议接入分发视频流或是转GB28181

LiveNVR支持海康NVR摄像头通EHOME接入ISUP接入LiveNVR分发视频流或是转GB28181 1、海康 ISUP 接入配置2、海康设备接入2.1、海康EHOME接入配置示例2.2、海康ISUP接入配置示例 3、通道配置3.1、直播流接入类型 海康ISUP3.2、海康 ISUP 设备ID3.3、启用保存3.4、接入成功 4、相关…...

一年一度的中秋节马上又要到了,给你的浏览器也来点氛围感吧

说在前面 一年一度的中秋节马上又要到了&#xff0c;给你的浏览器也来点氛围感吧 &#x1f315;&#x1f315;&#x1f315; 插件设计 效果 首先我们应该要先确定一下我们想要实现的效果是怎样的&#xff0c;如上图&#xff0c;我们希望在页面上鼠标点击的时候会在点击区域随…...

CentOS8安装mysql-community-client错误解决

安装MySQL5.7.37的mysql-community-client-5.7.37-1.el7.x86_64.rpm时&#xff0c;提示如下&#xff1a; 提示的意思是缺少依赖软件包。 使用如下命令安装依赖包&#xff1a; yum install libncurse* 实际安装如下两个软件包。 成功后再次安装mysql-community-client-5.7.37…...

故障排除指南:解决 Kibana Discover 加载中的 6 个常见问题

作者&#xff1a;Steffanie Nestor Discover 是 Elastic 的核心 Kibana UI&#xff0c;用于搜索、过滤和检查&#xff08;时间序列&#xff09;数据。 可视化用于数据聚合/摘要。 Discover UI 对于大数据 Elasticsearch 响应具有弹性&#xff0c;但有时会因&#xff08;未压缩的…...

创建一个简单的外卖订餐系统

在今天的快节奏生活中&#xff0c;外卖订餐系统已经成为了人们日常生活中不可或缺的一部分。这些系统通过在线点餐和配送服务&#xff0c;为用户提供了便捷的用餐体验。在本文中&#xff0c;我们将创建一个简单的外卖订餐系统&#xff0c;使用Python和Flask框架构建后端&#x…...

《追逐胜利:编程之路上的三子棋游戏实践》

文章目录 前言一、三子棋游戏规则二、步骤详解1.游戏菜单的实现2.棋盘的实现2.1 初始化棋盘2.2 打印棋盘 3.游戏逻辑实现3.1 玩家下棋3.2 电脑下棋 4.判断输赢4.1 win函数实现 5.完整代码 总结 前言 大家好&#xff01;我是艾老虎尤&#xff01;今天我很高兴来和大家分享我最近…...

身份和访问管理解决方案:混合型IAM

对于依赖于本地 IT 基础结构和传统安全模型的组织&#xff0c;可以更轻松地验证和授权企业网络内的所有内容&#xff0c;包括设备、用户、应用程序和服务器。尝试从公司网络外部获取访问权限的用户使用虚拟专用网络 &#xff08;VPN&#xff09; 和网络访问控制 &#xff08;NA…...

微信小程序动态添加表单模块

先来看看效果&#xff1a; 屏幕录制 2023-09-14 16.33.21 点击添加请假按钮&#xff0c;就会新增一个请假信息表单&#xff1b;点击左上角红色删除按钮&#xff0c;删除当前表单&#xff1b; 源码下载地址&#xff1a;https://download.csdn.net/download/xq30397022/88339822…...

HTML5+CSS3小实例:纯CSS实现彩虹倒映水面的唯美背景

实例:纯CSS实现彩虹倒映水面的唯美背景 技术栈:HTML+CSS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" …...

Beautiful Soup简介

BeautifulSoup是一个Python库&#xff0c;用于从HTML或XML文件中提取数据。它提供了一种简单而直观的方式来遍历、搜索和修改HTML或XML文档的结构。 以下是一些BeautifulSoup的主要功能&#xff1a; 解析器&#xff1a;BeautifulSoup支持多种解析器&#xff0c;包括Python的内置…...

Springboot2.7集成websocket及相关问题

1、集成websocket完整代码 导入maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency> 服务端代码 &#xff08;1&#xff09;注入bean Configur…...

MES管理系统和ERP系统在生产制造管理中的应用

MES生产管理系统通过过程管理、质量管理、设备管理、产品跟踪和溯源、性能分析和物料管理等方面来管理生产制造&#xff0c;旨在建立规范的生产管理信息平台&#xff0c;提高企业核心竞争力。ERP系统则通过制定生产计划、细分物料需求计划、车间订单下达和生产回报等步骤进行生…...

Netty Channel 详解

优质博文&#xff1a;IT-BLOG-CN 一、Netty 服务端启动过程 【1】创建服务端Channel&#xff1b; 【2】初始化服务端Channel&#xff1b; 【3】注册Selector&#xff1b; 【4】端口绑定&#xff1a;我们分析源码的入口从端口绑定开始&#xff0c;ServerBootstrap的bind(int in…...

技师学院物联网实训室建建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…...

SpringBoot项目--电脑商城【增加/减少购物车商品数量】

1.持久层[Mapper] 1.1规划需要执行的SQL语句 1.更新该商品的数量.此SQL语句无需重复开发 update t_cart set num?,modified_user?,modified_time? where cid? 2.首先进行查询需要操作的购物车数据信息【查看该条数据是否存在】 SELECT * FROM t_cart WHERE cid?2.接口…...

CSS元素浮动

概述 浮动简介 在最初&#xff0c;浮动是用来实现文字环绕图片效果的&#xff0c;现在浮动是主流的页面布局方式之一。 元素浮动后的特点 脱离文档流。不管浮动前是什么元素&#xff0c;浮动后&#xff0c;默认宽与高都是被内容撑开的&#xff08;尽可能小&#xff09;&am…...

MATLAB中islocalmin函数用法

目录 语法 说明 示例 向量中的局部最小值 矩阵行中的最小值 相隔最小值 最小值平台区 突出最小值 islocalmin函数的功能是计算局部最小值。 语法 TF islocalmin(A) TF islocalmin(A,dim) TF islocalmin(___,Name,Value) [TF,P] islocalmin(___) 说明 ​当在 A 的…...

Python+Requests+Pytest+YAML+Allure实现接口自动化

本项目实现接口自动化的技术选型&#xff1a;PythonRequestsPytestYAMLAllure &#xff0c;主要是针对之前开发的一个接口项目来进行学习&#xff0c;通过 PythonRequests 来发送和处理HTTP协议的请求接口&#xff0c;使用 Pytest 作为测试执行器&#xff0c;使用 YAML 来管理测…...

双视觉Transformer(Dual Vision Transformer)

摘要 已经提出了几种策略来减轻具有高分辨率输入的自注意机制的计算&#xff1a;比如将图像补丁上的全局自注意过程分解成区域和局部特征提取过程&#xff0c;每个过程都招致较小的计算复杂度。尽管效率良好&#xff0c;这些方法很少探索所有补丁之间的整体交互&#xff0c;因…...