ThankNeko's Blog ThankNeko's Blog
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)

Hoshinozora

尽人事,听天命。
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)
  • Python笔记

  • C笔记

  • C++笔记

  • Arduino笔记

  • Web笔记

    • Html与标签介绍
    • Html常用标签
    • CSS基础
    • JavaScript基础
    • BOM和DOM
    • Bootstrap5
    • Vue介绍
    • Vue指令系统
      • Vue指令系统
        • 介绍
        • 内容渲染指令
        • 条件渲染指令
        • 属性/事件绑定指令
        • 双向绑定指令
        • 列表渲染指令
        • 使用例子
    • Axios请求
    • Vue计算与监听属性
    • Vue组件
    • NodeJS环境
    • Vue项目
    • Vue路由
  • Dev
  • Web笔记
Hoshinozora
2025-11-09
目录

Vue指令系统

# Vue指令系统

# 介绍

Vue的指令系统是框架提供的一套以 v- 为前缀的特殊属性,用于在模板编译阶段把声明式的标记转换为响应式的DOM操作。指令本质上是语法糖,编译后会生成对应的JavaScript代码,从而在数据变化时自动更新页面。

任何Html标签都可以使用Vue指令。

# 内容渲染指令

# v-text

等价于element.textContent,把表达式的值作为纯文本插入到元素中,不会解析HTML。

注意:该指令会覆盖元素内部的所有子节点。

<!-- 和插值语法一样可以传入变量、数组、对象、JS表达式、函数、方法等 -->
<h1 v-text="JS表达式"></h1>

<!-- 如果想直接传入文本需要使用单引号包裹内容 -->
<h1 v-text="'Hello'"></h1>
1
2
3
4
5

# v-html

等价于element.innerHTML,把表达式的值作为HTML插入到元素中,会解析标签。

注意:该指令会覆盖元素内部的所有子节点。

<h1 v-html="JS表达式"></h1>
1

# 条件渲染指令

# v-show

通过切换元素的display样式实现显示/隐藏,元素始终保留在DOM中。

频繁切换可见性时比v-if更省性能。

<div v-show="JS表达式">内容</div>
1

# v-if

根据表达式的true / false来决定是否渲染该的元素。为真时渲染,在DOM中插入该元素。为假时不渲染,从DOM中移除该元素。

<div v-if="JS表达式">内容</div>
1

# v-else-if

为 v-if 添加多分支条件,必须紧跟在 v-if 或另一个 v-else-if 之后。

<div v-if="JS表达式1">内容1</div>
<div v-else-if="JS表达式2">内容2</div>
1
2

# v-else

为 v-if 添加否则分支条件,必须紧跟在 v-if 或 v-else-if 之后。

<div v-if="JS表达式1">内容1</div>
<div v-else-if="JS表达式2">内容2</div>
<div v-else>内容3</div>
1
2
3

# 属性/事件绑定指令

# v-bind

把JS表达式绑定到属性、class、style等。

它还支持使用各类修饰符,例如:.prop(绑定为DOM属性)、.camel(驼峰转 kebab-case)、.sync(实现双向绑定) 等。

<!--
格式:<标签名 v-bind:属性名="JS表达式"/>
简写:<标签名 :属性名="JS表达式"/>
-->

<img :src="img_path"/>
<div :style="div_style"/>
<div :class="div_class"/>
1
2
3
4
5
6
7
8

对于class、style这类值可能多个的属性,除了字符串还可以使用数组、对象,以方便追加、更改、删除其中元素。

# v-on

把JS表达式绑定到事件,也就是为元素添加事件监听,事件被触发则执行JS表达式。

它还支持使用各类修饰符,例如 .stop(阻止冒泡)、.prevent(阻止默认)、.once(只触发一次) 等。

格式:<标签名 v-on:事件名="JS表达式"/>

@简写:<标签名 @事件名="JS表达式"/>

使用修饰符:<标签名 @事件名.修饰符="JS表达式"/>

<button @click="handleClick()"></button>
<input @keyup.enter="handleInput()"/>
1
2

传参时,如果不指定"()"则会将事件对象作为第一个参数,传入给函数。

如果指定了"()"但还是想传入事件对象,则可以手动传入$event对象,例如:handleClick(123, $event)。

# 常用事件

