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

【Vue】Vue 快速教程

Vue tutorial

参考:教程 | Vue.js (vuejs.org)

该教程需要前置知识:HTML, CSS, JavaScript

学习前置知识,你可以去 MDN

Vue framework 是一个 JavaScript framework,以下简称 Vue,下面是它的特点

  • 声明式渲染(Declarative Rendering):即声明 JavaScript 对象,改变对象状态来更改 HTML,这个过程由 Vue 完成
  • 响应式(Reactivity):JavaScript 的对象状态改变会马上反映到 DOM(不知道 DOM 的去查 MDN 文档)

Declarative Rendering and Reactivity

Vue 实现了:

JavaScript Obejct <-> Vue <-> DOM

但是 Vue 显然是利用 JavaScript 机制,那就是 Proxy,Proxy 可以实现

JavaScript Object <-> Proxy <-> DOM

所以 Vue 把 Proxy 改造后封装成了 reactive(),调用这个 API 会返回一个特殊的对象,称之为响应式对象(reactive object)

  • reactive()
import { reactive } from 'vue'const counter = reactive({count: 0
})console.log(counter.count) // 0
counter.count++

但是 reactive() 参数只能是对象(还有数组和内置类型),Vue 又把 reactive() 改造封装成了 ref(),它也会返回一个响应式对象,并且带一个 .value property,只不过它的参数可以填写值。

  • ref()
import { ref } from 'vue'const message = ref('Hello World!')console.log(message.value) // "Hello World!"
message.value = 'Changed'

以上给 ref()reactive() 填写参数得到响应式对象的过程,就被成为数据绑定(data binding)。

Template syntax

在这里复习一下 HTML element 和 attribute 概念

Anatomy of an HTML element

这是 HTML element

在这里插入图片描述

这是 HTML attribute

在这里插入图片描述

在此之中,Class 为 attribute name,editor-note 为 attribute value

Vue 自己创造了一套 template language。最基本的数据绑定是文本插值(Text Interpolation),它可以改变 element 的 content,像这样

<span>{{ message }}</span>

这种语法被成为 “Mustache”语法 (即双大括号)。再结合上节讲到的 reactive object,我们可以这样写

import { ref } from 'vue'const message = ref('Hello World!')

效果是这样的

在这里插入图片描述

可能你会有些疑问,为什么不是写 {{message.value}},因为它是 top-level property,会自动解包(unwrapping)

const object = { id: ref(1) }

比如这个里面,id 就不是 top-level property,如果你这么写

{{ object.id + 1 }}

渲染的结果将是 [object Object]1

在这里插入图片描述

你需要手动解包,才会渲染出 2

{{ object.id.value + 1 }}

在这里插入图片描述

Directive

v-xxx 就是一种 attribute,在它的 template language 中被称为 directive

v-bind

Attribute bind

在 Vue 中,Mustache 语法只能用于文本插值来改变 element content,没法儿操作 element attribute。而且 element attribute 是静态的,为了给 element attribute 绑定一个动态值,需要使用 Vue 的 v-bind directive

<div v-bind:id="dynamicId"></div>

冒号后面的 id 被称为 directive 的参数(argument),dynamicId 则为参数值,它会和响应式对象的 property 同步。它可以简写为

<div :id="dynamicId"></div>

例子:

<script setup>
import { ref } from 'vue'const titleClass = ref('title')
</script><template><h1 :class="titleClass">Make me red</h1>
</template><style>
.title {color: red;
}
</style>

v-on

Event Listen

可以通过 v-on 来监听 DOM event

<button v-on:click="increment">{{ count }}</button>

简写

<button @click="increment">{{ count }}</button>

点击 button 会触发 increment() 这个函数。

例子:

<script setup>
import { ref } from 'vue'const count = ref(0)function increment() {count.value++
}
</script><template><button @click="increment">count is: {{ count }}</button>
</template>

v-model

Form bind

同时使用 v-bindv-on 对表单(form)进行绑定和监听

