Vue3實戰系列:結合 Ant-Design-of-Vue 實踐 Composition API

我是13發表於2020-10-12

Vue 3 出來之後,很多人有如下想法,“又要學新東西啦”、“我學不動了”等等。

但是事物總有它的兩面性,前端知識更新的很快,利好勤奮好學的同學。計算機行業的迭代速度很快,前端在計算機領域裡,算是前浪被拍在沙灘上比較快的。

我很羨慕還在學校的同學,因為現在的大學生,資訊的獲取量比我當年在大學的時候大得多,我希望你們能珍惜這樣的機會,好好的學習新知識,希望你們進入社會不要被毒打。

學習一門新的技術,我認為最好的方式就是通過 Demo 去製造使用場景,通覽一遍 API,需要做到什麼程度呢?

大概就是什麼場景用什麼 API 都能瞭如於心,再大白話一點就是可以將之前用 Vue2.x 寫的程式碼,用 Vue 3 無差別重構一遍。

構建 Vue3.0 的三種方式

就目前而言,構建 Vue 3 的專案有三種方式。

1、命令列工具 (CLI)

對於 Vue 3,你應該使用 npm 上可用的 Vue CLI v4.5 作為 @vue/cli@next。要升級,你應該需要全域性重新安裝最新版本的 @vue/cli

yarn global add @vue/cli@next
# OR
npm install -g @vue/cli@next

2、Vite

Vite 是一個 web 開發構建工具,由於其原生 ES 模組匯入方法,它允許快速提供程式碼。

通過在終端中執行以下命令,可以使用 Vite 快速設定 Vue 專案。

使用 npm:

npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

3、Webpack

很早的時候,Vue-CLI 還沒支援 Vue 3 專案的建立,Vue 團隊製作了一個 Webpack 的專案配置放在 Github,可以直接克隆下來使用。

git clone https://github.com/vuejs/vue-next-webpack-preview.git demov3
cd demov3
npm install
npm run dev

不推薦使用這種方式,現在都有上面兩種了~~

(以前看星星看月亮的時候叫人家“小甜甜”,現在新人勝舊人,叫人家“牛夫人”)

程式碼實踐

我們選擇比較新鮮的 Vite 去構建專案,要玩就玩最潮的。我的本地 Node 版本是 v12.6.0,儘量保持一致。

構建專案

npm init vite-app todo-v3
cd todo-v3
npm install // 建議使用淘寶映象 cnpm install
npm run dev

啟動之後如下所示,代表成功了:

入口頁面

首先映入眼簾的是 main.js 的變化:

// Vue 3.0
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')
// Vue 2.x
import Vue from 'vue'
import App from './App.vue'

new Vue({
  render: h => h(App)
}).$mount('#app')

第一段程式碼是 Vue 3 的建立 Vue 例項形式,通過 createApp 的形式,你別說,和 React 真的挺像的?。

第二段是 Vue 2.x 的建立 Vue 例項形式,通過 new 的形式建立。

新增路由 Vue-Router

截止目前,vue-router-next 更新到了 v4.0.0-beta.12 版本。

你如果用 cnpm install vue-router 安裝路由,是會下載到 vue-router 3.x 的版本,我們需要使用:

cnpm install vue-router@next -S

安裝完畢之後,我們開始配置專案路由,在 src 目錄下新建 rourer 資料夾,在資料夾下新建 index.js 檔案,新增如下內容:

import {createRouter, createWebHashHistory} from 'vue-router'

export default createRouter({
  history: createWebHashHistory(),
  routes: []
})

Vue 2.x 的路由模式通過 mode 選項為 history 或 hash 去控制。

而在 Vue 3 中,通過 createRouter 建立路由例項,history 屬性作為控制路由模式的引數,createWebHashHistory 方法返回的是 hash 模式,createWebHistory 返回的是 history 模式,本專案採用 hash 模式。

同樣,我們需要在 mian.js 中引入 router 例項:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'

createApp(App).use(router).mount('#app')

新增全域性狀態 Vuex

vuex 更新到了 v4.0.0-beta.4 版本,所以我們需要用如下指令安裝:

cnpm i vuex@next -S

接下來在 src 目錄下建立 store 資料夾,再新建 index.js 檔案,新增程式碼如下:

// Vue 3
import { createStore } from 'vuex'

export default createStore({
  state() {
    return {
      author: "十三",
    };
  },
});

對比 Vue 2.x 寫法:

// Vue 2.x
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  modules: {}
})

同樣是使用新的寫法,通過 vuex 內部丟擲的 createStore 方法,建立一個 Vuex 例項。

接下來我們將它引入到 main.js 中:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './index.css'

