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

vue3.0与vue2.0

一、生命周期的变化

1.vue2.响应式架构

2.vue3.0 响应式架构图

Vue3.0响应式框架在设计上,将视图渲染和数据响应式完全分离开来。将响应式核心方法effect从原有的Watcher中抽离。这样,当我们只需要监听数据响应某种逻辑回调(例如监听某个text属性的变化,对他进行正则校验等。),而不需要更新视图的时候,完全可以只从effect触发,屏蔽左边的视图渲染。

3.vue3.0与vue2.0页面比较

整体来看,变化不大,只是名字大部分需要 + on,功能上类似。使用上 Vue3 组合式 API 需要先引入;Vue2 选项 API 则可直接调用,如下所示。

// vue3
​
<script setup>     
import { onMounted } from  vue 
​
onMounted(() => {...
})
// 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不被覆盖
onMounted(() => {...
})
</script>
​
// vue2
​
<script>     export default {         mounted() {             ...         },           }
</script> 
​
​

常用生命周期表格如下所示。

Vue2.xVue3
beforeCreateNot needed
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted

Tips: setup是围绕beforeCreate和created生命周期钩子运行的,所以不需要显式地去定义。

二、多根节点 Vue3 支持了多根节点组件,也就是fragment。

Vue2中,编写页面的时候,我们需要去将组件包裹在<div>中,否则报错警告。

<template> <div> <header>...</header> <main>...</main> <footer>...</footer> </div> </template>

Vue3,我们可以组件包含多个根节点,可以少写一层,niceeee !

<template> <header>...</header> <main>...</main> <footer>...</footer> </template>

二、异步组件 Vue3 提供 Suspense组件,允许程序在等待异步组件时渲染兜底的内容,如 loading ,使用户体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default和fallback。Suspense确保加载完异步内容时显示默认插槽,并将fallback插槽用作加载状态。

<tempalte><suspense>
​<template #default><todo-list /></template><template #fallback><div>Loading...</div></template>
​</suspense>
</template>
​

真实的项目中踩过坑,若想在 setup 中调用异步请求,需在 setup 前加async关键字。这时,会受到警告async setup() is used without a suspense boundary。

解决方案:在父页面调用当前组件外包裹一层Suspense组件。

四、Teleport Vue3 提供Teleport组件可将部分DOM移动到 Vue app之外的位置。比如项目中常见的Dialog组件。

<button @click=dialogVisible = true>点击</button>
<teleport to=body>
​<div class=dialog v-if=dialogVisible></div>
​
</teleport>
​

五、组合式API Vue2 是 选项式API(Option API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期函数等),导致代码的可读性变差,需要上下来回跳转文件位置。Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起。

除了增强了代码的可读性、内聚性,组合式API 还提供了较为完美的逻辑复用性方案,举个🌰,如下所示公用鼠标坐标案例。

// main.vue
​
<template><span>mouse position {{x}} {{y}}</span>
</template>
​
​
<script setup>
import { ref } from  vue 
import useMousePosition from  ./useMousePosition 
​
​
const {x, y} = useMousePosition()
​
}
</script>
​
// useMousePosition.js
import { ref, onMounted, onUnmounted } from  vue 
​
function useMousePosition() {let x = ref(0)let y = ref(0)
​function update(e) {x.value = e.pageXy.value = e.pageY}
​onMounted(() => {window.addEventListener( mousemove , update)})
​onUnmounted(() => {window.removeEventListener( mousemove , update)})
​return {x,y}
}
</script>
​

解决了 Vue2 Mixin的存在的命名冲突隐患,依赖关系不明确,不同组件间配置化使用不够灵活。

六、响应式原理 Vue2 响应式原理基础是Object.defineProperty;Vue3 响应式原理基础是Proxy。

Object.defineProperty

基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。 Tips: writable 和 value 与 getter 和 setter 不共存。

let obj = {}
let name =  瑾行 
Object.defineProperty(obj,  name , {enumerable: true, // 可枚举(是否可通过for...in 或 Object.keys()进行访问)configurable: true, // 可配置(是否可使用delete删除,是否可再次设置属性)// value:   , // 任意类型的值,默认undefined// writable: true, // 可重写get: function() {return name},set: function(value) {name = value}
})

搬运 Vue2 核心源码,略删减。

