Vue 从 0 到 1:小白也能看懂的入门指南(附代码)
Vue 从 0 到 1:小白也能看懂的入门指南(附代码)
Vue 从 0 到 1:小白也能看懂的入门指南(附代码)
简介
Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架,由尤雨溪(Evan You)于 2014 年发布。它以其简洁的 API、灵活的组件化开发和高效的性能,迅速成为最受欢迎的前端框架之一。
Vue 的核心目标是帮助开发者更轻松地构建动态、交互式的 Web 应用。它既适合小型项目(如增强静态页面的交互性),也能胜任复杂的企业级单页应用(SPA)。Vue 的设计哲学强调渐进式采用,开发者可以根据需求逐步引入其功能,而不需要一开始就全面重构现有项目。
关键特性
- 响应式数据绑定:自动同步数据与视图。
- 组件化架构:复用 UI 和逻辑。
- 虚拟 DOM:高效渲染更新。
- 丰富的生态系统:路由(Vue Router)、状态管理(Pinia/Vuex)、构建工具(Vite)等。
适用场景
- 单页应用(SPA)
- 动态内容管理
- 交互式仪表盘
- 渐进式 Web 应用(PWA)
Vue的安装与配置
- Vue的安装与配置
Vue的安装与配置: https://blog.csdn.net/R_Feynman_/article/details/150268570?spm=1001.2014.3001.5501
一、Vue的项目结构
当你用 npm init vue@latest初始化项目后,面对生成的十几个文件夹和文件很多新手在使用 Vue 脚手架创建项目后,往往会陷入这样的困惑,下面让我们来了解一下里面生成的到底是一些什么