// 鏈式呼叫
createApp(App).use(router).use(store).mount('#app')

引入 Antd for Vue3 版本元件庫

Antdv2.x是唐金州老師(杭州校寶線上)研發的新一代適配 Vue 3.0 的元件庫,我們來嚐嚐鮮,這邊我們通過如下命令後下載:

cnpm i --save ant-design-vue@next -S

mian.js 內引入 ant-design-vue 元件如下所示:

import { createApp } from 'vue'
import Antd from 'ant-design-vue';
import App from './App.vue'
import router from './router'
import store from './store'
import 'ant-design-vue/dist/antd.css';
import './index.css'

// 本教程採用的是全域性引入元件庫

createApp(App).use(router).use(store).use(Antd).mount('#app')

測試一下是否成功,順便解釋一下 Vue 3 如何宣告變數,以及如何通過方法改變變數,程式碼如下:

<template>
  <a-button @click="add" type="primary">
    點我加{{ count }}
  </a-button>
  <a-button @click="add2('a')" type="primary">
    點我加a{{ state.a }}
  </a-button>
  <a-button @click="add2('b')" type="primary">
    點我加b{{ state.b }}
  </a-button>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    const count = ref(0)
    const state = reactive({
      a: 0,
      b: 0,
    })
    const add = () => {
      count.value += 1
    }
    const add2 = (type) => {
      state[type] += 1
    }
    return {
      state,
      count,
      add,
      add2
    }
  }
}
</script>

如上述程式碼所示,Vue 3 新增的 setup 方法,顛覆了之前傳統的 options 屬性方式,我們可以將業務邏輯都寫在 setup 方法中。

我們有兩種宣告變數的形式:

  • ref:它用於宣告簡單的基礎型別變數,如單個數字、boolean、字串等等。
  • reactive:它用於物件引用型別的複雜變數。

所有宣告好的變數和方法,如果想在 template 模板裡使用的話,必須在 setup 方法裡 return,否則無法呼叫。記住返回什麼就是宣告,如返回 count,模板中就用 {{ count }},返回 state,模板中就使用 {{ state.a }} 。效果如下所示:

待辦事項 TODO

首先我們新建 views 資料夾用於放置頁面元件,在 views 內新建 todo.vue 檔案,如下所示:

<template>
  <div id="components-layout-demo-basic">
    <a-layout>
      <a-layout-header>待辦事項</a-layout-header>
      <a-layout-content>內容</a-layout-content>
    </a-layout>
  </div>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {

  }
}
</script>

<style scoped>
  #components-layout-demo-basic {
    min-height: 100vh;
    max-width: 40%;
    margin: 0 auto;
    background-color: #ededed;
  }
  #components-layout-demo-basic .ant-layout-header,
  #components-layout-demo-basic .ant-layout-footer {
    background: #7dbcea;
    text-align: center;
    color: #fff;
  }
</style>

引入 antd-v 的佈局元件,再給一些基礎樣式。

然後前往 App.vuerouter/index.js 做如下改動:

// App.vue
<template>
  <router-view></router-view>
</template>

<script>
export default {
  name: 'App'
}
</script>
import {createRouter, createWebHashHistory} from 'vue-router'

export default createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/todo',
      component: () => import('../views/todo.vue')
    }
  ]
})

最後頁面出現如下所示,代表配置成功:

新增新增待辦事項輸入框:

<template>
  <div id="components-layout-demo-basic">
    <a-layout>
      <a-layout-header>待辦事項</a-layout-header>
      <a-layout-content>
        <a-input-search
          v-model:value="todo"
          placeholder="請輸入要代辦的事項"
          size="large"
          @search="addTodo"
        >
          <template v-slot:enterButton>
            <a-button>新增</a-button>
          </template>
        </a-input-search>
      </a-layout-content>
    </a-layout>
  </div>
</template>
<script>
import { ref, reactive } from 'vue'
import { ref, reactive } from 'vue'
export default {
  setup() {
    const todo = ref('')

    const addTodo = (value) => {
      console.log(value)
    }
    return {
      todo,
      onSearch
    }
  }
}
</script>

如下圖所示:

新增“待辦事項”和“已辦事項”模板,程式碼如下:

<template>
  <div id="components-layout-demo-basic">
    <a-layout>
      <a-layout-header>待辦事項</a-layout-header>
      <a-layout-content>
        <a-input-search
          v-model:value="todo"
          placeholder="請輸入要代辦的事項"
          size="large"
          @search="addTodo"
        >
          <template v-slot:enterButton>
            <a-button>新增</a-button>
          </template>
        </a-input-search>
        <h2 class="title">待辦事項</h2>
        <a-card title="標題">
          <template v-slot:extra>
            <a-switch />
          </template>
          內通
        </a-card>
        <h2 class="title">已辦事項</h2>
        <a-card title="標題">
          <template v-slot:extra>
            <a-switch />
          </template>
          內通
        </a-card>
      </a-layout-content>
    </a-layout>
  </div>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    const todo = ref('')

    const addTodo = (value) => {
      console.log(value)
    }
    return {
      todo,
      onSearch
    }
  }
}
</script>

