--- tags: 工作上的點點滴滴 --- # 前端初期架構規劃 > 這邊想來紀錄一下自己第一次規劃架構的經驗當作一個里程碑。在專案規劃的前期有很多需要考量的地方,但礙於自己沒有真的規劃過一個大型專案,所以只能先參考大神們的文章來思考哪些東西是有加入的必要。 ### 專案構思 思路流程: 1. 是否需要 SEO 2. 打包效率 3. 編碼檢查工具、自動編排工具 4. CSS 框架與 UI 框架的選擇 5. 有沒有需要將 Sass 模組化 ### 專案所需功能 - i18 多國語系切換 => [後續優化](https://hackmd.io/kC-5K27NSrWTu00SY427rQ) - 提升打包效率 - Vite - coding style 檢查工具(<span class="red">Eslint</span>)、自動編排工具(<span class="red">prettier</span>) - 路由控制器 - Vuex 作為狀態管理及應用程序中所有組件的集中存儲區 => 這邊後續優化更改成使用 Pinia 套件,因為 Vuex 在使用變數內容時要打的 code 太多了(<span class="red">store.state.isLoading</span>, 很長一串XD) - API 路徑 module - UI 框架 - <span class="red">Element^+^</span> - CSS 框架 - <span class="red">Tailwind</span> - 單元測試套件 - Jest ([踩坑心得分享](https://hackmd.io/uHyz0jaMTXaJT19LwOBrxw)) - Sass 模組化 - mock API - 全域的錯誤處理([middleware 異常狀態處理](https://hackmd.io/UCtXtiRfQJOfjA0Sx1K6TA)) ### Vite + Vue3 [Vite 官方中文文檔](https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project) 安裝指令: ``` npm create vite@latest my-vue-app -- --template vue ``` ### eslint + prettier 配置 [一篇文章搞定vite+vue3+eslint+prettier](https://juejin.cn/post/7020653715363217445) [eslint 和 prettier 配置文件不能是 .js 文件,必须是.cjs 文件怎么回事 ?](https://segmentfault.com/q/1010000042298464) :::warning 上述的配置問題因為 vite 新版在 package.json 裡面會配置 type: "module", 於是 .js 被默認為使用了 ES module 規範,如果自動生成的配置文件使用了 CommonJS,就會報錯。.cjs 的檔案會告诉 node.js 它使用了 CommonJS 規範,所以就不會報錯。 ::: :::danger 解決方法: 1. 檔案名稱更改為 *.cjs 2. 必須使用 module.exports 來匯出設定檔 3. 必須至擴充設置那邊將檔案名稱一併調整為 *.cjs, 不然會一直報「找不到檔案」的錯誤 ::: ```javascript= // .prettierrc 檔案 { /* * 這邊將 semi 改為 true,eslint 會跳 warning。 * 後續將 Vscode 的 Vetur 關掉之後,改用 Volar 擴充元件就沒問題 */ "semi": true, "singleQuote": true, "endOfLine": "auto", "printWidth": 100 } ``` ### Router [使用Vite构建Vue3项目,对路由Vue Router 4.x的设置](https://blog.csdn.net/xjtarzan/article/details/119736309) :::warning 在這篇文章中,讓我覺得有趣的地方是**步驟4的 alias 設置**,讓我了解了以往我們為什麼用 @ 這個符號,它會直接編譯為 src 資料夾的路徑。 原來是因為有特別針對這個符號去做一些路徑的處理 ::: [Router 集成設置參考](https://www.cnblogs.com/liuqin-always/p/14690044.html) 透過這篇文章了解到如何將路由模組化,範例如下: :::spoiler 路由模組化代碼 ```javascript= // modules/home.js export default { path: '/', name: 'index', title: '首頁', component: () => import('@/views/HomeView.vue'), }; ``` ```javascript= // routes.js import home from './modules/home'; const routes = [home]; export default routes; ``` ```javascript= // index.js import { createRouter, createWebHashHistory } from 'vue-router'; import routes from './routes'; const router = createRouter({ history: createWebHashHistory(), routes, }); // 這邊的想法是: // 1. 有 token 的情況下,只要路由想要到 login 都會被導回首頁。 // 2. 沒有 token 的情況下,只要不是 login 路由,都會自動導回到 login router.beforeEach((to, from) => { const access = Cookies.get('token'); if (access && to.path === '/login') { return { name: 'HomeView' }; } else if (!access && to.path !== '/login') { return { name: 'login' }; } }); export default router; ``` ::: #### 步驟流程: 1. 在 modules 裡面定義各頁面路由 2. 接著透過 routes.js 導入 3. 最後由 index.js 去建立一個 Router 實體,並把它匯出 4. 方便於 main.js 去引用。 ![](https://i.imgur.com/L9rrm7b.png) [路由的後續優化](https://hackmd.io/XGnLB8CfTwi8UAMRvJOPsg) ### Tailwind CSS [Install Tailwind CSS with Vue 3 and Vite](https://tailwindcss.com/docs/guides/vite) ### Vuex [Vue3 的資料狀態管理,provide / inject、vuex、props](https://penueling.com/%E6%8A%80%E8%A1%93%E7%AD%86%E8%A8%98/vue3-%E7%9A%84%E8%B3%87%E6%96%99%E7%8B%80%E6%85%8B%E7%AE%A1%E7%90%86%EF%BC%8Cprovide-inject%E3%80%81vuex/) => 參考 Vuex 集成設置 :::spoiler Vuex 相關代碼 ```javascript= // modules/auth.js export const state = { user: JSON.parse(sessionStorage.getItem('user')), password: '123', }; export const actions = {}; export const mutations = { setUser(state, payload) { sessionStorage.setItem('user', JSON.stringify(payload)); state.user = payload; }, }; export const getters = { isAuthenticated: (state) => !!state.user || !!sessionStorage.getItem('user'), }; export default { state, actions, mutations, getters, namespaced: true, }; ``` ```javascript= import { createStore } from 'vuex'; import auth from './modules/auth'; // Create a new store instance. export default createStore({ modules: { auth }, }); ``` ::: ![](https://i.imgur.com/9CLprIF.png) ### axios 集成設置 - [從 0 開始手把手帶你搭建一套規範的 Vue3 項目工程環境](https://www.readfog.com/a/1635089631515086848) ==這裡只有先寫一些註解讓大家清楚每個區塊所負責的功能是什麼,這樣未來如果要擴充或是新增功能的話,比較知道在哪個地方添加。== ```javascript= // api/axios.js import Axios from 'axios'; import { ElMessage, ElLoading } from 'element-plus'; const baseURL = 'https://desolate-gorge-38580.herokuapp.com'; const service = Axios.create({ baseURL, timeout: 20000, // 請求超時 20s }); service.interceptors.request.use( config => { // 發送請求前會做的事情 // 這邊目前想到2件事可以先做: // 1. 加入全域的 loading 效果在每次發請求前 // 2. 將 token 帶入 config.headers.Authorization 裏面 const loadingInstance = ElLoading.service({ lock: true, text: 'Loading', background: 'rgba(0, 0, 0, 0.7)' }); const token = Cookies.get('token'); if (token) { config.headers.common.Authorization = `Bearer ${token}`; loadingInstance.close(); } return config; }, (error) => { // 請求錯誤要做的事情 return Promise.reject(error); } ); // 後置攔截器(獲取到響應時的攔截) service.interceptors.response.use( // 狀態碼為 2xx 時觸發的 function (response) => { return response; }, // 狀態碼非 2xx 時觸發的 function (error) => { if (error.response && error.response.data) { const code = error.response.status; const msg = error.response.data.message; ElMessage.error(`Code: ${code}, Message: ${msg}`); console.error(`[Axios Error]`, error.response); } else { ElMessage.error(`${error}`); } return Promise.reject(error); } ); export default service; ``` - [Vue3.x中集成axios的方法](https://shawnzhou.world/2021/07/28/vue3-axios/) ```javascript= // api/home.js import service from './axios'; export function successTest() { return service({ url: '/posts', method: 'get', }); } export function errorTest() { return service({ url: '/rooms', method: 'get', }); } ``` ```javascript= // 這邊在 Vite 的文件配置失效,所以後續在各個 .vue 檔案引入 api 時 // 就直接沿用前面 Vite.config 裡面所設置的 alias proxy: { "/desktop": { target: "http://你的ip:你的端口/", changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ""), }, }, ``` ```javascript= // views/HomeView.vue import { successTest, errorTest } from '@/api/home'; ``` ![](https://i.imgur.com/YY3DPSp.png) ### i18n [i18n什麼的交給前端來處理吧](https://ithelp.ithome.com.tw/articles/10262689) [將 i18n Global](https://blog.flycode.com/step-by-step-how-to-create-a-vue-multi-language-app-with-vue-i18n) ```javascript= export default createI18n({ legacy: false, globalInjection: true, locale: process.env.VUE_APP_I18N_LOCALE || "en", fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || "en", messages: loadLocaleMessages(), }); ``` > 需要添加 globalInjection: true,然後系統會自動將其全域化,透過 $t 就可以獲得內容。 ```htmlmixed= <!-- template 獲取 i18n value 方式 --> <template> <h1>{{ $t('title') }}</h1> <p>{{ $t('description') }}</p> </template> ``` ==這邊紀錄一下,後續可能會需要處理關於切換語系後,畫面需要 reload 的部分== ### 最終資料夾內容呈現樣貌 ![](https://i.imgur.com/fuS7z6Q.png) <style> .red{ color: red } </style>