事件 触发时机 常用修饰符 / 备注
click 鼠标左键在元素上 按下并释放 .stop(阻止冒泡)、.prevent(阻止默认)
dblclick 鼠标左键 双击 常用于编辑、快速打开
mousedown 鼠标左键 按下(未释放) 与 mouseup 配合实现拖拽
mouseup 鼠标左键 释放 与 mousedown 配合
mouseenter 鼠标指针 进入 元素(不冒泡) 常配合 .once 只触发一次
mouseleave 鼠标指针 离开 元素(不冒泡) 同上
mousemove 鼠标在元素内部 移动(持续触发) 用于绘图、拖拽实时反馈
mouseover 鼠标指针 进入 元素(会冒泡) 与 mouseenter 区别在于冒泡
mouseout 鼠标指针 离开 元素(会冒泡) 与 mouseleave 区别在于冒泡
focus 元素(如 <input>)‍获得焦点 .prevent 常用于阻止默认聚焦行为
blur 元素 失去焦点 常配合表单校验
input 表单控件 内容变化(实时) 适用于 <input>、<textarea>,配合 v-model
change 表单控件 值改变后失去焦点(或选项切换) 常用于 <select>、复选框
keydown 键盘 按下(会持续触发) .enter、.esc 等键位修饰
keyup 键盘 释放 常用于提交、搜索等
reset <form> 重置(点击 reset 按钮) 可配合 @reset.prevent 阻止默认
submit <form> 提交(点击 submit 按钮或回车) .prevent 防止页面刷新
scroll 元素或窗口 滚动 常用于懒加载、滚动监听
dragstart / dragover / drop 拖拽操作的不同阶段 需要 event.preventDefault() 才能正常放置
contextmenu 右键 打开上下文菜单 可配合 .prevent 自定义右键菜单

# 常用事件修饰

基础事件修饰符

修饰符 作用说明
.stop 阻止事件冒泡,子元素的事件不会向父元素传播
.prevent 阻止默认行为(如 <a> 链接跳转、表单提交)
.self 仅当事件目标是绑定元素本身时才触发,忽略子元素冒泡
.once 事件只会触发一次,随后自动移除监听

键盘按键修饰符

修饰符 说明
.enter、.tab、.space、.esc、.delete、.backspace 仅在对应键被按下时触发
.up、.down、.left、.right 方向键
.ctrl、.shift、.alt、.meta 系统修饰键,可组合使用(如 .ctrl.enter)
.exact 只在精确匹配指定修饰键时触发,多按其他按键则不触发,Vue 3中可用
.[KeyCode] KeyCode对应的按键被按下时触发

鼠标按钮修饰符

修饰符 说明
.left、.middle、.right 指定鼠标左键/中键/右键点击
可与系统修饰键组合(如 .right.shift)实现更细粒度控制

# 双向绑定指令

# v-model

在表单控件(input、textarea、select等)上实现双向数据绑定,即实现Value与响应式变量的数据同步。

格式:<标签名 v-model="响应式变量名"/>

<!-- 绑定到username响应式变量 -->
<!-- 绑定之后,输入框中输入的内容,会实时同步到username变量中 -->
<input type="text" v-model="username">
1
2
3

对原生表单元素:v-model 绑定 value。

对自定义组件:v-model 默认绑定 modelValue,所以在使用如Naive-ui等自定义的组件时,需要使用 v-model:value 才能绑定 value。

# 列表渲染指令

# v-for

遍历数组、对象、字符串或数值,以循环生成标签或组件。

<!-- 
格式:<标签名 v-for="元素名 for 数组/对象/字符串/数字"/>
引用:{{元素名}}、{{元素名.属性名}}
 -->

<!-- 循环对象 -->
<!-- goods_list变量为[{id:xxx, name:xxx, price:xxx, count:xxx},...] -->
<tr v-for="item in goods_list">
    <!-- 取出对象 -->
    <th scope="row">{{item.id}}</th>
    <td>{{item.name}}</td>
    <td>{{item.price}}</td>
    <td>{{item.count}}</td>
</tr>

<!-- 循环字符串 -->
<!-- 会将字符串的每个字符作为元素进行迭代 -->
<span v-for="item in 'Hello world!'">{{item}}</span>

<!-- 循环数字 -->
<!-- 会生成值从1开始到指定数值的数组进行迭代 -->
<span v-for="item in 10">{{item}}</span>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 虚拟DOM标识

