天天看點

vue computed使用_vue 随記(3):“新時代”的姿勢

vue computed使用_vue 随記(3):“新時代”的姿勢

體驗vue3

對比2.x舊版本,vue3新增了什麼:

•性能上:最多比vue2 快2倍•靜态标記提升•proxy取代defineProperty•tree shaking:按需編譯打包代碼•composition api :類似hook的編碼風格•支援typescript:面向未來目前的代碼 98% 以上使用 TypeScript 編寫。如果你還沒有學習 TypeScript,請盡快學習,否則可能看不懂源碼。另外有件事情說出來可能會讓你非常驚訝,Vue 3 的源代碼完全沒有使用 class 關鍵字!(隻在測試代碼和示例代碼裡用到了 class 關鍵字)•custom renderer api:自定義渲染

1. 體驗姿勢

現在有三種姿勢體驗vue3。

1.1 腳手架工具

官方制定cli工具——更新最新版本。

npm install -g @vue/clivue create 01-vue3-clicd 01-vue3-clivue add vue-nextnpm run serve
           

bingo。

1.2 webpack

vue-cli一開始還沒支援的時候,vue官網整了一個webpack的項目配置,直接clone即可

git clone https://github.com/vuejs/vue-next-webpack-preview.git 01-vue3-webpackcd 01-vue3-webpacknpm install npm run dev
           

1.3 代碼倉庫

可在此處克隆最新的倉庫代碼:https://github.com/vuejs/vue-next.git,下載下傳下來之後運作dev指令打包:

npm run dev
           

在example中建立一個vue3.html,直接引入

vue.global.js

>   charset="UTF-8">   name="viewport" content="width=device-width, initial-scale=1.0">  vue3  
           

id="app">

// ...

1.4 vite(劃重點)

這是作者尤雨溪開發的新工具,目的是以後取代webpack,原理就是利用浏覽器現在已經支援es6的import,碰見import會發送一個http請求去加載檔案,vite攔截這些請求,做一些預編譯,就省去了webpack冗長的打包時間,進而提升開發體驗。(需要node 10.15以上版本)

npm install -g create-vite-appcreate-vite-app 01-vue3-vitecd 01-vue3-vitenpm installnpm run dev
           

打開http://localhost:3000,裡面內建了一個vue 3小demo(沒錯又是計數器)

vue computed使用_vue 随記(3):“新時代”的姿勢

看下network,大概就知道vite的工作原理:以http請求的形式加載子產品,這也是為什麼它能做到複雜項目的秒開,天生的按需加載。

vue computed使用_vue 随記(3):“新時代”的姿勢

2. Options API 和 composition API

補白:模闆fragement

其實fragement就是以後vue元件不需要一個根節點了,現在可以這麼寫template

哈喽

我真棒

用vue2的思路來寫計數器,代碼應該是這樣的:

export default {  name: 'HelloWorld',  props: {    msg: String  },  data() {    return {      count: 0    }  },  methods:{    click(){      this.count += 1;    }  }}
           

但是,在vue3的時代,變了。修改app.vue,用vue的新文法改寫下計數器:

vue computed使用_vue 随記(3):“新時代”的姿勢

alt="Vue logo" src="./assets/logo.png" />

Hello Vue 3.0 + Vite

@click="click">count is: {{ state.count }}

double is

{{ state.double }}code>

