天天看點

記一次基于vue的spa多頁簽實踐經驗

前言

最近收到一個這樣的需求,要求做一個基于 vue 和 element-ui 的通用背景架構頁,具體要求如下:

  1. 要求通用性高,需要在後期四十多個子項目中使用,是以大部分地方都做成可配置的.
  2. 要求做成腳手架的形式.可以 npm 安裝
  3. 要求實作多頁簽,并且可以通過浏覽器 url 回顯多頁簽.而且頁簽内要維護一個曆史記錄,可以後退
  4. 元件要求異步加載,減少首屏加載時間.

很明顯,這就是一個

類 ERP

的應用. 做過 JSP 等背景的同學,對多頁簽應該都很熟悉吧.

那接下來我們就來談談實作.

通用性高

這點其實沒啥難點,無非就是麻煩了點,把所有的資料,都提取出來,放在一個

config

檔案裡面.然後在架構頁裡面引入,并且綁定到相應的位置上去. 這邊有個比較難以取舍的問題,就是如果把一溜的資料全部綁定到 vue 的 data 上面,由于資料量比較多,會導緻性能問題,如果分開,又會使配置檔案看起來相對複雜,增加後期使用人員的學習成本。這塊要看具體的項目需求,由于我這邊暫時對前端的性能要求沒那麼高,是以暫時用全部綁定到 data 的方案

做成腳手架形式

起初産品對這個的需求使做成元件的形式,然後釋出 npm 包,友善後期更新的時候,隻需更新一下 npm 就可以了,無需每個項目去複制粘貼替換,但是基于這是一個架構頁,而且可配置項非常多,還要實作 tab 多頁簽等多方面的考慮,最終選擇了腳手架的方案,即便這樣後期更新會稍微麻煩一點(起初的方案是架構頁放在一個檔案夾裡,到時候直接替換該檔案夾),但相對于元件來說,還是更好維護的,況且後期可以再寫一個更新的腳手架,畢竟現在釋出一個 npm 工具的成本實在是太低了。

第一次開發腳手架,看了很多社群的文章,發現目前大部分腳手架,一般都基于2種形式,一種基于檔案複制的形式,另一種基于 git-clone 的形式,經過對比,我覺得檔案複制的有點複雜了,我其實隻是需要一個能一鍵安裝的工具而已,是以 git-clone 的形式還是比較适合我。

以下就是腳手架的代碼,雖然隻是簡單的五六十行代碼,不過查資料+趟坑,也花了我一個上午的時間。

#!/usr/bin/env node
const shell = require('shelljs');
const program = require('commander');
const inquirer = require('inquirer');
const ora = require('ora');
const fs = require('fs');
const path = require('path');
const spinner = ora();
const gitClone = require('git-clone')
const chalk = require('chalk')


program
	.version('1.0.0', '-v, --version')
	.parse(process.argv);

const questions = [{
  type: 'input',
  name: 'name',
  message: '請輸入項目名稱',
  default: 'my-project',
  validate: (name)=>{
    if(/^[a-z]+/.test(name)){
      return true;
    }else{
      return '項目名稱必須以小寫字母開頭';
    }
  }
}]

inquirer.prompt(questions).then((dir)=>{
  downloadTemplate(dir.name);
})

function downloadTemplate(dir){

  //  判斷目錄是否已存在
  let isHasDir = fs.existsSync(path.resolve(dir));
  if(isHasDir){
    spinner.fail('目前目錄已存在!');
    return false;
  }
  spinner.start(`您選擇的目錄是: ${chalk.red(dir)}, 資料加載中,請稍後...`);

  // 克隆 模闆檔案
  gitClone(`https://github.com/noahlam/vue-multi-tab.git`, dir , null, function(err) {
    // 移除無用的檔案
    shell.rm('-rf', `${dir}/.git`)
	  spinner.succeed('項目初始化成功!')
    // 運作常用指令
    shell.cd(dir)
	  spinner.start(`正在幫您安裝依賴...`);
    shell.exec('npm i')
	  spinner.succeed('依賴安裝成功!')
    shell.exec('npm run dev')
  })
}
複制代碼
           

如果你這個腳手架有疑問或者興趣,可以直接通路 github 上的代碼 tab-cli

實作多頁簽

要想實作多頁簽,那麼 vue-router 基本算是廢了,為什麼? vue-router 是根據 url 來切換單個元件的,而頁簽則需要再元件内部同時存在多個子元件的,是以路由無法勝任(至少我是這麼認為的,如果你有更好的方案,懇請不吝賜教)。

多個頁簽的顯示,其實不難, element 有現成的 tab 元件,于是老夫寫代碼就是一把梭,撸起袖子就是幹,噼裡啪啦一頓寫,寫完一測,沒有任何問題,實在是不要太簡單,丢給産品預覽:

  1. 複制浏覽器位址到别的地方粘貼,tab 不能正确回顯
  2. tab 内需要實作跳轉,而且要能傳回。

第一個問題比較簡單,自己手寫一個基于 hash 的

僞路由

把目前 tab 的 id 放到 url 上去,然後回顯的時候,根據 url 打開對應的 tab.

tip: 關于如何實作路由,請看我另外一篇部落格 自己動手實作一個前端路由

第二個問題,大概就是本文的重點了,這裡詳細說明一下需求,每個 tab 都可以在 tab 内部

跳轉

,這裡的跳轉,要做的跟 vue-router 的有大體上差不多,要能 push, replace, back,還能帶參數。

那麼怎麼實作呢? 首先維護一個打開的 tab 清單,然後每個清單裡面再維護一個用過的元件清單(包含參數),這樣大概就能實作了嗎?當然不是,元件的跳轉,參數的傳遞,不可能讓使用者自己去實作這些方法吧,我選擇把封裝一個公共對象,然後挂載在 vue.prototype上。然後類似 vue.

記一次基于vue的spa多頁簽實踐經驗

tab)可以在頁面的任何地方使用,如果你對具體的實作方法有興趣,歡迎點選本文結尾的連結,去我的Github倉庫上檢視。

元件異步加載

之前隻用過基于 vue-router 的異步加載方法,然而這個項目裡面并沒有使用 vue-router,怎麼異步呢? 翻了一下 vue 的官方文檔是這麼寫的:

Vue.component(
  'async-webpack-example',
  // 這個 `import` 函數會傳回一個 `Promise` 對象。
  () => import('./my-async-component')
)
複制代碼
           

然而我試了一下,發現報錯了,import 不能在這裡使用,換了 require 也不行,不知道上我哪裡沒弄好,如果你剛好知道又剛好有空,請告訴我,謝謝!後面在 segmentfault 上 看到 這一篇, 使用 webpack 的 require.ensure 可以實作

// 第一個字元串是 元件名,第二個是 元件路徑,第三個是 chunkName (如果不指定則以1.js,2.js....n.js命名)
vue.component('home', (resolve) => {require.ensure([], ()=>resolve(require('@/Views/index.vue')), 'home')})
複制代碼
           

順便還要在 webpack 裡面的 output 下面配置一下

chunkFilename: '[name].js',

, 當然檔案名格式可以按你項目的需求來,我這邊就按最簡單的

結束語

首先,當然上獻上該項目的 github位址 咯

其次是本文的的位址 個人技術帖合集

以上項目 歡迎随意

star

follow

, 和不随意的

issue

繼續閱讀