Vue 进阶实战:从待办清单到完整应用(路由 / 状态管理 / 性能优化全攻略)

在上一篇博客里,我们一起实现了能本地存储的待办清单,不少朋友留言说:“学会了基础,但遇到‘登录后才能访问页面’‘多组件共享数据’就卡壳了,该怎么突破?”

其实我刚学 Vue 时也有过这种困惑 —— 基础语法会用,但一到实际项目的复杂场景(比如用户权限、多页面数据共享)就手足无措。今天这篇进阶指南,就带你解决这些 “实战痛点”,把简单的待办清单升级成带登录拦截、分类管理、全局状态共享的完整应用,同时掌握让项目更流畅的性能优化技巧。

一、路由进阶:从 “页面跳转” 到 “权限控制”

新手对 Vue Router 的认知可能停留在 “点击导航跳页面”,但实际项目中,我们需要更灵活的路由控制 —— 比如 “未登录不能进待办页面”“不同用户看不同菜单”。这部分就带你掌握 3 个核心进阶技巧:

1. 嵌套路由:实现 “布局复用”(比如页面侧边栏 + 内容区)

很多管理系统、工具类应用都有 “侧边栏导航 + 顶部栏 + 内容区” 的布局,用嵌套路由就能实现 “布局只写一次,内容区动态切换”。

实操步骤:

① 先创建布局组件 src/views/Layout.vue(侧边栏 + 内容区框架):

