vue3的基本使用(超詳細)

2023-1-16    前端達人

一、初識vue3

1.vue3簡介

  • 2020年9月18日,vue3發(fā)布3.0版本,代號大海賊時代來臨,One Piece
  • 特點:
    • 無需構(gòu)建步驟,漸進式增強靜態(tài)的 HTML
    • 在任何頁面中作為 Web Components 嵌入
    • 單頁應(yīng)用 (SPA)
    • 全棧 / 服務(wù)端渲染 (SSR)
    • Jamstack / 靜態(tài)站點生成 (SSG)
    • 開發(fā)桌面端、移動端、WebGL,甚至是命令行終端中的界面

2.Vue3帶來了什么

  • 打包大小減少40%
  • 初次渲染快55%,更新渲染快133%
  • 內(nèi)存減少54%

3.分析目錄結(jié)構(gòu)

  • main.js中的引入
  • 在模板中vue3中是可以沒有根標簽了,這也是比較重要的改變
  • 應(yīng)用實例并不只限于一個。createApp API 允許你在同一個頁面中創(chuàng)建多個共存的 Vue 應(yīng)用,而且每個應(yīng)用都擁有自己的用于配置和全局資源的作用域。
//main.js //引入的不再是Vue構(gòu)造函數(shù)了,引入的是一個名為createApp的工廠函數(shù) import {createApp} from 'vue import App from './App.vue //創(chuàng)建應(yīng)用實例對象-app(類似于之前vue2中的vm實例,但是app比vm更輕) createApp(APP).mount('#app') //卸載就是unmount,卸載就沒了 //createApp(APP).unmount('#app') //之前我們是這么寫的,在vue3里面這一塊就不支持了,會報錯的,引入不到 import vue from 'vue';  new Vue({ render:(h) => h(App) }).$mount('#app') //多個應(yīng)用實例 const app1 = createApp({ /* ... */ }) app1.mount('#container-1') const app2 = createApp({ /* ... */ }) app2.mount('#container-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

安裝vue3的開發(fā)者工具

  • 方式一: 打開chrome應(yīng)用商店,搜索vue: 里面有個Vue.js devtools,且下面有個角標beta那個就是vue3的開發(fā)者工具
  • 方式二: 離線模式下,可以直接將包丟到擴展程序

二、 常用Composition API(組合式API)

1. setup函數(shù)

  1. 理解:Vue3.0中一個新的額配置項,值為一個函數(shù)

  2. 2.setup是所有Composition API(組合api) “表演的舞臺”

  3. 組件中所用到的:數(shù)據(jù)、方法等等,均要配置在setup中

  4. setup函數(shù)的兩種返回值:

    • 若返回一個對象,則對象中的屬性、方法,在模板中均可以直接使用。(重點關(guān)注)
    • 若返回一個渲染函數(shù):則可以自定義渲染內(nèi)容。
  5. 注意點:

    • 盡量不要與Vue2.x配置混用
      • Vue2.x配置(data ,methos, computed…)中訪問到setup中的屬性,方法
      • 但在setup中不能訪問到Vue2.x配置(data.methos,compued…)
      • 如果有重名,setup優(yōu)先
    • setup不能是一個async函數(shù),因為返回值不再是return的對象,而是promise,模板看不到return對象中的屬性
      在這里插入圖片描述
import {h} from 'vue' //向下兼容,可以寫入vue2中的data配置項 module default { name: 'App', setup(){ //數(shù)據(jù) let name = '張三', let age = 18, //方法 function sayHello(){ console.log(name) }, //f返回一個對象(常用) return { name, age, sayHello } //返回一個函數(shù)(渲染函數(shù)) //return () => {return h('h1','學習')}  return () => h('h1','學習') } } 
  • 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

1.1關(guān)于單文件組件<script setup></script >

  • 每個 *.vue 文件最多可以包含一個 <script setup>。(不包括一般的 <script>)
  • 這個腳本塊將被預(yù)處理為組件的 setup() 函數(shù),這意味著它將為每一個組件實例都執(zhí)行。<script setup> 中的頂層綁定都將自動暴露給模板。
  • <script setup> 是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。當同時使用 SFC 與組合式 API 時該語法是默認推薦。相比于普通的 <script> 語法,它具有更多優(yōu)勢:
    • 更少的樣板內(nèi)容,更簡潔的代碼。
    • 能夠使用純 TypeScript 聲明 props 和自定義事件。這個我下面是有說明的
    • 更好的運行時性能 (其模板會被編譯成同一作用域內(nèi)的渲染函數(shù),避免了渲染上下文代理對象)。
    • 更好的 IDE 類型推導(dǎo)性能 (減少了語言服務(wù)器從代碼中抽取類型的工作)。
(1)基本語法:
/* 里面的代碼會被編譯成組件 setup() 函數(shù)的內(nèi)容。
  這意味著與普通的 `<script>` 只在組件被首次引入的時候執(zhí)行一次不同,
  `<script setup>` 中的代碼會在每次組件實例被創(chuàng)建的時候執(zhí)行。*/ <script setup> console.log('hello script setup') </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