建议添加Key来提高DOM的更新数据,Vue中使用的是虚拟DOM,一但更新数据,就会生成新的虚拟DOM,然后和原生DOM进行比较,如果有两者差异则更新到原生DOM。有Key时Vue能精准定位并最小化更新,没有Key时只能靠位置匹配,可能导致大量不必要的DOM重建。

如果我们的列表渲染时添加了唯一Key值,则Vue在更新原生DOM时,只需要差异更新列表中发生改变的数据即可。

如果我们的列表渲染时不使用唯一Key值,则Vue无法判断我们的数据是在哪里进行更改,更新时可能需要将列表的虚拟DOM完全替换原生DOM,速度相较差异更新会慢很多。

格式::key="唯一值"

Key只要在同级节点之间唯一即可,所以列表渲染时只需要每个直接子节点的Key不同即可。

另外绑定的Key只在Vue的虚拟DOM中存在,不会出现在浏览器的原生DOM里。

<!--
此处是字符串拼接对象元素的ID作为唯一Key值
如果数据中的ID不是唯一,也可以加上索引作为补充
-->
<tr v-for="(item, index) in goods_list" :key="index+'_'+item.id">
    <th scope="row">{{item.id}}</th>
    <td>{{item.name}}</td>
    <td>{{item.price}}</td>
    <td>{{item.count}}</td>
</tr>
1
2
3
4
5
6
7
8
9
10

# 使用例子

# 内容渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
</head>
<body>
<div id="app">
    <h1 v-text="name"><p>我会被覆盖</p></h1>
    <h1 v-text="hello()"></h1>
    <h1 v-html="links"></h1>

</div>
</body>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            const name = ref('hello');
            return {
                name,
                hello() {
                    return "what?"
                },
                links: '<a href="https://www.baidu.com/">百度一下</a>'
            }
        }
    }).mount("#app")
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 条件渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
</head>
<body>
<div id="app">
    <h1 v-show="is_show_name">名称: {{name}}</h1>
    <h1 v-if="is_show_age">年龄: {{age}}</h1>
    <h1>你属于:
        <span v-if="age>=60">老年人</span>
        <span v-else-if="age>=18">成年人</span>
        <span v-else>你就是个Baby!</span>
    </h1>
</div>
</body>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            const name = ref('John');
            const age = ref(15);
            const is_show_name = ref(true);
            const is_if_age = ref(false);
            return {
                name,
                age,
                is_show_name,
                is_if_age
            }
        }
    }).mount("#app")
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 属性/事件绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
    <style>
        .my_color {
            background-color: bisque;
        }

        .my_area {
            height: 50px;
            width: 100px;
        }
    </style>
</head>
<body>
<div id="app">
    <div :style="div_style"></div>
    <br>
    <button @click="handleChangeDivStyleColor('red')">改变颜色</button>
    <br><br>
    <div :class="div_class"></div>
    <br><br>
    <button @click="click_times++">点击次数: {{click_times}}</button>
    <br><br>
    <button @click="handleToogleRotateImg()">点击我开关图片轮换</button>
    <br><br>
    <div><img :src="img_path" height="200px" width="200px"/></div>
</div>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            function getRandImgPath() {
                // 确保不会随机到上一张图片
                let rand_path = img_path.value;
                while (img_path.value === rand_path) {
                    rand_path = "./img/" + (Math.floor(Math.random() * 5) + 1).toString() + ".png"
                }
                return rand_path
            }

            function handleToogleRotateImg() {
                let _this = this
                if (this.t) {
                    clearInterval(this.t)
                    this.t = null
                } else {
                    _this.t = setInterval(function () {
                        img_path.value = getRandImgPath()
                    }, 1000)
                }
            }

            const div_style = ref({
                "width": "100px",
                "height": "100px",
                "backgroundColor": "aquamarine"
            });
            const div_class = ref(["my_color", "my_area"]);
            const click_times = ref(0);
            const img_path = ref();
            // 获取默认图片
            img_path.value = getRandImgPath();

            function handleChangeDivStyleColor(color){
                div_style.value.backgroundColor = color;
            }

            return {
                div_style,
                handleChangeDivStyleColor,
                div_class,
                click_times,
                img_path,
                handleToogleRotateImg
            }
        }
    }).mount("#app")
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

# 双向绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="username">
    <div>你输入了: {{username}}</div>
