Vue3 學習筆記 —— (一)深入理解組合式 API

目錄


image.png

[toc](Vue3 學習筆記梳理)
Author:Gorit
Date:2021/4/24
Refer:網易云課堂
2021年發表博文: 17/50

Vue3 學習

學習文檔
如果是第一次接觸 Vue3,可以看這個 Vue3 初體驗

零、Vue3.0 與 Vue2.x 的性能對比

  1. 框架內部做了大量的性能優化,包括虛擬 DOM,編譯模板、Proxy 的新數據監聽,更小的打包文件等
  2. 新的組合式 API (composition-api),更適合大型項目的編寫方式
  3. 對 TypeScript 支持更好,去除繁瑣的 this 操作,更強大的類型推導

一、搭建環境

  1. node 8.9.0 以上
  2. 安裝好 npm
  3. 安裝 vue
  4. 安裝 Vue Cli4 腳手架

二、創建項目

  1. vue create vue3-demo
image
  1. 其他的選擇默認配置即可
  2. 安裝 yarn :cnpm i yarn -g (提升安裝速度)
  3. 測試:yarn --versin

項目結構介紹

  • package.json 項目全局管理
  • node_modules 項目依賴包,占用大量空間
  • public 入口文件
  • src:main.js 為入口文件,項目代碼在這里編寫

三、Vue3 Composition API

Vue3 是向下兼容 Vue2 API 的,但是 Vue3 中提供了一種全新的 <font color="red">Composition API</font>

3.1 ref() or setup() ? reactive()

setup() 作為 Vue3.0 的入口函數

reactive() 作聲明式渲染,用來響應數據

ref() 顯示響應式數據,配合 reactve()

3.1.1 非響應式數據顯示 (reactive)

直接返回數據

<template>
    <div>直接返回 state【非響應式的數據】:{{count}}</div>
</template>

<script>
    // 如果是 Vue2 項目,想要用 Vue3 的語法,需要安裝 @vue/composition-api   
    // import { xxx } from '@vue/composition-api'
    import {
        reactive
    } from 'vue'
    export default {
        name: 'App',
        // Vue3.0 的入口函數,編寫 Vue3 代碼,beforeCreate之前進行觸發, 需要 return
        setup() { 
             const state = reactive({
                count: 0
             });
            // 不具備數據響應式的效果,1s 后數據沒有任何變化
             setTimeout(()=> {
                state.count++;
             },1000)
             return state;
        }
    }
</script>

3.1.2 響應式數據顯示 (reactive)

通過對象的形式

<template>
    <div>返回 state 對象 【響應式數據】{{state.count}}</div>
</template>

<script>
    import {
        reactive
    } from 'vue'
    export default {
        name: 'App',
        // Vue3.0 的入口函數,編寫 Vue3 代碼,beforeCreate之前進行觸發, 需要 return
        setup() { 
            // 具備數據響應式, 返回對象
            const state = reactive({
                count: 0
            });
            setTimeout(()=> {
                state.count++;
            },1000)
            return { state };
            
            // 對返回的結果 解構,這樣數據就可以只顯示 {{ count }} 就可以了
             // return {
            //  count: state.count
            //}
        }
    }
</script>

3.1.3 響應式數據展示(整合 ref() )

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>ref 實現響應式對象 {{ count }}</div>
</template>

<script>
    import {
        ref
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // 使用 ref() 實現響應式數據
            const count = ref(0); // 這樣 count 就實現了響應式的效果
            setInterval(()=> {
                count.value++;
            },1000)
            return {
                count
            };
        }
    }
</script>

因此,我們既要響應式,又要展示數據,折中的方案是直接使用 ref

3.1.4 小案例:實現一個計數器

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div class="my-app">
        <!-- Vue3 無法直接拿到值 -->
        <h3>{{counter}}</h3>
        <button @click="increment(1)">increment</button>
        <button @click="decrement(1)">decrement</button>
    </div>
</template>

