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

Vue3 核心模块源码解析(上)

Vue3相比大家也都有所了解,即使暂时没有使用上,但肯定也学习过!Vue3是使用TS进行重写,采用了MonoRepo的管理方式进行管理,本篇文章我们一起来看看 Vue3的使用,与Vue2有什么区别,以及我们该如何优雅的去使用?【中】篇会从源码的角度去学习,【下】篇主要是讲解Vue3的高频面试题,开始正文吧!!!

文章目录

    • 一、Vue2 与 Vue3响应式对比
      • 1. Vue2 的 Object.defineProperty
      • 2. Vue.set() 为什么可以解决上述问题?他具体经历了那些步骤你知道吗?
      • 3. 如何实现一个简单的 Vue2 响应式 ?
      • 2. Vue3 的 Proxy
    • 二、Vue3 新特性
        • Composition API
        • 1. 如何理解 setup ?
        • 2. 多根节点
          • 单文件的多根节点
          • 项目的多根节点——多个应用实例
        • 3. reactive() 与 shallowReactive()
        • 4. ref()、isRef() 、toRef()、toRefs()
        • 5. readOnly()、isReadonly() 、shallowReadonly()
        • 6. 生命周期
        • 7. 全局配置
        • 8. 异步组件
        • 9. Teleport
        • 9. 自定义 Hook
          • 举个栗子,自定义一个 Hook 来记录鼠标的位置

一、Vue2 与 Vue3响应式对比

Vue2 与 Vue3 最显著的差别就是响应式的差别,那么是什么原因导致 Vue3 的双向绑定原理采用了Proxy?我们下面来由浅入深的去了解一下。

1. Vue2 的 Object.defineProperty

基础使用:

const initData = { value: 1 };
const data = {};
Object.keys(initData).forEach(key => {Object.defineProperty(data, key, {get() {console.log('访问了', key);},set(v) {console.log('修改了', key);data[key] = v;}})
})data.value;
data.value = 2;
data;
initData.value2 = 2;
data.value2;

以上就是最基础的使用;
但是我们一起来看一下,下面几个问题会输出什么?

  1. 直接访问 data.value => 访问了 value
  2. 改变 data.value => 修改了 value
  3. 直接输出 data => 空对象: { }
  4. 给 initData 添加一个新值 => 输出新值结果:2
  5. data.value2 又会输出什么? => undefined

总结一下 Vue2 响应式弊端:给对象加属性和删除属性,响应式会检测不到。通常我们是使用 Vue.set( ) 来解决,那么面试官问 Vue.set( ) 为什么可以解决?他具体经历了那些步骤你知道吗?

2. Vue.set() 为什么可以解决上述问题?他具体经历了那些步骤你知道吗?

Vue.set(target, key, value)
// target 必须是一个响应式的数据源,在下面步骤会讲到

会经历一下三个步骤

对 target 进行数据校验
① 数据是 undefined、null或其他基本数据类型,会报错;
② 数据是 数组:则会取出当前数组的长度与当前 key 值的位置进行一个对比,取两者最大值,作为新数组的长度 -> max(target.length, key) ,然后使用 splice(key, 1, value); 当使用 splice 的时候,会自动遍历设置响应式。
③ 数据是 对象:key 是否在对象里,如果在则直接替换;如果不在则直接判断 target 是不是响应式对象;然后判断是不是 Vue 实例或者根的数据对象,如果是 throw error。如果不是直接给 target 的 key 赋值,如果 target 是响应式,使用 defineReactive 将新的属性添加到 target,进行依赖收集;

Vue.set( ) 源码

// example :
this.$set(data, a, 1);
function set(target: Array<any>  object, key: any, val: any): any {// isUndef 是判断 target 是不是等于 undefined 或者 nul1// isPrimitive 是判断 target 的数据类型是不是 string、number、symbol、boolean 中的一种if (process.env.NODE ENV !== 'production' &&(isUndef(target) isPrimitive(target))) {warn(`Cannot set readtive property on undefined, null, or primitive value: $((target: any))`)}// 数组的处理if (Array.isArray(target) && isValidArrayIndex(key)) {target.length = Math .max(target .length, key)target.splice(key,1, val)return val}// 对象,并且该属性原来已存在于对象中,则直接更新if (key in target && !(key in object.prototype)) {target[key] = valreturn val}// vue给响应式对象(比如 data 里定义的对象)都加了一个  ob  属性,// 如果一个对象有这个 ob属性,那么就说明这个对象是响应式对象,修改对象已有属性的时候就会触发页面渲染// 非 data 里定义的就不是响应式对象。const ob = (target: any).__ob__if (target. isVue  (ob && ob.vmCount)) {process.env.NODE ENV !== 'production' && warn('Avoid adding reactive properties to a Vue instance or its root $data' +'at runtime - declare it upfront in the data option.!'return val}// 不是响应式对象if (!ob) {target[key] = valreturn val}// 是响应式对象,进行依赖收集defineReactive(ob.value, key, val)// 触发更新视图ob.dep.notify()return val
}