頂層的綁定會被暴露給模板

當使用 <script setup> 的時候,任何在 <script setup> 聲明的頂層的綁定 (包括變量,函數(shù)聲明,以及 import 導(dǎo)入的內(nèi)容) 都能在模板中直接使用:

<script setup> // 變量 const msg = '王二麻子' // 函數(shù) function log() { console.log(msg) } </script> <template> <button @click="log">{{ msg }}</button> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

import 導(dǎo)入的內(nèi)容也會以同樣的方式暴露。這意味著我們可以在模板表達式中直接使用導(dǎo)入的 action 函數(shù),而不需要通過 methods 選項來暴露它:

<script setup> import { say } from './action' </script> <template> <div>{{ say ('hello') }}</div> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(2)響應(yīng)式

響應(yīng)式狀態(tài)需要明確使用響應(yīng)式 API 來創(chuàng)建。和 setup() 函數(shù)的返回值一樣,ref 在模板中使用的時候會自動解包:

<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <button @click="count++">{{ count }}</button> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
(3)使用組件:
  • <script setup> 范圍里的值也能被直接作為自定義組件的標簽名使用:
/**
*這里 MyComponent 應(yīng)當被理解為像是在引用一個變量。
*如果你使用過 JSX,此處的心智模型是類似的。
*其 kebab-case 格式的 <my-component> 同樣能在模板中使用——不過,
*強烈建議使用 PascalCase 格式以保持一致性。同時這也有助于區(qū)分原生的自定義元素。
*/ <script setup> import MyComponent from './MyComponent.vue' </script> <template> <MyComponent /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
動態(tài)組件
/**
*由于組件是通過變量引用而不是基于字符串組件名注冊的,
*在 <script setup> 中要使用動態(tài)組件的時候,應(yīng)該使用*動態(tài)的 :is 來綁定:
*/ <script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' </script> <template> <component :is="Foo" /> <component :is="someCondition ? Foo : Bar" /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
遞歸組件
  • 一個單文件組件可以通過它的文件名被其自己所引用。例如:名為 FooBar.vue 的組件可以在其模板中用 <FooBar/> 引用它自己。
  • 注意這種方式相比于導(dǎo)入的組件優(yōu)先級更低。如果有具名的導(dǎo)入和組件自身推導(dǎo)的名字沖突了,可以為導(dǎo)入的組件添加別名:
import { FooBar as FooBarChild } from './components' 
  • 1
命名空間組件
  • 可以使用帶 . 的組件標簽,例如 <Foo.Bar> 來引用嵌套在對象屬性中的組件。這在需要從單個文件中導(dǎo)入多個組件的時候非常有用:
<script setup> import * as Form from './form-components' </script> <template> <Form.Input> <Form.Label>label</Form.Label> </Form.Input> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
(4)使用自定義指令:
  • 全局注冊的自定義指令將正常工作。本地的自定義指令在 <script setup> 中不需要顯式注冊,但他們必須遵循 vNameOfDirective 這樣的命名規(guī)范:
<script setup> const vMyDirective = { beforeMount: (el) => { // 在元素上做些操作 } } </script> <template> <h1 v-my-directive>This is a Heading</h1> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 如果指令是從別處導(dǎo)入的,可以通過重命名來使其符合命名規(guī)范:
<script setup> import { myDirective as vMyDirective } from './MyDirective.js' </script> 
  • 1
  • 2
  • 3
(5)defineProps() 和 defineEmits():
  • 為了在聲明 props 和 emits 選項時獲得完整的類型推導(dǎo)支持,我們可以使用 defineProps 和 defineEmits API,它們將自動地在 <script setup> 中可用:
<script setup> const props = defineProps({ foo: String }) const emit = defineEmits(['change', 'delete']) // setup 代碼 </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • defineProps 和 defineEmits 都是只能在 <script setup> 中使用的編譯器宏。他們不需要導(dǎo)入,且會隨著 <script setup> 的處理過程一同被編譯掉。
  • defineProps 接收與 props 選項相同的值,defineEmits 接收與 emits 選項相同的值。
  • defineProps 和 defineEmits 在選項傳入后,會提供恰當?shù)念愋屯茖?dǎo)。
  • 傳入到 defineProps 和 defineEmits 的選項會從 setup 中提升到模塊的作用域。因此,傳入的選項不能引用在 setup 作用域中聲明的局部變量。這樣做會引起編譯錯誤。但是,它可以引用導(dǎo)入的綁定,因為它們也在模塊作用域內(nèi)。
(5)defineExpose:
  • 使用 <script setup> 的組件是默認關(guān)閉的——即通過模板引用或者 $parent 鏈獲取到的組件的公開實例,不會暴露任何在 <script setup> 中聲明的綁定。