<script>
    import { ref } from 'vue'
    /**
     * Options API Vue2 Class
     * Composition API Vue3 Function
     */
    export default {
        name: 'App',
        setup() {
            const counter = ref(1000)
            // console.log(counter.value)
            const increment = () => {
                counter.value += 1;
            }
            
            const decrement = () => {
                counter.value -= 1;
            }
            // 必須 return,外部才能拿到值
            return { counter, increment,decrement }
        }
    }
</script>

3.2 toRefs() ? toRef()

在上面的代碼中,我們使用 ref() 和 reactive() 分別可以實現響應式的數據,我們是否可以兩者一起使用呢?

3.2.1 ref() 和 reactive() 連用

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>ref() 和 reactive() 實現響應式對象 {{ count }}</div>
</template>

<script>
    // 如果是 Vue2 項目,想要用 Vue3 的語法,需要安裝 @vue/composition-api   
    // import { xxx } from '@vue/composition-api'
    import {
        ref,
        reactive
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // ---------------------------- ref 和 reactive 連用
            const count = ref(0);
            const state = reactive({
                count
            })
            setInterval(()=> {
                state.count++;
            },1000)
            return {
                count
            };
        }
    }
</script>

3.2.2 使用 toRefs() 和 reactive()

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>實現響應式對象 {{ count }}</div>
</template>

<script>
    import {
        reactive,
        toRefs
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // ---------------------------- //使用 toRefs() 將 reactive 轉換為 Ref
            const state = reactive({
                count: 0
            });
            
            const { count } = toRefs(state); // toRefs() 作用:將普通類型數據,轉換為 Ref 響應式數據
            setInterval(()=> {
                state.count++;
            },1000)
            return {
                count
            };
        }
    }
</script>

3.2.3 使用 toRef() 和 reactive()

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>實現響應式對象 {{ count }}</div>
</template>

<script>
    import {
        reactive,
        toRefs
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // ---------------------------- //使用 toRefs() 將 reactive 轉換為 Ref
            const state = reactive({
                count: 0
            });
            
            const  count  = toRef(state, 'count'); // toRef() 作用:將普通類型數據,轉換為 Ref 響應式數據,指定單個數據轉換
            setInterval(()=> {
                state.count++;
            },1000)
            return {
                count
            };
            // toRefs === {}
        }
    }
</script>

ref 和 reactive 分別是兩種響應式數據的變量風格,具體看個人情況使用

3.3 computed 計算屬性

3.3.1 配合 ref() 使用 實現響應式

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
        {{count}} , {{double}}
    </div>
</template>

<script>
    import {
        ref,
        computed
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // 使用計算屬性
            const count = ref(1)
            const double = computed(()=>count.value * 2)
            setTimeout(()=>{
                count.value++
            },1000)
            return {
                count,double
            }
        }
    }
</script>

3.3.2 配合 toRefs() 和 reactive() 實現響應式

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
        {{count}} , {{double}}
    </div>
</template>

<script>
    import {
        reactive,
         toRefs,
        computed
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            const state = reactive({
                count: 1,
                double: computed(()=>state.count * 2)
            })
            setTimeout(()=>{
                state.count++
            },1000)
            return toRefs(state)
        }
    }
</script>

3.4 定義函數

3.4.1 函數與 watch

<template>
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
        事件:{{count}}
    </div>
    <button @click="add">添加</button>
</template>

<script>
    import {
        ref,
         watch
    } from 'vue'
    export default {
        name: 'App',
        setup() { 
            // =============== 事件
            const count = ref(1)
            const add = () => { // 事件方法
                count.value++;
            }
            // 配合偵聽器
            watch(count ,(count, prevCount) => {
                console.log(count, prevCount)
            })
            return {count, add}
        }
    }
</script>

3.4.2 Vue3.0 函數與生命周期函數

文檔

新的生命周期函數鉤子要在 setup() 中使用

  1. onMounted
  2. onUpdated
  3. onUnmounted
export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}

