Vue技能

本文最后更新于:8 个月前

熟练使用Vue框架的API,称之为“Vue技能”

Vue技能

一、setup() 和 script setup

1. setup()

  • setup()Vue3独有的生命周期钩子函数,是组件中使用「组合式API」的入口,位于beforeCreate()之前;

  • setup()需要返回一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    export default{
    setup(){
    const count = ref(0)
    function increase() { count.value++ }
    return {
    count,
    increase,
    }
    }
    }
  • setup()自身没有对组件实例的访问权thisundefined,因为它在beforeCreate()之前,所以此时实例还不存在。如果在业务逻辑中需要获取组件实例,可以通过getCurrentInstance()方法

    1
    2
    3
    4
    5
    6
    7
    8
    import {ref, getCurrentInstance} from 'vue'
    export default {
    setup() {
    const {proxy, ctx} = getCurrentInstance()
    const _this = ctx
    ...
    }
    }
  • setup()返回的对象可以在其它钩子函数中使用,但是反过来不行。(从setup()返回ref时,会自动解析,所以在setup之外不用再写.value

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    export default {
    setup(){
    const count = ref(0)
    return {
    count
    }
    },
    mounted(){
    console.log(this.count)
    }
    }
  • setup()有两个参数,分别是props,和上下文对象

    1
    2
    3
    4
    5
    6
    7
    8
    export default {
    setup(props, context){
    // 将 `props` 转为一个其中全是 ref 的对象,然后解构
    const { title } = toRefs(props)

    // context是上下文对象
    }
    }

2. script setup

<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,推荐在SFC中使用,<script setup>里面的内容会被编译成组件setup()函数的内容。

  • <script setup>顶层声明的绑定(变量、函数、import导入的内容)会暴露给模板

    <script setup>中不需要显式地返回对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <script setup>
    import { ref } from 'vue'
    import { capitalize } from './helpers'
    const msg = ref('Hello!')
    function log() {
    console.log(msg)
    }
    </script>

    <template>
    <button @click="log">{{ msg }}</button>
    <div>{{ capitalize('hello') }} </div>
    </template>
  • 使用自定义指令,不需要显式注册

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <script setup>
    const vMyDirective = {
    beforeMount: (el) => {
    // 在元素上做些操作
    }
    }
    </script>
    <template>
    <h1 v-my-directive>This is a Heading</h1>
    </template>
  • 更多内容:script setup

二、computed 和 watch

computed watch
作用 计算属性,响应式依赖其它属性计算值,有缓存(“临时快照”) 侦听属性,监听值的变化,变化时触发回调
适用场景 模板中需要计算的复杂属性 观测值的变化,而完成指定的业务逻辑
  • watch侦听数据源

    • ref、reactive、computed对象(不能是对象的属性)
    • getter函数
    • 多个上述数据源组成的数组
  • watch侦听深度

    • 对响应式对象,默认是深层侦听
    • 对getter函数,默认是浅层侦听,即只有返回不同的对象才触发回调
    1
    2
    3
    4
    5
    6
    7
    8
    watch(()=>state.obj1, (newValue, oldValue)=>{
    ...//回调函数
    })
    // 只有当返回另一个对象时,才触发,obj1内部属性发生变化不触发,可以通过添加deep参数强制转换成深层监听,如下

    watch(()=>state.obj1, (newValue, oldValue)=>{
    ...//回调函数
    }, {deep:true})
  • watch立即回调,默认刚添加回调时是不执行的,可以通过immediate参数强制立即执行一次

    1
    2
    3
    watch(source, (newValue, oldValue) => {
    // 立即执行,且当 `source` 改变时再次执行
    }, { immediate: true })
  • watchEffect,自动侦听副作用函数中的依赖,不需要显式声明监听的对象

    watch,只能侦听指定的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 侦听todoId, watch写法
    const todoId = ref(1)
    const data = ref(null)

    watch(todoId, async () => {
    const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
    )
    data.value = await response.json()
    }, { immediate: true })
    1
    2
    3
    4
    5
    6
    7
    // 侦听todoId,watchEffect写法
    watchEffect(async ()=>{
    const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
    )
    data.value = await response.json()
    })

三、v-if 和 v-show

v-if v-show
作用 判断是否渲染节点 判断是否隐藏节点(已经渲染)
控制手段 添加 / 删除 dom元素 为元素添加 display:none
编译过程 有一个局部编译 / 卸载 的过程 基于css切换
触发生命周期 false -> true,触发beforeCreate、created、beforeMount、mounted true -> false,触发beforeDestory、destoryed 不触发生命周期钩子
性能消耗 更高的切换消耗 更高的初始渲染消耗
适用场景 不需要频繁切换条件 频繁切换条件

四、v-model

本质是语法糖,v-bind与DOM事件监听 的结合。它在内部为不同的组件绑定了参数,并抛出不同的事件

元素 动态绑定的参数 自动监听的事件
text, textarea value input
checkbox, radio checked chagne
select value change
1
2
3
4
5
6
7
8
9
10
<script>
export default{
data:{
str: 'hello vue'
}
}
</script>