//可以通過 defineExpose 編譯器宏來顯式指定在 <script setup> 組件中要暴露出去的屬性: <script setup> import { ref } from 'vue' const a = 1 const b = ref(2) defineExpose({ a, b }) </script> //當父組件通過模板引用的方式獲取到當前組件的實例, //獲取到的實例會像這樣 { a: number, b: number } (ref 會和在普通實例中一樣被自動解包) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(6)useSlots() 和 useAttrs():
  • 在 <script setup> 使用 slots 和 attrs 的情況應(yīng)該是相對來說較為罕見的,因為可以在模板中直接通過 $slots 和 $attrs 來訪問它們。在你的確需要使用它們的罕見場景中,可以分別用 useSlots 和 useAttrs 兩個輔助函數(shù):
<script setup> import { useSlots, useAttrs } from 'vue' const slots = useSlots() const attrs = useAttrs() </script> //useSlots 和 useAttrs 是真實的運行時函數(shù),它的返回與 setupContext.slots 和 setupContext.attrs 等價。 //它們同樣也能在普通的組合式 API 中使用。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
(7)與普通的 <script> 一起使用:

<script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有這些需要的情況下或許會被使用到:

  • 聲明無法在
<script> // 普通 <script>, 在模塊作用域下執(zhí)行 (僅一次) runSideEffectOnce() // 聲明額外的選項 export default { inheritAttrs: false, customOptions: {} } </script> <script setup> // 在 setup() 作用域中執(zhí)行 (對每個實例皆如此) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(8)頂層 await:
  • <script setup> 中可以使用頂層 await。結(jié)果代碼會被編譯成 async setup():
<script setup> const post = await fetch(`/api/post/1`).then((r) => r.json()) </script> // 另外,await 的表達式會自動編譯成在 await 之后保留當前組件實例上下文的格式。 
  • 1
  • 2
  • 3
  • 4

2.ref 函數(shù)

  • 作用:定義一個響應(yīng)式的數(shù)據(jù)
  • 語法: const xxx = ref(initValue)
    • 創(chuàng)建一個包含響應(yīng)式數(shù)據(jù)引用對象(reference對象)
    • JS中操作數(shù)據(jù):xxx.value
    • 模板中讀取數(shù)據(jù):不需要.value,直接:
      {{xxx}}
  • 備注:
    • 接收的數(shù)據(jù)可以是:基本類型、也可以是對象類型
    • 基本類型的數(shù)據(jù):響應(yīng)式依然靠的是Object.defineProperty()的get和set完成的
    • 對象類型的數(shù)據(jù): 內(nèi)部”求助“了Vue3.0中的一個新的函數(shù)——reactive函數(shù)

3.reactive 函數(shù)

  • 作用:定義一個對象類型的響應(yīng)式數(shù)據(jù)(基本類型別用他,用ref函數(shù))
  • 語法:const 代理對象 = reactive(被代理對象)接收一個對象(或數(shù)組),返回一個代理對象(proxy對象)
  • reactive定義的響應(yīng)式數(shù)據(jù)是”深層次的“
  • 內(nèi)部基于ES6的Proxy實現(xiàn),通過代理對象操作源對象內(nèi)部數(shù)據(jù)進行操作

4.Vue3.0中響應(yīng)式原理

  • 先來看一看vue2的響應(yīng)式原理
    • 對象類型: 通過Object.defineProperty()對屬性的讀取、修改進行攔截(數(shù)據(jù)劫持)
    • 數(shù)組類型:通過重寫更新數(shù)組的一系列方法來實現(xiàn)攔截。(對數(shù)組的變更方法進行了包裹)
Object.defineProperty( data, 'count', { get(){}, set(){} }) //模擬實現(xiàn)一下 let person = { name: '張三', age: 15, } let p = {} Object.defineProperty( p, 'name', { configurable: true, //配置這個屬性表示可刪除的,否則delete p.name 是刪除不了的 false get(){ //有人讀取name屬性時調(diào)用 return person.name }, set(value){ //有人修改時調(diào)用 person.name = value } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 存在問題:
    1. 新增屬性。刪除屬性。界面不會更新
    2. 直接通過下表修改數(shù)組,界面不會自動更新
  • vue3的響應(yīng)式
    • 實現(xiàn)原理:
      • 通過Proxy(代理):攔截對象中任意屬性的變化,包括:屬性值的讀寫、屬性的添加、屬性的刪除等等。
      • 通過Reflect(反射):對被代理對象的屬性進行操作
      • MDN文檔中描述的Proxy與Reflect:可以參考對應(yīng)的文檔
