2022.10.10 更新:這個專案用的套件,很多都有大幅度的更新,GitHub 的機器人也常常跳安全性有漏動,主動要求更新套件版本。本篇最後產出的 Demo 頁在今天關閉,程式碼留在 GitHub 上當一個紀錄跟練習。
關於「套件集合站」
集合站是寫給同事看的,有一些網站常用的功能不一定要前端手刻才可以使用,網路上很多猛人寫了套件後開放給大家使用,而那些套件的說明文件跟 Demo 有些都蠻齊全的。
通常對於使用套件會遇到的問題就是,同一個功能,比方輪播,就有很多人有出套件:Slick、Owl Carousel、Swiper……等,或是說明文件都英文,參數又多,等研究完了前端也差不多寫好了。
當初會製作這個套件集合站就是為了解決這二個問題。
首先,從常用的套件中挑出一個覺得好用的、常用的出來,比方輪播就選用了 Slick。
接著,附上要引用的 JS、CSS 的 CDN 網址,並加入一鍵 copy 路徑的功能。
最後,在 CodePen 上寫個使用的範例,iframe到頁面中。
原本的頁面是用 Vue CLI 2 開發,UI 是用 Vuetify 1.5.x 版。最近在看 Vue CLI 3,又發現 Vuetify 升到 2.0.5 版了,就想說不然來更新一下集合站,當作練習,也順便記錄一下開發的過程。
本篇使用資源
本篇用到的資源如下:
本文最後會附上套件集合站整理過的原始碼。
安裝 Vue CLI 3
安裝 Vue CLI 3 之前,必須先安裝 Node.js,官方是推薦 8.11.0 以上的版本。
裝完 Node.js 後,開啟終端機,輸入以下命令:
$ npm install -g @vue/cli
Mac 的話前面要多輸入「sudo
」。
安裝完後,要檢查有沒有安裝成功,就在終端機輸入:
$ vue --version
如果有出現版本號就代表成功了。
建立 Vue CLI 3 專案
在目錄底下開啟終端機,輸入:
$ vue create 專案名稱
接著選擇「Manually select features」,就會出現安裝的選項:

這邊就簡單的用編 ES6 的 Babel 跟控各頁網址的 Router。按下 Enter 後就會問 3 個問題。
Router 用 history mode。
config 一起寫進 package.json 裡。
要不要設成預設選項之一,就看自己未來的需求。
三個問題都回答後,就開始安裝專案。安裝完後會出現如何開啟服務的說明。

點進資料夾裡的 README.md,可以看到使用的命令,以下 2 個是比較常用的部份:
Compiles and hot-reloads for development
$ npm run serve
Compiles and minifies for production
$ npm run build
輸入 npm run serve 後,預設的網址是 http://localhost:8080/,打開就可以看見初始的頁面:

因為有選擇裝 Router,所以頁面預設有 2 頁:Home、About,點選頂部的導覽列可以切換。
看到設定的路由檔案,有一招是用 CLI 2 時沒看到的,就是對 Component 做 lazy-load:
{ path: '/about', name: 'about', component: () => import('./views/About.vue') }
以前還真的不知道可以這樣用,長知識了。
安裝 Vuetify
Vuetify 是走 Google Material Design的UI framework,裡面包了很多常用的 UI,裝了以後就不用花時間自己刻樣式。
Vue CLI 3 可以安裝插件,也有提供命令:
$ vue add 插件來源
Vuetify 安裝說明 中,也有說明怎麼在 Vue CLI 3 上安裝,只要在終端機上輸入:
$ vue add vuetify
裝完後會出現自定義安裝的選項,文件中說預設 Default 就有提供導覽列的 App,這邊就選 Default。
安裝完 Vuetify 後,會出現哪些檔案被更動的訊息:

執行「npm run serve」打開頁面後,會看見首頁整個不一樣了:

Vue CLI 3 裝完了,Vuetify 也裝完了,接下來就是製作頁面跟接資料。
製作頁面模版
製作一個頁面,一開始很花時間的就是做出一個模版給其它頁套用,模版中要包含天地跟導覽列。
而 Vuetify 在 Application 這段就有提供導覽列在側邊的版型,版型中也包了天地,因此就 copy 下來修改,把用按鈕控制側邊導覽列的部份也寫進去,整個模版的架構如下(App.vue):
側邊導覽列的部份綁一個 v-model="drawer"
,用 app-bar-nav-icon 去控制 true / false,就可以打開/關閉側邊導覽列了。
抓 Firebase 資料
選單的項目用 v-for
處理,資料放在 Firebase 上,這邊用 Realtime Database。
Firebase 的規則設定,如果 read 設成 true
,就可以直接 GET 資料回來,Request URL 在 Database 的灰底那塊(第一個紅框處):

比方這邊資料分成 article、nav,我們想抓的是 nav 的那個 JSON,那 URL 就是:
https://xxxxxxxxx.firebaseio.com/nav.json
抓回來後的資料在放寫 v-for
就行了。
這邊的資料架構是這樣:

article 是給內文用的,nav 是導覽列用,下一層都是每一個套件的id名稱。
之所以會這樣子用,就是為了方便路由抓 id。
August 的路由是這樣寫:
<v-list-item v-for="(n, v) in asideNav" :key="v" :to="'/plugin-' + v" >
用「plugin-」當開頭是為了路由可以判斷要渲染哪一個 Component。
「v」就是 ajaxLoading、alert…… 等等的 key。
因此當點擊了導覽列,進到頁面後,就可以抓到那一頁的 id 是什麼,用 id 去GET:
https://xxxxxxxx.firebaseio.com/article/${this.id}.json
這樣就可以抓到這個套件在 article 裡的資料。
這邊有踩到一個坑,說是坑,不如說是當初學 Vue Router 時,文件沒看仔細。
Vue Router:id、pathMatch
內文頁的路由是設這樣:
{ path: '/plugin-*', name: 'plugin', component: () => import('./views/Plugins.vue') }
之前在抓路由的 id 時,都是這樣抓:
this.$route.params.id
這邊一樣用相同方式抓時,就發現報錯,console.log 後又發現 params 裡沒有 id,卻有一個 pathMatch。
翻了一下文件後,才發現當路由path設成「*」,要抓任意路徑時,$route.params 會提供的參數就是 pathMatch。詳情可以看文件說明:
模版頁最後的原始碼在本文最後的原始檔中可以看到。
製作頁面
整個站的頁面就兩種版型:首頁、內文頁。
首頁主要是網站說明跟放公告,而內文頁都是針對路由給出的不同 id 去抓不同的資料回來渲染。
其實當模版頁完成後,做首頁、內文頁就快多了。
上段中有說資料庫的架構可以讓我從路由上判斷要去抓哪個資料了,因此做頁面的部份就是把抓到的資料塞進去而己。
這段要特別說明的是,因為用的是 Vue Router,所以實際頁面並不會重新讀取一次,這會造成一個問題,就是 GA 的數據不會因為進到不同頁面而 Page View +1。
為了解決頁面瀏覽量可以確實進到 GA,必需手動寫 PV + 1,寫在路由變動後執行的 methods 裡就行,如下:
gtag('config', 'UA-xxxxxxxx-x', { 'page_title': 頁面title, 'page_location': location.origin, 'page_path': location.pathname });
然後又會遇到另一個坑,就是因為首頁也是路由的一個網址,埋 GA code 時預設是 PV + 1 了,如果又因為在 methods 我們手動發 PV + 1,就會造成首頁的流量每次都多計一次。
解決這問題很簡單,就是把 GA 提供的追蹤碼,埋碼時刪掉 config 那句就行。
一般 GA 提供要埋的追蹤碼是這樣:
<!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-xxxxxxxx-x"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-xxxxxxxx-x'); </script>
gtag('config', 'UA-xxxxxxxx-x')
這行,因為我們在 method 裡有寫了,這行就要刪掉,就解決了瀏覽數重複計算的問題。
build 產出原始碼
code 都寫好後,最後一步就是打包原始碼傳到主機。
打包的命令是:
$ npm run build
在 Vue CLI 3 上,有一些打包上的 config 可以使用,可以參考 官方文件。
首先,要先新增一個檔案,檔名為「vue.config.js」,目前比較常用的設定是以下:
module.exports = { publicPath: '', outputDir: 'plugin-lib', // 產出的資料夾名稱 assetsDir: 'dist', // 靜態檔的資料夾名稱 filenameHashing: true // bundle出來的檔案是否要有hash值 }
由於有改變產出的資料夾名稱,在 build 後,會看到的資料夾結構如下:

一開始看到 build 出來,會 build 成一個獨立的資料夾,覺得蠻好的,這樣就可以開發、發行兩個分別控管。如果要跟後端合作時,也只要提供 build 後的資料夾,那就會是很乾淨的版本。
vue-router 用 history mode 的注意事項
把 build 的檔案傳到主機上,遇到了一個錯誤。
August 的 router 是用 history mode,當我點開其他頁面,然後按下重新整理時,就會出現 error500 的伺服器錯誤。
這點是沒想到的,因為在開發時沒這問題,上到主機後就有了。
看了一下 vue-router 的文件說明,上面有提到:
這種模式要玩好,還需要後台配置支持。
HTML5 History模式
因為我們的應用是個單頁客戶端應用,如果後台沒有正確的配置,當用戶在瀏覽器直接訪問http://oursite.com/user/id
就會返回404,這就不好看了。
所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果URL匹配不到任何靜態資源,則應該返回同一個index.html
頁面,這個頁面就是你app依賴的頁面。
照著說明新增 .htaccess 的檔案後,就沒出現這個錯誤了。
但……還是不知道原因啊!?希望對 Server 了解的大大可以開示一下。
本篇原始碼
本篇套件集合站的原始碼,整理上傳到 GitHub 了:
https://github.com/letswritetw/letswrite-plugin-lib


關於 Server error,提供淺見但不一定正確,供參考。
Vue Router 在 hash 模式下 URL 會多出 # 號,
不論 # 號後面的值,都還算是同一個 page file,
因此 refresh,server 都維持跑 index.html 檔,再由 Vue router 依據 # 號來判斷路由。
而在 history 模式時少了 # 號,如果還未設置 server 端的 URL 跳轉設定,
頁面 refresh 時,會用一般 server 跑 file path 方式去讀檔案,
如 /about ,就會因找不到 about 這個實體檔案,而出 error,
Vue router 所需的 server rewrite 規則,就是不論 URL 為何,
都讓它統一跑 index.html (或 index.php 之類),再由 Vue router 配置路由~