function defineReactive(obj, key, val) {// 一 key 一个 depconst dep = new Dep()
​// 获取 key 的属性描述符,发现它是不可配置对象的话直接 returnconst property = Object.getOwnPropertyDescriptor(obj, key)if (property && property.configurable === false) { return }
​// 获取 getter 和 setter,并获取 val 值const getter = property && property.getconst setter = property && property.setif((!getter || setter) && arguments.length === 2) { val = obj[key] }
​// 递归处理,保证对象中所有 key 被观察let childOb = observe(val)
​Object.defineProperty(obj, key, {enumerable: true,configurable: true,// get 劫持 obj[key] 的 进行依赖收集get: function reactiveGetter() {const value = getter ? getter.call(obj) : valif(Dep.target) {// 依赖收集dep.depend()if(childOb) {// 针对嵌套对象,依赖收集childOb.dep.depend()// 触发数组响应式if(Array.isArray(value)) {dependArray(value)}}}}return value})// set 派发更新 obj[key]set: function reactiveSetter(newVal) {...if(setter) {setter.call(obj, newVal)} else {val = newVal}// 新值设置响应式childOb = observe(val)// 依赖通知更新dep.notify()}
}

那 Vue3 为何会抛弃它呢?那肯定是有一些缺陷的。

主要原因:无法监听对象或数组新增、删除的元素。 Vue2 方案:针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象/数组新增属性。对象的新增/删除响应,还可以new个新对象,新增则合并新属性和旧对象;删除则将删除属性后的对象深拷贝给新对象。

Tips: Object.defineOProperty是可以监听数组已有元素,但 Vue2 没有提供的原因是性能问题,具体可看见参考第二篇 ~。

七、Proxy

Proxy是ES6新特性,通过第2个参数handler拦截目标对象的行为。相较于Object.defineProperty提供语言全范围的响应能力,消除了局限性。但在兼容性上放弃了(IE11以下)

八、局限性

对象/数组的新增、删除。

监测.length修改。

Map、Set、WeakMap、WeakSet的支持。

基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。

const handler = {get: function(obj, prop) {return prop in obj ? obj[prop] : },set: function() {},...
}
​

搬运 Vue3 的源码 reactive.ts 文件

function createReactiveObject(target, isReadOnly, baseHandlers, collectionHandlers, proxyMap) {...// collectionHandlers: 处理Map、Set、WeakMap、WeakSet// baseHandlers: 处理数组、对象const proxy = new Proxy(target,targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers)proxyMap.set(target, proxy)return proxy
}
​

以 baseHandlers.ts 为例,使用Reflect.get而不是target[key]的原因是receiver参数可以把this指向getter调用时,而非Proxy构造时的对象。

// 依赖收集
function createGetter(isReadonly = false, shallow = false) {return function get(target: Target, key: string | symbol, receiver: object) {...// 数组类型const targetIsArray = isArray(target)if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {return Reflect.get(arrayInstrumentations, key, receiver)}// 非数组类型const res = Reflect.get(target, key, receiver);​// 对象递归调用if (isObject(res)) {return isReadonly ? readonly(res) : reactive(res)}return res
​}
}
// 派发更新
function createSetter() {return function set(target: Target, key: string | symbol, value: unknown, receiver: Object) {value = toRaw(value)oldValue = target[key]// 因 ref 数据在 set value 时就已 trigger 依赖了,所以直接赋值 return 即可if (!isArray(target) && isRef(oldValue) && !isRef(value)) {oldValue.value = valuereturn true}
​// 对象是否有 key 有 key set,无 key addconst hadKey = hasOwn(target, key)const result = Reflect.set(target, key, value, receiver)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
​}
}

九、虚拟DOM Vue3 相比于 Vue2 虚拟DOM 上增加patchFlag字段。我们借助Vue3 Template Explorer来看。

技术摸鱼

今天天气真不错

{{name}}

渲染函数如下。

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
​
const _withScopeId = n => (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, 技术摸鱼, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 今天天气真不错, -1 /* HOISTED */))
​
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createElementBlock(div, _hoisted_1, [_hoisted_2,_hoisted_3,_createElementVNode(div, null, _toDisplayString(_ctx.name), 1 /* TEXT */)]))
}
​

注意第 3 个_createElementVNode的第 4 个参数即patchFlag字段类型,字段类型情况如下所示。1 代表节点为动态文本节点,那在 diff 过程中,只需比对文本对容,无需关注 class、style等。除此之外,发现所有的静态节点,都保存为一个变量进行静态提升,可在重新渲染时直接引用,无需重新创建。