//模擬vue3中實現(xiàn)響應(yīng)式 let person = { name: '張三', age: 15, } //我們管p叫做代理數(shù)據(jù),管person叫源數(shù)據(jù) const p = new Proxy(person,{ //target代表的是person這個源對象,propName代表讀取或者寫入的屬性名 get(target,propName){ console.log('有人讀取了p上面的propName屬性') return target[propName] }, //不僅僅是修改調(diào)用,增加的時候也會調(diào)用 set(target,propName,value){ console.log(`有人修改了p身上的${propName}屬性,我要去更新界面了`) target[propName] = value }, deleteProperty(target,propName){ console.log(`有人刪除了p身上的${propName}屬性,我要去更新界面了`) return delete target[propName] } }) //映射到person上了,捕捉到修改,那就是響應(yīng)式啊 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//vue3底層源碼不是我們上面寫的那么low,實現(xiàn)原理一樣,但是用了一個新的方式 window.Reflect ![Reflect的寫法](https://img-blog.csdnimg.cn/565f96b1be74435cacbc42e06706791d.png) let obj = { a: 1, b:2, } //傳統(tǒng)的只能通過try catch去捕獲異常,如果使用這種那么底層源碼將會有一堆try catch try{ Object.defineProperty( obj, 'c', { get(){ return 3 }, }) Object.defineProperty( obj, 'c', { get(){ return 4 }, }) } catch(error) { console.log(error) } //新的方式: 通過Reflect反射對象去操作,相對來說要舒服一點,不會要那么多的try catch const x1 = Reflect.defineProperty( obj, 'c', { get(){ return 3 }, }) const x2 = Reflect.defineProperty( obj, 'c', { get(){ return 3 }, }) //x1,和x2是有返回布爾值的 if(x2){ console.log('某某操作成功了') }else { console.log('某某操作失敗了') } 
  • 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
  • 所以vue3最終的響應(yīng)式原理如下:
let person = { name: '張三', age: 15, } //我們管p叫做代理數(shù)據(jù),管person叫源數(shù)據(jù) const p = new Proxy(person,{ //target代表的是person這個源對象,propName代表讀取或者寫入的屬性名 get(target,propName){ console.log('有人讀取了p上面的propName屬性') return Reflect.get(target, propName) }, //不僅僅是修改調(diào)用,增加的時候也會調(diào)用 set(target,propName,value){ console.log(`有人修改了p身上的${propName}屬性,我要去更新界面了`) Reflect.set(target, propName, value) }, deleteProperty(target,propName){ console.log(`有人刪除了p身上的${propName}屬性,我要去更新界面了`) return Reflect.deleteProperty(target,propName) } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.reactive對比ref

  • 從定義數(shù)據(jù)角度對比:

    • ref用來定義: 基本數(shù)據(jù)類型
    • reactive用來定義: 對象(或數(shù)組)類型數(shù)據(jù)
    • 備注: ref也可以用來定義對象(或數(shù)組)類型數(shù)據(jù),它內(nèi)部會自動通過reactive轉(zhuǎn)為代理對象
  • 從原理角度對比:

    • ref通過Object.defineProperty()的get和set來實現(xiàn)響應(yīng)式(數(shù)據(jù)劫持)
    • reactive通過Proxy來實現(xiàn)響應(yīng)式(數(shù)據(jù)劫持),并通過Reflect操作源對象內(nèi)部的數(shù)據(jù)
  • 從使用角度對比:

    • ref定義數(shù)據(jù):操作數(shù)據(jù)需要 .value ,讀取數(shù)據(jù)時模板中直接讀取不需要 .value
    • reactive 定義的數(shù)據(jù): 操作數(shù)據(jù)和讀取數(shù)據(jù)均不需要 .value

5.setup的兩個注意點

  • setup執(zhí)行的時機
    • 在beforeCreate之前執(zhí)行一次,this是undefined
    • setup的參數(shù)
      • props:值為對象,包含: 組件外部傳遞過來,且組件內(nèi)部聲明接收了屬性
      • context:上下文對象
        • attrs: 值為對象,包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性,相當于 this.$attrs
        • slots:收到插槽的內(nèi)容,相當于$slots
        • emit: 分發(fā)自定義事件的函數(shù),相當于this.$emit
//父組件 <script setup> // This starter template is using Vue 3 <script setup> SFCs // Check out https://vuejs.org/api/sfc-script-setup.html#script-setup import HelloWorld from './components/test3.vue'; const hello = (val) =>{ console.log('傳遞的參數(shù)是:'+ val); } </script> <template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="傳遞吧" @hello="hello"> <template v-slot:cacao> <span>是插槽嗎</span> </template> <template v-slot:qwe> <span>meiyou</span> </template> </HelloWorld> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//子組件 export default { name: 'test3', props: ['msg'], emits:['hello'], //這里setup接收兩個參數(shù),一個是props,一個是上下文context setup(props,context){ /**
         * props就是父組件傳來的值,但是他是Porxy類型的對象
         * >Proxy:{msg:'傳遞吧'}
         * 可以當作我們自定義的reactive定義的數(shù)據(jù)
         */ /**
         * context是一個對象 包含以下內(nèi)容:
         * 1.emit觸發(fā)自定義事件的 
         * 2.attrs 相當于vue2里面的 $attrs 包含:組件外部傳遞過來,但沒有在props配置中聲明的屬性
         * 3.slots 相當于vue2里面的 $slots
         * 3.expose 是一個回調(diào)函數(shù)
         */ console.log(context.slots); let person = reactive({ name: '張三', age: 17, }) function changeInfo(){ context.emit('hello', 666) } //返回對象 return { person, changeInfo } //返回渲染函數(shù)(了解) 這個h是個函數(shù) //return () => h('name','age') } } </script> 
  • 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

