# v-model 的原理
原理: v-model 本质上是一个语法糖。例如应用在输入框上,就是 Value 属性和 input 事件的和写
ps: v-model 应用于不同的表单元素 设置的属性和事件会稍有不同,例如在复选框是 checked 属性和 change 事件
作用: 提供数据的双向绑定
- 数据发生改变,页面会自动变 :value
- 页面输入改变,数据会自动变 @input
注意: $event 用于在模板中,获取事件的形参
<div id="app"> | |
<input type="text" v-model="text"> | |
<!-- v-model 等价于 value+@input 的组合 --> | |
<input type="text" :value="text" @input="text = $event.target.value"> | |
</div> |
# v-model 应用于组件
使用 v-model 来实现父组件数据和子组件双向绑定
- 子组件中: prop 必须通过 value 接收,事件触发必须用 input
- 父组件中:v-model 给组件直接绑定数据
父组件
<template> | |
<div id="app"> | |
<!-- v-model 给组件直接绑定数据 --> | |
<baseInput v-model="iptValue"></baseInput> | |
</div> | |
</template> | |
<script> | |
export default { | |
components: { | |
'baseInput': () => import('@/components/baseInput.vue') | |
}, | |
name: 'app', | |
data() { | |
return { | |
iptValue: "o.O" | |
}; | |
}, | |
} | |
</script> |
子组件
<template> | |
<div class="iptBox"> | |
<!-- 事件触发必须用 input --> | |
<input type="text" :value="value" @input="changeValue"> | |
</div> | |
</template> | |
<script> | |
export default { | |
props: { | |
//props 必须用 value 来接收 | |
value: { | |
default: "awa", | |
type: String | |
} | |
}, | |
methods: { | |
changeValue(e) { | |
// 将数据通过 input 方法传送给父组件 必须用 input 方法 | |
this.$emit('input', e.target.value) | |
} | |
} | |
} | |
</script> |
# .sync 修饰符
作用: 可以实现 子组件 与 父组件 的双向绑定,简化代码,与 v-model 作用一样,语法上稍有不同
特点: prop 属性名,可以自定义,非固定为 value
本质: 就是 :属性名
和 @update:属性名
合写
父组件
<baseDialog :visible.sync="isShow"></baseDialog> | |
<!-- 两者相同 --> | |
<baseDialog :visible="isShow" @update:visible="isShow=$event"></baseDialog> |
子组件
<template> | |
<div class="baseDialogMask" v-show="visible"> | |
<div class="baseDialogFooter"> | |
<button @click="closeDialog">确定</button> | |
<button>取消</button> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
export default { | |
props: { | |
visible: Boolean, | |
default: false | |
}, | |
methods: { | |
closeDialog() { | |
// 必须为 update | |
this.$emit('update:visible', false) | |
} | |
} | |
} | |
</script> |
# 计算属性
概念: 基于现有的数据,计算出来的新属性。依赖的数据变化,自动重新计算。
语法:
- 声明在
computed
配置项中,一个计算属性对应一个函数 - 使用起来和普通属性一样使用
{{计算属性名}}
// 在 computed 中定义一个计算属性 | |
data() { | |
return { | |
// 现有的数据 | |
list: [ | |
{ id: 1, name: '滋蹦', num: 6 }, | |
{ id: 2, name: '和平捍卫者', num: 5 }, | |
{ id: 3, name: 'R99', num: 19 } | |
] | |
}; | |
}, | |
computed: { | |
// 根据现有的数据 编写求值逻辑 | |
totalCount() { | |
//reducer 逐个遍历数组元素,每一步都将当前元素的值与前一步的结果相加(该结果是之前所有步骤结果的总和)—— 直到没有更多需要相加的元素。 | |
var total = this.list.reduce((sum, item) => sum + item.num, 0) | |
return total | |
} | |
} |
<!-- 在页面中渲染 --> | |
<div id="app"> | |
<table border="1px solid black"> | |
<tr> | |
<th>名字</th> | |
<th>数量</th> | |
</tr> | |
<tr v-for="item in list" :key="item.id"> | |
<td></td> | |
<td></td> | |
</tr> | |
</table> | |
<span>枪械总数:</span> | |
</div> |
# vue 异步更新,$nextTick
首先来看一个案例
需求:
- 点击显示按钮后显示 input 盒子
- 让 input 立刻获取焦点
<template> | |
<div id="app"> | |
<button @click="showInput">显示input</button> | |
<div class="inputBox" ref="inp" v-show="isShow"> | |
<input type="text"> | |
<button>确定</button> | |
</div> | |
</div> | |
</template> |
export default { | |
data() { | |
return { | |
isShow: false | |
} | |
}, | |
methods: { | |
showInput() { | |
// 显示对话框 | |
this.isShow = true | |
// 获取焦点 | |
this.$ref.inp.focus() | |
} | |
} | |
} |
看起来代码好像没有问题,实际上立刻获取焦点是无法成功的
因为 Vue 是异步 DOM 更新 (提升性能)
这个时候就需要使用 $nextTick
它的作用是等 DOM 更新完成之后,才会触发执行此方法里的函数体
更改后代码为;
showInput() { | |
this.isShow = true | |
this.$nextTick(() => { | |
this.$refs.inp.focus() | |
}) | |
} |
当然使用 setTimeout 也可以,但是 nextTick 更加精准
# 自定义指令
# 全局注册自定义指令
还是那个案例,当页面加载时让 input 获取焦点
在 main.js 中全局注册自定义指令
// 全局注册自定义指令 focus 是自定义指令名字 | |
Vue.directive('focus', { | |
//inserted 是当前指令所绑定的元素被添加到页面去的时 会自动调用 | |
//el 是指令所绑定的元素 | |
"inserted"(el) { | |
el.focus() | |
} | |
}) |
<!-- v - 指令名即可使用 --> | |
<input type="text" v-focus> |
让 input 表单只能输入正整数自定义指令
Vue.directive('numeric', { | |
'inserted' (el) { | |
el.addEventListener('input', function (e) { | |
const input = e.target | |
input.value = input.value.replace(/[^0-9]/g, '') // 只保留数字 | |
}) | |
} | |
}) |
# 自定义指令的值
在绑定指令时,可以通过 “等号” 的形式为指令 绑定 具体的参数值
语法:
- v - 指令名 = "指令值" 通过 等号 可以绑定指令的值
- 通过 binding.value 可以拿到指令的值
- 通过 update 钩子,可以监听指令值的变化,进行 dom 更新操作
<span v-color="'green'">嗡嗡嗡</span> |
Vue.directive('color', { | |
//inserted 提供的是元素被添加到页面时的逻辑 | |
"inserted"(el, binding) { | |
el.style.color = binding.value | |
}, | |
//update 会在指令的值修改的时候触发,提供值变化后 dom 更新的逻辑 | |
update(el, binding) { | |
el.style.color = binding.value | |
} | |
}) |
# 封装加载动画指令
一个工作中非常常用的自定义指令
场景: 实际开发过程中,发送请求需要时间,在请求的数据未返回时,页面会处于空白状态,这样会导致用户体验不好。
所以我们可以封装一个指令,实现加载效果
思路:
- 本质 Loading 效果就是一个蒙层,盖在了盒子上
- 数据请求中,开启 Loading 状态,添加蒙层
- 数据请求完毕,关闭 Loading 状态,移除蒙层
先准备一个 loading 类 通过伪元素定位设置宽高,实现蒙层
.loading:before { | |
content: ''; | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
background: black url('assets/loading.gif') no-repeat center; | |
} |
自定义 loading 指令,当获取数据时,移除 loading 遮罩层
data() { | |
return { | |
isLoading: true, | |
list: [] | |
} | |
}, | |
// 局部自定义指令 | |
directives: { | |
loading: { | |
inserted(el, binding) { | |
// 如果 isLoading 为 true 则添加 loading 类,没有则移除 | |
binding.value ? el.classList.add('loading') : el.classList.remove('loading') | |
}, | |
// 当数据发生更改时触发函数 | |
update(el, binding) { | |
binding.value ? el.classList.add('loading') : el.classList.remove('loading') | |
} | |
} | |
}, | |
created() { | |
// 模拟接收数据慢 | |
setTimeout(() => { | |
// 关闭 loading 遮罩层 | |
this.isLoading = false | |
this.list = list | |
}, 2000); | |
} |
<div class="box" v-loading="isLoading"> | |
</div> |
# 组件缓存 keep-alive
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁他们
优点
在组件切换过程中,把切换出去的组件保留在内存中,防止重复渲染 DOM
减少加载时间及性能消耗,提高用户体验性
使用方式
<template> | |
<div id="app"> | |
<keep-alive> | |
<router-view></router-view> | |
</keep-alive> | |
</div> | |
</template> |
keep-alive 的三个属性
- include 组件名数组,只有匹配的组件会被缓存
- exclude 组件名数组,任何匹配的组件都不会被缓存
- max 最多可以缓存多少组件实例
ps: 优先使用组件中的 name
来作组件名,如果组件没有配置 name 则会找文件名作组件名
<template> | |
<div> | |
<keep-alive :exclude="[`articleDetail`]"> | |
<router-view></router-view> | |
</keep-alive> | |
</div> | |
</template> |
- keep-alive 的两个生命周期函数
- activated 当组件被激活时触发 → 进入页面触发
- deactivated 当组件不被使用的时候触发 → 离开页面触发
<script> | |
export default { | |
name: 'layout', | |
activated() { | |
console.log('组件被激活,进入组件页面'); | |
}, | |
deactivated() { | |
console.log('组件失活,离开组件页面'); | |
}, | |
} | |
</script> |