export const enum PatchFlags { TEXT = 1, // 动态文本内容CLASS = 1 << 1, // 动态类名STYLE = 1 << 2, // 动态样式PROPS = 1 << 3, // 动态属性,不包含类名和样式FULL_PROPS = 1 << 4, // 具有动态 key 属性,当 key 改变,需要进行完整的 diff 比较HYDRATE_EVENTS = 1 << 5, // 带有监听事件的节点STABLE_FRAGMENT = 1 << 6, // 不会改变子节点顺序的 fragmentKEYED_FRAGMENT = 1 << 7, // 带有 key 属性的 fragment 或部分子节点UNKEYED_FRAGMENT = 1 << 8,  // 子节点没有 key 的fragmentNEED_PATCH = 1 << 9, // 只会进行非 props 的比较DYNAMIC_SLOTS = 1 << 10, // 动态的插槽HOISTED = -1,  // 静态节点,diff阶段忽略其子节点BAIL = -2 // 代表 diff 应该结束
}
​

十、事件缓存 Vue3 的 cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。加一个click事件。

技术摸鱼

今天天气真不错

{{name}}

{}>​

复制代码 渲染函数如下

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from vue
​
const _withScopeId = n => (_pushScopeId(scope-id),n=n(),_popScopeId(),n)
const _hoisted_1 = { id: app }
const _hoisted_2 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(h1, null, 技术摸鱼, -1 /* HOISTED */))
const _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(p, null, 今天天气真不错, -1 /* HOISTED */))
const _hoisted_4 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(span, { onCLick: () => {} }, [/*#__PURE__*/_createElementVNode(span)
], -1 /* HOISTED */))
​
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createElementBlock(div, _hoisted_1, [_hoisted_2,_hoisted_3,_createElementVNode(div, null, _toDisplayString(_ctx.name), 1 /* TEXT */),_hoisted_4]))
}
​

十一、Diff 优化 搬运 Vue3 patchChildren 源码。结合上文与源码,patchFlag帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。

function patchChildren(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {// 获取新老孩子节点const c1 = n1 && n1.childrenconst c2 = n2.childrenconst prevShapeFlag = n1 ? n1.shapeFlag : 0const { patchFlag, shapeFlag } = n2
​// 处理 patchFlag 大于 0 if(patchFlag > 0) {if(patchFlag && PatchFlags.KEYED_FRAGMENT) {// 存在 keypatchKeyedChildren()return} els if(patchFlag && PatchFlags.UNKEYED_FRAGMENT) {// 不存在 keypatchUnkeyedChildren()return}}
​// 匹配是文本节点(静态):移除老节点,设置文本节点if(shapeFlag && ShapeFlags.TEXT_CHILDREN) {if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {unmountChildren(c1 as VNode[], parentComponent, parentSuspense)}if (c2 !== c1) {hostSetElementText(container, c2 as string)}} else {// 匹配新老 Vnode 是数组,则全量比较;否则移除当前所有的节点if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense,...)} else {unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)}} else {​if(prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {hostSetElementText(container, )} if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {mountChildren(c2 as VNodeArrayChildren, container,anchor,parentComponent,...)}}
​}
}
​

patchUnkeyedChildren 源码如下。