四、Vue3 組件化(拆分+傳值+注冊)

這里主要是回顧 組件化編程

拆分的方式同 Vue2,注冊 + 引入
組件拆分的案例我們沿用上面的計數器來實現 (參考 3.1.4 小節的內容)

4.1 組件拆分

預覽效果:


在這里插入圖片描述

編碼如下:

  1. 定義一個新的 vue 文件,命名為 CounterView.vue 文件
  2. 使用 props 接受父組件 App.vue 穿過來的值
<template>
  <div>
      我是子組件 CounterView
      {{counter}}
  </div>
</template>

<script>
export default {
    name: 'CounterView',
    // props: ['counter']  vue2 一般都是這么寫的
    // 下面的這種寫法會更加規范
    props: {
        counter: {
            type: Number,
            required: true,
            default: 500
        }
    }
}
</script>
  1. 修改父組件 App.vue,改為引用 CounterView.vue ,并注冊為組件
<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png" />  
    <!-- 屬性綁定 -->
    <counter-view :counter="counter"/>
    <button @click="increament(1)">增加</button>
    <button @click="increament(-1)">減少</button>
  </div>
</template>

<script>
import { ref } from "vue";
import CounterView from '@/components/CounterView.vue'
export default {
  name: "App",
  components: {
    CounterView
  },
  setup() {
    const counter = ref(1000);
    console.log(counter.value);
    // 定義函數
    const increament = (num) => {
      counter.value += num;
    };
    return { counter,increament };
  },
};
</script>

<style>
</style>

4.2 事件拆分

這里我們在上面的基礎上,將 setup() 中定義的事件,拆分至另一個新的 vue 文件
首先我們需要補充一些前置概念:

  1. 在 setup() 中是沒有 this 關鍵字的
  2. setup() 是可以接受兩個參數的 (props, context),然后我們打印接受到的值如下


    在這里插入圖片描述
  3. cotext 中,可以看到 emit 關鍵字,是不是很熟悉,vue2 中我們要子組件傳事件給父組件,用的是 this.$emit("事件名稱", '值"), 在 Vue3 中也會用到類似的,后面會有具體的演示
  4. 編碼如下:

在子組件完成事件注冊

<template>
  <div>
    <button @click="increament(1)">增加</button>
    <button @click="increament(-1)">減少</button>
  </div>
</template>

<script>
export default {
    name: 'CounterController',
    setup(props,ctx) { 
       console.log(props, ctx)      // 這里打印的就是我們剛剛上面看到的
       const increament = (num) => {
            // ctx.emit 等價于 vue2 中 this.$emit("xxx",xxx)。 在 vue3 中 setup() 函數是沒有 this 的概念的
            ctx.emit("onIncreament",num) // 完成事件注冊,將操作的邏輯交給父組件來完成
        }
        return {increament}
    },
}
</script>

在父組件完成事件調用

<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png" />  
    <!-- 屬性綁定 -->
    <counter-view :counter="counter"/>
    <!-- <button @click="increament(1)">增加</button>
    <button @click="increament(-1)">減少</button> -->
    <!-- 子組件事件注冊完畢后,交給父組件進行觸發, 處理的函數需要傳 $event 就可以實現和上面一樣的效果了 -->
    <counter-controller @onIncreament="increament($event)" />
  </div>
</template>

<script>
import { ref } from "vue";
import CounterView from '@/components/CounterView.vue'
import CounterController from '@/components/CounterController.vue'
export default {
  name: "App",
  components: {
    CounterView,
    CounterController
  },
  setup() {
    const counter = ref(1000);
    // 定義函數
    const increament = (num) => {
      counter.value += num;
    };
    return { counter,increament };
  },
};
</script>

五、總結

我們來回顧一下所學內容

  1. setup(props, context), setup() 是代碼的入口
  2. 響應式 API:reactive() 、ref()、toRef()、toRefs()
  3. 計算屬性 computed 和 watch 偵聽器的使用
  4. 組件化思想
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容