This template should help get you started developing with Vue 3 in Vite.
- Resource -- frontend mentor
- 深色模式
- 明亮模式
- Vue3
- Vue router
- Vite
- ESLint + Prettier
- TailwindCSS + PostCSS
- Storybook
- SCSS
- axios
- pinia
- vitest
- storybook 用來測試 switch 元件
- unit-test 使用 vitest,用來測試 pinia global store
- web API fetching
- toggle dark/light mode
- toggle global font family
- 使用 tailwind 來設定 styles
- Web optimization: dynamic import + code split + gzip
原本想找網路上現成的,結果會跟 Vue3 衝突,只好自己手寫。
- 想在自製元件上使用
v-model
的話,子元件必須宣告modelvalue
和update:modelValue
- 詳細看官方文件
- 大家製作 Switch 的方式通常是用
button
包著一個input[type="checkbox"]
,按鈕上放置:@click="$emit('update:modelValue', !modelvalue)"
- 範例見 Stackoverflow 上的教學
透過 vite 搭建的 Vue 專案中使用 tailwind
- 安裝方式如下:官方手冊
- 只要按照手冊教學通常都會成功
-
npx storybook init
-
npm run storybook
-
在目前版本中使用 vite + storybook 通常會搭建失敗,顯示錯誤, 解決方式:看此 issue
"overrides": { "@storybook/core-common": { "glob": "7.2.3", "glob-promise": "4.2.0" }, "@storybook/builder-vite": { "glob-promise": "4.2.0", "glob": "7.2.3" } },
-
在 .storybook/preview.js 中第一行加上
import 'tailwindcss/tailwind.css'
- 參考方式
npm i --save @fortawesome/fontawesome-svg-core npm i --save @fortawesome/free-solid-svg-icons npm i --save @fortawesome/vue-fontawesome@latest-3
- 參考連結
- 參考連結 2
- 參考連結 3
if (localStorage.theme === 'dark') { document.documentElement.classList.add('dark') this.darkMode = true } dark(newValue) { if (newValue) { localStorage.theme = 'dark' document.documentElement.classList.add('dark') } else { localStorage.removeItem('theme') document.documentElement.classList.remove('dark') } }
參考解答 此方法不需要 psudo-element
input
裡的值在從/search?q=something
切換 到/
時要清除,但透過<router-link>
回首頁的話元件不會重渲染,裡面的值也沒清除- 解決方式是在元件上加上
:key="$route.path"
,這樣每次改變網址時key
也會改變,元件會強制重渲染
created()
中呼叫axios.get()
這樣第一次搜尋時會發送請求- 但之後再搜尋的話,因為此元件已經建立了,所以不會再呼叫 created(),也就不會再發送請求
- 加上
watch()
,追蹤網址後方帶的參數是否改變,如果改了就再請求 - (todo: 也許不需要
created()
)
- solution
const audio = new Audio('audio-url') audio.play()
- 參考方式
document.documentElement.style.setProperty('--font-size', newFontSize)
- 在呼叫 API 時啟動 pending
api.interceptors.request.use((config) => { pend.startPending() return config })
- 在接收到請求時關閉 pending
- 打包出來的檔案 (index.js) 有點太大了,通常會把 .js 拆成 index.js 和 vendor.js。前者是你的主程式碼,後者是外部套件,這就是 code splitting
- 根據[官方文件](https://cn.vitejs. dev/guide/build.html#chunking-strategy),除了只切出 vendor.js,還可以手動切分檔案
- 如果只想分 vendor.js,可以這樣寫
// vite.config.js import { splitVendorChunkPlugin } from 'vite' export default defineConfig({ plugins: [splitVendorChunkPlugin()] })
- 想手動切的話可以按照這個手冊這樣寫
// vite.config.js export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { // key => 想切出的檔名, value => 想包在裡面的套件 (Array) fontawesome: [ '@fortawesome/fontawesome-svg-core', '@fortawesome/free-solid-svg-icons', '@fortawesome/vue-fontawesome' ], vue: ['vue'], 'vue-router': ['vue-router'] } } } } })
- 檔案切割完了,但有些檔案還是很大 (例如 font-awesome.js),這時可以使用 gzip 把檔案壓縮 (可以縮小約 1/3 的大小)。如果要在 vite 使用 gzip 先安裝下面的套件
npm add -D vite-plugin-compression
- 在 vite.config.js 設定 gzip,設定完輸入
npm run build
就會自動壓縮了import viteCompression from 'vite-plugin-compression' export default defineConfig({ plugins: [ // ... viteCompression({ // 限制: 512 KB 以上的檔案才要壓縮 threshold: 512000 }) ] })
- gzip 參考教學
-
在引用元件時採用動態引入,系統打包時就會自動把元件拆分成獨立的 chunk
-
在 route 層級時動態引入:
{ path: '/search', component: () => import('../views/SearchView.vue'), }
-
在 local component 內部動態引入:
import { defineAsyncComponent } from 'vue' export default { components: { Switches: defineAsyncComponent(() => import('./Switches.vue')) } }
- 為了在 devtool 裡方便尋找程式碼的原出處,我們會新增 sourcemap 在打包的 js 當中
// vite.config.js export default defineConfig({ build: { // inline: 表示把 sourcemap 加在打包的 js 檔最末尾 sourcemap: 'inline' } })
VSCode + Volar (and disable Vetur) + TypeScript Vue Plugin (Volar).
npm install
npm run dev
npm run build
Run Unit Tests with Vitest
npm run test:unit
Lint with ESLint
npm run lint