【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-bind 和 v-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-if 和 v-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 参考:教程 | Vue.js (vuejs.org) 该教程需要前置知识:HTML, CSS, JavaScript 学习前置知识,你可以去 MDN Vue framework 是一个 JavaScript framework,以下简称 Vue,下面是它的特点 声明式渲染ÿ…...
SQLite数据库介绍
文章目录 SQLite常用接口 使用示例测试 SQLite SQLite是一个本地化的数据库,不需要客户端服务端什么的配置,主打就是轻量化方便化 他也不是一个独立的进程,而是可以根据应用程序的需求,可以进行静态或者动态的连接 而且他是直接存储在磁盘文件的,提供了简单易用的API接口 需…...
点击label 按钮起作用
要使点击 标签时能够触发与之关联的表单控件(如输入框、复选框或单选按钮)的作用,你需要正确地设置 标签的 for 属性,并确保该属性值与表单控件的 id 属性值相匹配。这样,当用户点击 标签时,与之关联的表…...
JPA、Hibernate、MyBatis三种ORM框架怎么选择
JPA(Java Persistence API)、Hibernate和MyBatis都是Java开发中常用的ORM(Object-Relational Mapping,对象关系映射)框架,它们提供了不同的方式来处理数据库交互。在选择这些框架时,需要考虑项目…...
【C++】map详解
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…...
力扣206.反转链表
题目链接:206. 反转链表 - 力扣(LeetCode) 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5]输出:[5,4,3,2,1] 示例 2: …...
如何查看服务器的带宽linux服务器
speedtest-cli ubuntu # 安装speedtest-cli sudo apt install speedtest-cli # 执行测试 speedtest --secure # 在其他博客可以看到使用以下的命令,但是我试了没用 speedtest-cli参考: https://ubuntu-mate.community/t/speedtest-cli-error/25722/2…...
云原生化 - 工具镜像(完整版)
在微服务和云原生环境中,容器化的目标之一是尽可能保持镜像小型化以提高启动速度和减少安全风险。然而,在实际操作中,有时候需要临时引入一些工具来进行调试、监控或问题排查。Kubernetes提供了临时容器(ephemeral containers)的功能,允许在不改变原始容器镜像的情况下,…...
leetcode68:文本左右对齐
给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可…...
Linux驱动学习——内核编译
1、从官网下载适合板子的Linux内核版本 选择什么版本的内核需要根据所使用的硬件平台而定,最好使用硬件厂商推荐使用的版本 https://www.kernel.org/pub/linux/kernel/ 2、将压缩包复制到Ubuntu内进行解压 sudo tar -xvf linux-2.6.32.2-mini2440-20150709.tgz 然…...
MES系统:制造业的智能大脑
引言 在当今快速变化的制造业环境中,企业面临着激烈的市场竞争和不断变化的客户需求。为了保持竞争力,制造企业必须提高生产效率、降低成本、缩短产品上市时间,并确保产品质量。MES(制造执行系统)作为一种先进的生产管…...
忘记 MySQL 密码怎么办:破解 root 账户密码
忘记 MySQL 密码怎么办:破解 root 账户密码 目录 忘记 MySQL 密码怎么办:破解 root 账户密码1、修改 MySQL 配置文件2、不使用密码登录 MySQL3、重置 root 用户密码4、修改 MySQL 配置文件并重启 MySQL 服务5、使用新密码登录 MySQL 如果忘记密码导致无法…...
【LeetCode每日一题】——17.电话号码的字母组合
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 回溯 二【题目难度】 中等 三【题目编号】 17.电话号码的字母组合 四【题目描述】 给定一个…...
Git管理远程仓库
添加远程仓库 要新增远程,请在终端上存储存储库的目录中使用 git remote add 命令。 git remote add 命令采用两个参数: 远程名称(例如 origin)远程 URL(例如 https://github.com/OWNER/REPOSITORY.git)…...
在 /var/cache/apt/archives/ 上没有足够的可用空间的解决方法
问题 apt-get upgrade 更新软件包时,提示没有足够的空间。 分析 一般来说,除非下载的文件过于大,整个服务器的内存都不够用,否则可以改变默认的下载路径进行下载。 解决方法 找一个空间足够的目录,新建一个单独的…...
FastAdmin Apache下设置伪静态
FastAdmin Apache下设置伪静态 一、引言 FastAdmin 是一个基于ThinkPHP和Bootstrap框架开发的快速后台开发框架,它以其简洁、高效、易于扩展的特点,广受开发者的喜爱。在部署FastAdmin项目时,为了提高访问速度和用户体验,我们通…...
MPI程序实例:自适应数值积分(主从模式)
目录 一、主从模式的自适应梯形公式 二、串行程序 三、基于非阻塞通信的并行程序 四、基于散发/收集通信的并行程序 上一节我们介绍了采用梯形公式结合自适应局部区间加密,计算一个函数在给定区间上的定积分达到指定精度。 MPI程序实例:自适应数值积分-CSDN博客…...
蓝桥杯—STM32G431RBT6(IIC通信--EEPROM(AT24C02)存储器进行通信)
一、什么是IIC?24C02存储器有什么用? IIC (IIC 是半双工通信总线。半双工意味着数据在某一时刻只能沿一个方向传输,即发送数据的时候不能接收数据,接收数据的时候不能发送数据)即集成电路总线(…...
【重学 MySQL】六十二、非空约束的使用
【重学 MySQL】六十二、非空约束的使用 定义目的关键字特点作用创建非空约束删除非空约束注意事项 在MySQL中,非空约束(NOT NULL Constraint)是一种用于确保表中某列不允许为空值的数据库约束。 定义 非空约束(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) # 输…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...