6.計算屬性與監(jiān)視

(1)computed函數(shù)
  • 與vue2.x中的寫法一致
  • 需要引入computed
<template> <h1>一個人的信息</h1> <div> 姓: <input type="text" v-model="person.firstName"> 名:<input type="text" v-model="person.lastName"> <div> <span>簡名:{{person.smallName}}</span> <br> <span>全名:{{person.fullName}}</span> </div> </div> </template> <script> import { computed,reactive } from 'vue' export default { name: 'test4', props: ['msg'], emits:['hello'], setup(){ let person = reactive({ firstName: '張', lastName: '三' }) //簡寫形式 person.smallName = computed(()=>{ return person.firstName + '-' + person.lastName }) //完全形態(tài) person.fullName = computed({ get(){ console.log('調(diào)用get'); return person.firstName + '*' + person.lastName }, set(value){ console.log('調(diào)用set'); const nameArr = value.split('*') person.firstName = nameArr[0] person.firstName = nameArr[1] }, }) return { person, } }, } </script> 
  • 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
(2)watch函數(shù)
  • 和computed一樣,需要引入api
  • 有兩個小坑:

1.監(jiān)視reactive定義的響應(yīng)式數(shù)據(jù)的時候:oldValue無法獲取到正確的值,強制開啟了深度監(jiān)視(deep配置無效)
2.監(jiān)視reactive定義的響應(yīng)式數(shù)據(jù)中某個屬性的時候:deep配置有效
具體請看下面代碼以及注釋

<template> <h1>當前求和為: {{sum}}</h1> <button @click="sum++">點我+1</button> <hr> <h1>當前信息為: {{msg}}</h1> <button @click="msg+='!' ">修改信息</button> <hr> <h2>姓名: {{person.name}}</h2> <h2>年齡: {{person.age}}</h2> <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增長年齡</button> </template> <script> //使用setup的注意事項 import { watch,ref,reactive } from 'vue' export default { name: 'test5', props: ['msg'], emits:['hello'], setup(){ let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //由于這里的this是指的是undefined,所以使用箭頭函數(shù) //情況一:監(jiān)視ref所定義的一個響應(yīng)式數(shù)據(jù) // watch(sum, (newValue,oldValue)=>{ //     console.log('新的值',newValue); //     console.log('舊的值',oldValue); // }) //情況二:監(jiān)視ref所定義的多個響應(yīng)式數(shù)據(jù) watch([sum,msg], (newValue,oldValue)=>{ console.log('新的值',newValue); //['sum的newValue', 'msg的newValue'] console.log('舊的值',oldValue); //['sum的oldValue', 'msg的oldValue'] },{immediate: true,deep:true}) //這里vue3的deep是有點小問題的,可以不用deep,(隱式強制deep) //情況三:監(jiān)視reactive定義的所有響應(yīng)式數(shù)據(jù), //1.此處無法獲取正確的oldValue(newValue與oldValue是一致值),且目前無法解決 //2.強制開啟了深度監(jiān)視(deep配置無效) /**
            * 受到碼友熱心評論解釋: 此處附上碼友的解釋供大家參考:
            * 1. 當你監(jiān)聽一個響應(yīng)式對象的時候,這里的newVal和oldVal是一樣的,因為他們是同一個對象【引用地址一樣】,
            *    即使里面的屬性值會發(fā)生變化,但主體對象引用地址不變。這不是一個bug。要想不一樣除非這里把對象都換了
            * 
            * 2. 當你監(jiān)聽一個響應(yīng)式對象的時候,vue3會隱式的創(chuàng)建一個深層監(jiān)聽,即對象里只要有變化就會被調(diào)用。
            *    這也解釋了你說的deep配置無效,這里是強制的。
            */ watch(person, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //情況四:監(jiān)視reactive對象中某一個屬性的值, //注意: 這里監(jiān)視某一個屬性的時候可以監(jiān)聽到oldValue watch(()=>person.name, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //情況五:監(jiān)視reactive對象中某一些屬性的值 watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); }) //特殊情況: 監(jiān)視reactive響應(yīng)式數(shù)據(jù)中深層次的對象,此時deep的配置奏效了 watch(()=>person.job, (newValue,oldValue)=>{ console.log('新的值',newValue); console.log('舊的值',oldValue); },{deep:true}) //此時deep有用 return { sum, msg, person, } }, } </script> 
  • 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
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
(3)watchEffect函數(shù)
  • watch的套路是:既要指明監(jiān)視的屬性,也要指明監(jiān)視的回調(diào)
  • watchEffect的套路是:不用指明監(jiān)視哪個屬性,監(jiān)視的回調(diào)中用到哪個屬性,那就監(jiān)視哪個屬性
  • watchEffect有點像computed:
    • 但computed注重的計算出來的值(回調(diào)函數(shù)的返回值),所以必須要寫返回值
    • 而watchEffect更注重的是過程(回調(diào)函數(shù)的函數(shù)體),所以不用寫返回值
<script> //使用setup的注意事項 import { ref,reactive,watchEffect } from 'vue' export default { name: 'test5', props: ['msg'], emits:['hello'], setup(){ let sum = ref(0) let msg = ref('你好啊') let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //用處: 如果是比較復(fù)雜的業(yè)務(wù),發(fā)票報銷等,那就不許需要去監(jiān)聽其他依賴,只要發(fā)生變化,立馬重新回調(diào) //注重邏輯過程,你發(fā)生改變了我就重新執(zhí)行回調(diào),不用就不執(zhí)行,只執(zhí)行一次 watchEffect(()=>{ //這里面你用到了誰就監(jiān)視誰,里面就發(fā)生回調(diào) const x1 = sum.value
                console.log('我調(diào)用了'); }) return { sum, msg, person, } }, } </script> 
  • 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

