Vue 3.0 组合式API 介绍 【Vue3 从零开始】
提示
在阅读文档之前,你应该已经熟悉了这两个 Vue 基础和创建组件。
在 Vue Mastery 上观看关于组合式 API 的免费视频。
通过创建 Vue 组件,我们可以将接口的可重复部分及其功能提取到可重用的代码段中。仅此一项就可以使我们的应用程序在可维护性和灵活性方面走得更远。然而,我们的经验已经证明,光靠这一点可能是不够的,尤其是当你的应用程序变得非常大的时候——想想几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要。
假设在我们的应用程序中,我们有一个视图来显示某个用户的仓库列表。除此之外,我们还希望应用搜索和筛选功能。处理此视图的组件可能如下所示:
// src/components/UserRepositories.vue
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: { type: String }
},
data () {
return {
repositories: [], // 1
filters: { ... }, // 3
searchQuery: '' // 2
}
},
computed: {
filteredRepositories () { ... }, // 3
repositoriesMatchingSearchQuery () { ... }, // 2
},
watch: {
user: 'getUserRepositories' // 1
},
methods: {
getUserRepositories () {
// 使用 `this.user` 获取用户仓库
}, // 1
updateFilters () { ... }, // 3
},
mounted () {
this.getUserRepositories() // 1
}
}
该组件有以下几个职责:
- 从假定的外部 API 获取该用户名的仓库,并在用户更改时刷新它
- 使用
searchQuery
字符串搜索存储库 - 使用
filters
对象筛选仓库
用组件的选项 (data
、computed
、methods
、watch
) 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。
一个大型组件的示例,其中逻辑关注点是按颜色分组。
这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果我们能够将与同一个逻辑关注点相关的代码配置在一起会更好。而这正是组合式 API 使我们能够做到的。
#组合式 API 基础
既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup
。
#setup
组件选项
观看 Vue Mastery 上的免费 setup 视频。
新的 setup
组件选项在创建组件之前执行,一旦 props
被解析,并充当合成 API 的入口点。
WARNING
由于在执行 setup
时尚未创建组件实例,因此在 setup
选项中没有 this
。这意味着,除了 props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
setup
选项应该是一个接受 props
和 context
的函数,我们将在稍后讨论。此外,我们从 setup
返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
让我们添加 setup
到我们的组件中:
// src/components/UserRepositories.vue
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: { type: String }
},
setup(props) {
console.log(props) // { user: '' }
return {} // 这里返回的任何内容都可以用于组件的其余部分
}
// 组件的“其余部分”
}
现在让我们从提取第一个逻辑关注点开始 (在原始代码段中标记为“1”)。
- 从假定的外部 API 获取该用户名的仓库,并在用户更改时刷新它
我们将从最明显的部分开始:
- 仓库列表
- 更新仓库列表的函数
- 返回列表和函数,以便其他组件选项可以访问它们
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
// 在我们的组件内
setup (props) {
let repositories = []
const getUserRepositories = async () => {
repositories = await fetchUserRepositories(props.user)
}
return {
repositories,
getUserRepositories // 返回的函数与方法的行为相同
}
}
这是我们的出发点,但它还不能工作,因为我们的 repositories
变量是非响应式的。这意味着从用户的角度来看,仓库列表将保持为空。我们来解决这个问题!
#带 ref
的响应式变量
在 Vue 3.0 中,我们可以通过一个新的 ref
函数使任何响应式变量在任何地方起作用,如下所示:
import { ref } from 'vue'
const counter = ref(0)
ref
接受参数并返回它包装在具有 value
property 的对象中,然后可以使用该 property 访问或更改响应式变量的值:
import { ref } from 'vue'
const counter = ref(0)
console.log(counter) // { value: 0 }
console.log(counter.value) // 0
counter.value++
console.log(counter.value) // 1
在对象中包装值似乎不必要,但在 JavaScript 中保持不同数据类型的行为统一是必需的。这是因为在 JavaScript 中,Number
或 String
等基本类型是通过值传递的,而不是通过引用传递的:
在任何值周围都有一个包装器对象,这样我们就可以在整个应用程序中安全地传递它,而不必担心在某个地方失去它的响应性。
提示
换句话说,ref
对我们的值创建了一个响应式引用。使用引用的概念将在整个组合式 API 中经常使用。
回到我们的例子,让我们创建一个响应式的 repositories
变量:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'
// in our component
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
return {
repositories,
getUserRepositories
}
}
完成!现在,每当我们调用 getUserRepositories
时,repositories
都将发生变化,视图将更新以反映更改。我们的组件现在应该如下所示:
// src/components/UserRepositories.vue
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: { type: String }
},
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
return {
repositories,
getUserRepositories
}
},
data () {
return {
filters: { ... }, // 3
searchQuery: '' // 2
}
},
computed: {
filteredRepositories () { ... }, // 3
repositoriesMatchingSearchQuery () { ... }, // 2
},
watch: {
user: 'getUserRepositories' // 1
},
methods: {
updateFilters () { ... }, // 3
},
mounted () {
this.getUserRepositories() // 1
}
}
我们已经将第一个逻辑关注点中的几个部分移到了 setup
方法中,它们彼此非常接近。剩下的就是在 mounted
钩子中调用 getUserRepositories
,并设置一个监听器,以便在 user
prop 发生变化时执行此操作。
我们将从生命周期钩子开始。
#生命周期钩子注册内部 setup
为了使组合式 API 的特性与选项式 API 相比更加完整,我们还需要一种在 setup
中注册生命周期钩子的方法。这要归功于从 Vue 导出的几个新函数。组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on
:即 mounted
看起来像 onMounted
。
这些函数接受在组件调用钩子时将执行的回调。
让我们将其添加到 setup
函数中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
// in our component
setup (props) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
onMounted(getUserRepositories) // on `mounted` call `getUserRepositories`
return {
repositories,
getUserRepositories
}
}
现在我们需要对 user
prop 所做的更改做出反应。为此,我们将使用独立的 watch
函数。
#watch
响应式更改
就像我们如何使用 watch
选项在组件内的 user
property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch
函数执行相同的操作。它接受 3 个参数:
- 一个响应式引用或我们想要侦听的 getter 函数
- 一个回调
- 可选的配置选项
下面让我们快速了解一下它是如何工作的
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
例如,每当 counter
被修改时 counter.value=5
,watch 将触发并执行回调 (第二个参数),在本例中,它将把 'The new counter value is:5'
记录到我们的控制台中。
以下是等效的选项式 API:
export default {
data() {
return {
counter: 0
}
},
watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}
有关 watch
的详细信息,请参阅我们的深入指南。
现在我们将其应用到我们的示例中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
// 在我们组件中
setup (props) {
// 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
// 更新 `prop.user` 到 `user.value` 访问引用值
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// 在用户 prop 的响应式引用上设置一个侦听器
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
你可能已经注意到在我们的 setup
的顶部使用了 toRefs
。这是为了确保我们的侦听器能够对 user
prop 所做的更改做出反应。
有了这些变化,我们就把第一个逻辑关注点移到了一个地方。我们现在可以对第二个关注点执行相同的操作——基于 searchQuery
进行过滤,这次是使用计算属性。
#独立的 computed
属性
与 ref
和 watch
类似,也可以使用从 Vue 导入的 computed
函数在 Vue 组件外部创建计算属性。让我们回到我们的 counter 例子:
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
在这里,computed
函数返回一个作为 computed
的第一个参数传递的 getter 类回调的输出的一个只读的响应式引用。为了访问新创建的计算变量的 value,我们需要像使用 ref
一样使用 .value
property。
让我们将搜索功能移到 setup
中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'
// in our component
setup (props) {
// 使用 `toRefs` 创建对 props 的 `user` property 的响应式引用
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
// 更新 `props.user ` 到 `user.value` 访问引用值
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// 在用户 prop 的响应式引用上设置一个侦听器
watch(user, getUserRepositories)
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(
repository => repository.name.includes(searchQuery.value)
)
})
return {
repositories,
getUserRepositories,
searchQuery,
repositoriesMatchingSearchQuery
}
}
对于其他的逻辑关注点我们也可以这样做,但是你可能已经在问这个问题了——这不就是把代码移到 setup
选项并使它变得非常大吗?嗯,那是真的。这就是为什么在继续其他任务之前,我们将首先将上述代码提取到一个独立的组合式函数。让我们从创建 useUserRepositories
开始:
// src/composables/useUserRepositories.js
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'
export default function useUserRepositories(user) {
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
然后是搜索功能:
// src/composables/useRepositoryNameSearch.js
import { ref, computed } from 'vue'
export default function useRepositoryNameSearch(repositories) {
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(repository => {
return repository.name.includes(searchQuery.value)
})
})
return {
searchQuery,
repositoriesMatchingSearchQuery
}
}
现在在单独的文件中有了这两个功能,我们就可以开始在组件中使用它们了。以下是如何做到这一点:
// src/components/UserRepositories.vue
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: { type: String }
},
setup (props) {
const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user)
const {
searchQuery,
repositoriesMatchingSearchQuery
} = useRepositoryNameSearch(repositories)
return {
// 因为我们并不关心未经过滤的仓库
// 我们可以在 `repositories` 名称下暴露过滤后的结果
repositories: repositoriesMatchingSearchQuery,
getUserRepositories,
searchQuery,
}
},
data () {
return {
filters: { ... }, // 3
}
},
computed: {
filteredRepositories () { ... }, // 3
},
methods: {
updateFilters () { ... }, // 3
}
}
此时,你可能已经知道了这个练习,所以让我们跳到最后,迁移剩余的过滤功能。我们不需要深入了解实现细节,因为这不是本指南的重点。
// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: { type: String }
},
setup(props) {
const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user)
const {
searchQuery,
repositoriesMatchingSearchQuery
} = useRepositoryNameSearch(repositories)
const {
filters,
updateFilters,
filteredRepositories
} = useRepositoryFilters(repositoriesMatchingSearchQuery)
return {
// 因为我们并不关心未经过滤的仓库
// 我们可以在 `repositories` 名称下暴露过滤后的结果
repositories: filteredRepositories,
getUserRepositories,
searchQuery,
filters,
updateFilters
}
}
}
我们完成了!
请记住,我们只触及了组合式 API 的表面以及它允许我们做什么。要了解更多信息,请参阅深入指南。
相关文章:

Vue 3.0 组合式API 介绍 【Vue3 从零开始】
提示 在阅读文档之前,你应该已经熟悉了这两个 Vue 基础和创建组件。 在 Vue Mastery 上观看关于组合式 API 的免费视频。 通过创建 Vue 组件,我们可以将接口的可重复部分及其功能提取到可重用的代码段中。仅此一项就可以使我们的应用程序在可维护性和…...
【算法数据结构体系篇class13、14】:贪心算法思想
一、贪心算法概念贪心算法概念:1)最自然智慧的算法2)用一种局部最功利的标准,总是做出在当前看来是最好的选择3)难点在于证明局部最功利的标准可以得到全局最优解4)对于贪心算法的学习主要以增加阅历和经验…...
C++知识点,关键字inline ,String,强制类型转化
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练 🔥座右铭:“不要等到什么都没有了,才下定决心去做” 🚀🚀🚀大家觉不错…...

MyBatis源码分析(六)MetaObject工具类的使用与源码分析
文章目录一、MetaObject基本使用二、关键类源码分析1、MetaObject的构造方法2、PropertyTokenizer分词器3、BeanWrapper4、MetaClass5、DefaultReflectorFactory6、Reflector7、总结三、MetaObject的getValue源码分析写在后面一、MetaObject基本使用 public class User {priva…...

文献资源最多的文献下载神器,99.99%的文献都可下载
用对工具事半功倍,查找下载文献用对工具能节约大量的时间和精力去做更多的事情。 文献党下载器(wxdown.org),几乎整合了所有文献数据库资源,涵盖各种文献类型,包含全部学科。文献党下载器整合的资源如&…...

工控机ARM工业边缘计算机搭建Node-Red环境
搭建Node-Red环境Node-RED是一个基于Node.js的开源可视化流程编程环境,可以轻松构建自定义应用程序,通过连接简单的节点来完成复杂的任务。Node-RED提供了一种简单的方法,可以快速连接到外部服务,从而实现物联网应用的开发。Node-…...

位图/布隆过滤器/海量数据处理方式
位图 位图的概念 所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。 直接来看问题: 给40亿个不重复的无符号整数,没排过序。给一个无符号整数࿰…...

Tomcat 配置文件数据库密码加密
几年前研究过Tomcat context.xml 中数据库密码改为密文的内容,因为当时在客户云桌面代码没有留备份也没有文章记录,最近项目又提出了这个需求就又重新拾起来学习一下。在网上找了一些资料,自己也大概试了一下,目前功能是实现了。参…...

k8s-Kubernetes集群部署
文章目录前言一、Kubernetes简介与架构1.Kubernetes简介2.kubernetes设计架构二、Kubernetes集群部署1.集群环境初始化2.所有节点安装kubeadm3.拉取集群所需镜像3.集群初始化4.安装flannel网络插件5.扩容节点6.设置kubectl命令补齐前言 一、Kubernetes简介与架构 1.Kubernetes…...

Python数据分析案例19——上市银行财务指标对比
我代码栏目都是针对基础的python数据分析人群,比如想写个本科毕业论文,课程论文,做个简单的案例分析等。过去写的案例可能使用了过多的机器学习和深度学习方法,文科的同学看不懂,可能他们仅仅只想用python做个回归或者…...
Python 中错误 ConnectionError: Max retries exceeded with url
出现错误“ConnectionError: Max retries exceeded with url”有多种原因: 向 request.get() 方法传递了不正确或不完整的 URL。我们正受到 API 的速率限制。requests 无法验证您向其发出请求的网站的 SSL 证书。 确保我们指定了正确且完整的 URL 和路径。 # ⛔️…...

SpringBoot下的Spring框架学习(Tedu)——DAY02
SpringBoot下的Spring框架学习(Tedu)——DAY02 目录SpringBoot下的Spring框架学习(Tedu)——DAY02Spring框架学习1.1 Spring介绍1.2 知识铺垫1.2.1 编辑Dog类1.2.2 编辑Cat类1.2.3 编辑测试类User.java1.2.4 上述代码的总结1.3 面…...
容易混淆的点:C语言中char* a[] 与 char a[] 的区别以及各自的用法
char* a[] 和 char a[] 的区别 char* a[] 和 char a[] 是 C 语言中数组的不同声明方式,二者具有以下区别: char a[] 声明的是一个字符数组,其中存储的是一串字符。此时,a 可以被视为一个指向字符的指针。 char* a[]则声明了一个…...

认识Spring(下)
作者:~小明学编程 文章专栏:Spring框架 格言:热爱编程的,终将被编程所厚爱。 目录 Spring更加高效的读取和存储对象 存储bean对象 五大注解 关于五大类注解 对象的注入 属性注入 构造方法注入 Setter注入 三种注入方式的…...
Educational Codeforces Round 144 (Rated for Div. 2) C - Maximum Set
传送门 题意: 对于一个集合,如果它的任意两个元素都能 有 其中一个能整除另一个,那么它是好的。问在区间[L,R] 中由这个区间某些数内构成的好的集合的最长长度是多少,以及且满足这个长度的好集合有多少个。(懒得想就借…...

学python的第四天---基础(2)
一、三角形类型读入数组并排序的方法nlist(map(float,input().split())) c,b,asorted(n)list_1 list(map(float, input().split())) list_1.sort() list_1.reverse()lengthssorted(map(float,input().split(" ")),reverseTrue)二、动物写法一:d{" &…...

spring之refresh流程-Java八股面试(六)
系列文章目录 第一章 ArrayList-Java八股面试(一) 第二章 HashMap-Java八股面试(二) 第三章 单例模式-Java八股面试(三) 第四章 线程池和Volatile关键字-Java八股面试(四) 第五章ConcurrentHashMap-Java八股面试(五) 动态每日更新算法题,想要学习的可以关注一下…...

【C语言】刷题|链表|双指针|指针|多指针|数据结构
主页:114514的代码大冒 qq:2188956112(欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ ) Gitee:庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 文章目录 目录 文章目录 前言 一、移除链表元素 二、反转链表 三,链表的中间结点 四&…...

糖化学类854262-01-4,Propargyl α-D-Mannopyranoside,炔丙基 α-D-吡喃甘露糖苷
外观以及性质:Propargyl α-D-Mannopyranoside一般为白色粉末状,糖化学类产品比较多,一般包括:葡萄糖衍生物、葡萄糖醛酸衍生物,氨基甘露糖衍生物、半乳糖衍生物、氨基半乳糖衍生物、核糖衍生物、阿拉伯糖衍生物、唾液…...

项目管理工具DHTMLX 在 G2 排名中再创新高
DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求,具备完善的甘特图图表库,功能强大,价格便宜,提供丰富而灵活的JavaScript API接口,与各种服务器端技术&am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...