function patchUnkeyedChildren(c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {c1 = c1 || EMPTY_ARRc2 = c2 || EMPTY_ARRconst oldLength = c1.lengthconst newLength = c2.lengthconst commonLength = Math.min(oldLength, newLength)let ifor(i = 0; i < commonLength; i++) {// 如果新 Vnode 已经挂载,则直接 clone 一份,否则新建一个节点const nextChild = (c2[i] = optimized ? cloneIfMounted(c2[i] as Vnode)) : normalizeVnode(c2[i])patch()}if(oldLength > newLength) {// 移除多余的节点unmountedChildren()} else {// 创建新的节点mountChildren()}
​
}
​

patchKeyedChildren源码如下,有运用最长递增序列的算法思想。

function patchKeyedChildren(c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, optimized) {let i = 0;const e1 = c1.length - 1const e2 = c2.length - 1const l2 = c2.length
​// 从头开始遍历,若新老节点是同一节点,执行 patch 更新差异;否则,跳出循环 while(i <= e1 && i <= e2) {const n1 = c1[i]const n2 = c2[i]​if(isSameVnodeType) {patch(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)} else {break}i++
​}
​// 从尾开始遍历,若新老节点是同一节点,执行 patch 更新差异;否则,跳出循环 while(i <= e1 && i <= e2) {const n1 = c1[e1]const n2 = c2[e2]if(isSameVnodeType) {patch(n1, n2, container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)} else {break}e1--e2--}
​// 仅存在需要新增的节点if(i > e1) {    if(i <= e2) {const nextPos = e2 + 1const anchor = nextPos < l2 ? c2[nextPos] : parentAnchorwhile(i <= e2) {patch(null, c2[i], container, parentAnchor, parentComponent, parentSuspense, isSvg, optimized)}}}
​// 仅存在需要删除的节点else if(i > e2) {while(i <= e1) {unmount(c1[i], parentComponent, parentSuspense, true)    }}
​// 新旧节点均未遍历完// [i ... e1 + 1]: a b [c d e] f g// [i ... e2 + 1]: a b [e d c h] f g// i = 2, e1 = 4, e2 = 5else {const s1 = iconst s2 = i// 缓存新 Vnode 剩余节点 上例即{e: 2, d: 3, c: 4, h: 5}const keyToNewIndexMap = new Map()for (i = s2; i <= e2; i++) {const nextChild = (c2[i] = optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))​if (nextChild.key != null) {if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {warn(`Duplicate keys found during update:`,JSON.stringify(nextChild.key),`Make sure keys are unique.`)}keyToNewIndexMap.set(nextChild.key, i)}}
​}
​let j = 0// 记录即将 patch 的 新 Vnode 数量let patched = 0// 新 Vnode 剩余节点长度const toBePatched = e2 - s2 + 1// 是否移动标识let moved = falselet maxNewindexSoFar = 0
​// 初始化 新老节点的对应关系(用于后续最大递增序列算法)const newIndexToOldIndexMap = new Array(toBePatched)for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
​// 遍历老 Vnode 剩余节点for (i = s1; i <= e1; i++) {const prevChild = c1[i]​// 代表当前新 Vnode 都已patch,剩余旧 Vnode 移除即可if (patched >= toBePatched) {unmount(prevChild, parentComponent, parentSuspense, true)continue}let newIndex// 旧 Vnode 存在 key,则从 keyToNewIndexMap 获取if (prevChild.key != null) {newIndex = keyToNewIndexMap.get(prevChild.key)// 旧 Vnode 不存在 key,则遍历新 Vnode 获取} else {for (j = s2; j <= e2; j++) {if (newIndexToOldIndexMap[j - s2] === 0 && isSameVNodeType(prevChild, c2[j] as VNode)){newIndex = jbreak}}           
​}
​// 删除、更新节点// 新 Vnode 没有 当前节点,移除if (newIndex === undefined) {unmount(prevChild, parentComponent, parentSuspense, true)} else {// 旧 Vnode 的下标位置 + 1,存储到对应 新 Vnode 的 Map 中// + 1 处理是为了防止数组首位下标是 0 的情况,因为这里的 0 代表需创建新节点newIndexToOldIndexMap[newIndex - s2] = i + 1​// 若不是连续递增,则代表需要移动if (newIndex >= maxNewIndexSoFar) {maxNewIndexSoFar = newIndex} else {moved = true}patch(prevChild,c2[newIndex],...)patched++
​}}
​// 遍历结束,newIndexToOldIndexMap = {0:5, 1:4, 2:3, 3:0}// 新建、移动节点const increasingNewIndexSequence = moved// 获取最长递增序列? getSequence(newIndexToOldIndexMap): EMPTY_ARR
​j = increasingNewIndexSequence.length - 1
​for (i = toBePatched - 1; i >= 0; i--) {const nextIndex = s2 + iconst nextChild = c2[nextIndex] as VNodeconst anchor = extIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor// 0 新建 Vnodeif (newIndexToOldIndexMap[i] === 0) {patch(null,nextChild,...)} else if (moved) {// 移动节点if (j < 0 || i !== increasingNewIndexSequence[j]) {move(nextChild, container, anchor, MoveType.REORDER)} else {j--}}}
}
​

十二、打包优化 tree-shaking:模块打包webpack、rollup等中的概念。移除 JavaScript 上下文中未引用的代码。主要依赖于import和export语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。

以nextTick为例子,在 Vue2 中,全局 API 暴露在 Vue 实例上,即使未使用,也无法通过tree-shaking进行消除。

import Vue from  vue 
​
Vue.nextTick(() => {// 一些和DOM有关的东西
})
​