</div>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            const username = ref();
            return {
                username
            }
        }
    }).mount("#app")
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 列表渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div id="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 offset-md-3">
                <h1 class="text-center">购物车</h1>
                <div class="text-center">
                    <button class="btn btn-success" @click="handleLoad">加载购物车</button>
                </div>
                <br>
                <div class="text-center" v-if="goods_list.length<=0">购物车是空的!</div>
                <table class="table table-striped table-bordered text-center" v-if="goods_list.length>0">
                    <thead>
                    <tr>
                        <th scope="col"><input type="checkbox" v-model="checked_all" @change="handleCheckAll"></th>
                        <th scope="col">商品ID</th>
                        <th scope="col">商品名称</th>
                        <th scope="col">商品价格</th>
                        <th scope="col">商品数量</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="(item, index) in goods_list" :key="index+'_'+item.id">
                        <td><input type="checkbox" v-model="checked_goods_list" :value="item" @change="handleCheckCase">
                        </td>
                        <th scope="row">{{item.id}}</th>
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td class="w-25">
                            <div class="input-group input-group-sm">
                                <button class="btn btn-secondary" type="button" @click="handleItemSub(item)">-</button>
                                <input type="text" class="form-control text-center" v-model="item.count">
                                <button class="btn btn-success" type="button" @click="item.count++">+</button>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
                <div class="text-lg-end" v-if="goods_list.length>0">
                    总价格:{{getTotalPrice()}}
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            function handleLoad() {
                goods_list.value = [
                    {id: 1, name: "手柄", price: 98, count: 700},
                    {id: 2, name: "水杯", price: 49, count: 50},
                    {id: 3, name: "咖啡", price: 24, count: 500},
                    {id: 4, name: "雨伞", price: 29, count: 100}
                ];
            }

            function getTotalPrice() {
                let tmp = 0;
                for (let i in checked_goods_list.value) {
                    let item_data = checked_goods_list.value[i]
                    tmp += item_data.price * item_data.count
                }
                return tmp
            }

            function handleCheckAll() {
                if (checked_all.value) {
                    checked_goods_list.value = goods_list.value
                } else {
                    checked_goods_list.value = []
                }
            }

            function handleCheckCase() {
                if (checked_goods_list.value.length === goods_list.value.length) {
                    checked_all.value = true;
                } else {
                    checked_all.value = false;
                }
            }

            function handleItemSub(item) {
                if ((item.count - 1) > 0) {
                    console.log(item.count--);
                }
            }

            const goods_list = ref([]);
            const checked_goods_list = ref([]);
            const checked_all = ref(false);
            return {
                goods_list,
                checked_goods_list,
                checked_all,
                handleLoad,
                getTotalPrice,
                handleCheckAll,
                handleCheckCase,
                handleItemSub
            }
        }
    }).mount("#app")
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

# 过滤框案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue测试页面</title>
    <script src="./vue.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div id="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 offset-md-3 mt-3 text-center">
                <h1>数据过滤</h1>
                <input type="text" class="form-control w-50 mt-3 offset-md-3" v-model="filter_text"
                       @input="handleFilterData">
                <table class="table table-bordered table-hover w-50 mt-3 offset-md-3">
                    <tbody>
                    <tr v-if="filtered_data.length>0" v-for="(item, index) in filtered_data">
                        <td>{{item}}</td>
                    </tr>
                    <tr v-else-if="filter_text.length>0">
                        <td>查询的数据为空!</td>
                    </tr>
                    <tr v-else v-for="(item, index) in content_data">
                        <td>{{item}}</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
<script>
    const {createApp, ref} = Vue;
    createApp({
        setup() {
            const filter_text = ref("");
            const content_data = ref(["Hello", "World", "WOW!", "ABCDEFG", "我来了!", "你好世界!", "我是谁,我在哪?"]);
            const filtered_data = ref([]);

            function handleFilterData() {
                // 数组的filter方法,如果过滤函数返回true则item会保留,返回false则不保留
                filtered_data.value = content_data.value.filter(function (item) {
                    if (item.toLowerCase().indexOf(filter_text.value.toLowerCase()) >= 0) {
                        return true
                    }
                    return false
                })
            }

            return {
                filter_text,
                content_data,
                filtered_data,
                handleFilterData
            }
        }
    }).mount("#app")
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#前端#Vue#指令系统
Vue介绍
Axios请求

← Vue介绍 Axios请求→

最近更新
01
Vue路由
12-09
02
FastAPI实现用户管理
11-23
03
Tortoise ORM
11-23
更多文章>
Theme by Vdoing | Copyright © 2022-2026 Hoshinozora | MIT License
湘ICP备2022022820号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式