<style scoped>
  #components-layout-demo-basic {
    min-height: 100vh;
    max-width: 40%;
    margin: 0 auto;
    background-color: #ededed;
  }
  #components-layout-demo-basic .ant-layout-header,
  #components-layout-demo-basic .ant-layout-footer {
    background: #7dbcea;
    color: #fff;
    text-align: center;
  }
  .title {
    margin: 0;
    padding: 10px;
  }
</style>

效果如下:

接下來我們來新增代辦的相應邏輯:

<template>
  <div id="components-layout-demo-basic">
    <a-layout>
      <a-layout-header>待辦事項</a-layout-header>
      <a-layout-content>
        <a-input-search
          v-model:value="todo"
          placeholder="請輸入要代辦的事項"
          size="large"
          @search="addTodo"
        >
          <template v-slot:enterButton>
            <a-button>新增</a-button>
          </template>
        </a-input-search>
        <h2 class="title">待辦事項</h2>
        <a-card :title="`${index + 1}、${item.time}`" v-for="(item, index) in todos" :key="item.id">
          <template v-slot:extra>
            <a-switch v-model:checked="item.done" @change="handleCheck(item, true)" />
          </template>
          {{ item.content }}
        </a-card>
        <h2 class="title">已辦事項</h2>
        <a-card :title="`${index + 1}、${item.time}`" v-for="(item, index) in dones" :key="item.id">
          <template v-slot:extra>
            <a-switch v-model:checked="item.done" @change="handleCheck(item, false)" />
          </template>
          內通
        </a-card>
      </a-layout-content>
    </a-layout>
  </div>
</template>

<script>
import { ref, reactive, computed } from 'vue'
export default {
  setup() {
    const todo = ref('')
    const time = `${new Date().getFullYear()}-${new Date().getMonth()}-${new Date().getDate()}`

    const state = reactive({
      todoList: [
        {
          id: 1,
          done: false,
          time: time,
          content: '前往老八食堂,共進午餐'
        },
        {
          id: 2,
          done: false,
          time: time,
          content: '和giao哥合唱一曲'
        },
        {
          id: 3,
          done: false,
          time: time,
          content: '做點陽間的需求'
        }
      ]
    })
    // 新增待辦事項
    const addTodo = (value) => {
      if(!value) {
        message.error('請輸入待辦事項')
        return
      }
      state.todoList.push({
        content: value,
        id: Date.now(),
        time: time,
        done: false
      })
      todo.value = ''
    }
    // 通過計算屬性,計算出生成的代辦事項列表
    const todos = computed(() => {
      return state.todoList.filter(item => !item.done)
    })
    // 通過計算屬性,計算出生成的已辦列表
    const dones = computed(() => {
      return state.todoList.filter(item => item.done)
    })
    // 修改狀態方法
    const handleCheck = (item ,status) => {
      item.done = status
    }

    return {
      todo,
      addTodo,
      state,
      todos,
      dones,
      handleCheck
    }
  }
}
</script>

<style scoped>
  #components-layout-demo-basic {
    min-height: 100vh;
    max-width: 40%;
    margin: 0 auto;
    background-color: #ededed;
  }
  #components-layout-demo-basic .ant-layout-header,
  #components-layout-demo-basic .ant-layout-footer {
    background: #7dbcea;
    color: #fff;
    text-align: center;
  }
  .title {
    margin: 0;
    padding: 10px;
  }
</style>

setup 內使用 computed 方法,接收一個回撥函式,回撥函式內的變數都會被監聽,比如上述 state.todoList 已經被監聽了,我們在 handleCheck 方法內修改待辦事項的狀態,也會被 computed 監聽,所以就會重新整理 template 模板,效果如下:

總結

Vue 3.0 還有很多值得我們探索的知識,上述內容只是簡單的教大家如何入門搭建一個專案,並且如何通過新的 API 去結合元件庫進行實踐,後續我也會繼續整理其他的實踐技巧,原始碼已經開源到 GitHub vue3-examples倉庫中,倉庫地址:https://github.com/newbee-ltd/vue3-examples,此倉庫將不定期更新各種 Vue3.0 相關的知識及各種整合 Demo 及 Vue3 使用小技巧,大家可以關注一下,有什麼建議也歡迎大家給我留言。

相關文章