<script setup>
import { ref } from 'vue'const text = ref('')function onInput(e) {text.value = e.target.value
}
</script><template><input :value="text" @input="onInput" placeholder="Type here"><p>{{ text }}</p>
</template>

啊,这么写实在太麻烦,所以 vue 提供了 v-model。当然,最好看一下它支持哪些 element。

<script setup>
import { ref } from 'vue'const text = ref('')
</script><template><input v-model="text" placeholder="Type here"><p>{{ text }}</p>
</template>

v-if and v-else

Conditional Rendering

v-ifv-else 可以根据条件来决定 element 是否在 DOM 中。

例子:

<script setup>
import { ref } from 'vue'const awesome = ref(true)function toggle() {awesome.value = !awesome.value
}
</script><template><button @click="toggle">toggle</button><h1 v-if="awesome">Vue is awesome!</h1><h1 v-else>Oh no 😢</h1>
</template>

改变 awesome 的值来显示 “Vue is awesome!” 和 “Oh no 😢”。

v-for

当你想写一个列表时,一个个写列表的 element 实在太累了,如果有 1000 个那不就完蛋了,所以 v-for 可以通过循环,直接渲染出列表(当然,你得给相应的数据)

<script setup>
import { ref } from 'vue'// 给每个 todo 对象一个唯一的 id
let id = 0const newTodo = ref('')
const todos = ref([{ id: id++, text: 'Learn HTML' },{ id: id++, text: 'Learn JavaScript' },{ id: id++, text: 'Learn Vue' }
])function addTodo() {todos.value.push({ id: id++, text: newTodo.value })newTodo.value = ''
}function removeTodo(todo) {todos.value = todos.value.filter((t) => t !== todo)
}
</script><template><form @submit.prevent="addTodo"><input v-model="newTodo" required placeholder="new todo"><button>Add Todo</button></form><ul><li v-for="todo in todos" :key="todo.id">{{ todo.text }}<button @click="removeTodo(todo)">X</button></li></ul>
</template>

computed()

template 里面可以写这种计算表达式

<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
const author = reactive({name: 'John Doe',books: ['Vue 2 - Advanced Guide','Vue 3 - Basic Guide','Vue 4 - The Mystery']
})

但是这种计算写在 template 里是真的不好理解,个人感觉在结构上 View 里不能有逻辑,所以尽量不要这么写。

所以我们可以用 computed(),这样

<script setup>
import { reactive, computed } from 'vue'const author = reactive({name: 'John Doe',books: ['Vue 2 - Advanced Guide','Vue 3 - Basic Guide','Vue 4 - The Mystery']
})// 一个计算属性 ref
const publishedBooksMessage = computed(() => {return author.books.length > 0 ? 'Yes' : 'No'
})
</script><template><p>Has published books:</p><span>{{ publishedBooksMessage }}</span>
</template>

它和 method() 最大区别在于,它是只有在响应式对象更新时才会重新被调用和计算,否则就会直接返回缓存值,即 books 不变,重渲染(比如页面刷新,更新)时 computed() 不会被调用,而 method() 则会。插嘴一句 method(),重渲染时总会被调用。

Lifecycle and Template Refs

手动用 JavaScript 操作 DOM 是一件苦差事,所以我们用 vue 来帮忙,但是有时我们不得不操作 DOM,这个时候我们就得使用模板引用(template ref)

这个时候就要使用 ref attribute

<p ref="pElementRef">hello</p>

如果要访问这个 ref,我们需要声明(declare)ref 并初始化

const pElementRef = ref(null)

注意我们使用给 ref 的 argument 为 null,这是因为<script setup> 执行时,DOM 还没有初始化,template ref 只能在挂在(mount)后访问,所以我们可以使用生命周期钩子(lifecycle hook)比如 onMounted(),关于 lifecycle 请看生命周期图示

例子:

<script setup>
import { ref, onMounted } from 'vue'const pElementRef = ref(null)onMounted(() => {pElementRef.value.textContent = 'mounted!'
})
</script><template><p ref="pElementRef">hello</p>
</template>

