手把手帶你實現基於 Vite+Vue3 的線上Excel表格系統

2022-07-27 18:01:03

今天,葡萄帶你瞭解如何基於Vite+Vue3實現一套純前端線上表格系統。
在正式開始專案介紹之前,首先咱們首先來介紹一下Vite和Vue3。

Vue3

2020年09月18日Vue.js 3.0釋出,經歷了兩年時間的對細節的不斷優化與調整,終於在今年2月正式成為新的預設版本。其作者尤雨溪將Vue3的目標描述為:
1、更快
2、更小
3、更易於維護
4、原生目標更容易
5、開發更輕鬆
只看上述內容,你可能感受不到Vue3究竟優化了什麼。這裡我們將它和Vue2來對比一下,為大傢俱體說明它的優越之處。

效能的提升

在官方檔案中針對Vue2和Vue3之間的效能差異有具體的資料介紹:
1、SSR速度提高了2~3倍
2、Update效能提高1.3~2倍
其中效能提升的重要一點是Vue3中對diff演演算法進行了優化。
在Vue2中,每當資料發生變化,就會生成一個新的DOM樹,並新DOM樹與舊的DOM樹進行對比,來判斷節點異同,並進行更新。但完整遍歷過程需要將兩棵樹所有節點進行比較,但實際情況中並不是所有節點內容都會變化,這就造成了效能的浪費。
Vue3新增了靜態標記,僅對標記了的節點進行對比並進一步更新,無需再遍歷整個節點,實現了效能提升。

組合式API

Vue2使用選項型API(Options API),這種方式下將程式碼分割為不同的屬性:data、computed、methods 等,這些方法屬性各司其職。
舉個例子,當我們想實現一個列表檢視功能,需要在data中寫此功能相關的資料,在methods中寫相關的邏輯判斷和後端互動方法等;如果還希望有搜尋和篩選,或者更多的功能,那麼邏輯關注點會越來越多,導致元件變得難以理解和維護。(下圖為範例元件)

組合式API(Composition API)正是為了解決原本Vue2專案中程式碼邏輯分散、不易理解和維護的問題。它使用方法(function)進行程式碼分割,使程式碼更為簡潔。

生命週期函數變更

與Vue2相比,Vue3中生命週期函數也發生了變更,總結如下:

有需要的同學可以截圖儲存,以備不時之需。

按需打包模組

在Vue專案中有眾多API和模組,但在一個專案中我們並不會用到全部內容, Vue3的按需打包模組,可以大幅度壓縮打包後的內容體積。
根據官網對比範例,Vue2中如果僅寫了Hello Word,未用到任何模組API,打包後大小約為32KB;而Vue3同理,打包後大小約為13.5KB,可以明顯看出升級後的Vue3相較於Vue2打包體積大幅減小。
說完了Vue3的改進,接下來我們來看看Vite又有什麼亮眼之處。

Vite

在Vue3正式釋出之前,尤雨溪就提到做了一個新的前端構建工具-Vite。其本人更是對Vite青睞有加,引得Webpack開發者直喊大哥:

Vite究竟有什麼樣的魔力呢?它做到了本地快速開發啟動:

  1. 快速冷啟動,而不需要等待打包操作
  2. 即時的模組熱更新
  3. 真正的按需編譯,不用等待整個專案編譯完成、

在使用Webpack時,會經歷分析依賴 => 編譯打包 => 交給開發伺服器渲染 整個過程。也就是說,需要先打包,之後將打包結果提供給伺服器進行載入。特別是隨著模組的不斷增多,打包的體積越來越大,造成熱更新速度明顯拖慢。
而Vite直接略過了打包步驟,直接啟動開發伺服器,請求具體的模組時再對該模組進行實時編譯,大大提高了啟動速度。