<input v-model="str" /> <!--这一行等于下一行-->
<input v-bind:value="str" v-on:input="str = $event.target.value" />

五、nextTick

1
function nextTick(callback?: () => void): Promise<void>

Vue中更改响应式状态后,DOM不是立即更新的,而是先缓存到队列中,等到下一个tick一起执行。nextTick()在DOM状态改变后立即调用。以下是两种使用方式:

  • 将回调函数传入nextTick()的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <script setup>
    import { ref, nextTick } from 'vue'

    const count = ref(0)

    async function increment() {
    count.value++

    // DOM 还未更新
    console.log(document.getElementById('counter').textContent) // 0

    nextTick(()=>{
    console.log(document.getElementById('counter').textContent) // 1
    })
    }
    </script>

    <template>
    <button id="counter" @click="increment">{{ count }}</button>
    </template>
  • await nextTick()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <script setup>
    import { ref, nextTick } from 'vue'

    const count = ref(0)

    async function increment() {
    count.value++

    // DOM 还未更新
    console.log(document.getElementById('counter').textContent) // 0

    await nextTick()
    // DOM 此时已经更新
    console.log(document.getElementById('counter').textContent) // 1
    }
    </script>

    <template>
    <button id="counter" @click="increment">{{ count }}</button>
    </template>

六、Mixin

抽离公共的业务逻辑,当组件初始化时,按照默认或者自定义的策略进行合并,类似于“对象的继承”

在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立,可以通过 Vue 的 mixin 功能抽离公共的业务逻辑,原理类似“对象的继承”,当组件初始化时会调用 mergeOptions 方法进行合并,采用策略模式针对不同的属性进行合并。当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

用法示例(以下使用的是Vue2的声明式API语法)

1
2
3
4
5
6
7
// 全局混入,所以子组件都会获得(以下使用的是Vue2的声明式API语法)
Vue.mixin({
created: function(){
console.log('全局混入')
}
})
// mixin中的内容会被注入到每一个子组件实例中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 局部混入,使用mixin属性
// 首先定义一个mixin对象
var myMixin = {
created:function(){
this.hello()
},
methods:{
hello:function(){
console.log('hello from mixin!')
}
}
}

Vue.component('componentA',{
mixins:[myMixin]
})

Vue3不推荐使用Mixin,而是用「组合式函数」替代

七、内置指令

以下列举5个最常用的内置指令,仔细阅读文档,你会发现有很多你未知的用法

1. v-on

  • 用途,给元素绑定事件
  • 接受的值类型:函数名、内联表达式、对象
  • 缩写:@
  • 修饰符
    • .stop,调用event.stopPropagation(),阻止事件在DOM中继续传播,即阻击下一步的捕获或冒泡阶段
    • .prevent,调用event.preventDeafult(),阻止浏览器原生的事件行为
    • .once,只触发一次处理函数
    • 其它…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<button v-on:click="doThis"></button>

<!-- 内联声明 -->
<button @click="doThat('hello', $event)"></button>

<!-- 阻止默认事件 -->
<button @click.prevent="doThis"></button>

<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>

<!-- 点击事件将最多触发一次 -->
<button v-on:click.once="doThis"></button>

<!-- 对象语法,同时绑定多个事件 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

2. v-once

v-once不是v-on.once,而是指元素和组件只渲染一次!
  • 用途:声明仅渲染元素和组件一次,并跳过之后的更新。
  • 作用:在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。

3. v-bind

  • 用途,动态绑定class、attribute、组件的prop
  • 缩写:
  • 修饰符:不是很常用

4. v-model

  • 用途,在「表单输入元素」或「组件」上创建双向绑定

  • 使用范围<input><textarea><select>、自定义组件

  • 修饰符(重点!)

    • .lazy - 监听change事件而非inpput事件
    • .trim - 移除输入内容前后的空格(不能自己傻傻去处理了)
    • .number - 将输入的合法字符转为数字

5. v-html

将标签之间的内容作为HTML直接插入。(1)慎用,防止XSS攻击!(2)v-html的内容无法使用scoped的样式

1
<div v-html="html"></div>

八、自定义指令

1. 作用

自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑

2. 使用方法

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数

eg:自定义focus指令,当input元素被插入到DOM中后,被自动聚焦

1
2
3
4
5
6
7
8
9
10
<script setup>
//
const vFocus = {
mounted: (el) => el.focus()
}
</script>

<template>
<input v-focus />
</template>

在没有使用scipt setup的情况下,需要通过directives选项来注册

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
export default {
setup(){...}
directives: {
focus : {
...
}
}
}
</script>
<template>
<input v-focus />
</template>

也可以注册到全局

1
2
3
4
const app = createApp({})
app.directive('focus', {

})
  • keep-alive
  • 组件通信方式
  • 生命周期钩子

参考链接

组合式 API:setup() | Vue.js (vuejs.org)

Vue3中使用this - 掘金 (juejin.cn)


Vue技能
http://timegogo.top/2023/05/15/Vue/Vue:Vue技能/
作者
丘智聪
发布于
2023年5月15日
更新于
2023年7月16日
许可协议