「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。
filter 过滤器
🚫Vue 3 中剔除了过滤器
过滤器常用于文本的格式化,可以用于插值表达式和 v-bind 属性绑定。
<!-- 在插值表达式中调用 capitalize 过滤器对 message 进行过滤 -->
<p>{{ message | capitalize }}</p>
<!-- 在 v-bind 中通过"管道符"调用 formatId 过滤器,对 rawId 过滤 -->
<div :id="rawId | formatId"></div>
过滤器定义分两种:私有过滤器和全局过滤器。
私有过滤器
在 filters 节点下定义的过滤器称为"私有过滤器",它只能在当前 vue 实例所控制的 el 区域内使用。
<template>
<div>
<h2>{{ message | capitalize('hi', 'hello') }}</h2>
</div>
</template>
<script>
export default {
data() {
return {
message: 'hello Vue!',
}
},
filters: {
// 过滤器也可以传参
capitalize(str, arg1, arg2) {
// arg1和arg2分别是传入过滤器的两个参数
console.log(arg1)
console.log(arg2)
// str是要过滤的内容
return str.charAt(0).toUpperCase() + str.slice(1)
},
},
}
</script>
全局过滤器
如果希望在多个 vue 实例之间可以共享过滤器,那可以如下定义全局过滤器。
// main.js
Vue.filter('capitalize', (str) => {
return str.charAt(0).toUpperCase() + str.slice(1)
})
🪁注:过滤器可以串联进行调用,且可传入参数。
<!-- message先交由filterA处理,处理结果再交由filterB进行过滤 -->
<span>{{ message | filterA(arg1) | filterB }}</span>
watch 数据监视
watch 侦听器可以监视数据的变化,从而针对数据的变化做特定的操作。
🐖注意:监听的属性必须在 data 中存在。
基本语法
<template>
<div>
<input type="text" v-model="username" />
</div>
</template>
<script>
export default {
data() {
return {
username: '阿拉斯加湾',
}
},
watch: {
// username 在 data 中存在!
username(newVal, oldVal) {
// newVal: 当前值
// oldVal: 旧值
console.log('旧值: ' + oldVal)
console.log('当前值: ' + newVal)
},
},
}
</script>
演示一下:
immediate 与 deep 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器(如上图),如果想让 watch 侦听器加载后立即被调用,则需要使用 immediate 选项。
export default {
data() {
return {
username: '阿拉斯加湾',
}
},
watch: {
username: {
// handler 是固定写法
handler: function (newVal, oldVal) {
console.log('新值: ' + newVal)
console.log('旧值: ' + oldVal)
},
// 页面初次渲染好后,立即触发 watch 侦听器
immediate: true,
},
},
}
效果大致如下:
出现 undefined 说明页面加载后立即被调用,
immediate成功应用✔
🔪如果 watch 侦听的是一个对象,对象中的属性改变却无法被监听到。Vue 提供了对应的 deep 选项:
<template>
<div id="app">
<input type="text" v-model="user.name" />
<input type="text" v-model="user.age" />
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: 'w',
age: 19,
},
}
},
watch: {
user: {
handler: function (val) {
console.log(val.name)
console.log(val.age)
},
immediate: true,
deep: true,
},
},
}
</script>
演示:
但你会发现修改对象中的任一属性都会触发 watch 侦听,那如果只想侦听对象中的单个属性呢?
那就得使用字符串指定具体要侦听的属性:
<template>
<div id="app">
<input type="text" v-model="user.name" />
<input type="text" v-model="user.age" />
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: 'w',
age: 19,
},
}
},
watch: {
'user.name': {
handler: function (val) {
console.log('只侦听user.name属性: ' + val)
}
},
},
}
</script>
演示:
经典案例
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用✔
watch: {
async username(newVal) {
const { data: result } = await axios.get('http://localhost:8090/api/findUser/' + newVal)
console.log(result)
},
}
computed 计算属性
🔍深入理解计算属性:Vue.js 计算属性的秘密
⭐computed 作用是通过一系列的运算/操作后,最终得到一个属性值,运算/操作中所依赖的值一旦发生改变就会重新求值。
⭐computed 还有一大特点:缓存计算结果(多处使用只会调用一次计算属性)
注意:
computed中的属性不能与data中的属性同名,否则会报错。- 虽然
computed在声明时被定义为方法,但本质却是一个属性。
基本用法
案例一:计算全名
<template>
<div id="app">
<input type="text" v-model="firstName" /><br />
<input type="text" v-model="lastName" />
{{ fullName }}
</div>
</template>
<script>
export default {
data() {
return {
firstName: 'K',
lastName: 'B',
}
},
computed: {
fullName() {
return this.firstName + `·` + this.lastName
},
},
}
</script>
案例二:根据 RGB 动态变更底色
<template>
<div id="app">
R: <input type="text" v-model="r" /><br />
G: <input type="text" v-model="g" /><br />
B: <input type="text" v-model="b" />
<div :style="{backgroundColor: rgb}">{{ rgb }}</div>
</div>
</template>
<script>
export default {
data() {
return {
r: '0',
g: '0',
b: '0',
}
},
computed: {
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
},
},
}
</script>
补充:watch 与 computed 的区别
watch支持异步,computed不支持异步watch不支持缓存,computed可以缓存计算结果
记住一个场景:只要一个属性是由其他属性计算而来的,这个属性依赖于其他属性,那么我们使用 computed;watch 一般只用于监听一个值/对象。
生命周期
简单来说,一个组件从创建到销毁所经历的各种状态,就是一个组件的生命周期。这个过程常常伴随着一些函数的子调用,我们把这些函数称为生命周期钩子函数。
注意:
- Vue 在执行过程中会自动调用
生命周期钩子函数,我们只需要提供这些提供函数即可。 - 钩子函数名称都是 Vue 中规定好的了。
生命周期图示
官网:下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
beforeCreate()
在实例初始化之后,数据观测和 event / watcher 事件配置之前被调用
🛠注意:el 没有绑定作用域,此时无法获取 data 中的数据、methods 中的方法!
created()
🌀这是一个十分常用的生命周期,可以调用 methods 中的方法、改变 data 中的数据!但页面仍没有渲染出来。
常用场景:页面渲染之前获取数据的方法就是在 created() 中调用(登录系统时就需要显示信息)
created() {
this.init()
},
methods: {
// 初始化方法
init() {
// axios 请求
}
}
beforeMounted()
🌡el 绑定了作用域但未挂载,但是页面还没有真实数据。
mounted()
💝此时,vue 实例已经挂载到页面中(即数据已经渲染到页面上了),且可以获取 el 中的 DOM 元素,进行 DOM 操作。
beforeUpdate()
当 data 数据更新时会调用该钩子函数,发生在虚拟 DOM 重新渲染和打补丁之前。
🌈此处获取的数据是更新后的数据,但页面数据仍为旧数据,未更新。
updated()
🔍该钩子函数紧随 beforeUpdate() 函数,组件 DOM 已经更新,此时页面上已经是更新后的数据了。
beforeDestroy()
🍹组件销毁之前调用,此时 data、methods 等还没有被销毁,仍然可以调用。
使用场景:实例销毁之前,执行清理任务,比如清除定时器等。
destroyed()
🧫Vue 实例销毁后调用,解绑所有事件监听器、子实例等。
一览众山小
对生命周期有了一定的了解后,我们就再来回顾下生命周期图示(带注释讲解):
接下来定义并使用一下所有的生命周期函数:
export default {
data() {
return {
}
},
methods: {
},
beforeCreate() {
console.log('beforeCreated()')
},
created() {
console.log('created()')
},
beforeMount() {
console.log('beforeMount()')
},
mounted() {
console.log('mounted()')
},
beforeUpdate() {
console.log('beforeUpdate()')
},
updated() {
console.log('updated()')
},
beforeDestroy() {
console.log('beforeDestroy()')
},
destroyed() {
console.log('destroyed()')
},
}
更多:Vue 实例生命周期
ref 引用
🚫需求:我们想在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
🔍方案:使用 ref 引用辅助开发,每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。
打印 this 试试(vue 实例)
引用 DOM 元素
基本使用
⭐案例 1:
<template>
<div>
<div ref="myDiv">DIV</div>
<button @click="modifyStyle">修改DIV样式</button>
</div>
</template>
<script>
export default {
methods: {
modifyStyle() {
// 可用于DOM元素, 同理也可用于Vue组件!
this.$refs.myDiv.style.backgroundColor = 'pink'
}
},
}
</script>
异步 DOM 更新: this.$nextTick(callback)
由案例 2 入手引出
this.$nextTick(callback)
👑案例 2:
<template>
<div>
<input type="text" v-if="inputVisible" ref="ipt" />
<button v-else @click="showInput">展示input输入框</button>
</div>
</template>
<script>
export default {
data() {
return {
inputVisible: false,
}
},
methods: {
showInput() {
// 显示 input 输入框
this.inputVisible = true
// 获取文本框的 DOM 引用,并调用 .focus() 时期自动获得焦点
this.$refs.ipt.focus()
}
}
}
</script>
运行结果:
🔎 Q:提示报错信息 Cannot read properties of undefined (reading 'focus'),怎么会这样?
🏹 A:有认真看上文的生命周期就会明白,data 变化后,页面还没来得及重新渲染 Virtual DOM re-render and patch,就继续向下执行 this.$refs.ipt.focus() 了,显然此时的 ref="ipt" 指向的 input 元素还为渲染到页面 (undefined),所以无法调用 focus() 函数。
👑 解决办法:让 this.$refs.ipt.focus() 执行后延,延迟到页面重新渲染后再执行。
🚀 Vue 为组件提供了这么一个方法: this.$nextTick(cb),将 callback 回调推迟到下一个 DOM 更新周期之后执行。
🎨 简单来说:等组件的 DOM 更新完成之后,再执行 callback 回调函数,从而保证 callback 回调函数可以操作到最新的 DOM 元素。
<template>
<div>
<input type="text" v-if="inputVisible" ref="ipt" />
<button v-else @click="showInput">展示input输入框</button>
</div>
</template>
<script>
export default {
data() {
return {
inputVisible: false,
}
},
methods: {
showInput() {
this.inputVisible = true
// 推迟到下个 DOM 更新周期再执行
this.$nextTick(() => {
this.$refs.ipt.focus()
})
}
}
}
</script>
执行结果:
没问题✔
引用组件实例
<template>
<div>
<my-counter ref="counterRef"></my-counter>
<button @click="print">引用组件实例</button>
</div>
</template>
<script>
export default {
methods: {
print() {
console.log(this.$refs.counterRef)
}
},
}
</script>
自定义指令
私有指令
在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。
<template>
<div>
<span v-color='color'>自定义(私有)指令 v-color</span><br>
<span v-color="'green'">自定义(私有)指令 v-color</span><br>
</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
},
// 自定义私有指令
directives: {
color: {
// 只调用一次, 指令第一次绑定到元素时调用, 在这里可以进行一次性的初始化设置
bind(el, binding) {
console.log(binding);
el.style.color = binding.value
},
// DOM重新渲染时调用
update(el, binding) {
el.style.color = binding.value
}
},
}
}
</script>
演示结果:
🔍分析
binding对象中的属性
🥊简写方式:
如果
bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
directives: {
// 对象格式!
color: {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
},
// 函数格式!
colors(el, binding) {
el.style.color = binding.value
}
}
全局指令
Directive Options
🍸全局自定义指令基本语法:
// 全局自定义指令(最全)
Vue.directive('directiveName', {
bind(el, binding, vnode) {
// el: 指令所绑定 DOM 元素, 可以直接用来操作 DOM
// binding: 上文私有指令已详解
// vnode: Vue 编译生成的虚拟节点
// oldVnode: 上一个虚拟节点, 仅在 update 和 componentUpdated 钩子中使用
},
// inserted 钩子函数调用时, 当前元素已经插入页面中了, 也就是可以获取到父级节点
inserted(el, binding, vnode) { },
// DOM 重新渲染时
update(el, binding, vnode, oldVnode) { },
// DOM 重新渲染后
componentUpdated(el, binding, vnode, oldVnode) { },
// 只调用一次, 指令与元素解绑时调用
unbind(el) {
// 指令所在的元素在页面中消失时触发
}
})
🚀如果你想在 bind 和 update 时触发相同行为,而不关心其它的钩子:
// 如果你想在 bind 和 update 时触发相同行为,而不关心其它的钩子
Vue.directive('directiveName', function (el, binding) {
// TODO
})
使用:全局指令同私有指令一致
ES6
let 关键字
ES6 新增了 let 关键字,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
let实际上为 JavaScript 新增了块级作用域。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
for循环的计数器,就很合适使用let命令。
for (let i = 0; i < 10; i++) {
console.log(i);
}
console.log(i);
//ReferenceError: i is not defined
const 常量
const 声明一个只读的常量。一旦声明,常量必须进行初始化且初始化的值就不能改变。且声明 const 不赋值就会报错。
const w = 1
w = 2 // Uncaught TypeError: "w" is read-only
const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
所以对象
obj不可被重新赋值,但其属性可以✔
const obj = {
name: 'Q',
age: 21
}
// 可以给其属性赋值
obj.name = 'w'
obj.age = 20
console.log(obj); // {name: 'w', age: 20}
// 但不能给obj重新赋值
// Uncaught TypeError: "obj" is read-only
obj = {
name: 'w',
sex: 'M'
}
💌当我们修饰的标识符不会被再次赋值时,就可以使用 const 来保证数据的安全性。
// 场景: 发送 axios 请求向后端接口请求数据
async conditionalSelect() {
// 获取到的 data 数据就是不会被再次赋值的常量!使用 const 保证其安全性。
const { data: list } = await this.$axios.post('/selectList')
this.list = list
}
数组常用操作
forEach
遍历数组
语法:
arr.forEach(function(currentValue, index, arr))
function():必需,数组中每个元素需要调用的函数currentValue:必需,当前元素index:可选,当前元素索引arr:可选,当前元素所属数组对象
案例:
var colors = [1, 2, 3, 5]
var sum = 0
colors.forEach(function (currentValue, index, arr) {
console.log('当前值: ' + currentValue + ', 索引: ' + index);
sum += currentValue
})
console.log('和: ' + sum);
join
通过指定分隔符分隔数组
var arr = [1, 3, 2, 4]
var str = arr.join(',')
console.log(str);
reverse
反转数组
var nums = [1, 2, 3]
nums.reverse()
console.log(nums);
concat
连接两个/多个数组,返回连接后的数组
var colors = ["red", "green", "blue"]
var others = ["white", "black"]
var newColors = colors.concat(others)
console.log(newColors.toString());
push、unshift
push:在数组尾部添加元素,并返回新数组的长度
unshift:在数组头部添加元素,并返回新数组的长度
var colors = ["red", "green", "blue"]
colors.push(1)
colors.unshift(2)
console.log(colors);
pop、shift
pop:删除并返回数组的最后一个元素
shift:删除并返回数组的第一个元素
var colors = ["red", "green", "blue"]
console.log('pop(): ' + colors.pop())
console.log('shift(): ' + colors.shift())
console.log(colors);
slice
选取数组的一部分,并返回一个新数组
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1, 3);
console.log(citrus);
some、every、filter
some: 检测数组元素中是否有元素符合指定条件。
every: 检测数值元素的每个元素是否都符合条件。
filter: 检测数值元素,并返回符合条件所有元素的数组。
(1)some(function(item, index, array){ }):
var num = [1, 3, 4, 6, 2, 7, 9]
// 只要有一个大于6,则打印true
flag = num.some((item, index, array) => {
return (item > 6)
})
console.log(flag); // true
(2)every(function(item, index, array){ }):
var booleans = [
{ id: 1, name: "w", price: 11, state: true },
{ id: 2, name: "q", price: 12, state: true },
{ id: 3, name: "k", price: 13, state: false }
]
// state皆为true、result才为true
var result = booleans.every(item => booleans.state)
console.log(result); // false
(3)filter()
var num = [1, 3, 4, 6, 2, 7, 9]
var newNums = num.filter((val, index, array) => {
return (val > 5)
})
console.log(newNums); // [6, 7, 9]
reduce
通过回调函数将数组元素计算为一个值(从左到右)
案例:
var list = [1, 2, 3, 4, 5]
var result = list.reduce(function (pre, cur, curIndex, arr) {
return pre + cur
})
console.log(result);
splice
强大的数组操作方法
基本语法:
splice(index, count, item1, item2, ...)
index:起始位置,规定从何处添加/删除元素。count:删除元素的个数item1, item2, ..:要添加的新元素
常用操作:
- 删除:
splice(xx, 1) - 插入:
splice(xx, 0, arg3, arg4, arg5..) - 替换:
splice(xx, ?, arg3, arg4..)
var colors = ["red", "green", "blue"]
// start、deleteCount、number
// 从index=0的地方删除一个元素,然后添加一个"white",相当于red替换为white
var removed = colors.splice(0, 1, "white")
console.log(removed); // red
console.log(colors); // white, green, blue
colors.splice(1, 1, "red", "purple")
console.log(colors); // white red purple blue
🌤更多:JavaScript Array
❤️/ END / 如果本文对你有帮助,点个「赞」支持下吧。