学习VUE3——组件(一)

组件注册

分为全局注册和局部注册两种。

全局注册:

main.jsmain.ts中,使用 Vue 应用实例的 .component() 方法,让组件在当前 Vue 应用中全局可用。

import { createApp } from 'vue'
import MyComponent from './App.vue'const app = createApp({})app.component('MyComponent', MyComponent)

.component() 方法可以被链式调用:

app.component('ComponentA', ComponentA).component('ComponentB', ComponentB).component('ComponentC', ComponentC)

全局注册后,组件可以在此应用的任意组件的模板中使用:

<!-- 这在当前应用的任意组件中都可用 -->
<ComponentA/>
<ComponentB/>
<ComponentC/>

全局注册存在的问题:

  1. 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
  2. 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

局部组件:

一、在使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册:

<script setup>
import ComponentA from './ComponentA.vue'
</script><template><ComponentA />
</template>

二、如果没有使用 <script setup>,则需要使用 components 选项来显式注册:

import ComponentA from './ComponentA.js'export default {components: {ComponentA: ComponentA // 根据ES2015可直接缩写成ComponentA},setup() {// ...}
}

Tips:

局部注册的组件仅在当前组件中可用,在后代组件中不可用。

组件数据传递

props接收声明

在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏(所有的宏都无需引入)来声明:

<script setup>
const props = defineProps(['foo'])console.log(props.foo)
</script>

没有使用 <script setup> 的组件中,props 可以使用 props 选项来声明:

export default {props: ['foo'],setup(props) {// setup() 接收 props 作为第一个参数console.log(props.foo)}
}

除了使用字符串数组来声明 prop 外,还可以使用对象的形式,并声明prop项的类型:

// 对于以对象形式声明的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。
// 使用 <script setup>
defineProps({title: [String, Number], // 字符串或数字类型likes: Number
})
// 非 <script setup>
export default {props: {title: [String, Number],likes: Number}
}// 在ts中也可以使用类型标注来声明
<script setup lang="ts">defineProps<{title?: stringlikes?: number}>()
</script>

Prop 名字格式

为了和 HTML attribute 对齐,我们通常在向子组件传递 props时,会将其写为 kebab-case 形式:

<MyComponent greeting-message="hello" />

还可以传入动态值:

<!-- 根据一个变量的值动态传入 -->
<BlogPost :likes="post.likes" /><!-- 仅写上 prop 但不传值,会隐式转换为 `true` -->
<BlogPost is-published />

使用一个对象绑定多个 prop

如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。如下所示:

const post = {id: 1,title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
// 等价于
<BlogPost :id="post.id" :title="post.title" />

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。如果强行在子组件上修改props,Vue 会在控制台上向你抛出警告。

如果实在有修改的需求,可以用prop作为初始值后,新定义一个局部数据属性:

const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)

后子组件通过抛出事件defineEmits,来通知父组件同步更改的传入的prop。

Prop校验

写了类型的声明时,如果传入的值不满足类型要求,Vue 会在浏览器控制台中抛出警告来提醒使用者。这在开发给其他开发者使用的组件时非常有用。

声明对 props 的校验有如下方法:

defineProps({// 基础类型检查// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)propA: Number,// 多种可能的类型propB: [String, Number],// 必传,且为 String 类型propC: {type: String,required: true},// 必传但可为空的字符串propD: {type: [String, null],required: true},// Number 类型的默认值propE: {type: Number,default: 100},// 对象类型的默认值propF: {type: Object,// 对象或数组的默认值// 必须从一个工厂函数返回。// 该函数接收组件所接收到的原始 prop 作为参数。default(rawProps) {return { message: 'hello' }}// 可简写为default: () => ({ message: 'hello' })},// 自定义类型校验函数// 在 3.4+ 中完整的 props 作为第二个参数传入propG: {validator(value, props) {// The value must match one of these stringsreturn ['success', 'warning', 'danger'].includes(value)}},// 函数类型的默认值propH: {type: Function,// 不像对象或数组的默认,这不是一个// 工厂函数。这会是一个用来作为默认值的函数default() {return 'Default function'}}
})

vue3+TS时,语法如下:

export interface Props {msg?: stringlabels?: string[]
}const props = withDefaults(defineProps<Props>(), {msg: 'hello',labels: () => ['one', 'two']
})

Tips:

  • defineProps() 宏中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中
  • 所有 prop 默认都是可选的,除非声明了 required: true
  • Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
  • Boolean 类型的未传递 prop 将被转换为 false。这可以通过为它设置 default 来更改——例如:设置为 default: undefined 将与非布尔类型的 prop 的行为保持一致。
  • 如果声明了 default 值,那么在 prop 的值被解析为 undefined 时,无论 prop 是未被传递还是显式指明的 undefined,都会改为 default 值

运行时类型检查

Vue 主要通过 instanceof 来检查类型是否匹配。

如果 type 仅为 null 而非使用数组语法,它将允许任何类型。

校验中的type可以是哪些:

1、构造函数

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • Error

2、type 也可以是自定义的类或构造函数

例子:

class Person {constructor(firstName, lastName) {this.firstName = firstNamethis.lastName = lastName}
}
defineProps({author: Person
})

Boolean 类型转换

声明为 Boolean 类型的 props 有特别的类型转换规则。

例子:

defineProps({disabled: Boolean
})
// 应用
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />

当prop被声明为多种类型,并且类型中有String时,Boolean放置的位置会影响Boolean 的转换规则应用。

// disabled 将被转换为 true
defineProps({disabled: [Boolean, Number]
})// disabled 将被转换为 true
defineProps({disabled: [Boolean, String]
})// disabled 将被转换为 true
defineProps({disabled: [Number, Boolean]
})// disabled 将被解析为空字符串 (disabled="")
defineProps({disabled: [String, Boolean]
})

组件事件

触发与监听事件

子组件在模板表达式中,可以直接使用 $emit 方法触发自定义事件:

<!-- MyComponent -->
<button @click="$emit('someEvent')">Click Me</button>

父组件可以通过 v-on (缩写为 @) 来监听事件(组件的事件监听器也支持 .once 修饰符):

<MyComponent @some-event="callback" />

Tips:

和原生 DOM 事件不一样,组件触发的事件没有冒泡机制。你只能监听直接子组件触发的事件。平级组件或是跨越多层嵌套的组件间通信,应使用一个外部的事件总线,或是使用一个状态管理 | Vue.js。

带参数事件

有时候我们会需要在触发事件时附带一个特定的值。

子组件中使用$emit 方法触发自定义事件,提供一个额外的参数:

<button @click="$emit('increaseBy', 1)">Increase by 1
</button>

父组件接收:

// 方法一
<MyButton @increase-by="(n) => count += n" />// 方法二
<MyButton @increase-by="increaseCount" />
function increaseCount(n) {count.value += n
}

Tips:

所有传入 $emit() 的额外参数都会被直接传向监听器。举例来说,$emit('foo', 1, 2, 3) 触发后,监听器函数将会收到这三个参数值。

用defineEmits()声明触发的事件

组件可以显式地通过 defineEmits() 宏来声明它要触发的事件:

<script setup>
defineEmits(['inFocus', 'submit'])
</script>

<template> 中使用的 $emit 方法不能在组件的 <script setup> 部分中使用,但 defineEmits() 会返回一个相同作用的函数供我们使用:

<script setup>
const emit = defineEmits(['inFocus', 'submit'])function buttonClick() {emit('submit')
}
</script>

defineEmits() 宏不能在子函数中使用。如上所示,它必须直接放置在 <script setup> 的顶级作用域下。

使用选项式setup的写法:

export default {emits: ['inFocus', 'submit'],setup(props, ctx) {ctx.emit('submit')}
}
// ctx支持解构
export default {emits: ['inFocus', 'submit'],setup(props, { emit }) {emit('submit')}
}

emits 选项和 defineEmits() 宏还支持对象语法。通过 TypeScript 为参数指定类型,它允许我们对触发事件的参数进行验证:

<script setup lang="ts">
const emit = defineEmits({submit(payload: { email: string, password: string }) {// 通过返回值为 `true` 还是为 `false` 来判断验证是否通过}
})
</script>

完整版:

<script setup lang="ts">
const emit = defineEmits({// 没有校验click: null,// 校验 submit 事件submit: (payload: { email: string, password: string }) => {if (email && password) {return true} else {console.warn('Invalid submit event payload!')return false}}
})function submitForm(email: string, password: string) {emit('submit', { email, password })
}
</script>

也可以使用纯类型标注来声明触发的事件:

<script setup lang="ts">
const emit = defineEmits<{(e: 'change', id: number): void(e: 'update', value: string): void
}>()// 3.3+: 可选的、更简洁的语法
const emit = defineEmits<{change: [id: number]update: [value: string]
}>()
</script>

Tips:

如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。

组件v-model

基本用法

v-model 可以在组件上使用以实现双向绑定。

Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:

<!-- Parent.vue -->
<Child v-model="countModel" /><!-- Child.vue -->
<script setup>
const model = defineModel()function update() {model.value++
}
</script><template><div>Parent bound v-model is: {{ model }}</div>
</template>

defineModel() 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:

  • 它的 .value 和父组件的 v-model 的值同步;
  • 当它被子组件变更了,会触发父组件绑定的值一起更新。

底层机制:

defineModel 是一个便利宏。编译器将其展开为以下内容:

  • 一个名为 modelValue prop,本地 ref 的值与其同步;
  • 一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。

defineModel 的实现原理如下:

子组件

<!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script><template><input:value="props.modelValue"@input="emit('update:modelValue', $event.target.value)"/>
</template>

父组件

<!-- Parent.vue -->
<Child:modelValue="foo"@update:modelValue="$event => (foo = $event)"
/>

未使用defineModel 的代码显得十分冗长,使用defineModel 后可简洁如下:

子组件

<!-- Child.vue -->
<script setup>
const model = defineModel()
</script><template><span>My input</span> <input v-model="model">
</template>

父组件

<!-- Parent.vue -->
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'const msg = ref('Hello World!')
</script><template><h1>{{ msg }}</h1><Child v-model="msg" />
</template>

因为 defineModel 相当于在底层声明了一个 prop,你可以通过给 defineModel 传递选项,来声明底层 prop 的选项:

// 使 v-model 必填
const model = defineModel({ required: true })// 提供一个默认值
const model = defineModel({ default: 0 })

注意:

如果为 defineModel prop 设置了一个 default 值且父组件没有为该 prop 提供任何值,会导致父组件与子组件之间不同步。在下面的示例中,父组件的 myRef 是 undefined,而子组件的 model 是 1:

// 子组件:
const model = defineModel({ default: 1 })// 父组件
const myRef = ref()
<Child v-model="myRef"></Child>

v-model 的参数

组件上的 v-model 也可以接受一个参数:

<MyComponent v-model:title="bookTitle" />

在子组件中,我们可以通过将字符串作为第一个参数传递给 defineModel() 来支持相应的参数:

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script><template><input type="text" v-model="title" />
</template>

如果需要额外的 prop 选项,应该在 model 名称之后传递:

const title = defineModel('title', { required: true })

3.4 之前的用法

<!-- MyComponent.vue -->
<script setup>
defineProps({title: {required: true}
})
defineEmits(['update:title'])
</script><template><inputtype="text":value="title"@input="$emit('update:title', $event.target.value)"/>
</template>

多个v-model的使用方法

<!-- Parent.vue -->
<UserNamev-model:first-name="first"v-model:last-name="last"
/>
<!-- Child.vue -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script><template><input type="text" v-model="firstName" /><input type="text" v-model="lastName" />
</template>

3.4 之前的用法

<script setup>
defineProps({firstName: String,lastName: String
})defineEmits(['update:firstName', 'update:lastName'])
</script><template><inputtype="text":value="firstName"@input="$emit('update:firstName', $event.target.value)"/><inputtype="text":value="lastName"@input="$emit('update:lastName', $event.target.value)"/>
</template>

处理 v-model 修饰符

如下所示,capitalize(将输入的字符串值第一个字母转为大写)为自定义的修饰符:

<MyComponent v-model.capitalize="myText" />

解构 defineModel() 的返回值,可以在子组件中访问添加到组件 v-model 的修饰符:


<script setup>
const [model, modifiers] = defineModel()console.log(modifiers) // { capitalize: true }
</script><template><input type="text" v-model="model" />
</template>

 defineModel() 中传入 getset 这两个选项,在从模型引用中读取或设置值时会接收到当前的值,并且它们都应该返回一个经过处理的新值。下面是一个例子,展示了如何利用 set 选项来应用 capitalize (首字母大写) 修饰符:

<script setup>
const [model, modifiers] = defineModel({set(value) {if (modifiers.capitalize) {return value.charAt(0).toUpperCase() + value.slice(1)}return value}
})
</script><template><input type="text" v-model="model" />
</template>

3.4 之前的用法

<script setup>
const props = defineProps({modelValue: String,modelModifiers: { default: () => ({}) }
})const emit = defineEmits(['update:modelValue'])function emitValue(e) {let value = e.target.valueif (props.modelModifiers.capitalize) {value = value.charAt(0).toUpperCase() + value.slice(1)}emit('update:modelValue', value)
}
</script><template><input type="text" :value="modelValue" @input="emitValue" />
</template>

使用多个不同参数的 v-model 时使用修饰符:

<!-- Parent.vue -->
<UserNamev-model:first-name.capitalize="first"v-model:last-name.uppercase="last"
/><!-- Child.vue -->
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>

透传 Attributes

Attributes 继承

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props emits attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id及其他传递了但没有声明使用的prop

如下例子,一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。

<!-- <MyButton> -->
<button>Click Me</button><!-- Parent.vue -->
<MyButton class="large" /><!-- 最后渲染出的 DOM 结果 -->
<!-- 父组件中的class透传到了MyButton组件的根元素button 上 -->
<button class="large">Click Me</button>

对 class 和 style 的合并

如果一个子组件的根元素已经有了 class 或 style attribute,它会和从父组件上继承的值合并。

<!-- <MyButton> -->
<button class="btn">Click Me</button><!-- Parent.vue -->
<MyButton class="large" /><!-- 最后渲染出的 DOM 结果 -->
<button class="large btn">Click Me</button>

v-on 监听器继承

规则同上,而且事件也会合并。如果原生 button 元素自身也通过 v-on 绑定了一个事件监听器,则这个监听器和从父组件继承的监听器都会被触发

深层组件继承

如果一个组件 (甲) 的根节点是另一个组件 (乙) ,甲组件接收的透传 attribute 会直接继续传给乙组件。

注意:

  • 透传的 attribute 不会包含 甲组件 上声明过的 props 或是针对 emits 声明事件的 v-on 侦听函数,换句话说,声明过的 props 和侦听函数被 甲组件 “消费”了。
  • 透传的 attribute 若符合声明,也可以作为 props 传入 乙组件

禁用 Attributes 继承

如果要禁止一个组件自动继承attribute,可以在组件选项中设置 inheritAttrs: false

Vue3.3+的使用方法,直接在 <script setup> 中使用 defineOptions

<script setup>
defineOptions({inheritAttrs: false
})
// ...setup 逻辑
</script>

最常见的需要禁用 attribute 继承的场景就是 attribute 需要应用在根节点以外的其他元素上。通过设置 inheritAttrs 选项为 false,你可以完全控制透传进来的 attribute 被如何使用。
这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。

<span>Fallthrough attribute: {{ $attrs }}</span>

注意:

  • 和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样的一个 attribute 需要通过 $attrs['foo-bar'] 来访问。
  • @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick

如下例,如果我们要想在 <MyButton> 中的button接收透传进来的 attribute,而不是外层div上

<!-- <MyButton> -->
<div class="btn-wrapper"><button class="btn">Click Me</button>
</div><!-- Parent.vue -->
<MyButton class="large" />

则需要将 <MyButton> 改为:

<script setup>
defineOptions({inheritAttrs: false
})
</script><div class="btn-wrapper"><button class="btn" v-bind="$attrs">Click Me</button>
</div>

多根节点的 Attributes 继承

和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为如果 $attrs 没有被显式绑定,将会抛出一个运行时警告

如下例中,子组件中因为有多个根节点,并且没有显式绑定$attrs,控制台就会抛出一个警告。

<!-- Parent.vue -->
<CustomLayout id="custom-layout" @click="changeValue" /><!-- Child.vue -->
<header>...</header>
<main>...</main>
<footer>...</footer>

需要在子组件中绑定$attrs,警告就会消失:

<!-- Child.vue -->
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

在 JavaScript 中访问透传 Attributes

如果需要,你可以在 <script setup> 中使用 useAttrs() API 来访问一个组件的所有透传 attribute:

<script setup>
import { useAttrs } from 'vue'const attrs = useAttrs()
</script>

如果使用的setup选项:

export default {setup(props, ctx) {// 透传 attribute 被暴露为 ctx.attrsconsole.log(ctx.attrs)}
}

注意:

        虽然这里的 attrs 对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。如果你需要响应性,可以使用 prop。或者你也可以使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用

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

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

相关文章

11、“十四五”大数据产业发展规划

数据是新时代重要的生产要素, 是国家基础性战略资源。 大数据是数据的集合, 以容量大、 类型多、 速度快、 精度准、 价值高为主要特征, 是推动经济转型发展的新动力,是提升政府治理能力的新途径, 是重塑国家竞争优势的新机遇。 大数据产业是以数据生成、 采集、 存储、 加…

QT C++ QTableWidget 表格合并 setSpan 简单例子

这里说的合并指的是单元格&#xff0c;不是表头。span的意思是跨度、宽度、范围。 setSpan函数需要设定行、列、行跨几格&#xff0c;列跨几格。 //函数原型如下 void QTableView::setSpan(int row, i nt column, 、 int rowSpanCount,/*行跨过的格数*/ int columnSpanCount…

【算法无用系列】电影推荐——余弦相似度计算用户相似度原理

【算法无用系列】通过余弦相似度计算电影、用户相似度 话不多说&#xff0c;本文通过电影推荐系统中&#xff0c;基于余弦相似度算法计算出用户相似和电影相似原理。希望可以帮助一些代码不懂的同学一些思路。 记录用户电影评分数据 一般情况来说&#xff0c;会根据用户的行为…

什么是数字化转型?

作者&#xff1a; 峡山老曹 数字神化 ”企业如何实现数字化转型“是摆在现代企业面前一个无法回避的问题&#xff0c;数字化转型的重要性不容忽视&#xff0c;它不仅是企业适应数字化时代的必然要求&#xff0c;更是提升竞争力、实现可持续发展的关键。随着科技的飞速发展和市场…

学会spring boot 的这些技巧,编程瞬间变得简单了,效率也提高了!

在Spring Boot应用中,断言主要用于测试环境中验证代码行为是否符合预期。虽然Spring Boot自身不直接包含断言库,但通常我们会使用JUnit(一个广泛应用于Java的单元测试框架)来进行测试,其中包含了丰富的断言方法来帮助我们进行各种条件验证。下面通过一些具体的示例来详细说…

二叉树的层序遍历-力扣

本题是二叉树的层序遍历&#xff0c;通过一个队列来控制遍历的节点&#xff0c;二叉树每层的节点和上一层入队的节点个数是相同的&#xff0c;根据这一点编写循环条件。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* …

八、C语言:操作符详解

一、移位操作符 1.1左移操作 左边丢弃&#xff0c;右边补0 1.2右移操作 算数右移&#xff1a;右边丢弃&#xff0c;左边补原符号位 逻辑右移&#xff1a;右边丢弃&#xff0c;左边补0 int main() {int a -1;int b a >> 1;printf("b%d\n",b);return 0; } 原码…

【Linux进程篇】Linux进程管理应用——虚假的shell脚本

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#xff1a;我们已经了解了进程的工作原理&#xff0c;并且学习了进程创建、进程终止、进程等待以及进程程序替换。为了更好的巩固这些知识&#xff0c;我们可以创建一个简易的shell命令行。 目录 做一个简易的s…

GAT1399协议分析(六)--校时

一、官方消息定义 DeviceIDType &#xff1a;GA/T1400.1,采集设备、 卡口点位、 采集系统、分析系统、视图库、应用平台等设备编码规则 TimeCorrectModeType&#xff1a; dateTime时间格式&#xff1a; TimeZone&#xff1a;时区&#xff0c;GAT1400里面没有找到具体内容&…

卧式搅拌机:一键自动称重轻松搞定

在现代化工业生产中&#xff0c;G效、精准的设备是提高生产效率、降低生产成本。近年来&#xff0c;卧式搅拌机一键自动称重包装机的出现&#xff0c;无疑为众多行业带来了变革。这种集搅拌、称重、包装于一体的智能化设备&#xff0c;以其G效、便捷、精准的特点&#xff0c;迅…

六轴机器手臂运动控制——直流伺服反馈系统设计(比赛项目计划书+设计总结+硬件+源代码+上位机等)

TW的硬件一直很强&#xff0c;这是难得的硬件创新比赛的优秀作品&#xff0c;资料非常完整理。 硬件设计&#xff0c;源码&#xff0c;项目计划书&#xff0c;甚至包含了事后的复盘总结文档。 是不可多得的好资料。 项目系统框架图 1. 硬件系统框架图 (请以方块图形式呈现) …

C#.net MassTransit和DotNetCore.CAP区别

MassTransit和DotNetCore.CAP对比 https://github.com/MassTransit/MassTransit https://github.com/dotnetcore/CAP MassTransit和DotNetCore.CAP是两种不同的.NET库&#xff0c;它们在核心概念、设计目的和技术实现等方面存在差异。具体分析如下&#xff1a; 核心概念 Mas…

idea的代码没有提交到仓库怎么撤回到本地?

代码已经提交到变更列表但是还没有push推送到仓库上&#xff0c;可以用这个方法 点击日志-右键要撤回的记录-选择撤销提交 撤销的又回到本地变更 当然你只能撤销自己提交的&#xff0c;别人的你撤销不了

python-题库篇-为什么数组下标从0 开始而不是 1

为什么很多编程语言要把 0 作为第一个下标索引&#xff0c;而不是直观的 1 呢&#xff1f; 这个问题 Dijkstra 已经解答过了&#xff0c;没错&#xff0c;就是你知道的 Dijkstra&#xff0c;Dijkstra 最短路径算法&#xff0c;荷兰语全名是 Edsger Wybe Dijkstra&#xff0c;于…

定制高温隧道炉,如何判断质量好坏

在现代工业生产中&#xff0c;高温隧道炉扮演着不可或缺的角色。对于特定工艺要求&#xff0c;如陶瓷烧制、金属热处理等&#xff0c;定制化的高温隧道炉更是不可或缺。然而&#xff0c;面对市场上琳琅满目的产品&#xff0c;如何判断高温隧道炉的质量好坏成为了企业决策者面临…

js终止递归

终止递归 1. 实现目标&#xff1a;js 编写递归方法 查找指定节点&#xff1b; 2. 需解决问题&#xff1a;找到所需节点后&#xff0c;递归不会终止&#xff0c;直到所有节点遍历完成后才会停止&#xff0c;会消耗性能 3. 解决方案&#xff1a;优化递归方法&#xff0c;在找到…

解决vscode终端不显示conda环境变量名称问题【详细步骤!实测可行!!】

最近在使用Visual Studio Code (VSCode) 时候&#xff0c;发现终端没有正确显示激活的conda环境名称&#xff0c;搜了一下&#xff0c;找到原因&#xff0c;记录一下&#xff0c;如果有人也遇到同样的问题&#xff0c;可以收藏一下。   分别两种情况&#xff0c;一是windows系…

一周学会Django5 Python Web开发 - Django5内置Auth认证系统-用户登录实现

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计57条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

JVM学习-内存泄漏

内存泄漏的理解和分类 可达性分析算法来判断对象是否是不再使用的对象&#xff0c;本质都是判断一上对象是否还被引用&#xff0c;对于这种情况下&#xff0c;由于代码的实现不同就会出现很多内存泄漏问题(让JVM误以为此对象还在引用&#xff0c;无法回收&#xff0c;造成内存泄…

旭日X3与英伟达Orin NX通过TCP传输图片

观前提醒&#xff1a;本文主要内容为使用Python在局域网内建立TCP连接并传输图片信息&#xff0c;计算机为一块旭日X3和一块英伟达Orin NX。 一、什么是TCP TCP&#xff08;传输控制协议&#xff09;是一种可靠的、面向连接的协议&#xff0c;它确保数据包的顺序传输和完整性…