<template>​<div class="layout">​<router-link to="/category" class="link">分类管理</router-link>​<button @click="logout" class="logout-btn">退出登录</button>​</aside>​<!-- 内容区:嵌套路由的出口,匹配的子路由会渲染在这里 -->​<main class="content">​<router-view />​</main>​</div>​
</template>​
​
<script>​
export default {​methods: {​logout() {​// 清除本地存储的登录状态​localStorage.removeItem("isLogin");​// 跳回登录页​this.$router.push("/login");​}​}​
};​
</script>​
​
<style scoped>​
.layout { display: flex; height: 100vh; }​
.sidebar { width: 200px; background: #333; color: #fff; padding: 20px; }​
.link { display: block; color: #fff; text-decoration: none; margin: 15px 0; }​
.link.active { color: #42b983; } /* 路由激活时的样式 */​
.content { flex: 1; padding: 20px; overflow: auto; }​
.logout-btn { margin-top: 30px; padding: 8px 16px; background: #f44336; color: #fff; border: none; cursor: pointer; }​
</style>

② 配置嵌套路由(修改 src/router/index.js):

import Vue from "vue";​
import Router from "vue-router";​
// 引入组件​
import Login from "@/views/Login";​
import Layout from "@/views/Layout";​
import Todo from "@/views/Todo"; // 待办清单页面(原App.vue内容迁移过来)​
import Category from "@/views/Category"; // 新增分类管理页面​
​
Vue.use(Router);​
​
export default new Router({​routes: [​// 登录页(无嵌套)​{ path: "/login", name: "Login", component: Login },​// 布局页(嵌套路由的父路由)​{​path: "/",​component: Layout,​meta: { requiresAuth: true }, // 标记:该路由需要登录才能访问​children: [​// 待办清单(子路由,路径空表示默认显示)​{ path: "", name: "Todo", component: Todo },​// 分类管理(子路由)​{ path: "category", name: "Category", component: Category }​]​}​]​
});
  • 效果:访问 /login 是单独的登录页;登录后进入 /,会显示侧边栏 + 内容区,点击侧边栏切换 /todo/category 时,只有内容区变化,侧边栏始终存在 —— 这就是嵌套路由的核心价值:复用公共布局。

2. 路由守卫:实现 “登录拦截”(未登录不准进)

前面我们给 Layout 路由加了 meta: { requiresAuth: true },现在需要用 “路由守卫” 检测这个标记:如果用户没登录就想进 /todo/category,自动跳回登录页。

src/router/index.js 末尾添加全局前置守卫:

// 全局前置路由守卫:每次路由跳转前都会执行​
router.beforeEach((to, from, next) => {​// 1. 判断目标路由是否需要登录(看meta.requiresAuth)​const needLogin = to.meta.requiresAuth;​// 2. 判断用户是否已登录(从localStorage取状态)​const isLogin = localStorage.getItem("isLogin") === "true";​
​if (needLogin) {​// 3. 需要登录:已登录则放行,未登录跳登录页​if (isLogin) {​next(); // 放行,继续跳转到目标路由​} else {​next({ path: "/login" }); // 强制跳登录页​}​} else {​// 不需要登录:直接放行(比如登录页)​next();​}​
​
export default router; // 注意:这里要把原来的export default new Router(...)改成先赋值给router,再导出

再写个简单的登录页 src/views/Login.vue

<template>​<div class="login-container">​data() {​return { username: "" };​},​methods: {​login() {​if (this.username.trim()) {​// 存储登录状态和用户名(实际项目会对接后端接口,这里简化)​localStorage.setItem("isLogin", "true");​localStorage.setItem("username", this.username);​// 登录成功跳回之前想访问的页面(比如用户直接输/todo,被拦截后登录,登录后自动跳/todo)​this.$router.push(this.$route.query.redirect || "/");​} else {​alert("请输入用户名!");​}​}​}​
};​
</script>​
​
<style scoped>​
.login-container { width: 300px; margin: 100px auto; text-align: center; }​
.input { width: 100%; padding: 10px; margin: 15px 0; border: 1px solid #ddd; border-radius: 4px; }​
.login-btn { width: 100%; padding: 10px; background: #42b983; color: #fff; border: none; border-radius: 4px; cursor: pointer; }​
</style>
  • 避坑点:路由守卫里一定要调用 next()!新手常忘写,导致页面卡住;另外,next({ path: "/login" }) 会触发新一轮守卫,别在登录页也加 requiresAuth,否则会无限循环。

二、状态管理:用 Pinia 解决 “多组件数据共享”

上一篇的待办数据存在组件里,现在有了 Todo 和 Category 两个页面,需要共享 “分类列表”(比如待办要按分类筛选,分类管理要增删分类)—— 如果还用组件传值,会非常麻烦。这时候就需要 “状态管理工具”,Vue 3 推荐用 Pinia(比 Vuex 更简洁,支持 Vue 2 和 3)。

1. 先装 Pinia 并初始化

① 安装 Pinia(Vue 2 需要额外装适配包):

\# Vue 2项目npm install pinia @pinia/vue2-plugin\# Vue 3项目直接装pinia即可:npm install pinia

② 在 src/main.js 中引入并使用 Pinia:

import Vue from "vue";​
import App from "./App.vue";​
import router from "./router";​
// 引入Pinia​
import { createPinia, PiniaVuePlugin } from "pinia";​
​
Vue.use(PiniaVuePlugin); // Vue 2必须加这行​
const pinia = createPinia();​
​
new Vue({​router,​pinia, // 挂载Pinia到Vue实例​render: h => h(App)​
}).$mount("#app");

2. 创建 Pinia 仓库:管理 “分类” 和 “待办” 状态

src/store 文件夹下新建 todoStore.js(Pinia 的 “仓库” 相当于 Vuex 的 “模块”):

import { defineStore } from "pinia";​
​
​// 删除分类(同时删除该分类下的所有待办)​deleteCategory(categoryId) {​this.categories = this.categories.filter(c => c.id !== categoryId);​this.todos = this.todos.filter(t => t.categoryId !== categoryId);​this.saveToLocal();​// 如果删除的是当前选中的分类,切换到“全部”​if (this.activeCategoryId === categoryId) {​this.activeCategoryId = 0;​}​},​
​// 切换当前选中分类(用于筛选)​setActiveCategory(categoryId) {​this.activeCategoryId = categoryId;​},​
​// 同步state到localStorage(Pinia状态默认不持久化,需手动处理)​saveToLocal() {​localStorage.setItem("vueTodos", JSON.stringify(this.todos));​localStorage.setItem("vueCategories", JSON.stringify(this.categories));​}​}​
});

3. 在组件中使用 Pinia 仓库

以 Todo 页面(src/views/Todo.vue)为例,用 Pinia 替代原来的组件内数据:

<template>​<div class="todo-page">​useTodoStore().addTodo(this.newTodoText, this.selectedCategoryId);​this.newTodoText = "";​}​},​toggleTodoDone(todoId) {​useTodoStore().toggleTodoDone(todoId);​},​deleteTodo(todoId) {​useTodoStore().deleteTodo(todoId);​},​setActiveCategory(categoryId) {​useTodoStore().setActiveCategory(categoryId);​}​}​
};​
</script>​
​
<style scoped>​
/* 样式省略,可参考上一篇的待办清单样式,新增分类筛选的样式 */​
.category-filter { margin: 15px 0; }​
.category-filter button { margin-right: 10px; padding: 5px 10px; }​
.category-filter button.active { background: #42b983; color: #fff; border: none; }​
.add-todo .category-select { margin: 0 10px; padding: 5px; }​
.todo-item .done { text-decoration: line-through; color: #999; }​
</style>
  • 核心优势:现在 Category 页面也能直接用 useTodoStore() 获取和修改分类数据,不用再通过父子组件传值;而且所有组件修改数据后,其他使用该状态的组件会自动更新 —— 这就是全局状态管理的价值。

三、组件通信高级技巧:不止 props 和 $emit

除了 “父子组件用 props/$emit”“全局数据用 Pinia”,实际项目中还会遇到 “兄弟组件通信”“跨多层级组件通信”,这时候用以下两种方法更高效:

1. EventBus:解决 “兄弟组件 / 无关联组件” 通信

比如 “分类管理页面删除分类后,待办页面要实时更新筛选状态”,用 EventBus 可以快速实现:

① 在 src/main.js 中创建 EventBus:

// 给Vue原型添加\$bus,所有组件都能访问Vue.prototype.\$bus = new Vue();

② 发送事件(Category 页面删除分类时):

// Category.vue中删除分类的方法里,添加发送事件​
deleteCategory(categoryId) {​useTodoStore().deleteCategory(categoryId);​// 发送事件:通知其他组件“分类已删除”​this.$bus.$emit("categoryDeleted", categoryId);​
}

③ 接收事件(Todo 页面监听事件,更新筛选状态):

// Todo.vue的created钩子中监听事件​
created() {​// 监听“分类已删除”事件​this.$bus.$on("categoryDeleted", (deletedId) => {​if (this.activeCategoryId === deletedId) {​this.setActiveCategory(0); // 切换到“全部”分类​}​});​
},​
// 组件销毁时移除监听,避免内存泄漏​
beforeDestroy() {​this.$bus.$off("categoryDeleted");​
}
  • 避坑点:一定要在组件销毁时用 $off 移除事件监听,否则组件重复创建会导致事件多次触发。

2. provide/inject:解决 “跨多层级组件” 通信

比如 “Layout 组件的侧边栏需要显示用户名,而用户名在 Login 组件登录后存储在 localStorage”,如果用 props 传,需要 Layout→Sidebar 层层传递,很麻烦。用 provide/inject 可以直接 “跨级传递”:

① 父组件(比如 Layout.vue)用 provide 提供数据:

<script>​
export default {​provide() {​// 提供“用户名”数据,所有子组件(无论层级多深)都能注入​return {​username: localStorage.getItem("username") || ""​};​}​
};​
</script>

② 子组件(比如 Sidebar.vue,假设是 Layout 的子组件)用 inject 接收数据:

<template>​<aside class="sidebar">​<div class="user-info">欢迎,{{ username }}</div>​<!-- 其他导航链接 -->​</aside>​
</template>​
​
<script>​
export default {​// 注入父组件提供的“username”​inject: ["username"]​
};​
</script>
  • 适用场景:全局配置(如主题色、接口基础 URL)、跨多层级的固定数据传递;不适合频繁变化的数据(频繁变化建议用 Pinia)。

四、性能优化:让你的 Vue 项目更流畅

新手写的项目常出现 “页面卡顿”“加载慢”,其实只需几个小技巧就能大幅提升性能,这部分带你掌握 4 个高频优化点:

1. v-for 必须加唯一 key,且不用 index 当 key

很多新手图方便用 v-for="(item, index) in list" :key="index",但当列表删除、排序时,index 会变化,Vue 会误判组件 “复用”,导致渲染错误。正确做法是用数据的唯一 ID 当 key:

\<!-- 错误:用index当key -->\<li v-for="(todo, index) in todos" :key="index">{{ todo.text }}\</li>\<!-- 正确:用数据的唯一ID当key -->\<li v-for="todo in todos" :key="todo.id">{{ todo.text }}\</li>

2. 用 computed 缓存计算结果,避免重复计算

如果组件中多次用到 “筛选后的待办列表”,直接写表达式会重复计算,用 computed 缓存后只算一次:

<!-- 错误:多次重复计算 -->​
<div>{{ todos.filter(t => !t.done).length }}</div>​
<div>{{ todos.filter(t => !t.done).map(t => t.text).join(",") }}</div>​
​
<!-- 正确:用computed缓存 -->​
<template>​<div>{{ unDoneTodos.length }}</div>​<div>{{ unDoneTodos.map(t => t.text).join(",") }}</div>​
</template>​
<script>​
export default {​computed: {​unDoneTodos() {​return this.todos.filter(t => !t.done); // 只计算一次,多次复用​}​}​
};​
</script>

3. 组件懒加载:减少首屏加载时间

默认情况下,Vue 会把所有组件打包成一个大 JS 文件,首屏加载慢。用 “路由懒加载” 让组件在需要时才加载:

// src/router/index.js 中修改组件引入方式​
// 原来的方式:import Todo from "@/views/Todo";​
// 懒加载方式:​
const Todo = () => import("@/views/Todo");​
const Category = () => import("@/views/Category");​
const Login = () => import("@/views/Login");​
const Layout = () => import("@/views/Layout");​
​
// 路由配置不变​
export default new Router({​routes: [/* ... */]​
});
  • 效果:首屏只加载 Login 或 Layout 的核心代码,进入 Todo 页面时才加载 Todo 组件的代码,首屏加载时间大幅缩短。

4. v-if 和 v-show 按需使用,避免频繁 DOM 操作

  • v-if:条件不满足时会 “销毁组件”,满足时 “重新创建”(适合不常切换的场景,如登录 / 未登录状态)

  • v-show:条件不满足时只是 “隐藏(display: none)”,组件始终存在(适合频繁切换的场景,如标签页、下拉菜单)

\<!-- 适合v-if:登录状态切换不频繁 -->\<div v-if="isLogin">欢迎回来\</div>\<div v-else>请登录\</div>\<!-- 适合v-show:标签页频繁切换 -->\<div v-show="activeTab === 'todo'">待办内容\</div>\<div v-show="activeTab === 'category'">分类内容\</div>

五、实战升级:把待办应用变成 “可部署的产品”

现在我们的应用已经有登录、待办、分类功能了,最后做两个 “产品级” 优化,让它能真正部署上线:

1. 数据持久化优化:Pinia 结合 localStorage 自动同步

之前我们在 Pinia 的 actions 里手动调用 saveToLocal(),现在可以用 Pinia 的 “订阅” 功能,让 state 变化时自动同步到 localStorage,不用每次手动调用:

// src/store/todoStore.js 中添加订阅​
export const useTodoStore = defineStore("todo", {​state: () => ({ /* ... */ }),​getters: { /* ... */ },​actions: { /* ... */ }​
});​
​
// 订阅state变化:每次state修改后自动同步到localStorage​
if (localStorage) {​const todoStore = useTodoStore();​todoStore.$subscribe((mutation, state) => {​localStorage.setItem("vueTodos", JSON.stringify(state.todos));​localStorage.setItem("vueCategories", JSON.stringify(state.categories));​});​
}

2. 打包部署:生成可上线的静态文件

执行以下命令,Vue 会把项目打包成静态 HTML/CSS/JS 文件(在 dist 文件夹下):

npm run build
  • 打包后,把 dist 文件夹里的文件上传到服务器(如 Nginx、Netlify、Vercel),就能通过域名访问你的 Vue 应用了!

  • 避坑点:打包后如果打开 index.html 是空白页,需要修改 vue.config.js 配置公共路径(如果部署在服务器子目录):

// 项目根目录新建vue.config.jsmodule.exports = {publicPath: "./" // 表示相对路径,适合本地打开或部署在子目录};

六、进阶后的学习方向:从 “会用” 到 “精通”

掌握以上内容后,你已经能独立开发中小型 Vue 应用了,接下来可以向这些方向深入:

  1. Vue 3 + Composition API:用更灵活的语法组织代码(比如把 Pinia 仓库的逻辑拆分成组合式函数),适合大型项目维护;

  2. TypeScript 整合:给 Vue 项目加类型校验,减少 bug,尤其适合团队协作(推荐先学 TS 基础,再用 defineProps defineEmits 等 Vue 3 的 TS 语法);

  3. 后端接口对接:用 Axios 发送请求,处理异步数据(比如登录对接后端接口,待办数据存数据库而非 localStorage);

  4. Nuxt.js(服务端渲染):解决 Vue 单页应用 “SEO 差” 的问题,适合做博客、商城等需要 SEO 的项目;

  5. 组件库二次开发:基于 Element Plus/Vant 封装业务组件(比如公司专属的表单组件、表格组件),提升团队开发效率。

最后:进阶的核心是 “解决实际问题”

很多人学进阶知识时会陷入 “只看文档不实践” 的误区,其实最好的学习方式是:找一个小项目(比如个人博客、简易商城),遇到 “权限控制” 就学路由守卫,遇到 “数据共享” 就学 Pinia,遇到 “卡顿” 就学性能优化 —— 带着问题学,才能真正把知识变成能力。

如果你在实践中遇到具体问题(比如 Pinia 状态同步失败、路由守卫循环跳转),欢迎在评论区留言,咱们一起拆解解决!也可以把你升级后的待办应用分享出来,互相交流学习~

(附:进阶学习资源:Pinia 官方文档、Vue Router 官方文档(进阶部分)、B 站 “Vue 3+TS 实战项目” 教程)

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

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

相关文章

uniApp开发XR-Frame微信小程序 | 动态加载与删除模型

在使用xr-frame开发3D小程序时&#xff0c;我们经常需要根据需求去动态加载模型或删除模型&#xff0c;在官方的说明中&#xff0c;提到了相关方法&#xff0c;但并不太明确&#xff0c;也没有确切的实例。 我们先来看一下官方给出的说明。 一. Shadow元素 我们需要用代码动…

把多个 PPT 合并在一起,三步告别复制粘贴

制作部门汇报分册、项目阶段文件等工作需要将多个零散的PPT合并为一份完整文档。手动复制粘贴不仅效率低下&#xff0c;还容易导致格式错乱、动画丢失。本文介绍一种高效方法&#xff0c;三步操作即可将多个PPT文件快速合并为单一文档。无论是整合汇报材料&#xff0c;还是准备…

安卓旋转屏幕后如何防止数据丢失-ViewModel入门

Android ViewModel 入门教程 在日常开发中&#xff0c;当 Activity 因为旋转屏幕或内存回收被销毁重建时&#xff0c;UI 中的数据也会丢失。 这时候&#xff0c;Android Jetpack 提供的 ViewModel 就能帮我们解决这个问题。 1. 什么是 ViewModel ViewModel 是一种架构组件。它专…

Linux 下的 Vim 使用与网络安全配置详解

目录 引言 一、Vim 编辑器的使用 1. Vim 的模式 2. 常用操作命令 3. 保存与退出 4. 多窗口与 Shell 切换 二、Linux 网络基础 1. 网络分类 2. IP 地址与分类 三、网络配置与工具 1. ifconfig 2. netstat 3. wget 4. 主机名与 IP 映射 四、Linux 防火墙与安全设置…

Docker 容器传输文件的常用方法

Docker 容器传输文件的常用方法 在 Docker 日常使用中&#xff0c;经常需要在主机与容器之间传输文件&#xff08;如配置文件、代码包、日志等&#xff09;。以下是四种最常用的实现方式&#xff0c;覆盖临时传输、持久共享、构建集成等不同场景。 1. 使用 docker cp 命令&…

视频转音频在线工具大比拼,哪家体验更胜一筹?

最近工作上遇到了个挺有意思的需求&#xff0c;需要从几个教学视频里提取出音频内容&#xff0c;方便做成播客形式&#xff0c;让学员能随时随地学习。一开始&#xff0c;我以为这活儿挺简单的&#xff0c;不就是把视频里的声音单独弄出来嘛&#xff0c;结果一上手才发现&#…

KafKa02:Kafka配置文件server.properties介绍

一、配置文件位置二、配置文件介绍默认下&#xff1a;9092 是处理消息队列核心业务&#xff08;客户端与 broker 交互&#xff09;的端口9093 是集群内部控制器通信的端口# 指定节点角色&#xff0c;这里同时作为 broker&#xff08;消息代理&#xff09;和 controller&#xf…

哈尔滨云前沿服务器租用托管

黑龙江前沿数据&#xff0c;始建于2005年&#xff0c;多年的历史&#xff0c;专业从事域名注册&#xff0c;虚拟主机&#xff0c;服务器租用&#xff0c;云主机&#xff0c;网站建设等互联网服务。电信/联通/双线/机房/众多机房供您选择&#xff0c;总有一个适合您的服务器&…

Qt开发经验 --- Qt 修改控件样式的方式(16)

文章目录[toc]1 概述2 Qt Style Sheets (QSS)3 使用 QStyle 和 QProxyStyle4 设置 Palette (调色板)5 使用预定义的 QStyle6 直接设置控件属性7 自定义控件绘制更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt开发经验 &#x1f448;1 概述 Qt 提供了多种修改…

Vue3》》Svg图标 封装和使用

SVG 安装插件 npm i vite-plugin-svg-icons // vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import { createSvgIconsPlugin } from vite-plugin-svg-icons import { resolve } from path export default defineConfig({//配置路径别…

【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡

【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡引言本次二开布局没有变&#xff0c;但是下一次整体布局会有变&#xff0c;不过本次开发发现朋友圈跳转功能的流程步骤也做了一定的变化。原生项目复杂就复杂于就算一个颜色…

龙蜥8.10中spark各种集群及单机模式的搭建spark3.5.6(基于hadoop3.3.6集群)

先说最终的访问端口&#xff0c;如我这里ip为172.20.94.37、172.20.94.38、172.20.94.39&#xff0c;主机名分别为&#xff1a;hadoop37、hadoop38、hadoop39. 最终访问&#xff08;默认端口&#xff09;&#xff1a; hadoop webui 172.20.94.37:9870 hdfs 端口 8020 yarn 172.…

关于我重新学习 react 的第一遍

今天是25年9月11号&#xff0c;很久很久没有学习前端知识了&#xff0c;坦诚来说还清楚记得在大学里因为前端技术第一次获奖的心情&#xff0c;也清晰记得写完第一篇博客后的心情&#xff0c;工作和运动给我最大程度的成就感。 打破自己 重新开始 完全地 版本一 25.9.11 文章目…

第2课:Agent系统架构与设计模式

第2课&#xff1a;Agent系统架构与设计模式 课程目标 理解Agent的基本概念和特性掌握多Agent系统的设计模式学习Agent通信协议和消息传递实践创建简单的Agent框架 课程内容 2.1 Agent基础概念 什么是Agent&#xff1f; Agent是一个具有自主性、反应性、社会性和主动性的计…

Day42 51单片机中断系统与8×8 LED点阵扫描显示

day42 51单片机中断系统与88 LED点阵扫描显示一、51单片机引脚位操作 —— sbit 关键字 作用 sbit 是专用于 51 单片机架构的 C 语言扩展关键字&#xff0c;用于定义特殊功能寄存器&#xff08;SFR&#xff09;中的某一位&#xff0c;从而实现对单个 I/O 引脚的直接位操作。 示…

差分: 模板+题目

题目&#xff1a;【模板】差分 应用场景&#xff1a;快速解决将某一个区间所有元素加上 “一个数” 的操作。 第一步&#xff0c;预处理差分数组。 f[i] 表示&#xff1a;当前元素与前一个元素的差值 a[i] - a[i-1]; 但在题目中&#xff0c;我们其实可以不用到a[]这个数组…

GD32 Timer+ADC多通道+DMA+PWM调试记录

本例记录使用GD32307C开发板&#xff0c;实现以内部Timer1 CH1为触发源&#xff0c;触发ADC0的两个通道&#xff0c;进行并行非连续采样&#xff0c;病通过DMA传输采样结果。同时输出PWM&#xff0c;用来检测Timer1 CH1的触发周期。下面介绍具体实现过程&#xff1a;1. gpio初始…

阻塞 IO为什么叫BIO,非阻塞IO为什么叫NIO,异步IO为什么叫AIO

IOIO的核心就是数据传输&#xff0c;也就是程序与外部设备之间进行传输&#xff0c;通过IO的核心可以分为&#xff0c;文件IO和网络IO文件IO交互的对象就是本地存储设备&#xff0c;比方说读写本地文件。网络IO交互的对象就是网络设备&#xff0c;核心的应用场景就是网络通信。…

10分钟了解什么是多模态大模型

10分钟了解什么是多模态大模型&#xff08;MM-LLMs&#xff09; 1. 什么是多模态 Multimodality 多模态&#xff08;Multimodality&#xff09;是指集成和处理两种或两种以上不同类型的信息或数据的方法和技术。在机器学习和人工智能领域&#xff0c;多模态涉及的数据类型通常…

通过DSL生成Jenkins流水线

代码化管理 Jenkins 流水线&#xff08;Infrastructure as Code&#xff09; 版本控制&#xff1a;DSL 脚本可以像代码一样存入 Git、GitLab 等版本控制系统&#xff0c;所有任务配置的变更都有提交记录&#xff0c;便于追溯历史、回滚错误。协作效率&#xff1a;团队成员可以通…