Vue3 中针对全局 和内部的API进行了重构,并考虑到tree-shaking的支持。因此,全局 API 现在只能作为ES模块构建的命名导出进行访问。

import { nextTick } from  vue 
​
nextTick(() => {// 一些和DOM有关的东西
})
​

通过这一更改,只要模块绑定器支持tree-shaking,则 Vue 应用程序中未使用的api将从最终的捆绑包中消除,获得最佳文件大小。受此更改影响的全局API有如下。

Vue.nextTick

Vue.observable (用 Vue.reactive 替换)

Vue.version

Vue.compile (仅全构建)

Vue.set (仅兼容构建)

Vue.delete (仅兼容构建)

内部 API 也有诸如 transition、v-model等标签或者指令被命名导出。只有在程序真正使用才会被捆绑打包。

根据 尤大 直播可以知道如今 Vue3 将所有运行功能打包也只有22.5kb,比 Vue2 轻量很多。

自定义渲染API Vue3 提供的createApp默认是将 template 映射成 html。但若想生成canvas时,就需要使用custom renderer api自定义render生成函数。

// 自定义runtime-render函数
import { createApp } from  ./runtime-render 
import App from  ./src/App 
​
createApp(App).mount( #app )

十三、TypeScript 支持 Vue3 由TS重写,相对于 Vue2 有更好地TypeScript支持。

Vue2 Option API中 option 是个简单对象,而TS是一种类型系统,面向对象的语法,不是特别匹配。

Vue2 需要vue-class-component强化vue原生组件,也需要vue-property-decorator增加更多结合Vue特性的装饰器,写法比较繁琐。

了解更多,我可关注wx视频号:石工记  DouY:石工记

相关文章:

vue3.0与vue2.0

一、生命周期的变化 1.vue2.响应式架构 2.vue3.0 响应式架构图 Vue3.0响应式框架在设计上&#xff0c;将视图渲染和数据响应式完全分离开来。将响应式核心方法effect从原有的Watcher中抽离。这样&#xff0c;当我们只需要监听数据响应某种逻辑回调(例如监听某个text属性的变化…...

HTML 中的常用标签用法

HTML是构建Web页面的基础语言&#xff0c;其中包含许多不同类型的标签。这些标签由尖括号包围&#xff0c;以指示浏览器如何呈现文本。下面是HTML中的一些常用标签以及它们的使用方法&#xff1a; 标题标签&#xff08;h1-h6&#xff09; 标题标签用于标识页面内容的标题&…...

【C++】指针 - 定义和使用,所占内存空间,空指针,野指针,const 修饰指针,指针和数组,指针和函数

文章目录 1. 定义和使用2. 所占内存空间3. 空指针4. 野指针5. const 修饰指针6. 指针和数组7. 指针和函数 1. 定义和使用 数据类型 * 变量名; 指针的作用是&#xff0c;可以通过指针间接访问内存。 内存编号是从 0 开始记录的&#xff0c;一般用十六进制数字表示。可以利用指…...

新规之下产业园区如何合理收费水电费用

一、政策背景 2018年3月30日&#xff0c;国家发改委发布《国家发展改革委关于降低一般工商业电价有关事项的通知》。明确提出进一步规范和降低电网环节收费&#xff0c;一是提高两部制电价的灵活性&#xff1b;二是全面清理规范电网企业在输配电价之外的收费项目&#xff0c;重…...

1011. 在 D 天内送达包裹的能力

传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天&#xff0c;我们都会按给出重量&#xff08;weights&#xff09;的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。 返回能在 days 天内将…...

基于SpringBoot养老院管理系统

目录 一、项目介绍 二. 运行环境 三、项目技术 四、部署项目 五、项目运行 六、项目展示 五、项目下载 一、项目介绍 基于springboot的养老院管理系统拥有多种角色账号&#xff1a;管理员和用户 管理员&#xff1a;管理员管理、用户管理、健康管理、病例方案管理、药品…...

1.3 eBPF的工作原理初探

写在前面 上一节提到过,eBPF程序是面向BPF体系结构指令集编写的,它并不直接运行在Linux内核中,我们可以理解为它是运行在eBPF虚拟机,由eBPF虚拟机来执行eBPF字节码,就像java运行在jvm一样。 我们用一张原理图来看下eBPF程序的编译,加载,验证,钩子,映射等结点。 如上是…...

【CH32】| 02——常用外设 | GPIO

系列文章目录 【CH32】| 00——开发环境搭建 【CH32】| 01——新建工程 | 下载 | 运行 |调试 【CH32】| 02——常用外设 | GPIO 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1. GPIO简介2. IO口的内部结构框图保护二极管上下拉电阻施密特触发器两个MOS管输出寄存器…...

第四章 测试用例编

本科程目标 1.什么是测试用例 2.测试用例的重要性 3.测试用例的八大要素&#xff08;重点&#xff09; 4.测试用例的评审 一、什么叫软件测试用例 测试用例&#xff08;TestCase&#xff09;是为项目需求而编制的一组测试输入、执行条件以及预期结果&#xff0c;以便测试…...

解决dpdk reserve的内存返回的虚拟地址和iova地址一样的问题

1. 背景: 在ubuntu20.04上用dpdk API: rte_memzone_reserve_aligned("L1L2_PCIE_MEMORY", 1.5*1024*1024*1024, rte_socket_id(), RTE_MEMZONE_1GB|RTE_MEMZONE_IOVA_CONTIG, RTE_CACHE_LINE_SIZE); 分配1.5…...

JQuery实现小项目

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、JQuery是什么 二、JQuery项目 2.1 猜数字 2.2 表白墙 2.3 聚合搜索 2.4 计算器 一、JQuery是什么 jQuery是一个快速、简洁的JavaScript框架&#xff0c;是继Prototype之…...

【C++/嵌入式笔试面试八股】一、23.结构体指针 | 指针和引用 | 万能指针 | 野指针

结构体指针 28.将结构体作为参数向函数中传递 传递方式有两种: 值传递地址传递,利用操作符 -> 可以通过结构体指针访问结构体属性//学生结构体定义 struct student {//成员列表string name; //姓名int age; //年龄int score; //分数 };//值传递...

【C++初阶】类和对象(下)构造函数(初始化列表) + explicit关键字 +static成员

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…...

chatgpt赋能python:Python代码怎么用?一个10年编程经验工程师的实践总结

Python代码怎么用&#xff1f;一个10年编程经验工程师的实践总结 如果你正在学习Python或已经是一名Python开发者&#xff0c;你需要知道如何正确地使用Python代码以实现项目需求。在本文中&#xff0c;我将分享我的10年Python编程经验&#xff0c;并介绍一些关于如何使用Pyth…...

【Android定制】修改BUILD_AGO_GMS = no 和 BUILD_GMS=no属性

文章目录 概要名词解释细节小结 概要 在安卓底层源码中&#xff0c;有这样的两个属性&#xff0c;这两个第一眼看上去都像是带不带谷歌&#xff0c;BUILD_AGO_GMS no和BUILD_GMSno有什么区别&#xff1f;&#xff1f; 如果带了谷歌&#xff0c;那么这个设备就差不多是国外定…...

第十章:C语言的调试

很多小伙伴刚开始听到C语言的调试&#xff0c;这是个啥&#xff0c;表示很怀疑&#xff0c;敲代码不就是直接就是干嘛&#xff0c;结果很多小白们&#xff0c;一运行错误多的数都数不过来。就开始这改改&#xff0c;那删删&#xff0c;莫名奇妙就运行成功了。到最后都不知道到底…...

【20】SCI易中期刊推荐——计算机信息系统工程电子与电气(中科院3区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…...

初识网络之UDP网络套接字

目录 一、UDP中的socket编程常用接口 1. socket的含义 2. sockaddr结构 3. socket编程中UDP协议常用接口介绍 3.1 创建socket文件描述符&#xff08;TCP/UDP、客户端 服务器&#xff09; 3.2 绑定端口号&#xff08;TCP/UDP&#xff0c;服务器&#xff09; 3.3 接收数据…...

数据中心末端配电的数字化方案及设备选型

普通PDU和智能PDU有什么区别&#xff1f; 机架安装配电盘或机架配电单元 (PDU) 是一种配备许多插座的设备&#xff0c;可将电力分配给位于数据中心机架或机柜内的服务器、存储设备和网络设备。领先的分析公司 IHS 将它们分为两大类&#xff1a; 1) 基本 PDU 提供可靠的配电。 2…...

k8s入门实战-Service

k8s入门实战-Service Service 和 Label Service 通过一组 Pod 路由通信。Service 是一种抽象&#xff0c;它允许 Pod 死亡并在 Kubernetes 中复制&#xff0c;而不会影响应用程序。在依赖的 Pod (如应用程序中的前端和后端组件)之间进行发现和路由是由Kubernetes Service 处理…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...