3. 如何实现一个简单的 Vue2 响应式 ?

export function Vue(options) {this.__init(options);
}// initMixin
Vue.prototype.__init = function (options) {this.$options = options;// 假如这里是一个字符串,就需要使用 document.querySelector 去获取this.$el = options.el;this.$data = options.data;this.$methods = options.methods;// beforeCreate -- initState -- initDataproxy(this, this.$data);// Object.definePropertyobserver(this.$data);new Compiler(this);
};// this.$data.message ---> this.message
function proxy(target, data) {let that = this;Object.keys(data).forEach((key) => {Object.defineProperty(target, key, {enumerable: true,configurable: true,get() {return data[key];},set(newVal) {// 考虑 NaN 的情况// this 指向已经改变if (!isSameVal(data[key], newVal)) {data[key] = newVal;}},});});
}function observer(data) {new Observer(data);
}class Observer {constructor(data) {this.walk(data);}walk(data) {if (data && typeof data === "object") {Object.keys(data).forEach((key) =>this.defineReactive(data, key, data[key]));}}//要把 data 里面的数据,收集起来defineReactive(obj, key, value) {let that = this;this.walk(value);let dep = new Dep();Object.defineProperty(obj, key, {enumerable: true,configurable: true,get() {// get时 Dep 收集依赖// 4. 对于 num 来说,就要执行这一句// 5. num 中的 dep,就有了这个 watcherDep.target && dep.add(Dep.target);return value;},set(newVal) {if (!isSameVal(value, newVal)) {//赋值进来的新值,是没有响应式的,所以我要在 walk 一次,添加响应式value = newVal;that.walk(newVal);// 重新 set时,notify 通知更新// 6.dep.notify();}},});}
}// 视图怎么更新?
// 数据改变,视图才会更新。需要去观察
// 1. new Watcher(vm, 'num', ()=>{ 更新视图上的 num 显示 })
class Watcher {constructor(vm, key, callback) {this.vm = vm; // VUE 的一个实例this.key = key;this.callback = callback;// 2. 此时 Dep.target 作为一个全局变量理解,放的就是就是 watcherDep.target = this;// 3. 一旦进行了这一句赋值,是不是就触发了这个值的 getter 函数this.__old = vm[key];Dep.target = null;}// 8. 执行所有的 callback 函数update() {let newVal = this.vm[this.key];if (!isSameVal(newVal, this.__old)) this.callback(newVal);}
}// 每一个数据都要有一个 Dep 依赖
class Dep {constructor() {this.watchers = new Set();}add(watcher) {if (watcher && watcher.update) this.watchers.add(watcher);}// 7. 让所有的 watcher 执行 update 方法notify() {this.watchers.forEach((watch) => watch.update());}
}class Compiler {constructor(vm) {this.vm = vm;this.el = vm.$el;this.methods = vm.$methods;this.compile(vm.$el);}// 这里是递归编译 #app 下面的所有的节点内容compile(el) {let childNodes = el.childNodes;// childNodes 为类数组Array.from(childNodes).forEach((node) => {// 判断如果是文本节点if (node.nodeType === 3) {this.compileText(node);}// 判断如果是元素节点else if (node.nodeType === 1) {this.compileElement(node);}// 判断如果还有子节点,就递归下去if (node.childNodes && node.childNodes.length) this.compile(node);});}compileText(node) {// 匹配出来 messagelet reg = /\{\{(.+?)\}\}/;let value = node.textContent;if (reg.test(value)) {let key = RegExp.$1.trim();// 开始时赋值node.textContent = value.replace(reg, this.vm[key]);// 给 message 添加观察者new Watcher(this.vm, key, (val) => {// 数据改变时更新node.textContent = val;});}}compileElement(node) {if (node.attributes.length) {Array.from(node.attributes).forEach((attr) => {let attrName = attr.name;if (attrName.startsWith("v-")) {// v- 指定匹配成功,可能是 v-on:click 或者 v-model// 假设我们这里就处理两个指令,Vue源码对这一块是有特殊处理的attrName =attrName.indexOf(":") > -1? attrName.substr(5): attrName.substr(2);let key = attr.value;this.update(node, key, attrName, this.vm[key]);}});}}update(node, key, attrName, value) {if (attrName === "model") {node.value = value;new Watcher(this.vm, key, (val) => (node.value = val));node.addEventListener("input", () => {this.vm[key] = node.value;});} else if (attrName === "click") {node.addEventListener(attrName, this.methods[key].bind(this.vm));}}
}function isSameVal(a, b) {return a === b || (Number.isNaN(a) && Number.isNaN(b));
}

Vue2 的响应式我们简单介绍一下,下来一起来看 Vue3 的 Proxy!

2. Vue3 的 Proxy

Proxy:代理或拦截器,Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写;Proxy的思路和 React 的 HOC 很像,组件外面包裹一层,对外界的访问进行过滤和改写;

const initData = {value:1};
const proxy = new Proxy(initData, {get(target, key, receiver) {console.log('访问了', key);return Reflect.get(target, key, receiver);},set(target, key, value, receiver){console.log('修改了', key);return Reflect.set(target, key, value, receiver);}
})proxy.value;
proxy.value = 2;
proxy;
proxy.value2 = 2;
proxy.value2;

具体这里就不详细说了,感兴趣的大家可以移步下面链接:
Vue3中的响应式原理,为什么使用Proxy(代理) 与 Reflect(反射)

二、Vue3 新特性

Composition API

composition api : 组合式 api,通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与 < script setup> 搭配使用。这个 setup attribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如, < script setup> 中的导入和顶层变量/函数都能够在模板中直接使用。

1. 如何理解 setup ?

通俗一点的讲,setup 可以把他理解为 Vue3 组件模块的入口文件,Vue3 中组件的新特性 ,作为组件统一的入口支持;

未使用 setup 语法糖的写法 (了解即可,实际开发还是使用语法糖写法更加便捷):

setup(props, context){context.attrs  -->  this.$attrscontext.slot  -->  this.$slotcontext.emit  -->  this.$emitcontext.expose
}

使用 setup 语法糖写法

<script setup>
// 变量
const msg = 'Hello!'// 函数
function log() {console.log(msg)
}
</script><template><button @click="log">{{ msg }}</button>
</template>

为什么推荐使用 setup 语法糖?

  1. 更少的样板内容,更简洁的代码。
  2. 能够使用纯 TypeScript 声明 props 和自定义事件。
  3. 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
  4. 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。

setup 是在 beforeCreate 和 created 之前去执行

2. 多根节点

什么是多根节点呢?看下图代码

单文件的多根节点

vue3 中之所以可以有多个节点,是因为引入了Fragment的概念,这是一个抽象的节点,如果发现组件有多个根,就创建一个Fragment节点,把多个根节点作为它的children,将来path的时候,如果发现是一个Fragement节点,则直接遍历children创建或更新。

在这里插入图片描述

项目的多根节点——多个应用实例

应用实例并不只限于一个。createApp API 允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

如果你正在使用 Vue 来增强服务端渲染 HTML,并且只想要 Vue 去控制一个大型页面中特殊的一小部分,应避免将一个单独的 Vue 应用实例挂载到整个页面上,而是应该创建多个小的应用实例,将它们分别挂载到所需的元素上去。

3. reactive() 与 shallowReactive()

reactive:通过 proxy 声明一个深层的响应式对象,响应式是深层次的,会影响所有嵌套。 等同于 Vue2 的 Vue.observable()

const person = {name: 'Barry',age: 18,contacts: {phone: 1873770}
}const personReactive = reactive(person);
console.log(personReactive); // proxy
const contacts = personReactive.contacts;
console.log(contacts); // proxy

shallowReactive:和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。

const person = {name: 'Barry',age: 18,contacts: {phone: 1873770}
}const personshallowReactive = shallowReactive(person);
console.log(personshallowReactive); // proxy
const contactsShallowReactive = personshallowReactive.contacts
console.log(contactsShallowReactive); // no proxy

4. ref()、isRef() 、toRef()、toRefs()

ref:返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value ,所以我们想要修改 ref 声明的响应式数据需要带上 .value ;
如果将一个对象赋值给 ref, 那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref 它们将被深层地解包。

const count = ref(10);
const cObj = reactive({a: 100,count
})console.log(cObj.count);
console.log(cObj.count === count.value);
count.value = 20;
console.log(count.value, cObj.count);
cObj.count = 30;
console.log(count.value, cObj.count);

isRef:检查某个值是否为 ref。

使用 ref 或者 reactive 声明的响应式数据,通过 结构会失去响应式

解决办法:
1. 使用 ref 声明的响应式可以通过 toRef() API
2. 使用 reactive 声明的响应式可以通过 toRefs API

toRef:基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值

const state = reactive({foo: 1,bar: 2
})const fooRef = toRef(state, 'foo')// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

toRefs:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

const state = reactive({foo: 1,bar: 2
})const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{foo: Ref<number>,bar: Ref<number>
}
*/// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2stateAsRefs.foo.value++
console.log(state.foo) // 3

5. readOnly()、isReadonly() 、shallowReadonly()

readOnly:类似于 Object.freeze() 的效果,把一个响应式对象变成一个只读的对象,只读代理是深层的:对任何嵌套属性的访问都将是只读的。会递归的去阻止 Proxy set 的触发,【中】篇会从源码的角度去学习 中间会讲到 readOnly 源码是如何处理的

const original = reactive({ count: 0 })const copy = readonly(original)watchEffect(() => {// 用来做响应性追踪console.log(copy.count)
})// 更改源属性会触发其依赖的侦听器
original.count++// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!

isReadonly:检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值。
通俗一点讲就是检查你传入的值,是否由 readonly 创建出来的

function isReadonly(value: unknown): boolean

shallowReadonly readonly() 的浅层作用形式
这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着 值为 ref 的属性不会被自动解包了

const state = shallowReadonly({foo: 1,nested: {bar: 2}
})// 更改状态自身的属性会失败
state.foo++// ...但可以更改下层嵌套对象
isReadonly(state.nested) // false// 这是可以通过的
state.nested.bar++

6. 生命周期

新版的生命周期函数,可以按需导入到组件中,且只能在 setup 函数中使用,但是也可以在 setup 外定义,在 setup 中使用;
setup 是围绕 beforeCreatecreated 生命周期钩子运行的,所以不需要显式的定义它们。换句话说在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

选项式 API组合式 API
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonBeforeUpdate
beforeUnmountonBeforeUnmount
ummountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactived

注意,若要在 setup 中引入,需要 vue 中引入对应 hook
官网图片
官网生命周期钩子地址

7. 全局配置

Vue2 中我们通常是使用 Vue.property.xxx = xxx 的方式定义,
第一个弊端 :全局配置很容易意外的污染其他测试用例
第二个弊端 :全局配置使得同一个页面上的多个“应用”在全局配置不同时共享同一个 Vue 副本非常困难;

Vue3 我们是利用: app.config.globalProperties.xxx= xxx 的方式来实现的。通过 Vue 实例上 config 来配置,包含Vue应用程序全局配置的对象,您可以在挂载应用程序之前修改对应的属性;
具体点击右侧链接查看:Vue3中全局配置 axios 的两种方式

可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。同时,可以在组件通过 getCurrentInstance() 来获取全局 globalProperties 中配置的信息, getCurrentInstance 用于获取当前的组件实例,然后通过 ctx 属性获得当前上下文,这样我们就可以在 setup 中使用。

const app = Vue.createApp({});
app.config = {......}
app.config.globalProperties.$htpp = xxx;app.config.errorhandler = (err, vm, info) => {}const { ctx } = getCurrentInstance();
ctx.$http

8. 异步组件

异步组件:在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能;

全局注册

// 可以利用返回值的实例去自定义异步组件,在那个应用里生效,在那个应用里注册
const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComp.vue'));
app.component('async-comp', AsyncComp)

局部注册

// main.js
const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComp.vue'));
// app.vue
import AsyncComp  from './components/AsyncComp.vue';
{
components: 'async-comp', AsyncComp
}

异步组件的作用:

  1. 打包后不会集成在 index.js 中,会单独进行打包,方便后续操作,可以进行缓存,如多个页面都使用一个相同的组件,可以将打包文件缓存下来;
  2. 如果组件包过大,可以使用 loading 代替显示;

Vue3 支持 suspense,< Suspense > 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
< Suspense > 组件有两个插槽:#default 和 #fallback。两个插槽都只允许一个直接子节点。在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。
React V16.6.0 中,官方提出了lazy suspense 组件

<Suspense><!-- 具有深层异步依赖的组件 --><Dashboard /><!-- 在 #fallback 插槽中显示 “正在加载中” --><template #fallback>Loading...</template>
</Suspense>

9. Teleport

< Teleport> 是一个内置组件,可以将子节点渲染到存在于父组件以外的 DOM 节点的方案

当处理某些类型的组件(如模式,通知或提示) 时,模板HTML的逻辑可能位于与我们希望染元素的位置不同的文件中
很多时候,与我们的 Vue 应用程序 DOM 完全分开处理时,这些元素的管理要容易得多。所有这些都是因为处理嵌套组件的位置,z-index 和样式可能由于处理其所有父对象的范围而变得棘手。这种情况就是 Teleport 派上用场的地方。我们可以在逻辑所在的组件中编写模板代码,这意味着我们可以使用组件的数据或 props。 但是,然后完全将其渲染到我们Vue应用程序的范围之外。

<button @click="open = true">Open Modal</button><Teleport to="body"><div v-if="open" class="modal"><p>Hello from the modal!</p><button @click="open = false">Close</button></div>
</Teleport>

9. 自定义 Hook

Vue3 的 hooks 其实可以参考 React 的自定义 hooks 的定义,在 React 中,在函数组件中保留 state 数据的同时,融入生命周期函数,将组件整体作为一个钩子函数。
当组件复杂时,多个组件中一些重复的逻辑可以被抽象出来。在 Hook 诞生之前,React 和 Vue 都拥有高阶组件的设计模式,在 React 使用到 HOC,在 Vue 2 中使用到 mixin。为什么要舍弃它们而使用 Hook,使用自定义 Hook又有哪些优点,我们先简单了解一下 HOC 和 mixin ,对比后便知。

在这里插入图片描述
HOC 的原理是把组件作为参数传入一个函数,加入复用部分后将新的组件作为返回值,使用了装饰器模式mixin 像是把复用的部分拆解成一个个小零件,某个组件需要时就拼接进去
在实践中,mixin 有如下缺点:
1.引入了隐式依赖关系。
2不同 mixins之间可能会有先后顺序甚至代码冲突覆盖的问题
3.mixin 代码会导致滚雪球式的复杂性
4多个 mixin 导致合并项不明来源
为了避开这些问题,React 采用 HOC,但它依然存在缺陷
1.一个组件的state影响许多组件的props
2.造成地狱嵌套
不过使用全新的 Hook 组件结构,可以实现平铺式调用组件的复用部分,解决了 mixin 的来源不明和 HOC 的地狱嵌套问题。

举个栗子,自定义一个 Hook 来记录鼠标的位置

Tips: 一般我们的自定义 Hook 都需要使用 use 开头!

// src/hooks/useMousePosition. ts
import { ref, onMounted, onUnmounted, Ref } from 'vue
function useMousePosition() {const x = ref(0)const y = ref(0)const updateMouse = (e) => {x.value = e.pageXy.value = e.pageY}onMounted(() => {document.addEventListener('click', updateMouse)})onUnmounted(() => {document.removeEventListener('click', updateMouse)})return { x, y }
}
export default useMousePosition
<template><div><p>X: {{ x }}</p><p>Y: {{ y }}</p> I</div>
</template>
<script lang="ts">
import { defineComponent} from 'vue'
//引入hooks
import useMousePosition from ' ../ ../hooks/useMousePosition'
export default defineComponent({setup () {//使用hooks功能const { x, y} = useMousePosition()return {X,}}
})
</script>


结语:【Vue3 核心模块源码解析(上)】到此结束,此篇还是以Vue2的部分回顾,加上Vue3的新特性的基础使用,以及部分见解,有不对的地方欢迎大家及时指出,本文到此结束!!!

相关文章:

Vue3 核心模块源码解析(上)

Vue3相比大家也都有所了解&#xff0c;即使暂时没有使用上&#xff0c;但肯定也学习过&#xff01;Vue3是使用TS进行重写&#xff0c;采用了MonoRepo的管理方式进行管理&#xff0c;本篇文章我们一起来看看 Vue3的使用&#xff0c;与Vue2有什么区别&#xff0c;以及我们该如何优…...

【C进阶】指针的高级话题

文章目录:star:1. 字符指针:star:2. 指针数组2.1 指针数组的定义2.2 指针数组的使用:star:3. 数组指针3.1 数组的地址3.2 数组指针的使用:star:4. 数组参数和指针参数:star:5. 函数指针5.1 函数名和函数的地址5.2 练习:star:6. 函数指针数组6.1 转移表:star:7. 指向函数指针数组…...

无源晶振匹配电容—计算方法

以前有写过一篇文章“晶振”简单介绍了晶振的一些简单参数&#xff0c;今天我们来说下无源晶振的匹配电容计算方法&#xff1a; 如上图&#xff0c;是常见的的无源晶振常见接法&#xff0c;而今天来说到就是这种常见电路的电容计算方法&#xff0c;有两种&#xff1a; A&#…...

【测试】自动化测试03(JUnit)

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录JUnit一&#xff09; 注解1. Test2. BeforeEach3. BeforeAll4. AfterEach5. AfterAll二&#xff09; 断言&#xff08;Assertions类&#xff09;三&#xff09;用例的执行顺序四&#xff09;参数化五&#xff09;测试…...

《计算机视觉和图像处理简介 - 中英双语版》:神经网络中的激活函数 ReLU vs Sigmoid

文章大纲 Neural Network Module and Training Function创建数据集Define Neural Network, Criterion function, Optimizer and Train the ModelTest Sigmoid and ReluAnalyze Results参考文献与学习路径在本文中,我们使用含有两个隐藏层的神经网络基于MNIST数据集测试Sigmoid…...

(三十七)大白话SQL标准中对事务的4个隔离级别,都是如何规定的呢?

之前我们给大家讲了数据库中多个事务并发时可能产生的几种问题&#xff0c;包括了脏写、脏读、不可重复读、幻读&#xff0c;几种问题 那么针对这些多事务并发的问题&#xff0c;实际上SQL标准中就规定了事务的几种隔离级别&#xff0c;用来解决这些问题。 注意一下&#xff…...

全国计算机等级考试三级网络技术考试大纲(2022年版)

全国计算机等级考试三级网络技术考试大纲&#xff08;2022年版&#xff09;基本要求 &#xff11;&#xff0e; 了解大型网络系统规划、管理方法&#xff1b; &#xff12;&#xff0e; 具备中小型网络系统规划、设计的基本能力&#xff1b; &#xff13;&#xff0e; 掌握中小…...

服务器部署—若依【vue】如何部署到nginx里面?nginx刷新页面404怎么办?【完美解决建议收藏】

服务器部署项目我们大家都会遇到&#xff0c;但是有些铁子会遇到很多的问题&#xff0c;比如前端部署nginx如何操作&#xff1f; 前端有单纯的静态页面、还有前后端分离的项目&#xff1b;这里博主直接分享最牛最到位的前后端分离项目的前端部署到nginx上面&#xff0c;以若依项…...

算法练习(特辑)算法常用的数据结构、集合和方法总结

一、栈stack 1、初始化&#xff1a;Stack<Integer> st new Stack<Integer>(); 2、常用方法&#xff1a; boolean empty() &#xff1a;测试堆栈是否为空。Object peek( )&#xff1a;查看堆栈顶部的对象&#xff0c;但不从堆栈中移除它。Object pop( )&#xff…...

Apk转Aab(Android-App-Bundle)

这篇文章是参考Apk转Aab(Android-App-Bundle)_YoungBillsohu的博客-CSDN博客 基本照着这个大佬的步骤来就行&#xff0c;但是要注意的是apkTool最好是下新的&#xff0c;否则&#xff0c;会出现说一堆无语的错误&#xff0c;然后导致AAPT2关联资源的时候报错 类似这样的&#…...

大学物理期末大题专题训练总结-热学大题

今天下午去找郑老师权老师等去答疑&#xff0c;老师说大题会考查得比较套路&#xff0c;计算不难。明天就要考试了&#xff0c;再把大题常见题型总结一下&#xff0c;热学这块我做完了蓝本的热学题目&#xff0c;发现了如下三种&#xff1a;有关循环过程曲线的&#xff1a;给出…...

有趣的Hack-A-Sat黑掉卫星挑战赛——卫星平台内存dump

国家太空安全是国家安全在空间领域的表现。随着太空技术在政治、经济、军事、文化等各个领域的应用不断增加&#xff0c;太空已经成为国家赖以生存与发展的命脉之一&#xff0c;凝聚着巨大的国家利益&#xff0c;太空安全的重要性日益凸显[1]。而在信息化时代&#xff0c;太空安…...

OAK相机如何将yoloV8模型转换成blob格式?

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…...

Python解题 - CSDN周赛第32期 - 运输石油(三维背包)

上期周赛因为最后一题出现bug&#xff0c;再加上都是经典的模板题&#xff0c;问哥就懒得写题解了。 本期也是有两道考过的题目&#xff0c;不过最后一题因为考到了背包问题的特殊类型&#xff0c;还是值得拿出来记个笔记。 第一题&#xff1a;传奇霸业 传奇霸业&#xff0c;是…...

JVM - G1垃圾收集器深入剖析

​​​​​​​1、G1收集器概述 HotSpot团队一直努力朝着高效收集、减少停顿(STW: Stop The World)的方向努力&#xff0c;也贡献了从串行Serial收集器、到并行收集器Parallerl收集器&#xff0c;再到CMS并发收集器&#xff0c;乃至如今的G1在内的一系列优秀的垃圾收集器。 G…...

角度制与弧度制的相互转换np.deg2radnp.rad2deg

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】角度制与弧度制的相互转换np.deg2radnp.rad2deg选择题以下关于python代码表述错误的一项是?import numpy as npprint("【执行】np.rad2deg(np.pi)")print(np.rad2deg(np.pi))print(&…...

【SAP Abap】X-DOC:SAP ABAP 语法更新之一(Open SQL新增特性)

SAP ABAP 语法更新之一&#xff08;Open SQL新增特性&#xff09;1、前言2、演示1、前言 自从 SAP 推出 SAP ON HANA&#xff0c;与之相随的 AS ABAP NW 7.40 版本以后&#xff0c;ABAP 语法也有了较多的更新&#xff0c;本篇对 Open Sql的语法更新部分做一个DEMO演示。 NW 7…...

【改进灰狼优化算法】改进收敛因子和比例权重的灰狼优化算法【期刊论文完美复现】(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...

Linux C代码获取线程ID

Linux C代码获取线程ID gettid可以获取线程id,但是通过man gettid可以看到下面这两句 也就是说glibc没有为这个gettid封装系统调用&#xff0c;需要使用syscall。 #define _GNU_SOURCE#include <unistd.h>#include <sys/syscall.h>#include <sys/types.h>pi…...

基本密码技术

AESAES取代DES&#xff0c;是一种对称加密技术&#xff0c;分为AES-128/192/256, 其分组长度固定为128b&#xff0c;若最后一个分组长度不够&#xff0c;需要补全至128b长度。所支持的秘钥长度分别为128b/192b/256b.分组密码模式AES是对明文进行分组之后逐块进行加密&#xff0…...

【力扣周赛#334】6369. 左右元素和的差值 + 6368. 找出字符串的可整除数组 + 6367. 求出最多标记下标

目录 6369. 左右元素和的差值 - 前缀后缀和 ac 6368. 找出字符串的可整除数组 - 操作余数ac 6367. 求出最多标记下标 - 二分答案 贪心 6369. 左右元素和的差值 - 前缀后缀和 ac class Solution {public int[] leftRigthDifference(int[] nums) {int nnums.length;int[] re…...

行测-判断推理-图形推理-位置规律-平移

位置平移&#xff0c;选D空白每次顺时针移动一格&#xff0c;黑色圆每次逆时针移动2格选C两个黑色⚪&#xff0c;每次顺时针移动2格白色⚪&#xff0c;先到对角位置&#xff0c;再顺时针移动一格选B三角形的底&#xff0c;顺时针移动三角形的顶点&#xff0c;在正方形的内部顺时…...

数据库基础知识(一)

目录 什么是数据库 表&#xff0c;列&#xff0c;行 主键 什么是SQL 什么是数据库 数据库(database):保存有组织的数据的容器&#xff08;通常是一个文件或一组文件&#xff09;。 数据库软件(DMBS):又名数据库管理系统。数据库是通过数据库软件创建和操纵的容器。因为你并…...

MyBatis 的工作原理解析

文章目录前言一、mybatis工作原理1.1 流程图1.2 步骤解析1.3 代码实现前言 本文记录 Mybatis 的工作原理&#xff0c;做到知识梳理总结的作用。 一、mybatis工作原理 Mybatis 的总体工作原理流程图如下图所示 1.1 流程图 1.2 步骤解析 Mybatis 框架在工作时大致经过8个步骤…...

终端软件架构说

目录 零&#xff1a;前言 一&#xff0c;基于服务的架构 二&#xff0c;基于多进程多线程的架构 三&#xff0c;以数据为中心的架构 四&#xff0c;类Android的分层架构设计 五&#xff0c;总结 零&#xff1a;前言 谈到架构&#xff0c;可能大家的第一感觉是信息系统的…...

LearnOpenGL-入门-你好,三角形

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject LearnOpenGL中文官网&#xff1a;https://learnopengl-cn.github.io/ 文章目录图形渲染管线基本介绍着色器…...

SOEM 源码解析 ecx_init_redundant

/* Initialise lib in redundant NIC mode* 在冗余网卡模式下初始化lib库* param[in] context context struct* 上下文结构体* param[in] redport pointer to redport, redundant port data* 指向冗余端口的指针&#xff…...

网页唤起 APP中Activity的实现原理

疑问的开端大家有没有想过一个问题&#xff1a;在浏览器里打开某个网页&#xff0c;网页上有一个按钮点击可以唤起App。这样的效果是怎么实现的呢&#xff1f;浏览器是一个app&#xff1b;为什么一个app可以调起其他app的页面&#xff1f;说到跨app的页面调用&#xff0c;大家是…...

【操作系统】概述

基本特征 1. 并发 并发是指宏观上在一段时间内能同时运行多个程序&#xff0c;而并行则指同一时刻能运行多个指令。 并行需要硬件支持&#xff0c;如多流水线、多核处理器或者分布式计算系统。 操作系统通过引入进程和线程&#xff0c;使得程序能够并发运行 2. 共享 共享…...

Flume三种组件的选择对比

文章目录1.source2.channel3.sink1.source Source: 数据源:通过source组件可以指定让Flume读取哪里的数据&#xff0c;然后将数据传递给后面的 channel Flume内置支持读取很多种数据源&#xff0c;基于文件、基于目录、基于TCP\UDP端口、基于HTTP、Kafka的 等等、当然了&#x…...

什么是网站流量优化/软文发稿平台有哪些

文章目录1. 编码与调制编码调制1. 编码与调制 基带信号&#xff1a;将数字信号1和0直接用两种不同的电压表示&#xff0c;再送到数字信道上去传输&#xff08;基带传输) 宽带信号&#xff1a;将基带信号进行调制后形成的频分复用模拟信号&#xff0c;再传送到模拟信道上去传输…...

建站行业现状探讨/优化网站有哪些方法

隐式意图在很多app中应用广泛&#xff0c;使用隐式意图不仅可以启动自己程序中的Activity&#xff0c;还可以启动其他程序中的Activity&#xff0c;使得程序之间可以共享某种功能。以下将对于用过的setAction()的方法作以归纳整理&#xff0c;持续更新… 1、android.intent.ac…...

怎样做模具钢网站/seo范畴有哪些

注意 presto中都是用单引号: select dt from tb1 where dt date 2021-02-01;...

那个网站做问卷好/苏州企业网站关键词优化

在前面随笔《C#开发微信门户及应用(41)--基于微信开放平台的扫码登录处理》介绍了基于微信开放平台接口实现的微信扫码直接登录的过程。本篇介绍对扫码登录的一些改进和处理&#xff0c;以便更方便应用在实际项目中。 1、扫码登录配置处理 在我前面的随笔扫码登录处理中&#x…...

做网站的域名/张家港seo建站

WPF 程序出现&#xff1a; 参数计数不匹配&#xff0c;未处理System.Reflection.TargetParameterCountException解决方法引用http://www.cnblogs.com/wene/p/4668830.html根据调试的实际情况显示&#xff0c;委托出现问题&#xff0c;此异常是在使用Invoke调用时&#xff0c;没…...

做网站 先上线再调整/2021年最为成功的营销案例

可以将JAVA变量在JSP头部定义成全局变量。 然后可以分别在JS和JSP里调用。 因为可以将JS和JSP理解为两个相互独立的块。这2个块里面的变量都是局部变量。 而局部变量是不共享的。 所以没法取到。...