watch()

watch() 可以监察一个 ref,并触发一个 callback function,比如下面的例子就是监察 todoId,触发 fetchData

<script setup>
import { ref, watch } from 'vue'const todoId = ref(1)
const todoData = ref(null)async function fetchData() {todoData.value = nullconst res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)todoData.value = await res.json()
}fetchData()watch(todoId, fetchData)
</script><template><p>Todo id: {{ todoId }}</p><button @click="todoId++" :disabled="!todoData">Fetch next todo</button><p v-if="!todoData">Loading...</p><pre v-else>{{ todoData }}</pre>
</template>

Component

Vue application 常常是多个 component 嵌套创建,所以就有 parent component 包含 child component

如果要使用 child component,就需要导入它

import ChildComp from './ChildComp.vue'

使用 child component

<ChildComp />

例子:

<!--App.vue--><script setup>
import ChildComp from './ChildComp.vue'
</script><template><ChildComp />
</template>

Props

child component 可以通过 props 从 parent component 获取动态数据

<!--ChildComp.vue-->
<script setup>
const props = defineProps({msg: String
})
</script>

注意,defineProps() 是一个 runtime marcro,不需要导入。

这样,msg 就可以在 child component 的 <template> 中使用

<template><h2>{{ msg || 'No props passed yet' }}</h2>
</template>

而 parent component 则可以用 v-bind 传递数据

<!--App.vue-->
<ChildComp :msg="greeting" />

例子:

<!--App.vue-->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'const greeting = ref('Hello from parent')
</script><template><ChildComp :msg="greeting" />
</template>
<!--ChildComp.vue-->
<script setup>
const props = defineProps({msg: String
})
</script><template><h2>{{ msg || 'No props passed yet' }}</h2>
</template>

child component 中采用的 “runtime declaration”,还有一种是如果你用 typescript,需要采用 “type-based declaration”,具体看官方文档。

Emits

child component 可以向 parent component 传 event,emit() 中,第一个 argument 是 event name,其他的会传给 event listener。

parent component 可以通过 v-on 监听 child-emitted event,并且可以将额外的 argument 赋值给 local state

<!--App.vue-->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'const childMsg = ref('No child msg yet')
</script><template><ChildComp @response="(msg) => childMsg = msg" /><p>{{ childMsg }}</p>
</template>
<!--ChildComp.vue-->
<script setup>
const emit = defineEmits(['response'])emit('response', 'hello from child')
</script><template><h2>Child component</h2>
</template>

Solots

除了 props,parent component 可以将 template 片段传给 child component,而在 child component,则可以使用 <slot> 来显示片段的内容。

<!--App.vue-->
<script setup>
import { ref } from 'vue'
import ChildComp from './ChildComp.vue'const msg = ref('from parent')
</script><template><ChildComp>Message: {{ msg }}</ChildComp>
</template>
<template><slot>Fallback content</slot>
</template>

以上 parent component 中,<ChildComp> 的内容会传给 child component 中的 <slot>,最后渲染在 parent component 上。

在这里插入图片描述

Essential

Create application

application instance and root component

Vue application 通常是通过 createApp 来创建一个 application instance

createApp 的参数则被称为 root component,而且其他 component 则作为 root 的 child component,所以 vue application 是由 root component 和 child component 组成的。

一般这种创建代码都在 <project-name>/src/main.js

<!--main.js-->
import { createApp } from 'vue'
// 导入一个单组件
import App from './App.vue'
// 将这个单组件作为根组件
const app = createApp(App)

一个 Todo application 的例子

App (root component)
├─ TodoList
│  └─ TodoItem
│     ├─ TodoDeleteButton
│     └─ TodoEditButton
└─ TodoFooter├─ TodoClearButton└─ TodoStatistics

mount application

application instance 必须调用 mount() 才能渲染,而必须的 argument 则为 DOM 的 element 或者 CSS selector

在 vue project 中一般在 <project-name>/index.html

<div id="app"></div>