7.生命周期函數(shù)

 <template> <h1>生命周期</h1> <p>當前求和為: {{sum}}</p> <button @click="sum++">加一</button> </template> <script> //使用setup的注意事項 import { ref,reactive,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from 'vue' export default { name: 'test7', setup(){ let sum = ref(0) //通過組合式API的形式去使用生命周期鉤子 /**
             * beforeCreate 和  created 這兩個生命周期鉤子就相當于 setup 所以,不需要這兩個
             * 
             * beforeMount   ===>  onBeforeMount
             * mounted       ===>  onMounted
             * beforeUpdate  ===>  onBeforeUpdate
             * updated       ===>  onUpdated
             * beforeUnmount ===>  onBeforeUnmount
             * unmounted     ===>  onUnmounted
             */ console.log('---setup---'); onBeforeMount(()=>{ console.log('---onBeforeMount---'); }) onMounted(()=>{ console.log('---onMounted---'); }) onBeforeUpdate(()=>{ console.log('---onBeforeUpdate---'); }) onUpdated(()=>{ console.log('---onUpdated---'); }) onBeforeUnmount(()=>{ console.log('---onBeforeUnmount---'); }) onUnmounted(()=>{ console.log('---onUnmounted---'); }) return { sum } }, //這種是外層的寫法,如果想要使用組合式api的話需要放在setup中 beforeCreate(){ console.log('---beforeCreate---'); }, created(){ console.log('---created---'); }, beforeMount(){ console.log('---beforeMount---'); }, mounted(){ console.log('---mounted---'); }, beforeUpdate(){ console.log('---beforeUpdate---'); }, updated(){ console.log('---updated---'); }, //卸載之前 beforeUnmount(){ console.log('---beforeUnmount---'); }, //卸載之后 unmounted(){ console.log('---unmounted---'); } } </script> 
  • 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
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

8.自定義hook函數(shù)

  • 什么是hook函數(shù): 本質(zhì)是一個函數(shù),把setup函數(shù)中使用的Composition API進行了封裝
  • 類似于vue2.x中的 mixin
  • 自定義hook的優(yōu)勢: 復(fù)用代碼,讓setup中的邏輯更清楚易懂
  • 使用hook實現(xiàn)鼠標打點”:
    創(chuàng)建文件夾和usePoint.js文件
    在這里插入圖片描述
//usePoint.js import {reactive,onMounted,onBeforeUnmount } from 'vue' function savePoint(){ //實現(xiàn)鼠標打點的數(shù)據(jù) let point = reactive({ x: null, y: null }) //實現(xiàn)鼠標點的方法 const savePoint = (e)=>{ point.x = e.pageX
         point.y = e.pageY } //實現(xiàn)鼠標打點的生命周期鉤子 onMounted(()=>{ window.addEventListener('click',savePoint) }) onBeforeUnmount(()=>{ window.removeEventListener('click',savePoint) }) return point } export default savePoint 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//組件test.vue <template> <p>當前求和為: {{sum}} </p> <button @click="sum++">加一</button> <hr> <h2>當前點擊時候的坐標: x: {{point.x}} y:{{point.y}}</h2> </template> <script> import { ref } from 'vue' import usePoint from '../hooks/usePoint' export default { name: 'test8', setup(props,context){ let sum = ref(0) let point = usePoint() return { sum, point } } } </script> 
  • 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

9.toRef

  • 作用: 創(chuàng)建一個ref對象,其value值指向另一個對象中的某個屬性值
  • 語法: const name = toRef(person, ‘name’)
  • 應(yīng)用:要將響應(yīng)式對象中的某個屬性單獨提供給外部使用
  • 擴展: toRefs與toRef功能一致,但是可以批量創(chuàng)建多個ref對象,語法: toRefs(person)
 <template> <h2>姓名: {{name2}}</h2> <h2>年齡: {{person.age}}</h2> <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增長年齡</button> </template> <script> //使用setup的注意事項 import { reactive, toRef, toRefs } from 'vue' export default { name: 'test9', setup(){ let person = reactive({ name: '張三', age: 18, job:{ salary: '15k' }, }) //toRef const name2 = toRef(person,'name') //第一個參數(shù)是對象,第二個參數(shù)是鍵名 console.log('toRef轉(zhuǎn)變的是',name2); //ref定義的對象 //toRefs,批量處理對象的所有屬性 //const x  = toRefs(person) //console.log('toRefs轉(zhuǎn)變的是',x); //是一個對象 return { person, name2, ...toRefs(person) } }, } </script> 
  • 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