-
.vscode
VS Code 编辑器配置文件夹,可能包含工作区设置或调试配置 -
node_modules
node_modules目录是由 npm install 命令自动生成的,用于存放项目依赖的所有第三方库和工具包。 -
public
存放静态资源文件(如图片、字体等),这些文件会被直接复制到构建输出目录 -
src
核心源代码目录,通常包含:
main.js(入口文件)
App.vue(根组件)
components/(子组件)
assets/(项目资源)
-
.gitignore
Git 版本控制忽略规则文件 -
index.html
项目主 HTML 文件,Vue 应用的挂载点 -
jsconfig.json
JavaScript 项目配置,用于提供代码智能提示和模块解析 -
package-lock.json
锁定安装时的依赖版本(由 npm 自动生成) -
package.json
项目配置文件,包含:
项目元信息
依赖列表
脚本命令(如 dev/build) -
README.md
项目说明文档 -
vite.config.js
Vite 构建工具配置文件
| 文件/文件夹 | 类型 | 作用描述 |
|---|---|---|
| .vscode | 文件夹 | VS Code 编辑器特定配置 |
| node_modules | 文件夹 | 存储所有安装的 npm 依赖包 |
| public | 文件夹 | 存放无需处理的静态资源文件 |
| src | 文件夹 | 项目核心源代码目录(组件、逻辑、资源等) |
| .gitignore | 文件 | 指定 Git 版本控制应忽略的文件和目录 |
| index.html | 文件 | 应用的主 HTML 文件,Vue 应用的挂载点 |
| jsconfig.json | 文件 | 定义 JavaScript 项目的编译器选项 |
| package-lock.json | 文件 | 锁定依赖版本,确保安装一致性 |
| package.json | 文件 | 项目配置和依赖管理文件 |
| README.md | 文件 | 项目说明文档 |
| vite.config.js | 文件 | Vite 构建工具的配置文件 |
在我们开发的时候我们可以清空src里面的components/(子组件)和 assets/(项目资源)保留一个App.vue,这样我们就有了一个纯净的Vue项目
接下来在App.vue里面写入如下程序,在main.js里面删除import './assets/main.css' 此时恭喜你,你的第一个vue程序就诞生了
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "hello",
data() {
return {
msg: "欢迎来到Vue3!",
};
},
};
</script>
在运行npm run dev之后就可以见到欢迎来到Vue3!的字样
二、详细解剖Vue的第一个程序HelloWorld
作为Vue.js的初学者,你可能听说过各种复杂的构建工具和脚手架,但在刚开始学习时,最简单直接的方式就是在HTML文件中引入Vue.js的全局文件。
1. 使用CDN引入Vue.js
- 使用unpkg CDN来引入Vue.js(这会引入Vue 3的最新版本):
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
- 如果你想指定版本,可以这样写:
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script>
2. 创建你的第一个Vue应用
在第二个<script>标签中,添加以下代码:
const { createApp } = Vue;
createApp({
data() {
return {
message: '你好,Vue!'
}
},
template: `<div>{{ message }}</div>`
}).mount('#app');
const { createApp } = Vue;
作用:解构Vue的工厂函数
从全局Vue对象提取createApp方法,等价于:const createApp = Vue.createApp;
特点:
Vue 3的创建方式(区别于Vue 2的new Vue()) ,工厂函数模式更灵活
createApp({...})
功能:创建应用实例
配置对象包含:
data() - 响应式数据源,template - 视图模板,其他可选选项(methods/computed等)
执行过程:
接收配置对象,初始化响应式系统,编译模板,返回未挂载的应用实例
data() {...}
核心作用:声明响应式数据
接下来给出完整的示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>你好,Vue3</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
message: "你好,Vue3!",
};
},
template: `<div>{{ message }}</div>`,
}).mount("#app");
</script>
</body>
</html>
以下是其工作原理:
①. HTML 结构
<div id="app"></div>是 Vue 应用的挂载点,Vue 会将渲染的内容插入到这个 div 中
②. Vue 引入
<script src="...vue.global.js">引入了 Vue 3 的全局构建版本,这会暴露Vue全局变量
③. Vue 应用创建
const { createApp } = Vue;从 Vue 对象中解构出createApp方法createApp()创建了一个 Vue 应用实例
④. 应用配置对象
data()函数返回一个包含响应式数据的对象,这里定义了message属性template定义了应用的模板,使用双大括号{{ }}语法绑定message数据
⑤. 挂载应用
.mount("#app")将 Vue 应用挂载到 id 为 “app” 的 DOM 元素上
三、声明式渲染(Declarative Rendering)
声明式渲染是 Vue.js 的核心思想之一,贯穿了整个 Vue 的设计理念和开发模式。Vue 通过响应式数据绑定和模板语法,让开发者可以声明式地描述 UI 应该是什么样子,而无需手动操作 DOM。
1. 基本声明式渲染
<div id="app">
{{ message }}
</div>`
const app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
在这个例子中:
• 双大括号 {{ }}是 Vue 的模板语法
• message是 Vue 实例的 data 对象中的一个属性
• 当 data 中的 message改变时,视图会自动更新
2. 指令系统
Vue 提供了多种指令来实现声明式渲染:
v-bind (绑定属性)
<div v-bind:title="message">
鼠标悬停查看动态绑定的标题
</div>
简写形式
<div :title="message">
简写形式
</div>
v-for (列表渲染)
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
3. 计算属性
对于复杂逻辑,可以使用计算属性:
new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName
}
}
})
四、模板语法
温馨提示这个框架可以把下面的任何一段代码放进来运行
下面的代码为了方便调试,全部都是在HTML文件中引入Vue.js的全局文件的形式的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 表单绑定示例</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<!-- 温馨提示这个框架可以把下面的任何一段代码放进来运行 -->
</body>
</html>
1. 插值
1.1 文本插值
使用双大括号 {{ }}(Mustache 语法)进行文本插值:
<div id="app">
<p>{{ message }}</p>
<button @click="change">更改</button>
</div>
<script>
Vue.createApp({
data() {
return { message: "Hello Vue!" }
},
methods: {
change() { this.message = "Changed!" }
}
}).mount('#app')
</script>
1.2 原始 HTML
双大括号会将数据解释为普通文本,使用 v-html 指令可以输出真正的 HTML:
<div id="app">
<div :class="{ active: isActive }">红色文字</div>
<button @click="toggle">切换</button>
</div>
<script>
Vue.createApp({
data() { return { isActive: true } },
methods: {
toggle() { this.isActive = !this.isActive }
}
}).mount('#app')
</script>
1.3 使用 JavaScript 表达式
Vue 支持在模板中使用 JavaScript 表达式:
1.基本运算:{{ number + 1 }}→ 显示 11
2.三元表达式:{{ ok ? ‘YES’ : ‘NO’ }}→ 显示 YES
3.方法调用:{{ message.split(‘’).reverse().join(‘’) }}→ 显示 “olleH”
4.属性绑定中的表达式::id="‘list-’ + id"→ 生成 id=“list-1”
<div id="app">
<!-- 基本运算 -->
<p>数字加1: {{ number + 1 }}</p>
<!-- 三元表达式 -->
<p>状态判断: {{ ok ? 'YES' : 'NO' }}</p>
<!-- 方法调用 -->
<p>反转字符串: {{ message.split('').reverse().join('') }}</p>
<!-- 属性绑定中使用表达式 -->
<div :id="'list-' + id">这个div的ID是: list-{{ id }}</div>
<!-- 显示原始数据 -->
<hr>
<h3>当前数据状态:</h3>
<p>number: {{ number }}</p>
<p>ok: {{ ok }}</p>
<p>message: {{ message }}</p>
<p>id: {{ id }}</p>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
number: 10,
ok: true,
message: 'Hello',
id: 1
}
}
}).mount('#app');
</script>
2. 指令
指令是带有 v-前缀的特殊 attribute:
1.条件渲染 (v-if):
显示/隐藏文本
添加了切换按钮显示当前状态
2.事件监听:
展示了完整语法(v-on:click)和简写语法(@click)
点击按钮会触发alert
3.动态参数:
动态绑定属性名和事件名
显示当前使用的属性名和事件名
4.表单输入绑定 (v-model):
实现输入框和数据的双向绑定
实时显示输入内容
<div id="app">
<h2>Vue 3 指令演示</h2>
<!-- 条件渲染 -->
<div>
<h3>1. 条件渲染 (v-if)</h3>
<p v-if="seen">现在你看到我了</p>
<button @click="seen = !seen">切换显示状态</button>
<p>当前seen值: {{ seen }}</p>
</div>
<!-- 事件监听 -->
<div>
<h3>2. 事件监听</h3>
<button v-on:click="doSomething">点击我 (完整语法)</button>
<button @click="doSomething">点击我 (简写语法)</button>
</div>
<!-- 动态参数 -->
<div>
<h3>3. 动态参数</h3>
<a v-bind:[attributeName]="url">动态绑定href属性的链接</a>
<br>
<a @[eventName]="doSomething">动态绑定click事件的链接</a>
<p>当前attributeName: "{{ attributeName }}"</p>
<p>当前eventName: "{{ eventName }}"</p>
</div>
<!-- 表单输入绑定 -->
<div>
<h3>4. 表单输入绑定 (v-model)</h3>
<input v-model="message" placeholder="编辑我">
<p>Message is: {{ message }}</p>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
seen: true,
url: 'https://vuejs.org',
attributeName: 'href',
eventName: 'click',
message: ''
}
},
methods: {
doSomething() {
alert('按钮被点击了!');
}
}
}).mount('#app');
</script>
3. 条件语句
1.v-if / v-else 基本用法:
根据 awesome 的值显示不同的标题,添加了切换按钮和状态显示
2.带 key 的模板切换:
使用 key 属性确保输入框在切换时重新渲染,演示了两种不同的登录表单切换
3.v-show 用法:
通过 display 样式控制元素显示/隐藏,对比 v-if 和 v-show 的区别
<div id="app">
<h2>Vue 3 条件渲染演示</h2>
<div>
<h3>1. v-if / v-else 基本用法</h3>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
<button @click="awesome = !awesome">切换状态</button>
<p>当前awesome值: {{ awesome }}</p>
</div>
<div>
<h3>2. 带key的模板切换</h3>
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
<br>
<button @click="toggleLoginType">切换登录类型</button>
<p>当前登录类型: {{ loginType }}</p>
</div>
<div>
<h3>3. v-show 用法</h3>
<div v-show="isShow">这个元素会被显示/隐藏</div>
<button @click="isShow = !isShow">切换显示状态</button>
<p>当前isShow值: {{ isShow }}</p>
<p>注意:v-show只是切换display样式,元素始终存在于DOM中</p>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
awesome: true,
loginType: 'username',
isShow: true
}
},
methods: {
toggleLoginType() {
this.loginType = this.loginType === 'username' ? 'email' : 'username';
}
}
}).mount('#app');
</script>
4. 循环语句
在元素上使用 v-for 指令,根据源数据的数组或对象进行循环渲染元素。
遍历数组:
v-for="(item, index) in items"
遍历对象:
v-for="(value, key, index) in object"
key 的作用: 使用 v-for 渲染列表时,必须为每个项提供一个唯一的 key 属性,以便 Vue 能够识别每个项的唯一性,从而进行高效的 DOM 更新。
4.1 单次循环
<div id="app">
<h2>商品列表</h2>
<ul>
<li v-for="(product, index) in products" :key="product.id">
{{ index + 1 }}. {{ product.name }} - ¥{{ product.price }}
</li>
</ul>
<h2>用户信息</h2>
<div v-for="(value, key) in user" :key="key">
{{ key }}: {{ value }}
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
products: [
{ id: 1, name: '笔记本电脑', price: 5999 },
{ id: 2, name: '智能手机', price: 3999 },
{ id: 3, name: '无线耳机', price: 299 }
],
user: {
name: '张三',
age: 28,
email: 'zhangsan@example.com'
}
}
}
}).mount('#app')
</script>
4.2 嵌套循环
嵌套循环: 可以嵌套使用多个 v-for 来渲染多维数组或对象结构。
<div id="app">
<h2>部门员工列表</h2>
<div v-for="department in departments" :key="department.name">
<h3>{{ department.name }}</h3>
<ul>
<li v-for="employee in department.employees" :key="employee.id">
{{ employee.name }} - {{ employee.position }}
</li>
</ul>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
departments: [
{
name: '技术部',
employees: [
{ id: 1, name: '李四', position: '前端工程师' },
{ id: 2, name: '王五', position: '后端工程师' }
]
},
{
name: '市场部',
employees: [
{ id: 3, name: '赵六', position: '市场经理' },
{ id: 4, name: '钱七', position: '市场专员' }
]
}
]
}
}
}).mount('#app')
</script>
5. 属性绑定
Mustache 语法不能作用在 HTML attribute 上,应该使用 v-bind指令:v-bind 在处理 class 和 style 时, 表达式除了可以使用字符串之外,还可以是对象或数组。v-bind:class 可以简写为 :class。
<div id="app">
<!-- 完整语法 -->
<div v-bind:id="dynamicId">动态ID元素 (完整语法)</div>
<!-- 简写 -->
<div :id="dynamicId">动态ID元素 (简写语法)</div>
<!-- 绑定布尔值 -->
<button :disabled="isButtonDisabled">可点击按钮 (当前状态: {{ isButtonDisabled ? '禁用' : '可用' }})</button>
<button @click="toggleButton">切换按钮状态</button>
<!-- 动态绑定多个值 -->
<div v-bind="objectOfAttrs">绑定多个属性的元素</div>
<!-- 显示当前状态 -->
<p>当前按钮禁用状态: {{ isButtonDisabled }}</p>
<p>objectOfAttrs 内容: {{ objectOfAttrs }}</p>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
dynamicId: 'my-id',
isButtonDisabled: false,
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
},
methods: {
toggleButton() {
this.isButtonDisabled = !this.isButtonDisabled;
}
}
}).mount('#app');
</script>
6. 计算属性和侦听器
1.计算属性:
reversedMessage自动反转 message 的内容,添加按钮演示响应式更新
2.侦听器:
监听 question 的变化,当检测到问号时自动触发回答
3.异步方法:
模拟异步获取答案的过程,显示"思考中…"状态和最终回答
<div id="app">
<h2>Vue 3 计算属性和侦听器演示</h2>
<div>
<h3>1. 计算属性</h3>
<p>原始消息: "{{ message }}"</p>
<p>计算后反转消息: "{{ reversedMessage }}"</p>
<button @click="message = message + '!'">添加感叹号</button>
</div>
<div>
<h3>2. 侦听器和异步方法</h3>
<input v-model="question" placeholder="输入问题(包含问号时自动回答)">
<p>回答: {{ answer }}</p>
<p>(提示:输入包含问号的问题,如"你好吗?")</p>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
message: 'Hello',
question: '',
answer: '请先输入问题'
}
},
computed: {
// 计算属性
reversedMessage() {
return this.message.split('').reverse().join('')
}
},
watch: {
// 侦听器
question(newQuestion) {
if (newQuestion.includes('?')) {
this.getAnswer()
}
}
},
methods: {
getAnswer() {
this.answer = '思考中...'
setTimeout(() => {
this.answer = '这是对"' + this.question + '"的回答'
}, 1000)
}
}
}).mount('#app');
</script>
7. 列表渲染
1.数组渲染:
使用v-for遍历数组,显示索引和内容
使用:key提高性能,添加了添加/移除项目的按钮
2.对象渲染:
使用v-for遍历对象,显示键、值和索引
3.范围渲染:
使用v-for渲染1-10的数字
4.template渲染:
使用<template>标签渲染多个元素,为每个项目添加分隔线
<div id="app">
<h2>Vue 3 列表渲染演示</h2>
<div>
<h3>1. 数组渲染 (带索引和key)</h3>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.message }} (ID: {{ item.id }})
</li>
</ul>
<button @click="addItem">添加项目</button>
<button @click="removeItem">移除项目</button>
</div>
<div>
<h3>2. 对象渲染 (键值对)</h3>
<ul>
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
</div>
<div>
<h3>3. 范围渲染 (1-10)</h3>
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
</div>
<div>
<h3>4. 使用template渲染多个元素</h3>
<ul>
<template v-for="item in items">
<li>{{ item.message }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
</div>
<div>
<h3>当前数据状态</h3>
<pre>{{ JSON.stringify({ items, myObject }, null, 2) }}</pre>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
items: [
{ id: 1, message: 'Foo' },
{ id: 2, message: 'Bar' }
],
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2025-01-01'
},
nextId: 3
}
},
methods: {
addItem() {
this.items.push({
id: this.nextId++,
message: 'New Item ' + (this.nextId - 1)
});
},
removeItem() {
if (this.items.length > 0) {
this.items.pop();
}
}
}
}).mount('#app');
</script>
8. 表单输入绑定
1.文本输入 - 基本的v-model绑定
2.多行文本 - textarea的使用
3.单个复选框 - 绑定布尔值
4.多个复选框 - 绑定数组
5.单选按钮 - 绑定单个值
6.选择框 - select和option的使用
7.修饰符:
.lazy - 在change时而非input时更新
.number - 自动转为数字类型
.trim - 自动去除首尾空格
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 3 表单绑定示例</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
div {
margin: 15px 0;
padding: 10px;
border: 1px solid #eee;
border-radius: 5px;
}
input, textarea, select {
margin: 5px;
padding: 5px;
}
</style>
</head>
<body>
<div id="app">
<h2>Vue 3 表单绑定演示</h2>
<!-- 文本输入 -->
<div>
<h3>1. 文本输入</h3>
<input v-model="text" placeholder="编辑我">
<p>文本是: {{ text || '空' }}</p>
</div>
<!-- 多行文本 -->
<div>
<h3>2. 多行文本</h3>
<textarea v-model="message" placeholder="添加多行文本"></textarea>
<p>多行文本是: <pre>{{ message || '空' }}</pre></p>
</div>
<!-- 单个复选框 -->
<div>
<h3>3. 单个复选框</h3>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">状态: {{ checked }}</label>
</div>
<!-- 多个复选框 -->
<div>
<h3>4. 多个复选框</h3>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<p>选中的名字: {{ checkedNames.length ? checkedNames : '无' }}</p>
</div>
<!-- 单选按钮 -->
<div>
<h3>5. 单选按钮</h3>
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<p>选中的是: {{ picked || '无' }}</p>
</div>
<!-- 选择框 -->
<div>
<h3>6. 选择框</h3>
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>选中的是: {{ selected || '无' }}</p>
</div>
<!-- 修饰符 -->
<div>
<h3>7. 修饰符</h3>
<div>
<label>.lazy修饰符 (change时更新):</label>
<input v-model.lazy="msg">
<p>值: {{ msg || '空' }}</p>
</div>
<div>
<label>.number修饰符 (转为数字):</label>
<input v-model.number="age" type="number">
<p>类型: {{ typeof age }}, 值: {{ age }}</p>
</div>
<div>
<label>.trim修饰符 (去除首尾空格):</label>
<input v-model.trim="trimmedMsg">
<p>值: "{{ trimmedMsg || '空' }}"</p>
</div>
</div>
<div>
<h3>当前所有数据状态</h3>
<pre>{{ JSON.stringify({
text,
message,
checked,
checkedNames,
picked,
selected,
msg,
age,
trimmedMsg
}, null, 2) }}</pre>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
text: '',
message: '',
checked: false,
checkedNames: [],
picked: '',
selected: '',
msg: '',
age: 0,
trimmedMsg: ''
}
}
}).mount('#app');
</script>
</body>
</html>
9. 事件处理
9.1 事件绑定语法:
完整写法:v-on:click="methodName"
简写形式:@click="methodName"
<div id="app">
<h2>Vue3 事件处理</h2>
<p>计数器: {{ count }}</p>
<!-- 使用v-on:click -->
<button v-on:click="increment">增加 (v-on语法)</button>
<!-- 使用@click简写 -->
<button @click="decrement">减少 (@简写)</button>
<!-- 带参数的事件 -->
<button @click="sayHello('Vue3')">打招呼</button>
<!-- 访问原生DOM事件 -->
<button @click="showEventInfo($event)">显示事件信息</button>
<!-- 事件修饰符示例 -->
<div @click="parentClick" style="padding:20px; background:#eee; margin-top:20px;">
<button @click.stop="childClick">阻止冒泡(.stop)</button>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
console.log('计数器增加')
},
decrement() {
this.count--
console.log('计数器减少')
},
sayHello(name) {
alert(`你好,${name}!`)
},
showEventInfo(event) {
console.log('事件对象:', event)
console.log('触发元素:', event.target)
},
parentClick() {
console.log('父元素点击')
},
childClick() {
console.log('子元素点击(不会冒泡)')
}
}
}).mount('#app')
</script>
当点击按钮时,事件会:
1.先触发按钮的childClick
2.然后向上冒泡触发父元素的parentClick
9.2 为什么要阻止冒泡?
有时我们不希望事件向上传播,例如:
1.点击下拉菜单项时,不希望触发关闭菜单的事件
2.点击模态框内容时,不希望触发关闭模态框的事件
3.嵌套的可点击元素需要独立处理各自的事件
<div id="app">
<h2>阻止事件冒泡示例</h2>
<div @click="logEvent('父元素被点击')">
<button @click="logEvent('按钮被点击(会冒泡)')">普通按钮</button>
<button @click.stop="logEvent('按钮被点击(阻止冒泡)')">阻止冒泡按钮(.stop)</button>
</div>
<div>
<p v-for="(log, index) in logs" :key="index">{{ log }}</p>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
logs: []
}
},
methods: {
logEvent(message) {
this.logs.push(`${new Date().toLocaleTimeString()}: ${message}`);
console.log(message);
}
}
}).mount('#app')
</script>
五、组件
1. 组件基础
组件是 Vue 的核心功能之一,它允许我们将 UI 划分为独立、可复用的部分。
1. 组件是可复用的 Vue 实例
2. 使用 components选项注册组件
3. 组件名在模板中使用时通常使用 kebab-case(短横线分隔命名)
4. 每个组件必须有单个根元素
<div id="app">
<greeting></greeting>
<greeting></greeting>
</div>
<script>
const { createApp } = Vue;
// 定义一个组件
const Greeting = {
template: `<p>你好,我是Vue组件!</p>`
};
const app = createApp({
// 注册组件
components: {
Greeting
}
});
app.mount('#app');
</script>
kebab-case 是一种常见的编程命名约定,在计算机科学与软件开发的诸多领域中被广泛应用。 它得名于英文中的 kebab,即烤肉串。 之所以命名为 kebab-case,是因为这种命名方式的形式像一串连在一起的词语,通过短横线(或称为连字符)相连接,形似烤肉串中的各个肉块被串在一起。 例如,字符串hello-world就是 kebab-case 的一个典型例子。
2. 组件 Props
Props 是父组件向子组件传递数据的方式
1. Props 是组件的自定义属性
2. 子组件通过 props选项声明它接受的属性
3. 父组件通过属性方式传递数据
4. 推荐使用 kebab-case 命名属性(HTML 特性不区分大小写)
<div id="app">
<user-card
name="张三"
age="25"
job="前端开发"
></user-card>
<user-card
name="李四"
age="30"
job="UI设计师"
></user-card>
</div>
<script>
const { createApp } = Vue;
const UserCard = {
props: ['name', 'age', 'job'],
template: `
<div>
<h3>{{ name }}</h3>
<p>年龄: {{ age }}</p>
<p>职业: {{ job }}</p>
</div>
`
};
const app = createApp({
components: {
UserCard
}
});
app.mount('#app');
</script>
父组件是 app(根组件),它被挂载到 #app元素上。父组件中注册了 UserCard组件。
子组件是 UserCard,它通过 props接收父组件传递的数据(name、age、job)。
具体关系:
• 父组件(根组件)在模板中使用了<user-card>标签
• 子组件 UserCard通过 props 接收父组件传递的数据
• 父组件向子组件传递数据的方式是通过 <user-card>标签的属性(name=“张三” 等)
总结:
- 父:挂载到 #app的根组件
- 子:UserCard组件
3. 组件事件
子组件可以通过事件与父组件通信
1. 子组件通过 $emit触发事件
2. 父组件通过 v-on或 @ 监听子组件事件
3. 事件名推荐使用 kebab-case
4. 可以传递参数:$emit('event-name', arg1, arg2)
<div id="app">
<p>计数器: {{ count }}</p>
<counter-button @increment="count++"></counter-button>
</div>
<script>
const { createApp } = Vue;
const CounterButton = {
template: `
<button @click="$emit('increment')">
增加计数
</button>
`
};
const app = createApp({
data() {
return {
count: 0
}
},
components: {
CounterButton
}
});
app.mount('#app');
</script>
4. 插槽
插槽允许组件接收模板片段作为内容。
1. <slot> 元素作为内容分发的出口
2. 父组件模板中放入组件标签之间的内容会替换子组件的 <slot>
3. 可以有默认内容:<slot>默认内容</slot>
4. 具名插槽允许有多个插槽
<div id="app">
<alert-box>
这是一个重要的消息!
</alert-box>
<alert-box>
<strong>警告!</strong> 系统即将关闭。
</alert-box>
</div>
<script>
const { createApp } = Vue;
const AlertBox = {
template: `
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;">
<slot></slot>
</div>
`
};
const app = createApp({
components: {
AlertBox
}
});
app.mount('#app');
</script>
接下来让我们仔细看看插槽到底是怎么渲染的
4.1 Vue 组件定义部分
const AlertBox = {
template: `
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;">
<slot></slot>
</div>
`
};
• 定义了一个名为 AlertBox的组件
• 组件模板包含一个带有边框样式的 div
• <slot> </slot>是插槽的位置,这里的内容会被父组件中 <alert-box>标签内的内容替换
4.2 插槽(Slot)的工作原理
-
父组件(这里就是 #app)在 标签内放置内容
-
子组件(AlertBox)通过 标签定义内容插入的位置
-
Vue 会自动将父组件中的内容插入到子组件的 位置
4.3 最终渲染结果
第一个 <alert-box>会渲染为:
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;">
这是一个重要的消息!
</div>
第二个 <alert-box>会渲染为:
<div style="border: 1px solid #ccc; padding: 10px; margin: 5px;">
<strong>警告!</strong> 系统即将关闭。
</div>
插槽让组件更加灵活,可以:
- 在组件内部预留位置,由使用者决定插入什么内容
- 创建可复用的布局组件
- 允许组件接收复杂的HTML结构而不仅仅是简单的数据
5. 动态组件
动态组件可以在多个组件间动态切换。
1. 使用 <component :is="..."> 实现动态组件
2. is 的值可以是已注册组件的名字或组件选项对象
3. 动态切换组件时,默认会销毁和重新创建组件实例
4. 可以使用 <keep-alive> 缓存不活动的组件实例
<!DOCTYPE html>
<html>
<head>
<title>Vue3 动态组件</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="currentComponent = 'home'">首页</button>
<button @click="currentComponent = 'about'">关于</button>
<button @click="currentComponent = 'contact'">联系</button>
<component :is="currentComponent"></component>
</div>
<script>
const { createApp } = Vue;
const Home = {
template: `<div>这是首页内容</div>`
};
const About = {
template: `<div>这是关于我们的内容</div>`
};
const Contact = {
template: `<div>这是联系方式</div>`
};
const app = createApp({
data() {
return {
currentComponent: 'home'
}
},
components: {
Home,
About,
Contact
}
});
app.mount('#app');
</script>
</body>
</html>
5.1 组件定义部分
const Home = {
template: `<div>这是首页内容</div>`
};
const About = {
template: `<div>这是关于我们的内容</div>`
};
const Contact = {
template: `<div>这是联系方式</div>`
};
定义了三个简单的组件:Home、About 和 Contact 。
5.2 Vue 应用配置
const app = createApp({
data() {
return {
currentComponent: 'home' // 默认显示Home组件
}
},
components: {
Home,
About,
Contact
}
});
• 在 data 中定义了 currentComponent变量,初始值为 ‘home’
• 注册了三个组件
5.3 模板部分
<button @click="currentComponent = 'home'">首页</button>
<button @click="currentComponent = 'about'">关于</button>
<button @click="currentComponent = 'contact'">联系</button>
<component :is="currentComponent"></component>
• 三个按钮分别点击时修改 currentComponent 的值
• <component :is="currentComponent"> 会根据 currentComponent 的值动态渲染对应的组件
从用户体验角度:推荐组件放在按钮后
即<component :is="currentComponent"></component>放在前三个按钮的后面
六、Vue3常用指令速查表
1.常用指令
| 指令 | 缩写 | 说明 | 示例 |
|---|---|---|---|
v-text |
- | 更新元素的文本内容 | <span v-text="message"></span> |
v-html |
- | 更新元素的innerHTML | <div v-html="rawHtml"></div> |
v-show |
- | 根据条件显示/隐藏元素(display切换) | <div v-show="isVisible">内容</div> |
v-if |
- | 条件渲染元素(销毁/重建) | <div v-if="isActive">内容</div> |
v-else |
- | 必须跟在v-if/v-else-if后 | <div v-else>其他内容</div> |
v-else-if |
- | v-if的"else if"块 | <div v-else-if="type === 'B'">B</div> |
v-for |
- | 循环渲染元素 | <li v-for="item in items" :key="item.id">{{ item.text }}</li> |
v-on |
@ |
绑定事件监听器 | <button @click="doThis">点击</button> |
v-bind |
: |
动态绑定属性 | <img :src="imageSrc"> |
v-model |
- | 表单输入双向绑定 | <input v-model="message"> |
v-slot |
# |
定义插槽内容 | <template #header><h1>标题</h1></template> |
v-pre |
- | 跳过编译 | <div v-pre>{{ 这里不编译 }}</div> |
v-once |
- | 只渲染一次 | <span v-once>{{ 不会改变 }}</span> |
v-memo |
- | 缓存模板子树 | <div v-memo="[valueA, valueB]">...</div> |
v-cloak |
- | 隐藏未编译的模板 | [v-cloak] { display: none } |
2.修饰符速查
2.1 v-model 修饰符
| 修饰符 | 说明 | 示例 |
|---|---|---|
.lazy |
改为change事件触发 | <input v-model.lazy="msg"> |
.number |
输入转为数字 | <input v-model.number="age"> |
.trim |
去除首尾空格 | <input v-model.trim="text"> |
2.2 v-on 修饰符
| 修饰符 | 说明 | 示例 |
|---|---|---|
.stop |
阻止事件冒泡 | <button @click.stop="doThis"> |
.prevent |
阻止默认行为 | <form @submit.prevent="onSubmit"> |
.capture |
使用捕获模式 | <div @click.capture="doThis"> |
.self |
仅当event.target是元素自身触发 | <div @click.self="doThat"> |
.once |
只触发一次 | <button @click.once="doThis"> |
.passive |
提升滚动性能 | <div @scroll.passive="onScroll"> |
2.3 按键修饰符
| 修饰符 | 说明 | 示例 |
|---|---|---|
.enter |
回车键 | <input @keyup.enter="submit"> |
.tab |
Tab键 | <input @keyup.tab="nextField"> |
.delete |
删除/退格键 | <input @keyup.delete="clear"> |
.esc |
ESC键 | <input @keyup.esc="cancel"> |
.space |
空格键 | <button @keyup.space="jump"> |
.up |
上箭头 | <input @keyup.up="moveUp"> |
.down |
下箭头 | <input @keyup.down="moveDown"> |
.left |
左箭头 | <input @keyup.left="moveLeft"> |
.right |
右箭头 | <input @keyup.right="moveRight"> |
七、自定义指令
1. 什么是自定义指令?
自定义指令是 Vue 提供的强大功能,允许你直接操作 DOM 元素。当内置指令不够用时,自定义指令可以扩展 Vue 的能力。
2. 基本语法
2.1 全局注册
全局注册可以在任何 Vue 实例的模板中使用
const app = createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
mounted(el) {
el.focus()
}
})
2.2 局部注册
局部注册只能在该组件及其子组件中使用
const MyComponent = {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
},
template: `<input v-focus>`
}
3. 钩子函数速查表
| 钩子函数 | 调用时机 | 参数 | 使用场景示例 |
|---|---|---|---|
created |
绑定元素的 attribute 或事件监听器应用之前 | el, binding, vnode |
在绑定前修改元素属性 |
beforeMount |
元素被插入到 DOM 前调用 | el, binding, vnode |
准备 DOM 操作前的初始化 |
mounted |
元素被插入到 DOM 后调用 | el, binding, vnode |
最常见的 DOM 操作时机(如添加事件监听) |
beforeUpdate |
在包含组件的 VNode 更新前调用 | el, binding, vnode, prevVnode |
更新前清理旧状态 |
updated |
在包含组件的 VNode 及其子组件的 VNode 更新后调用 | el, binding, vnode, prevVnode |
更新后执行 DOM 操作 |
beforeUnmount |
在绑定元素的父组件卸载前调用 | el, binding, vnode |
清理即将卸载的组件(如定时器) |
unmounted |
在绑定元素的父组件卸载后调用 | el, binding, vnode |
最终清理工作 |
3.1 参数说明
el:指令绑定的 DOM 元素binding:包含以下属性的对象:value:指令的绑定值oldValue:之前的值(仅在update和componentUpdated中可用)arg:指令参数(如v-my-directive:foo中的foo)modifiers:包含修饰符的对象(如v-my-directive.foo.bar中的{ foo: true, bar: true })instance:使用指令的组件实例
vnode:Vue 编译生成的虚拟节点prevVnode:上一个虚拟节点(仅在update和componentUpdated钩子中可用)
3.2 生命周期流程图
created → beforeMount → mounted → beforeUpdate → updated → beforeUnmount → unmounted
提示:大多数情况下只需使用
mounted和updated钩子即可满足需求
八、概念引入Json
8.1 JSON 是什么?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于 ECMAScript(JavaScript)的一个子集,采用完全独立于语言的文本格式来存储和表示数据。
| 特性 | JSON | JavaScript 对象 |
|---|---|---|
| 本质 | 字符串(文本格式) | 内存中的数据结构 |
| 用途 | 数据交换格式 | 程序内部数据处理 |
| 键名要求 | 必须使用双引号 | 可不用引号或使用单/双引号 |
| 值类型 | 仅支持基本类型、数组和对象 | 支持函数、undefined等所有JS类型 |
| 注释 | 不支持 | 支持 |
| 末尾逗号 | 不允许 | 允许 |
8.2 JSON 字符串 → JavaScript 对象
• JSON.parse()是JSON对象的解析方法
• 参数必须是合法的JSON格式字符串
• 转换后会生成标准的JavaScript对象
• 可以通过点符号或方括号访问属性
// JSON字符串
const jsonStr = '{"name":"李四","age":28,"skills":["JavaScript","HTML"]}';
// 使用JSON.parse()转换为JS对象
const person = JSON.parse(jsonStr);
// 访问转换后的JS对象属性
console.log(person.name); // 输出:李四
console.log(person.age); // 输出:28
console.log(person.skills); // 输出:["JavaScript", "HTML"]
8.3 JavaScript 对象 → JSON 字符串
• JSON.stringify()是JSON对象的序列化方法
• 会过滤掉JS对象中的非JSON兼容值(如函数、undefined等)
• 转换结果是一个标准的JSON格式字符串
• 可以添加格式化参数(缩进等)
// JavaScript对象
const person = {
name: "李四",
age: 28,
skills: ["JavaScript", "HTML"]
};
// 使用JSON.stringify()转换为JSON字符串
const newJson = JSON.stringify(person);
console.log(newJson);
// 输出:'{"name":"李四","age":28,"skills":["JavaScript","HTML"]}'
九、JSON 与 AJAX 的关系详解
JSON 和 AJAX 是现代 Web 开发中密切相关的两项核心技术,它们共同构成了前后端数据交互的基础架构。
1. 核心关系图解
前端 JavaScript
│
├─ AJAX ── 发起请求 ──→ 服务器
│ │ │
│ │ │
│ ←─ 接收响应 ──── 返回JSON
│
└─ 解析JSON → 更新页面
2. JSON 在 AJAX 中的作用
• AJAX 需要传输数据:当浏览器与服务器异步通信时
• JSON 作为载体:轻量级、易解析的数据格式
十、同步与异步的基本区别
1. 直观理解
① 同步模式 - 排队买奶茶
function buyMilkTea() {
console.log("1. 排队付款");
console.log("2. 等待制作"); // 必须等待
console.log("3. 拿到奶茶"); // 前两步完成后执行
console.log("4. 离开店铺");
}
特点:必须等待前一个步骤完成才能进行下一步
② 异步模式 - 扫码点单
function asyncBuyMilkTea() {
console.log("1. 扫码下单");
// 异步通知
setTimeout(() => {
console.log("3. 收到取餐通知");
}, 3000);
console.log("2. 先去逛街"); // 不用等待制作
}
输出顺序:
1. 扫码下单
2. 先去逛街
(3秒后)
3. 收到取餐通知
2.关键差异
| 特性 | 同步 (Synchronous) | 异步 (Asynchronous) |
|---|---|---|
| 执行机制 | 顺序执行,阻塞式 | 非阻塞式,回调/事件驱动 |
| 代码流程 | 从上到下依次执行 | 通过回调函数/Promise处理结果 |
| 性能影响 | 会阻塞后续代码执行 | 不会阻塞主线程 |
| 适用场景 | 简单计算、即时操作 | 网络请求、文件IO、定时操作等 |
| 错误处理 | try-catch | 回调参数/catch方法 |
十一、AJAX 全面详解
11.1 AJAX 基础概念
什么是Ajax技术?
AJAX (Asynchronous JavaScript and XML) 是一种异步通信技术,允许网页:
-
不刷新页面的情况下与服务器交换数据
-
局部更新网页内容
-
异步执行网络请求(不阻塞用户界面)
Vue版本推荐使用axios来完成 ajax 请求。Axios是一个基于Promise的HTTP库,可以用在浏览器和node.js中。
Github 开源地址 : https://github.com/axios/axios
11.2 引入Axios的方式
11.2.1. CDN 引入(浏览器环境,最简单)
直接在 HTML 文件中引入 Axios 的 CDN 链接:
<!-- 使用 jsDelivr(推荐) -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- 或 unpkg -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
11.2.2. npm/yarn 安装(Node.js 或前端工程化项目)
适用于 Vue/React/Node.js 等现代 JavaScript 项目:
# 使用 npm
npm install axios
# 或使用 yarn
yarn add axios
使用方式:
// CommonJS(Node.js)
const axios = require('axios');
// ES Modules(现代前端项目)
import axios from 'axios';
11.2.3. 下载本地引入(离线环境)
1、从 axios GitHub Releases下载 axios.min.js。
2、放入项目目录(如 js/axios.js)。
3、在 HTML 中引入:
<script src="./js/axios.min.js"></script>
11.3 使用Axios的请求
此处给出html的框架,一下代码放入body均可运行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Axios 请求示例</title>
<!-- 直接引入 axios CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
</body>
</html>
11.3.1 Get请求
F12,在控制台可以查看响应数据
<input type="button" value="获取数据(GET)" id="btnGet" />
<script>
const handleError = (err) => {
console.error("请求失败:", err);
if (err.response) {
console.log("状态码:", err.response.status);
console.log("响应数据:", err.response.data);
}
};
// GET 请求
document.querySelector("#btnGet").addEventListener("click", () => {
axios
.get("https://mock.apifox.cn/m1/3083103-0-default/emps/list")
.then((response) => {
console.log("GET 请求成功:", response.data);
})
.catch(handleError);
});
</script>
11.3.2 Post请求
F12,在控制台可以查看响应数据
<input type="button" value="提交数据(POST)" id="btnPost" />
<script>
// 统一错误处理函数
const handleError = (err) => {
console.error("请求失败:", err);
if (err.response) {
console.log("状态码:", err.response.status);
console.log("响应数据:", err.response.data);
}
};
// POST 请求(推荐传对象格式)
document.querySelector("#btnPost").addEventListener("click", () => {
const postData = { id: 1 }; // 更规范的 JSON 格式数据
axios
.post(
"https://mock.apifox.cn/m1/3083103-0-default/emps/update",
postData,
{
headers: {
"Content-Type": "application/json", // 明确指定 JSON 格式
},
}
)
.then((response) => {
console.log("POST 请求成功:", response.data);
})
.catch(handleError);
});
</script>
11.3.3 简单写法
<input type="button" value="获取数据GET" id="btnGet">
<input type="button" value="操作数据POST" id="btnPost">
<script>
//发送GET请求
document.querySelector('#btnGet').addEventListener('click', () => {
axios.get('https://mock.apifox.cn/m1/3083103-0-default/emps/list').then((result) => {
console.log(result.data);
});
console.log('==========================');
})
//发送POST请求
document.querySelector('#btnPost').addEventListener('click', () => {
axios.post('https://mock.apifox.cn/m1/3083103-0-default/emps/update', 'id=1').then((result) => {
console.log(result.data);
});
})
</script>
11.3.4 请求方式别名
| 别名方法 | HTTP方法 | 说明 |
|---|---|---|
axios.request(config) |
- | 通用请求方法 |
axios.get(url[, config]) |
GET | 获取资源 |
axios.post(url[, data[, config]]) |
POST | 创建资源 |
axios.put(url[, data[, config]]) |
PUT | 替换/更新整个资源 |
axios.patch(url[, data[, config]]) |
PATCH | 局部更新资源 |
axios.delete(url[, config]) |
DELETE | 删除资源 |
axios.head(url[, config]) |
HEAD | 获取响应头信息 |
axios.options(url[, config]) |
OPTIONS | 获取服务器支持的通信选项 |
11.4 AJAX 工作原理
事件触发 → 创建XHR对象 → 配置请求 → 发送请求
↓
服务器处理请求
↓
返回响应数据
↓
触发onreadystatechange
↓
调用回调函数处理数据
↓
更新DOM内容
更多推荐



所有评论(0)