注意 mount() 应该始终在应用配置完成后调用,简单点儿说就是最后调用。

Applicaiton configuration

application instance 会提供一个 config object,这样就可以配置 vue app,比如 app-level option,capture error

app.config.errorHandler = (err) => {/* 处理错误 */
}

或者 component registration

比如 global registration

app.component('TodoDeleteButton', TodoDeleteButton)

其他细节清参考 vue 文档。

Component registration

如果要使用 component,则必须是 registered

Global registration

使用 .component() method:

import { createApp } from 'vue'const app = createApp({})app.component(// the registered name'MyComponent',// the implementation{/* ... */}
)

如果使用 SFC,则

import MyComponent from './App.vue'app.component('MyComponent', MyComponent)

Local registration

使用 component option

import ComponentA from './ComponentA.js'export default {components: {ComponentA},setup() {// ...}
}

使用 SFC

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

Toolchain

当你创建一个 vue project 时,手动新建目录和文件是很麻烦的事情,所有我们有项目脚手架(Project Scaffolding)来自动创建 project 基本的目录和文件。

Vite

尤雨溪开发的 build tool,支持 SFC,通过 Vite 创建项目:

npm create vue@latest

Vue CLI

基于 webpack 的 build tool,但现在是维护状态,建议使用 Vite。

相关文章:

【Vue】Vue 快速教程

Vue tutorial 参考&#xff1a;教程 | Vue.js (vuejs.org) 该教程需要前置知识&#xff1a;HTML, CSS, JavaScript 学习前置知识&#xff0c;你可以去 MDN Vue framework 是一个 JavaScript framework&#xff0c;以下简称 Vue&#xff0c;下面是它的特点 声明式渲染&#xff…...

SQLite数据库介绍

文章目录 SQLite常用接口 使用示例测试 SQLite SQLite是一个本地化的数据库,不需要客户端服务端什么的配置,主打就是轻量化方便化 他也不是一个独立的进程,而是可以根据应用程序的需求,可以进行静态或者动态的连接 而且他是直接存储在磁盘文件的,提供了简单易用的API接口 需…...

点击label 按钮起作用

要使点击 标签时能够触发与之关联的表单控件&#xff08;如输入框、复选框或单选按钮&#xff09;的作用&#xff0c;你需要正确地设置 标签的 for 属性&#xff0c;并确保该属性值与表单控件的 id 属性值相匹配。这样&#xff0c;当用户点击 标签时&#xff0c;与之关联的表…...

JPA、Hibernate、MyBatis三种ORM框架怎么选择

JPA&#xff08;Java Persistence API&#xff09;、Hibernate和MyBatis都是Java开发中常用的ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;框架&#xff0c;它们提供了不同的方式来处理数据库交互。在选择这些框架时&#xff0c;需要考虑项目…...

【C++】map详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…...

力扣206.反转链表

题目链接&#xff1a;206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5]输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; …...

如何查看服务器的带宽linux服务器

speedtest-cli ubuntu # 安装speedtest-cli sudo apt install speedtest-cli # 执行测试 speedtest --secure # 在其他博客可以看到使用以下的命令&#xff0c;但是我试了没用 speedtest-cli参考&#xff1a; https://ubuntu-mate.community/t/speedtest-cli-error/25722/2…...

云原生化 - 工具镜像(完整版)

在微服务和云原生环境中,容器化的目标之一是尽可能保持镜像小型化以提高启动速度和减少安全风险。然而,在实际操作中,有时候需要临时引入一些工具来进行调试、监控或问题排查。Kubernetes提供了临时容器(ephemeral containers)的功能,允许在不改变原始容器镜像的情况下,…...

leetcode68:文本左右对齐

给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff1b;也就是说&#xff0c;尽可能多地往每行中放置单词。必要时可…...

Linux驱动学习——内核编译

1、从官网下载适合板子的Linux内核版本 选择什么版本的内核需要根据所使用的硬件平台而定&#xff0c;最好使用硬件厂商推荐使用的版本 https://www.kernel.org/pub/linux/kernel/ 2、将压缩包复制到Ubuntu内进行解压 sudo tar -xvf linux-2.6.32.2-mini2440-20150709.tgz 然…...

MES系统:制造业的智能大脑

引言 在当今快速变化的制造业环境中&#xff0c;企业面临着激烈的市场竞争和不断变化的客户需求。为了保持竞争力&#xff0c;制造企业必须提高生产效率、降低成本、缩短产品上市时间&#xff0c;并确保产品质量。MES&#xff08;制造执行系统&#xff09;作为一种先进的生产管…...

忘记 MySQL 密码怎么办:破解 root 账户密码

忘记 MySQL 密码怎么办&#xff1a;破解 root 账户密码 目录 忘记 MySQL 密码怎么办&#xff1a;破解 root 账户密码1、修改 MySQL 配置文件2、不使用密码登录 MySQL3、重置 root 用户密码4、修改 MySQL 配置文件并重启 MySQL 服务5、使用新密码登录 MySQL 如果忘记密码导致无法…...

【LeetCode每日一题】——17.电话号码的字母组合

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 回溯 二【题目难度】 中等 三【题目编号】 17.电话号码的字母组合 四【题目描述】 给定一个…...

Git管理远程仓库

添加远程仓库 要新增远程&#xff0c;请在终端上存储存储库的目录中使用 git remote add 命令。 git remote add 命令采用两个参数&#xff1a; 远程名称&#xff08;例如 origin&#xff09;远程 URL&#xff08;例如 https://github.com/OWNER/REPOSITORY.git&#xff09;…...

在 /var/cache/apt/archives/ 上没有足够的可用空间的解决方法

问题 apt-get upgrade 更新软件包时&#xff0c;提示没有足够的空间。 分析 一般来说&#xff0c;除非下载的文件过于大&#xff0c;整个服务器的内存都不够用&#xff0c;否则可以改变默认的下载路径进行下载。 解决方法 找一个空间足够的目录&#xff0c;新建一个单独的…...

FastAdmin Apache下设置伪静态

FastAdmin Apache下设置伪静态 一、引言 FastAdmin 是一个基于ThinkPHP和Bootstrap框架开发的快速后台开发框架&#xff0c;它以其简洁、高效、易于扩展的特点&#xff0c;广受开发者的喜爱。在部署FastAdmin项目时&#xff0c;为了提高访问速度和用户体验&#xff0c;我们通…...

MPI程序实例:自适应数值积分(主从模式)

目录 一、主从模式的自适应梯形公式 二、串行程序 三、基于非阻塞通信的并行程序 四、基于散发/收集通信的并行程序 上一节我们介绍了采用梯形公式结合自适应局部区间加密,计算一个函数在给定区间上的定积分达到指定精度。 MPI程序实例:自适应数值积分-CSDN博客…...

蓝桥杯—STM32G431RBT6(IIC通信--EEPROM(AT24C02)存储器进行通信)

一、什么是IIC&#xff1f;24C02存储器有什么用&#xff1f; IIC &#xff08;IIC 是半双工通信总线。半双工意味着数据在某一时刻只能沿一个方向传输&#xff0c;即发送数据的时候不能接收数据&#xff0c;接收数据的时候不能发送数据&#xff09;即集成电路总线&#xff08;…...

【重学 MySQL】六十二、非空约束的使用

【重学 MySQL】六十二、非空约束的使用 定义目的关键字特点作用创建非空约束删除非空约束注意事项 在MySQL中&#xff0c;非空约束&#xff08;NOT NULL Constraint&#xff09;是一种用于确保表中某列不允许为空值的数据库约束。 定义 非空约束&#xff08;NOT NULL Constra…...

Python获取json返回的字符串获取方法大全

1、使用 json.loads() 解析JSON字符串 import jsonjson_string {"name": "Alice", "age": 25, "city": "Beijing"} data json.loads(json_string)# 获取字符串值 name data[name] print("Name:", name) # 输…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...