三、TypeScript 與組合式 API

1.為組件的 props 標注類型

//場景一: 使用<script setup> <script setup lang="ts"> const props = defineProps({ foo: { type: String, required: true }, bar: Number }) props.foo // string props.bar // number | undefined </script> //也可以將 props 的類型移入一個單獨的接口中 <script setup lang="ts"> interface Props { foo: string
  bar?: number } const props = defineProps<Props>() </script> //場景二: 不使用<script setup> import { defineComponent } from 'vue' export default defineComponent({ props: { message: String }, setup(props) { props.message // <-- 類型:string } }) 
  • 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
  • 注意點:為了生成正確的運行時代碼,傳給 defineProps() 的泛型參數(shù)必須是以下之一:
//1.一個類型字面量: defineProps<{ /*... */ }>() //2.對同一個文件中的一個接口或?qū)ο箢愋妥置媪康囊?/span> interface Props {/* ... */} defineProps<Props>() //3.接口或?qū)ο笞置骖愋涂梢园瑥钠渌募?dǎo)入的類型引用,但是,傳遞給 defineProps 的泛型參數(shù)本身不能是一個導(dǎo)入的類型: import { Props } from './other-file' // 不支持! defineProps<Props>() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • Props 解構(gòu)默認值
//當使用基于類型的聲明時,失去了對 props 定義默認值的能力。通過目前實驗性的響應(yīng)性語法糖來解決: <script setup lang="ts"> interface Props { foo: string
  bar?: number } // 對 defineProps() 的響應(yīng)性解構(gòu) // 默認值會被編譯為等價的運行時選項 const { foo, bar = 100 } = defineProps<Props>() </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.為組件的 emits 標注類型

//場景一: 使用<script setup> <script setup lang="ts"> const emit = defineEmits(['change', 'update']) // 基于類型 const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() </script> //場景二: 不使用<script setup> import { defineComponent } from 'vue' export default defineComponent({ emits: ['change'], setup(props, { emit }) { emit('change') // <-- 類型檢查 / 自動補全 } }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.為 ref() 標注類型

import { ref } from 'vue' import type { Ref } from 'vue' //1.ref 會根據(jù)初始化時的值推導(dǎo)其類型: // 推導(dǎo)出的類型:Ref<number> const year = ref(2020) // => TS Error: Type 'string' is not assignable to type 'number'. year.value = '2020' //2.指定一個更復(fù)雜的類型,可以通過使用 Ref 這個類型: const year: Ref<string | number> = ref('2020') year.value = 2020 // 成功! //3.在調(diào)用 ref() 時傳入一個泛型參數(shù),來覆蓋默認的推導(dǎo)行為: // 得到的類型:Ref<string | number> const year = ref<string | number>('2020') year.value = 2020 // 成功! //4.如果你指定了一個泛型參數(shù)但沒有給出初始值,那么最后得到的就將是一個包含 undefined 的聯(lián)合類型: // 推導(dǎo)得到的類型:Ref<number | undefined> const n = ref<number>() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.為reactive() 標注類型

