本文最后更新于:1 年前
一、Vue介绍
1、Vue是什么
Vue是一套用于构建用户界面的 ==渐进式框架== 。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
2、Vue的特点
(1) Vue 的核心库只关注视图层。
(2) 不仅易于上手,还便于与第三方库或既有项目整合。
(3) 遵循 ==MVVM模式== ,能够实现视图与模型的 ==双向绑定==。
- MVVM模式:就是数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化.
(4) 编码简洁,体积小,运行效率高,适合移动/PC端开发。
二、Vue.js的安装
1、使用Vite创建
Vite 是一个 web 开发构建工具,由于其原生 ES 模块导入方式,可以实现闪电般的 ==冷服务器启动==。
它做到了 本地快速开发启动, 在生产环境下基于 Rollup 打包。
- 快速的冷启动,不需要等待打包操作;
- 即时的热模块更新,替换性能和模块数量的解耦让更新飞起;
- 真正的按需编译,不再等待整个应用编译完成,这是一个巨大的改变。
通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目。
使用 npm:
1 2 3 4 5 6 7 8 9
| # npm 6.x $ npm init vite@latest <project-name> --template vue
# npm 7+,需要加上额外的双短横线 $ npm init vite@latest <project-name> -- --template vue
$ cd <project-name> $ npm install $ npm run dev
|
完成!
三、Vue声明式渲染
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
main.js入口文件
1 2 3 4 5
| import { createApp } from 'vue' import './style.css' import App from './App.vue'
createApp(App).mount('#app')
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script>
export default { data() { return { msg: "Hello vue" } } } </script>
<template> <div> <p>{{ msg }}</p> </div> </template>
|
我们已经成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的
示例:其中 每点击一次按钮,counter就会加一,你将看到渲染的 DOM 是如何变化的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script>
export default { data() { return { msg: "Hello vue", counter: 0 } } } </script>
<template> <div> <p>{{ msg }}</p> <h1>{{ counter }}</h1> <button @click="counter++">改变counter</button> </div> </template>
|
注意:
- 想让Vue工作,就必须创建一个 Vue实例,且要传入一个 配置对象
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
- root容器里的代码被称为 Vue模板
- Vue实例与容器是一一对应的
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用
- 中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
- 响应式 就是一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新
四、Data Property 和方法
1、Data Property
组件的 ==data== 选项是一个函数。Vue 会在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 ==$data== 的形式存储在组件实例中。为方便起见,该对象的任何顶级 property 也会直接通过组件实例暴露出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const app = Vue.createApp({ data() { return { count: 4 } } })
const vm = app.mount('#app')
console.log(vm.$data.count) console.log(vm.count)
vm.count = 5 console.log(vm.$data.count)
vm.$data.count = 6 console.log(vm.count)
|
这些实例 property 仅在实例首次创建时被添加,所以你需要确保它们都在 ==data== 函数返回的对象中。必要时,要对尚未提供所需值的 property 使用 ==null==、==undefined== 或其他占位的值。
直接将不包含在 ==data== 中的新 property 添加到组件实例是可行的。但由于该 property 不在背后的响应式 ==$data==对象内,所以 Vue 的响应性系统不会自动跟踪它。
Vue 使用 $ 前缀通过组件实例暴露自己的内置 API。它还为内部 property 保留 _ 前缀。你应该避免使用这两个字符开头的顶级 data property 名称。
2、方法
我们用 methods 选项向组件实例添加方法,它应该是一个包含所需方法的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const app = Vue.createApp({ data() { return { count: 4 } }, methods: { increment() { this.count++ } } })
const vm = app.mount('#app')
console.log(vm.count)
vm.increment()
console.log(vm.count)
|
Vue 自动为 ==methods== 绑定 ==this==,以便于它始终指向组件实例。这将确保方法在用作事件监听或回调时保持正确的 ==this==指向。在定义 ==methods==时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的 ==this==指向。
这些 ==methods==和组件实例的其它所有 property 一样可以在组件的模板中被访问。在模板中,它们通常被当做事件监听使用:
1
| <button @click="increment">Up vote</button>
|
在上面的例子中,点击 < button> 时,会调用 increment 方法。
也可以直接从模板中调用方法。通常换做计算属性会更好。但是,在计算属性不可行的情况下,使用方法可能会很有用。你可以在模板支持 JavaScript 表达式的任何地方调用方法:
1 2 3
| <span :title="toTitleDate(date)"> {{ formatDate(date) }} </span>
|
如果 ==toTitleDate== 或 ==formatDate== 访问了任何响应式数据,则将其作为渲染依赖项进行跟踪,就像直接在模板中使用过一样。
从模板调用的方法不应该有任何副作用,比如更改数据或触发异步进程。如果你想这么做,应该使用生命周期钩子来替换。
五、Vue模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层组件实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成 ==虚拟 DOM 渲染函数==。结合响应性系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
1、插值
概述:在插值表达式中用户可以把vue中所定义的数据显示在页面上。 插值表达式允许用户输入”JS表达式”
语法:==NaN==
1)文本
数据绑定最常见的形式就是使用 (双大括号) 语法的文本插值:
1 2
| <p>{{ num }}</p> <p>{{ name }}</p>
|
Mustache 标签将会被替代为对应组件实例中 property 的值。无论何时,绑定的组件实例上 property 发生了改变,插值处的内容都会更新。
通过使用 ==v-once== 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
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
| <script>
export default { data() { return { num: 1, name: "linzy", } }, methods: { changeName: function () { this.name = "勇敢牛牛" }, } }; </script>
<template> <div> <h3>1、插值表达式 文本</h3> <p>{{ num }}</p> <p>{{ name }}</p> <h3>2、v-once 当属性数据改变时,插值处的内容不会更新</h3> <p v-once>{{ name }}</p> <button @click="changeName">改变名字</button> </div> </template>
|
2)原始 HTML
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html 指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script>
export default { data() { return { msg: "<h2>标题</h2>", } }, }; </script>
<template> <div> <p>{{ msg }}</p> <p v-html="msg"></p> </div> </template>
|
这个 p 标签的内容将会被替换成为 msg property 的值,直接作为 HTML——会忽略解析 property 值中的数据绑定。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。
TIP
在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 ==XSS 攻击==。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值。
3)Attribute属性
Mustache 语法不能在 HTML attribute 中使用,然而,可以使用 v-bind 指令:
1
| <div v-bind:id="dynamicId"></div>
|
如果绑定的值是 null 或 undefined,那么该 attribute 将不会被包含在渲染的元素上。
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
| <script>
export default { data() { return { id: "d1" } }, methods: { changeColor: function () { this.id = "d2" }, }, }; </script>
<template> <div> <p v-bind:id="id">v-bind绑定属性</p> <button @click="changeColor">改变颜色</button> <button @click="id = 'd3'">改变颜色</button> </div> </template>
<style scoped> #d1 { color: red; }
#d2 { color: green; }
#d3 { color: pink; } </style>
|
4)使用 JavaScript 表达式
迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
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
| <script>
export default { data() { return { id: "d1", num: 1, name: "linzy", } }, }; </script>
<template> <div> <p>{{ num + 1 }}</p> <p v-bind:id="id">{{ name.split('').reverse().join('') }}</p> <button @click="id = id + 1">改变颜色</button> </div> </template>
<style scoped> #d1 { color: red; } </style>
|
这些表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
1 2 3 4 5
| {{ var a = 1 }}
{{ if (ok) { return message } }}
|
2、指令
概述:指令 (Directives) 是带有 v- 前缀的特殊 attribute,指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM,指令用于解析标签(包括:标签属性、标签体内容、绑定事件等)
语法:v-xxx
1
| <p v-if="seen">现在你看到我了</p>
|
这里,v-if 指令将根据表达式 seen 的值的 true/false 来插入/移除
元素。
1)参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。
例如,v-bind 指令可以用于响应式地更新 HTML attribute,v-on 指令,它用于监听 DOM 事件
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
| <script>
export default { data() { return { id: "d1", } }, methods: { changeColor: function () { this.id = "d2"; } } }; </script>
<template> <div> <p :id="id">v-bind绑定</p> <button @click="changeColor">改变颜色</button> </div> </template>
<style scoped> #d1 { color: red; }
#d2 { color: pink; } </style>
|
2)动态参数
可以在指令参数中使用 JavaScript 表达式,方法是用方括号括起来:
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
| <script>
export default { data() { return { name: "linzy", id: "d1", attribute: "id", mouseEvent: "click", } }, methods: { changeFontSize: function () { this.attribute = "class" } } }; </script>
<template> <div> <p :[attribute]="id">{{ name }}</p> <button @click="changeFontSize">改变属性</button> <button @[mouseEvent]="changeFontSize">改变字体大小</button> <button @click="mouseEvent = 'mouseover'">改变事件</button> </div> </template>
<style scoped> #d1 { color: red; }
#d2 { color: pink; }
.d1 { font-size: 61px; } </style>
|
这里的 attribute 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的组件实例 data property 有一个 attribute,其值为 “id”,那么这个绑定将等价于 v-bind:id。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
在这个示例中,当 mouseEvent 的值为 “click” 时,v-on:[mouseEvent] 将等价于 v-on:click
六、v-on事件处理
1、监听事件
我们可以使用 v-on 指令 (通常缩写为 @ 符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为 v-on:click=”methodName” 或使用简写方式 @click=”methodName”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script>
export default { data() { return { counter: 1, } }, }; </script>
<template> <div> <h2 @click="counter++">{{ counter }}</h2> </div> </template>
<style scoped> </style>
|
注意: Vue支持html中所有已知事件. 如: @click, @submit等
2、事件处理方法
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <script>
export default { data() { return { counter: 1, } }, methods: { addCounter: function () { this.counter++ }, }, }; </script>
<template> <div> <h2 @click="addCounter">{{ counter }}</h2> </div> </template>
|
3、内联处理器中的方法
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法
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
| <script>
export default { data() { return { counter: 1, } }, methods: { addCounter: function () { this.counter++ }, addC: function (value, e) { this.counter += value console.log(e); }, }, }; </script>
<template> <div> <h3>1、传递参数</h3> <h2 @click="addC(5)">{{ counter }}</h2> <h3>2、绑定事件 传递参数和事件对象</h3> <h2 @click="addC(5, $event)">{{ counter }}</h2> <h3>3、一个事件,绑定多个处理函数</h3> <h2 @click="addC(10, $event), addCounter()">{{ counter }}</h2> </div> </template>
|
注意:事件处理程序中可以有多个方法,这些方法由逗号运算符分隔,并且方法都要加上()
4、事件修饰符
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
- .stop 阻止单击事件继续冒泡
- .prevent 阻止默认行为 提交事件不再重载页面
- .capture 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理
- .self 即事件不是从内部元素触发的
- .once 触发一次回调
- .passive 滚动事件的默认行为 (即滚动行为) 将会立即触发
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
| <script>
export default { methods: { divClick: function () { console.log("父属性"); }, btnClick: function () { console.log("子属性"); }, submitClick: function () { console.log("prevent事件"); }, onceClick: function () { console.log("once事件"); }, }, }; </script>
<template> <div> <div @click="divClick"> <button @click.stop="btnClick">按钮</button> </div> <form action=""> <input type="submit" value="提交" @click.prevent="submitClick" /> </form> <button @click.once="onceClick">once按钮</button> </div> </template>
<style scoped> </style>
|
官方文档的样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <a @click.stop="doThis"></a>
<form @submit.prevent="onSubmit"></form>
<a @click.stop.prevent="doThat"></a>
<form @submit.prevent></form>
<div @click.capture="doThis">...</div>
<div @click.self="doThat">...</div>
<a @click.once="doThis"></a>
|
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self 会阻止元素本身及其子元素的点击的默认行为,而 @click.self.prevent 只会阻止对元素自身的点击的默认行为。
1 2 3 4
|
<div @scroll.passive="onScroll">...</div>
|
这个 .passive 修饰符尤其能够提升移动端的性能。
注意:不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。
5、按键修饰符
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许为 v-on 或者 @ 在监听键盘事件时添加按键修饰符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <script>
export default { methods: { keyUp: function () { console.log("keyUp事件"); } }, }; </script>
<template> <div> <input type="text" @keyup.enter="keyUp"> </div> </template>
<style scoped> </style>
|
1)按键别名
Vue 为最常用的键提供了别名:
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
七、计算属性和侦听器watch
1、计算属性
概述:计算属性可以看做一个提前定义好的方法, 该方法可以看作是一个特殊的值, 可以在插值表达式中使用。
例子
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
| <script>
export default { data() { return { message: "hello vue", } }, computed: { reverseMsg: function () { console.log("计算属性"); return this.message.split('').reverse().join('') } }, methods: { reverseMessage: function () { console.log("methods"); return this.message.split('').reverse().join('') } }, }; </script>
<template> <div> <p>{{ message }}</p> <h3>1、js表达式 总共计算两次</h3> <p>{{ message.split('').reverse().join('') }}</p> <p v-once>v-once:{{ message.split('').reverse().join('') }}</p> <h3>2、计算属性 计算一次</h3> <p>{{ reverseMsg }}</p> <p v-once>v-once:{{ reverseMsg }}</p> <h3>3、methods方法 计算两次</h3> <p>{{ reverseMessage() }}</p> <p v-once>v-once:{{ reverseMessage() }}</p> <button @click="message = '勇敢牛牛'">改变message</button> </div> </template>
|
这里声明了一个计算属性 reverseMsg。
尝试更改应用程序 data 中 message 数组的值,你将看到 reverseMsg如何相应地更改。
你可以像普通属性一样将数据绑定到模板中的计算属性。Vue 知道 reverseMsg 依赖于 message ,因此当message 发生改变时,所有依赖 reverseMsg 的绑定也会更新。而且最妙的是我们以声明的方式创建了这个依赖关系:计算属性的 getter 函数没有副作用,它更易于测试和理解。
1)计算属性缓存 vs 方法
我们发现方法和计算属性都能达到同样的效果,不同的是计算属性将基于它们的响应依赖关系缓存。计算属性只会在相关响应式依赖发生改变时重新求值。这就意味着只要 message 还没有发生改变,多次访问 reverseMsg 时计算属性会立即返回之前的计算结果,而不必再次执行函数。
简而言之,就是方法每次调用,都要运行一遍方法里的语句,而计算属性,只有在相关响应式依赖值发生改变,才会重新计算,计算完会重新缓存,不改变时,则直接返回之前的计算结果,大大节省了渲染时间。
2)计算属性的 Setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
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
| <script>
export default { data() { return { message: "hello vue", } }, computed: { reverseMsg: { get: function () { return this.message.split('').reverse().join('') }, set: function (newValue) { console.log(newValue); this.message = newValue } } }, methods: { reverseMessage: function () { console.log("methods"); return this.message.split('').reverse().join('') } }, }; </script>
<template> <div> <p>{{ message }}</p> <h3>1、js表达式 总共计算两次</h3> <p>{{ message.split('').reverse().join('') }}</p> <p v-once>v-once:{{ message.split('').reverse().join('') }}</p> <h3>2、计算属性 计算一次</h3> <p>{{ reverseMsg }}</p> <p v-once>v-once:{{ reverseMsg }}</p> <h3>3、methods方法 计算两次</h3> <p>{{ reverseMessage() }}</p> <p v-once>v-once:{{ reverseMessage() }}</p> <button @click="message = '勇敢牛牛'">改变message</button> <button @click="reverseMsg = '勇敢牛牛'">改变message</button> </div> </template>
|
现在再运行下面的按钮 reverseMsg = ‘勇敢牛牛’ 时,setter 会被调用,message 也会相应地被更新。
2、侦听器
概述:侦听器可以监听简单属性值及其对象中属性值的变化。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
侦听器watch:
- 当被监视的数据变化时,回调函数自动调用,进行相关操作
- 监视的数据必须存在,才能进行监视
基本例子
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
| <script>
export default { data() { return { message: "你好", } }, watch: { message: function (newValue, oldValue) { console.log(newValue); console.log(oldValue); if (newValue.length < 5 || newValue.length > 10) { console.log("输入框中的内容长度必须在5-10位"); } }
}, }; </script>
<template> <div> <p>{{ message }}</p> <input type="text" v-model="message"> </div> </template>
|
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),并设置一个执行该操作的条件。这些都是计算属性无法做到的。
除了 watch 选项之外,你还可以使用命令式的 vm.$watch API。
1 2 3 4 5 6
| vm.$watch('isHot',{ immediate:true, handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } })
|
1)计算属性 vs 侦听器
Vue 提供了一种更通用的方式来观察和响应当前活动的实例上的数据变动:==侦听属性==。当你有一些数据需要随着其它数据变动而变动时,watch 很容易被滥用——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
2)深度监听
深度监听:
- Vue中的watch默认不监听对象内部值的改变。
- 在watch中配置deep:true可以逐层监听对象内部值的改变。
注意:
- Vue自身可以监听对象内部值的改变,但Vue提供的watch默认不可以。
- 使用watch时根据监听数据的具体结构,决定是否采用深度监听deep。
- 如果使用deep逐层监听是会监听对象内部所有属性,如果单独监听需要
“监听对象.监听属性”: function (newName) {
执行语句
}
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
| <script>
export default { data() { return { message: "你好", user: { name: "linzy", age: 23, } } }, watch: { user: { handler: function (newName) { console.log("我是深度监听,监听所有对象user里的所有属性"); console.log(newName); }, deep: true }, "user.name": function (newName) { console.log("我是单独监听user.name"); } }, }; </script>
<template> <div> <p>{{ user.name }}</p> <p>{{ user.age }}</p> <button @click="user.name = 'hello vue'">改变名字</button> <button @click="user.age = 18">改变年龄</button> </div> </template>
|
八、Class 与 Style 绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
1、绑定 HTML Class
1)对象语法
我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class:
1
| <p class="active">hello vue</p>
|
上面的语法表示 active 这个 class 存在与否将取决于 data property 里的 active 是true还是false。
你可以在对象中传入更多字段来动态切换多个 class。此外,:class 指令也可以与普通的 class attribute 共存。当有如下模板:
1
| <p :class="{ active: isActive, hellovue: !isActive }">hello vue</p>
|
数组语法
我们可以把一个数组传给 :class,以应用一个 class 列表:
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
| <script>
export default { data() { return { classObj: { active: true, hellovue: true, }, } }, }; </script>
<template> <div> <p :class="classObj">hello vue</p> <p :class="{ active: isActive }" class="hellovue">hello vue</p> </div> </template>
<style scoped> .active { font-size: 61px; }
.hellovue { color: greenyellow; background-color: pink; } </style>
|
3)基本例子
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
| <script>
export default { data() { return { message: "hellovue", isActive: true, error: null, classObj: { active: true, hellovue: true, }, activeClass: "active", } }, computed: { classObjCom: function () { return { active: this.isActive && !this.error, hellovue: this.error } } } }; </script>
<template> <div> <h3>1、放置字符串</h3> <p class="active">hello vue</p> <h3>2、放置对象 常用</h3> <p :class="{ active: isActive }">hello vue</p> <p :class="{ active: isActive, hellovue: isActive }">hello vue</p> <button @click="isActive = !isActive">改变class</button> <h3>3、数组语法</h3> <p :class="classObj">hello vue</p> <h3>4、与普通类同时存在不会冲突</h3> <p :class="{ active: isActive }" class="hellovue">hello vue</p> <h3>5、使用计算属性</h3> <p :class="classObjCom">hello computed</p> <h3>6、数组表示 不常用</h3> <p :class="[message, activeClass]">hello slice</p> <h3>7、数组和对象结合使用</h3> <p :class="[message, { active: isActive }]">hello</p> </div> </template>
<style scoped> .active { font-size: 61px; }
.hellovue { color: greenyellow; background-color: pink; } </style>
|
2、绑定内联样式
1)对象语法
style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
1
| <p style="color:red">hello vue</p>
|
2)数组语法
:style 的数组语法可以将多个样式对象应用到同一个元素上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script>
export default { data() { return { styleObj: { color: "red", fontSize: "51px", 'background-color': "yellow", } } }, }; </script>
<template> <div> <p :style="[styleObj, { border: 'blue 10px solid' }]">hello vue</p> </div> </template>
|
3)基本例子
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
| <script>
export default { data() { return { activeColor: "greenyellow", fontSize: "61px", backgroundColor: "pink", styleObj: { color: "red", fontSize: "51px", 'background-color': "yellow", } } }, }; </script>
<template> <div> <h3>1、对象语法</h3> <p style="color:red">hello vue</p> <h3>2、对象语法 与数据双向绑定 实现对style样式的控制</h3> <p :style="{ color: activeColor, fontSize: fontSize, 'background-color': backgroundColor }">hello vue</p> <h3>3、对象语法 用对象来绑定样式</h3> <p :style="styleObj">hello vue</p> <h3>4、数组语法</h3> <p :style="[styleObj, { border: 'blue 10px solid' }]">hello vue</p> </div> </template>
|
九、v-if条件渲染
1、v-if
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
简而言之,v-if 条件为false时,对应的元素以及子元素都不会被渲染,控制dom元素的创建和销毁,运行时条件很少改变,一次性
写法:
- v-if=”表达式”
- v-else-if=”表达式”
- v-else
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script>
export default { data() { return { age: 18, } }, }; </script>
<template> <div> <p v-if="age > 18">我是成年人</p> <p v-else-if="age == 18">我正好18岁</p> <p v-else>我是小孩子</p> <input type="text" v-model="age"> </div> </template>
|
2、v-show
v-show 指令与v-if 指令用法大致一样:
带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 display CSS property 频繁切换状态
写法:v-show=”表达式”
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script>
export default { data() { return { sex: 'man', } }, }; </script>
<template> <div> <p v-show="sex == 'man'">男生</p> <p v-show="sex == 'woman'">女生</p> <input type="text" v-model="sex"> </div> </template>
|
3、v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
十、v-for列表渲染
v-for指令:
- 用于展示列表数据
- 语法:< li v-for=”(item, index) in xxx” :key=”yyy”>,其中key可以是index下标索引,也可以是遍历对象的唯一标识
- 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
1、遍历数组
我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <script>
export default { data() { return { person: ["张三", "李四", "王五"], } }, }; </script>
<template> <div> <ul> <li v-for="item in person">{{ item }}</li> </ul> <ul> <li v-for="(item, index) in person">{{ item }} -- > {{ index }}</li> </ul> </div> </template>
|
2、遍历对象
可以用 v-for 来遍历一个对象的 property。
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
| <script>
export default { data() { return { personObj: { name: "linzy", age: 18, sex: "男" } } }, }; </script>
<template> <div> <ul> <li v-for="item in personObj">{{ item }}</li> </ul> <ul> <li v-for="(value, name, index) of personObj">{{ name }} -- > {{ value }} -- > {{ index }}</li> </ul> </div> </template>
|
在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它在不同 JavaScript 引擎下的结果都一致。
3、维护状态
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key attribute:
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
| <script>
export default { data() { return { person: ["张三", "李四", "王五"], } }, methods: { addPerson: function () { this.person.unshift("linzy") } }, }; </script>
<template> <div> <ul> <li v-for="item in person" :key="item"><input type="checkbox" name="" id="">{{ item }}</li> </ul> <button @click="addPerson">添加数组</button> </div> </template>
|
建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
提示:不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
4、数组更新检测
1)变更方法
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
- push() 数组末尾增加单个或多个元素
- pop() 删除数组末尾元素
- shift() 删除数组首位元素
- unshift() 数组首部增加单个或多个元素
- splice() 可以插入元素,删除元素,替换元素
- sort() 从小到大排序
- reverse() 数组翻转
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
| <script>
export default { data() { return { list: [1, 5, 9, 4, 7, 3] } }, methods: { changeList: function () { this.list.sort().reverse() } }, }; </script>
<template> <div> <ul> <li v-for="item in list"> {{ item }}</li> </ul> <button @click="changeList">改变数组</button> </div> </template >
|
十一、v-model数据绑定
1、基本用法
你可以用 v-model 指令在表单 < input >、< textarea > 及 < select > 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理。
提示:
v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值。它将始终将当前活动实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
例子
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
| <script>
export default { data() { return { message: "hello vue", } }, methods: { changeValue: function (e) { console.log(e); this.message = e.target.value } }, }; </script>
<template> <div> <input type="text" v-model="message"> <h2>{{ message }}</h2>
<input type="text" :value="message" @input="changeValue"><br> </div> </template>
|
1)文本 (Text)
1 2 3
| <input type="text" v-model="message"> <h2>{{ message }}</h2>
|
2)多行文本 (Textarea)
1 2 3 4
| <span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br /> <textarea v-model="message" placeholder="add multiple lines"></textarea>
|
注意:插值在 textarea 中不起作用,请使用 v-model 来代替。
3)复选框 (Checkbox)
单个复选框,绑定到布尔值
1 2 3 4
| <input type="checkbox" v-model="checked"> <h2>{{ checked }}</h2>
|
多个复选框,绑定到同一个数组
1 2 3 4 5 6 7
| <input type="checkbox" v-model="fruits" value="苹果">苹果 <input type="checkbox" v-model="fruits" value="梨">梨 <input type="checkbox" v-model="fruits" value="西瓜">西瓜 <input type="checkbox" v-model="fruits" value="香蕉">香蕉 <input type="checkbox" v-model="fruits" value="哈密瓜">哈密瓜 <h2>喜欢的水果{{ fruits }}</h2>
|
4)单选框 (Radio)
1 2 3 4
| <input type="radio" v-model="sex" value="男"> 男 <input type="radio" v-model="sex" value="女"> 女 <h2>{{ sex }}</h2>
|
5)选择框 (Select)
1 2 3 4 5 6 7 8 9
| <select name="" id="" v-model="city"> <option value="杭州">杭州</option> <option value="嘉兴">嘉兴</option> <option value="宁波">宁波</option> <option value="台州">台州</option> <option value="湖州">湖州</option> </select> <h2>{{ city }}</h2>
|
6)基本例子
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
| <script>
export default { data() { return { message: "hello vue", checked: '', fruits: [], sex: '', city: '台州', counter: '', } }, methods: { changeValue: function (e) { console.log(e); this.message = e.target.value } }, }; </script>
<template> <div> <h3>1、v-model 双向绑定 文本框</h3> <input type="text" v-model="message"> <h2>{{ message }}</h2>
<h3>2、v-model本质操作</h3> <input type="text" :value="message" @input="changeValue"><br> <h3>3、 单个勾选框, v-model为布尔值</h3> <input type="checkbox" v-model="checked"> <h2>{{ checked }}</h2> <h3>4、多个勾选框 v-model绑定的是数组</h3> <input type="checkbox" v-model="fruits" value="苹果">苹果 <input type="checkbox" v-model="fruits" value="梨">梨 <input type="checkbox" v-model="fruits" value="西瓜">西瓜 <input type="checkbox" v-model="fruits" value="香蕉">香蕉 <input type="checkbox" v-model="fruits" value="哈密瓜">哈密瓜 <h2>喜欢的水果{{ fruits }}</h2> <h3>5、单选框</h3> <input type="radio" v-model="sex" value="男"> 男 <input type="radio" v-model="sex" value="女"> 女 <h2>{{ sex }}</h2> <h3>6、多选select</h3> <select name="" id="" v-model="city"> <option value="杭州">杭州</option> <option value="嘉兴">嘉兴</option> <option value="宁波">宁波</option> <option value="台州">台州</option> <option value="湖州">湖州</option> </select> <p>{{ city }}</p> </div> </template>
|
2、修饰符
修饰符:
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
| <script>
export default { data() { return { message: "hello vue", counter: '', } }, methods: { changeValue: function (e) { console.log(e); this.message = e.target.value } }, }; </script>
<template> <div> <h3>.lazy 当输入框失去焦点,再去同步输入框中的数据</h3> <input type="text" v-model.lazy="message"><br> <h3>.number将输入框的内容自动转为数字类型</h3> <input type="text" v-model.number="counter"> <h2>{{ typeof counter }}</h2> <h3>.trim 自动过滤用户输入的首尾空白字符</h3> <input type="text" v-model.trim="message"> <h2>{{ message }}</h2> </div> </template>
|