尤雨溪本人也在微博發言解釋了其原理:「Vite,一個基於瀏覽器原生 ES imports 的開發伺服器。利用瀏覽器去解析 imports,在伺服器端按需編譯返回,完全跳過了打包這個概念,伺服器隨起隨用。同時不僅有 Vue 檔案支援,還搞定了熱更新,而且熱更新的速度不會隨著模組增多而變慢。針對生產環境則可以把同一份程式碼用 rollup 打。雖然現在還比較粗糙,但這個方向我覺得是有潛力的,做得好可以徹底解決改一行程式碼等半天熱更新的問題。」
(Vite具體的實現原理可參考文章:https://juejin.cn/post/6844904136299790349)

使用Vite初始化Vue3專案

在這裡需要需要注意:根據官網檔案說明,使用Vite需要node版本在12以上,請在建立專案前檢查node版本

初始化專案命令:

 $ npm init vite-app <project-name>  // (project-name 為專案名)建立vite專案腳手架包
 $ cd <project-name>  //進入專案目錄
 $ npm install  //安裝專案所需依賴
 $ npm run dev  //啟動專案

做個範例:搭建一個名為 myVue3 的專案。
執行命令:npm intit vite-app myVue3

可以看到,在Practice資料夾中已經搭建好了一個專案。專案結構如下:

執行命令:cd myVue3 進入專案目錄
執行命令:npm install 安裝相關模組。

專案結構如下:模組已下載成功。

最後執行命令:npm run dev 啟動這個專案

進入地址,當我們看到這個頁面時,說明專案已經成功啟動了。

鋪墊都準備好了,話不多說我們正式開始。

專案實戰

瞭解了Vue3和Vite後,接下來我們用一個實際專案體驗一下。
思路:
使用表格元件做一個簡單的線上Excel填報系統。

其中A頁面使用編輯器進行模板設計並儲存。
B頁面使用SpreadJS匯入模板並進行填報上傳。
實現機制為SpreadJS的資料繫結功能,大家可以先通過下方連結了解其作用
https://demo.grapecity.com.cn/spreadjs/SpreadJSTutorial/features/data-binding/table-binding/purejs

主要程式碼如下:
先來安裝需要的模組

"dependencies": {
    "vue": "^3.0.4",
    "@grapecity/spread-sheets-designer-vue": "15.1.0",
    "@grapecity/spread-sheets-designer": "15.1.0",
    "@grapecity/spread-sheets-designer-resources-cn": "15.1.0",
    "@grapecity/spread-sheets": "15.1.0",
    "@grapecity/spread-sheets-resources-zh": "15.1.0",
    "@grapecity/spread-excelio": "15.1.0",
    "@grapecity/spread-sheets-barcode": "15.1.0",
    "@grapecity/spread-sheets-charts": "15.1.0",
    "@grapecity/spread-sheets-languagepackages": "15.1.0",
    "@grapecity/spread-sheets-print": "15.1.0",
    "@grapecity/spread-sheets-pdf": "15.1.0",
    "@grapecity/spread-sheets-shapes": "15.1.0",
    "@grapecity/spread-sheets-tablesheet": "15.1.0",
    "@grapecity/spread-sheets-pivot-addon": "15.1.0",
    "@grapecity/spread-sheets-vue": "15.1.0",
    "@types/file-saver": "^2.0.1",
    "vue-router": "^4.0.0-rc.5"
  }

執行命令 npm install 來安裝所有依賴專案。
接下來我們來設定路由。
1、在src資料夾下新建檔案。

router/index.js

2、進行路由的設定

import { createRouter, createWebHistory } from "vue-router";

const routes = [
  {
    path: "/",
    name: "Designer",
    component: () => import("../views/Designer.vue"),
  },
  {
    path: "/spreadSheet",
    name: "SpreadSheet",
    component: () => import("../views/SpreadSheet.vue"),
  }
];

export const router = createRouter({
  history: createWebHistory(),
  routes:routes
});

3、在main.js引入

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

const app = createApp(App)
app.use(router);
app.mount('#app')

4、修改App.vue

<template>
  <div id="app">
    <div>
        <router-link to="/">Designer</router-link> |
        <router-link to="/spreadSheet">SpreadSheet</router-link>
    </div>
  <router-view/>
</div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

看到這裡大家應該會發現,路由的設定以及 main.js 引入的方式較Vue2有所不同了。簡而言之,Vue Router的Vue3版本的主要區別在於我們必須匯入新方法才能使程式碼正常工作。其中最重要的是createRouter 和 createWebHistory。總體來說,它仍然與vue2非常相似,但是通過這些更改,可以更好的支援Typescript和進行優化。

程式碼中也體現出vue3 組合式API的特點。相較於vue2選項型API(將程式碼分割為 data、methods等),vue3在setup方法內部定義資料和方法,將業務邏輯抽離為函數,並通過return返回,使程式碼邏輯更為簡潔清晰。

設定完路由之後,我們開始整合元件化表格編輯器(Designer)和SpreadJS。

一、 整合Designer
程式碼如下所示:

<template>
  <div>
      <div id="ssDesigner" style="height:700px;width:100%;text-align: left;"></div>
  </div>
</template>

<script>

import {onMounted, ref, reactive} from "vue";
import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css';
import '@grapecity/spread-sheets-designer/styles/gc.spread.sheets.designer.min.css';
import "@grapecity/spread-sheets-shapes";
import '@grapecity/spread-sheets-pivot-addon';
import "@grapecity/spread-sheets-tablesheet";
import GC from '@grapecity/spread-sheets'
import "@grapecity/spread-sheets-resources-zh";
GC.Spread.Common.CultureManager.culture("zh-cn");
import "@grapecity/spread-sheets-designer-resources-cn";
import "@grapecity/spread-sheets-designer";
import {designerConfig} from '../files/config'
//import {myBudget } from '../files/budget.js';
import {myBudget} from '../files/right_demo.js';

export default {
  name: 'Designer',
  props: {
  },
  setup() {
    let designer;
    let spreadDom;
    let spread;
    onMounted(() => {
      designer = new GC.Spread.Sheets.Designer.Designer(document.getElementById("ssDesigner"), designerConfig);
      spreadDom = designer.getWorkbook().getHost();
      spread = GC.Spread.Sheets.findControl(spreadDom);
      //spread.fromJSON(myBudget);
    })
  
    return {
      designer,
      spread
    };
  }
}
</script>

<style scoped>
  
</style>

1、在模板中新增一個div,這個div就是設計器的容器,可以通過css設定容器的寬高位置等,也就是自定義了設計器的顯示大小及位置。
2、匯入設計器所需要的依賴。
3、在setup函數中初始化designer

這時頁面就可以載入顯示設計器了,說明我們已經成功將設計器整合在專案中。

Designer的頁面與Excel類似,利用工具列提供的UI按鈕和特有的資料繫結功能,我們可以輕鬆實現模板設計。
當然也可以通過匯入按鈕或者使用介面(fromJSON)直接載入預設好的模板。

完成模板設計後點選儲存按鈕進行提交,這裡我們先將資料儲存至sessionStorage,方便後面的獲取。

注:
原生Designer並未包含儲存按鈕,我們可以利用其強大的自客製化能力根據業務需求來執行相關程式碼邏輯,儲存按鈕的程式碼邏輯如下圖:

自客製化元件完整程式碼可以參考文章末尾的demo,這裡不再一一介紹了。
到此Designer的整合與模板設計完成,接下來看下如何整合表格控制元件並進行資料的填報和收集。

二、 整合SpreadJS

與整合Designer類似,首先先建立一個名為SpreadSheet的vue頁面。

<template>
  <div>
    <div>
      <button :style="{margin: '20px'}" @click="importTemplate()">匯入模板</button>
      <button @click="setDataSource()">繫結資料來源</button>
      <button @click="saveTemplate()">儲存</button>
    </div>
    <div id="ss" style="height:700px;width:100%;text-align: left;"></div>
  </div>
</template>

<script>
import { onMounted, ref} from "vue";
import "../../node_modules/@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css"
import GC from "@grapecity/spread-sheets"
import "@grapecity/spread-sheets-resources-zh";

export default {
  name: 'SpreadSheet',
  components: {
  },
  setup(){
    let spread, sheet;
    onMounted(() => {
      let workbook = new GC.Spread.Sheets.Workbook(document.getElementById("ss"));
      let spreadDom = workbook.getHost();
      spread = GC.Spread.Sheets.findControl(spreadDom);
    });

    let importTemplate = () => {
      const json = JSON.parse(sessionStorage.getItem("templateJson"));
      spread.fromJSON(json);
    };

    let setDataSource = () => {
      sheet = spread.getActiveSheet();
      let table = sheet.tables.all()[0];
      table.allowAutoExpand(true);
      table.expandBoundRows(true);
      let data = {
          budget: [
              {item:"部門活動", Jan: 15, Feb: 25, Mar: 10, Apr: 25, May: 13, Jun: 15},
              {item:"差旅費", Jan: 15, Feb: 25, Mar: 10, Apr: 25, May: 13, Jun: 15},
              {item:"辦公費", Jan: 15, Feb: 25, Mar: 10, Apr: 25, May: 13, Jun: 15},
              {item:"廣告費", Jan: 15, Feb: 25, Mar: 10, Apr: 25, May: 13, Jun: 15},
              {item:"招待費", Jan: 15, Feb: 25, Mar: 10, Apr: 25, May: 13, Jun: 15}
          ]
      }
      let datasource = new GC.Spread.Sheets.Bindings.CellBindingSource(data);
      sheet.setDataSource(datasource);
    };

    let saveTemplate = () => {
      let source = sheet.getDataSource().getSource();
      sessionStorage.setItem("dataSource", JSON.stringify(source));
      console.log(source);
      alert("儲存填報資料成功");
    };

    return {
      spread,
      importTemplate,
      setDataSource,
      saveTemplate
    }
  }
}
</script>

1、在模板中新增一個div,這個div就是spread的容器,可以通過css設定容器的寬高位置等,也就是自定義了spread的顯示大小及位置。
2、新增匯入模板、繫結資料來源、儲存按鈕。
3、匯入此元件所需要的依賴。
4、在setup方法中初始化spread。
5、實現各按鈕對應的程式碼邏輯。

importTemplate 方法中使用fromJSON方法來載入Designer設計好的模板。
setDataSource中利用資料繫結功能繫結了預設好的資料來源,或者你也可以修改或手動填報。

saveTemplate方法中獲取修改/填報後的資料來源,並可將資料來源儲存至後臺資料庫(本例儲存至sessionStorage,僅作範例作用)。後期做填報彙總時就可以直接從後臺資料庫直接讀取該資料來源了。

至此,一個簡單的線上Excel填報系統完成,感興趣的小夥伴可以下載下方工程程式碼自己嘗試一下。
https://gcdn.grapecity.com.cn/forum.php?mod=attachment&aid=MjIzMDM0fGIyODc2MDlmfDE2NTg4MjgyNzB8NjI2NzZ8OTk3MTg%3D

如果大家對更多範例感興趣可以檢視:
https://demo.grapecity.com.cn/spreadjs/gc-sjs-samples/index.html