import { reactive, watchEffect, computed } from "vue";export default { name: "App", setup() { // 定義響應式資料層,包括計算屬性 const state = reactive({ count: 0, double: computed(() => state.count * 2), }); // 資料變化的副作用 watchEffect(() => { console.log(`資料變更為${state.count}`); }); const click = () => { state.count += 1; }; return { state, click, }; },};

有點像react hooks'的風格了。你每點選一次按鈕,都會觸發一次計數,并通過計算屬性double傳回視圖層“雙倍”資訊。并且通過

watchEffect

,當資料變更時,在控制台列印出變更提示。

setup

: 它隻是一個函數,它将屬性和函數傳回到模闆。可在此聲明所有的響應式屬性、計算屬性、觀察者和生命周期鈎子,然後傳回它們,以便它們可以在模闆中使用。沒有在setup函數傳回的内容将在模闆中不可用。•

reactive

:響應式,用的

Proxy

的getter和setter,取代

Object.defineProperty

。幾乎等價于 2.x 中現有的 

Vue.observable()

 API。這裡傳回的 

state

 是一個所有 Vue 使用者都應該熟悉的響應式對象。•

computed

:按需引入 tree-shaking生效->如果這裡沒有用到computed,vue3就會把它從打包中删掉。•

useEffect

:屬性變更監聽。

watchEffect

 應該接收一個應用預期副作用 (這裡即設定 

innerHTML

) 的函數。它會立即執行該函數,并将該執行過程中用到的所有響應式狀态的 property 作為依賴進行追蹤。•這裡的 

state.count

 會在首次執行後作為依賴被追蹤。當 

state.count

 未來發生變更時,裡面這個函數又會被重新執行。

這正是 Vue 響應式系統的精髓所在了!當你在元件中從 

data()

 傳回一個對象,内部實質上通過調用 

reactive()

 使其變為響應式。而模闆會被編譯為渲染函數 ,因而可以使用這些響應式的 property。

實際上通過

ref

還可以再極簡風一些:

import { ref, computed } from "vue";export default {  setup() {    // 初始化count為0    let count = ref(0);    const click = () => {      count.value += 1;    };    const double = computed(() => count.value * 2);    return {      count,      double,      click,    };  },};
           
關于 reactive 和 ref 的差別,目前可以這麼了解:是把一個對象變為響應式,後者是把單獨的一個值變為響應式。

類似第一種寫法,被稱為Options API(配置式API)。而第2/3種寫法,被稱為Compositon API(組合式API)。你甚至可以同時混用它們——雖然作者承諾,options API現在以後都不會廢棄。但是組合式api看起來更像是一種更面向未來的vue文法。

組合式API是一組低侵入式的、函數式的 API,使得我們能夠更靈活地「組合」元件的邏輯。

——https://composition-api.vuejs.org/zh/

主要的改變在于:options API中諸如

data

/

conputed

/

created

等,到了都需要獨立從vue對象中引入。能夠很好的支援按需引入(tree shaking )。

在options API中,為了将邏輯添加到Vue元件中,我們一個個填充(options)屬性,如data、methods、computed等。這種方法最大的缺點是,它本身不是一個工作的JavaScript代碼。你需要确切地知道模闆中可以通路哪些屬性以及this關鍵字的行為——當項目需求變得越發複雜時,你就會在method,data,computed以及watch中“反複橫跳”。在底層,Vue編譯器需要将此屬性轉換為工作代碼。正因為如此,我們無法從自動建議或類型檢查中獲益。(如下為一段OptionsAPI代碼,新增/删除一處功能要改三四個地方,非常痛苦)

vue computed使用_vue 随記(3):“新時代”的姿勢

作為options api的替代方案,Composition API希望将通過目前元件屬性、可用的機制公開為JavaScript函數來解決這個問題。Vue核心團隊将元件Composition API描述為“一套附加的、基于函數的api,允許靈活地組合元件邏輯”。使用Composition API編寫的代碼更易讀,并且場景不複雜,這使得閱讀和學習變得更容易。(如圖,setup中的每個資料,監聽方法都被抽離出來了)

vue computed使用_vue 随記(3):“新時代”的姿勢

舉個例子,我可以把所有内容封裝為一個

useCount

函數:

import { ref, computed } from "vue";/*** initValue 初始值*/const useCount = (initValue) => {  // 初始化count為0  let count = ref(initValue);  const click = () => {    count.value += 1;  };  const double = computed(() => count.value * 2);  return {    count,    double,    click,  };};export default {  setup() {    const { count, double, click } = useCount(0);    return { count, double, click };  },};
           

完成了一個最簡單的邏輯複用。useCount可以放到單獨的檔案進行管理。你還可以給

useCount

增加更多的邏輯:

import { ref, computed, onMounted, watchEffect } from "vue";const useCount = (initValue) => {  // 初始化count為0  let count = ref(initValue);  const click = () => {    count.value += 1;  };  const double = computed(() => count.value * 2);  onMounted(() => {    console.log("視圖層渲染完畢");  });  watchEffect(() => {    console.log(`count内容被變更為${count.value}`);  });  return {    count,    double,    click,  };};
           

這也許是目前最好的邏輯複用解決方案(之前是根據mixin來做的)。

3. todoMVC執行個體

現在就用composition API寫一個todoMVC。

3.1 原始需求

需求:寫一個todoList ,實作增删查。

上面有輸入框,下面有todos。可删除。

根據前面的示例可以很快寫出

todoMVC

type="text" v-model="state.addData" />

@click="add(state.addData)">add

v-for="todo in state.list" v-bind:key="todo.id">

{{ todo.name }}

>  

>deletea

>

import { ref, reactive, computed, onMounted, watchEffect } from "vue";export default { setup() { const state = reactive({ list: [ { id: 1, name: "aaa" }, { id: 2, name: "bbb" }, { id: 3, name: "ccc" }, ], addData: "", }); // 新增 const add = (value) => { const newData = { id: new Date().getTime(), name: value, }; state.list.push(newData); state.addData = ""; }; // 删除 const del = (id) => { let _i = ""; for (let i = 0; i < state.list.length; i++) { if (state.list[i].id == id) { _i = i; } } state.list.splice(_i, 1); }; return { state, add, del }; },};

3.2 加滾動需求

清單長度過長,滾動的時候輸入框吸頂 ,這是一個比較常見的操作,如果options api 我們就用有兩個解決方案

1.data裡加個變量判斷滾動,mounted和onUnmounted修改top,2.上述功能用mixin導入,缺點是來源不清晰,容易有重名bug

現在的思路是:

通過

useScroll

,定義top及其更新邏輯。當top小于60時,加上固定定位的class

fixed

:class="{ fiexed: top > 60 }">

todoMVC

type="text" v-model="state.addData" />

@click="add(state.addData)">add

v-for="todo in state.list" v-bind:key="todo.id">

{{ todo.name }}

>  

>deletea

>

import { ref, reactive, computed, onMounted, onUnmounted, watchEffect,} from "vue";// 新增滾動const useScroll = (initVal) => { let top = ref(initVal); const update = () => { top.value = window.scrollY; }; onMounted(() => { window.addEventListener("scroll", update); }); onUnmounted(() => { window.removeEventListener("scroll", update); });return { top, };};export default { setup() { const state = reactive({ list: [ { id: 1, name: "aaa" }, { id: 2, name: "bbb" }, { id: 3, name: "ccc" }, { id: 4, name: "aaa" }, { id: 5, name: "bbb" }, { id: 6, name: "ccc" }, { id: 7, name: "aaa" }, { id: 8, name: "bbb" }, { id: 9, name: "ccc" }, ], addData: "", }); // 新增 const add = (value) => { const newData = { id: new Date().getTime(), name: value, }; state.list.push(newData); state.addData = ""; }; // 删除 const del = (id) => { let _i = ""; for (let i = 0; i < state.list.length; i++) { if (state.list[i].id == id) { _i = i; } } state.list.splice(_i, 1); }; const { top } = useScroll(0); return { state, add, del, top }; },};scope>

li {

height: 200px;

}

.fiexed {

position: fixed;

}

看到這個useScroll,完全可以從另外一個檔案引入。管理起來相當輕松。

vue computed使用_vue 随記(3):“新時代”的姿勢