import { reactive } from 'vue' //1.reactive() 也會隱式地從它的參數(shù)中推導(dǎo)類型: // 推導(dǎo)得到的類型:{ title: string } const book = reactive({ title: 'Vue 3 指引' }) //2.要顯式地標注一個 reactive 變量的類型,我們可以使用接口: interface Book { title: string
  year?: number } const book: Book = reactive({ title: 'Vue 3 指引' }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.為 computed() 標注類型

import { ref, computed } from 'vue' //1.computed() 會自動從其計算函數(shù)的返回值上推導(dǎo)出類型: const count = ref(0) // 推導(dǎo)得到的類型:ComputedRef<number> const double = computed(() => count.value * 2) // => TS Error: Property 'split' does not exist on type 'number' const result = double.value.split('') //2.通過泛型參數(shù)顯式指定類型: const double = computed<number>(() => { // 若返回值不是 number 類型則會報錯 }) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6.為事件處理函數(shù)標注類型

//在處理原生 DOM 事件時,應(yīng)該為我們傳遞給事件處理函數(shù)的參數(shù)正確地標注類型 <script setup lang="ts"> function handleChange(event) { // 沒有類型標注時 `event` 隱式地標注為 `any` 類型, // 這也會在 tsconfig.json 中配置了 "strict": true 或 "noImplicitAny": true 時報出一個 TS 錯誤。 console.log(event.target.value) } </script> <template> <input type="text" @change="handleChange" /> </template> //因此,建議顯式地為事件處理函數(shù)的參數(shù)標注類型,需要顯式地強制轉(zhuǎn)換 event 上的屬性: function handleChange(event: Event) { console.log((event.target as HTMLInputElement).value) } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

7.為 provide / inject 標注類型

/*
provide 和 inject 通常會在不同的組件中運行。要正確地為注入的值標記類型,
Vue 提供了一個 InjectionKey 接口,它是一個繼承自 Symbol 的泛型類型,
可以用來在提供者和消費者之間同步注入值的類型:
*/ import { provide, inject } from 'vue' import type { InjectionKey } from 'vue' const key = Symbol() as InjectionKey<string> provide(key, 'foo') // 若提供的是非字符串值會導(dǎo)致錯誤 const foo = inject(key) // foo 的類型:string | undefined //建議將注入 key 的類型放在一個單獨的文件中,這樣它就可以被多個組件導(dǎo)入。 //當使用字符串注入 key 時,注入值的類型是 unknown,需要通過泛型參數(shù)顯式聲明: const foo = inject<string>('foo') // 類型:string | undefined //注意注入的值仍然可以是 undefined,因為無法保證提供者一定會在運行時 provide 這個值。 //當提供了一個默認值后,這個 undefined 類型就可以被移除: const foo = inject<string>('foo', 'bar') // 類型:string //如果你確定該值將始終被提供,則還可以強制轉(zhuǎn)換該值: const foo = inject('foo') as string 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

8.為模板引用標注類型

//模板引用需要通過一個顯式指定的泛型參數(shù)和一個初始值 null 來創(chuàng)建: <script setup lang="ts"> import { ref, onMounted } from 'vue' const el = ref<HTMLInputElement | null>(null) onMounted(() => { el.value?.focus() }) </script> /**
    注意為了嚴格的類型安全,有必要在訪問 el.value 時使用可選鏈或類型守衛(wèi)。這是因為直到組件被掛載前,
    這個 ref 的值都是初始的 null,并且在由于 v-if 的行為將引用的元素卸載時也可以被設(shè)置為 null。
*/ <template> <input ref="el" /> </template> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

9.為組件模板引用標注類型

//有時,你可能需要為一個子組件添加一個模板引用,以便調(diào)用它公開的方法。舉例來說,我們有一個 MyModal 子組件,它有一個打開模態(tài)框的方法 <!-- MyModal.vue --> <script setup lang="ts"> import { ref } from 'vue' const isContentShown = ref(false) const open = () => (isContentShown.value = true) defineExpose({ open }) </script> //為了獲取 MyModal 的類型,我們首先需要通過 typeof 得到其類型,再使用 TypeScript 內(nèi)置的 InstanceType 工具類型來獲取其實例類型: <!-- App.vue --> <script setup lang="ts"> import MyModal from './MyModal.vue' const modal = ref<InstanceType<typeof MyModal> | null>(null) const openModal = () => { modal.value?.open() } </script> //注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用這種技巧,需要開啟 Volar 的Takeover 模式。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

四、Vuex與組合式API

  • 組合式API 可以通過調(diào)用 useStore 函數(shù),來在 setup 鉤子函數(shù)中訪問 store。這與在組件中使用選項式 API 訪問 this.$store 是等效的。
import { useStore } from 'vuex' export default { setup () { const store = useStore() } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.訪問 state 和 getter

  • 為了訪問 state 和 getter,需要創(chuàng)建 computed 引用以保留響應(yīng)性,這與在選項式 API 中創(chuàng)建計算屬性等效。
import { computed } from 'vue' import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 在 computed 函數(shù)中訪問 state count: computed(() => store.state.count), // 在 computed 函數(shù)中訪問 getter double: computed(() => store.getters.double) } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.訪問 Mutation 和 Action

  • 要使用 mutation 和 action 時,只需要在 setup 鉤子函數(shù)中調(diào)用 commit 和 dispatch 函數(shù)。
import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 使用 mutation increment: () => store.commit('increment'), // 使用 action asyncIncrement: () => store.dispatch('asyncIncrement') } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

藍藍設(shè)計建立了UI設(shè)計分享群,每天會分享國內(nèi)外的一些優(yōu)秀設(shè)計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務(wù)合作,也請與我們聯(lián)系01063334945。


分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設(shè)計尊重原作者,文章的版權(quán)歸原作者。如涉及版權(quán)問題,請及時與我們?nèi)〉寐?lián)系,我們立即更正或刪除。


藍藍設(shè)計www.yvirxh.cn )是一家專注而深入的界面設(shè)計公司,為期望卓越的國內(nèi)外企業(yè)提供卓越的UI界面設(shè)計、BS界面設(shè)計 、 cs界面設(shè)計 、 ipad界面設(shè)計 、 包裝設(shè)計 、 圖標定制 、 用戶體驗 、交互設(shè)計、 網(wǎng)站建設(shè) 、平面設(shè)計服務(wù)UI設(shè)計公司、界面設(shè)計公司、UI設(shè)計服務(wù)公司、數(shù)據(jù)可視化設(shè)計公司、UI交互設(shè)計公司、高端網(wǎng)站設(shè)計公司、UI咨詢、用戶體驗公司、軟件界面設(shè)計公司

日歷

鏈接

個人資料

藍藍設(shè)計的小編 http://www.yvirxh.cn

存檔