vue3+vite+ts搭建
vite初始化
使用 NPM:
npm init @vitejs/app
使用 Yarn:
yarn create @vitejs/app
選擇模板
選擇 vue-ts
或通過附加的命令行選項直接指定項目名和模板
# npm 6.x
npm init @vitejs/app vite-vue3-starter --template vue-ts
# npm 7+(需要額外的雙橫線)
npm init @vitejs/app vite-vue3-starter -- --template vue-ts
# yarn
yarn create @vitejs/app vite-vue3-starter --template vue-ts
安裝依賴
npm install
啟動項目
npm run dev
vite配置文件
vite.config.ts根目錄下
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import Components from 'unplugin-vue-components/vite' //針對 Vue 的按需組件自動導入
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import viteCompression from 'vite-plugin-compression'
import WindiCSS from 'vite-plugin-windicss'
import { createHtmlPlugin } from 'vite-plugin-html'
import AutoImport from 'unplugin-auto-import/vite'
import autoprefixer from 'autoprefixer'
// import viteStylelint from '@amatlash/vite-plugin-stylelint'
export default defineConfig({
plugins: [
vue(),
createHtmlPlugin({
minify: true,
/**
* 在這里寫entry后,你將不需要在`index.html`內添加 script 標簽,原有標簽需要刪除
* @default src/main.ts
*/
// entry: 'src/main.ts',
/**
* 如果你想將 `index.html`存放在指定文件夾,可以修改它,否則不需要配置
* @default index.html
*/
// template: './index.html',
/**
* 需要注入 index.html ejs 模版的數據
*/
inject: {
data: {
title: 'SW網',
injectScript: ``
},
tags: [
{
injectTo: 'body-prepend',
tag: 'div',
attrs: {
id: 'app'
}
}
]
}
}),
// viteStylelint({
// // 對某些文件排除檢查
// exclude: /windicss|node_modules/
// }),
AutoImport({
//自動導入vue3的hooks
imports: [
'vue',
'vue-router',
{
axios: [
['default', 'axios'] // import { default as axios } from 'axios',
]
}
],
resolvers: [
/* ... */
]
}),
WindiCSS(),
Components({
//針對 Vue 的按需組件自動導入
dts: true, // ts支持
dirs: ['src/components', 'src/views'], // 自定義路徑按需導入
resolvers: [AntDesignVueResolver()] // antd直接使用組件,無需在任何地方導入組件
}),
viteCompression({
verbose: true, //是否在控制台輸出壓縮結果
disable: false, //是否禁用
threshold: 10240, //體積大於 threshold 才會被壓縮,單位 b
algorithm: 'gzip', //壓縮算法,可選 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw']
ext: '.gz' //生成的壓縮包后綴
})
],
resolve: {
//extensions: ['.vue', '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.node', '.scss'],
alias: {
'@': resolve('./src'),
'@vi': resolve('./src/views'),
'@api': resolve('./src/api'),
'@h': resolve('./src/hooks'),
'@page': resolve('./src/views/page'),
'@comp': resolve('./src/components'),
'@assets': resolve('./src/assets')
}
},
base: '/', // 打包路徑
server: {
port: 4000, // 服務端口號
open: false, // 服務啟動時是否自動打開瀏覽器
cors: true, // 允許跨域
fs: {
strict: false
}
},
css: {
// 進行 PostCSS 配置
postcss: {
plugins: [
autoprefixer({
// 指定目標瀏覽器
overrideBrowserslist: ['Chrome > 40', 'ff > 31', 'ie 11']
})
]
},
preprocessorOptions: {
// less: {},
scss: {
// 避免出現: build時的 @charset 必須在第一行的警告
charset: false,
additionalData: '@import "./src/design/com.scss";'
}
}
},
build: {
// 打包配置
// minify: 'terser', //esbuild terser
assetsInlineLimit: 8 * 1024, //如果靜態資源體積 >= 4KB,則提取成單獨的文件 如果靜態資源體積 < 4KB,則作為 base64 格式的字符串內聯
terserOptions: {
compress: {
// 打包后移除console和注釋
drop_console: true,
drop_debugger: true
}
},
// 拆分打包的配置方法
assetsDir: 'public/static',
rollupOptions: {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: {
// 拆分代碼,這個就是分包,配置完后自動按需加載
vue: ['vue', 'vue-router']
}
}
}
}
})
集成路由
安裝支持 Vue3 的路由工具 vue-router@4
npm i vue-router@4
創建 src/router/index.ts 文件
在 src 下創建 router 目錄,然后在 router 目錄里新建 index.ts 文件:
└── src/
├── router/
├── index.ts // 路由配置文件
import { createRouter, createWebHistory, RouteRecordRaw, _RouteRecordBase } from 'vue-router'
declare module 'vue-router' {
interface _RouteRecordBase {
hidden?: boolean | string | number
}
}
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/vuex',
name: 'Vuex',
component: Vuex
},
{
path: '/axios',
name: 'Axios',
component: () => import('@/views/axios.vue') // 懶加載組件
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
main.ts
文件中掛載路由配置
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
// use
const app = createApp(App)
app.use(router)
集成狀態管理工具
安裝支持 Vue3 的狀態管理工具 vuex@next
npm i vuex@next
創建 src/store/index.ts
文件
在 src
下創建 store
目錄,然后在 store
目錄里新建 index.ts
文件:
import { createStore } from 'vuex'
const defaultState = {
count: 0
}
// Create a new store instance.
export default createStore({
state() {
return defaultState
},
mutations: {
increment(state: typeof defaultState) {
state.count++
}
},
actions: {
increment(context) {
context.commit('increment')
}
},
getters: {
double(state: typeof defaultState) {
return 2 * state.count
}
}
})
在 main.ts
文件中掛載 Vuex 配置
import { createApp } from 'vue'
import App from './App.vue'
import store from './store/index'
createApp(App).use(store).mount('#app')
集成 Axios
npm i axios
import axios from 'axios'
import qs from 'qs'
import store from '@/store/index'
import router from '@/router/index'
import { aspShow } from '@/hooks/data'
import { message } from 'ant-design-vue/es'
import NProgress from 'nprogress'
console.log(import.meta.env.VITE_API_DOMAIN)
axios.defaults.baseURL = import.meta.env.VITE_API_DOMAIN
axios.defaults.timeout = 60000
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
axios.defaults.headers.post['Access-Control-Allow-Origin-Type'] = '*'
axios.interceptors.request.use(
function (config: any) {
aspShow.value = true
NProgress.start()
// 在發送請求之前做某件事
if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
if (config.method !== 'post' && config.headers['Content-Type'] !== 'multipart/form-data') {
config.data = qs.parse(config.data)
}
}
// 若是有做鑒權token , 就給頭部帶上token
if (store.state.token) {
config.headers.Authorization = store.state.token
}
return config
},
(error: { data: { error: { message: any } } }) => {
return Promise.reject(error.data.error.message)
}
)
axios.interceptors.response.use(
function (config: any) {
aspShow.value = false
NProgress.done()
if (config.status === 200 || config.status === 204) {
return Promise.resolve(config)
}
return Promise.reject(config)
},
function (error: any) {
aspShow.value = false
NProgress.done()
if (error.response.status) {
switch (error.response.status) {
// 401: 未登錄
// 未登錄則跳轉登錄頁面,並攜帶當前頁面的路徑
// 在登錄成功后返回當前頁面,這一步需要在登錄頁操作。
case 401:
router.replace({
path: '/login',
query: {}
})
break
// 403 token過期
case 403:
// Toast({
// message: '登錄過期,請重新登錄',
// duration: 1000,
// forbidClick: true
// });
// 清除token
store.dispatch('FedLogOut').then(() => {
// 跳轉登錄頁面,並將要瀏覽的頁面fullPath傳過去,登錄成功后跳轉需要訪問的頁面
router.replace({
path: '/login',
query: {
// redirect: router.currentRoute.fullPath
}
})
})
break
// 404請求不存在
case 404:
// Toast({
// message: '網絡請求不存在',
// duration: 1500,
// forbidClick: true
// });
break
// 其他錯誤,直接拋出錯誤提示
default:
message.error(`${error.response.status}:${error.response.statusText}`)
}
aspShow.value = false
NProgress.done()
return Promise.reject(error)
}
// 處理斷網的情況
// eg:請求超時或斷網時,更新state的network狀態
// network狀態在app.vue中控制着一個全局的斷網提示組件的顯示隱藏
// 關於斷網組件中的刷新重新獲取數據,會在斷網組件中說明
store.commit('changeNetwork', false)
}
)
export default axios
集成 CSS 預編譯器
npm i stylus -D
# or
npm i sass -D
npm i less -D
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true
},
scss: {
// 避免出現: build時的 @charset 必須在第一行的警告
charset: false,
additionalData: '@import "./src/design/methodCss.scss";'
}
}
}
代碼規范
使用 EditorConfig + Prettier + ESLint 組合來實現代碼規范化
這樣做帶來好處:
- 解決團隊之間代碼不規范導致的可讀性差和可維護性差的問題。
- 解決團隊成員不同編輯器導致的編碼規范不統一問題。
- 提前發現代碼風格問題,給出對應規范提示,及時修復。
- 減少代碼審查過程中反反復復的修改過程,節約時間。
- 自動格式化,統一編碼風格,從此和臟亂差的代碼說再見。
集成 EditorConfig 配置
在項目根目錄下增加 .editorconfig
文件:
# Editor configuration, see http://editorconfig.org
# 表示是最頂層的 EditorConfig 配置文件
root = true
[*] # 表示所有文件適用
charset = utf-8 # 設置文件字符集為 utf-8
indent_style = space # 縮進風格(tab | space)
indent_size = 2 # 縮進大小
end_of_line = lf # 控制換行類型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始終在文件末尾插入一個新行
[*.md] # 表示僅 md 文件適用以下規則
max_line_length = off
trim_trailing_whitespace = false
復制代碼
注意:
- VSCode 使用 EditorConfig 需要去插件市場下載插件 EditorConfig for VS Code 。
集成 Prettier 配置
Prettier 是一款強大的代碼格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等語言,基本上前端能用到的文件格式它都可以搞定,是當下最流行的代碼格式化工具。
-
安裝 Prettier
npm i prettier -D pnpm add prettier -D
-
創建 Prettier 配置文件
Prettier 支持多種格式的配置文件,比如
.json
、.yml
、.yaml
、.js
等。在本項目根目錄下創建
.prettierrc
文件。 -
配置
.prettierrc
在本項目中,我們進行如下簡單配置,關於更多配置項信息,請前往官網查看 Prettier-Options 。
module.exports = { printWidth: 120, //單行長度,超過則自動換行 // 設置tab寬度為2個空格 tabWidth: 2, // 語句末尾要加分號 semi: false, useTabs: false, // 使用單引號 singleQuote: true, // (x) => {} 箭頭函數參數只有一個時是否要有小括號。avoid:省略括號 always始終包含 arrowParens: 'avoid', // 在對象,數組括號與文字之間加空格 "{ foo: bar }" bracketSpacing: true, endOfLine: 'auto', //避免報錯delete (cr)的錯 維護現有的行尾(通過查看第一行之后使用的內容,對一個文件中的混合值進行規范化) trailingComma: 'none', // 是否使用尾逗號, embeddedLanguageFormatting: 'off', //"auto"– 格式化嵌入式代碼,如果 Prettier 可以自動識別它. off"- 從不自動格式化嵌入式代碼。 htmlWhitespaceSensitivity: 'ignore', // 18. vue script和style標簽中是否縮進,開啟可能會破壞編輯器的代碼折疊 vueIndentScriptAndStyle: false, // 20.embeddedLanguageFormatting: "off",默認是auto,控制被引號包裹的代碼是否進行格式化 embeddedLanguageFormatting: 'off', bracketSameLine: true //將多行 HTML(HTML、JSX、Vue、Angular)元素放在最后一行的末尾,而不是單獨放在下一行(不適用於自閉合元素) }
-
Prettier 安裝且配置好之后,就能使用命令來格式化代碼
# 格式化所有文件(. 表示所有文件) npx prettier --write .
注意:
- VSCode 編輯器使用 Prettier 配置需要下載插件 Prettier - Code formatter 。
package.json中添加命令
{
"scripts": {
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
}
}
運行該命令,會將我們項目中的文件都格式化一遍,**如果添加其他格式的文件,可在該命令中添加,例如:.less
后綴的文件
集成 ESLint 配置
ESLint 是一款用於查找並報告代碼中問題的工具,並且支持部分問題自動修復。其核心是通過對代碼解析得到的 AST(Abstract Syntax Tree 抽象語法樹)進行模式匹配,來分析代碼達到檢查代碼質量和風格問題的能力。
正如前面我們提到的因團隊成員之間編程能力和編碼習慣不同所造成的代碼質量問題,我們使用 ESLint 來解決,一邊寫代碼一邊查找問題,如果發現錯誤,就給出規則提示,並且自動修復,長期下去,可以促使團隊成員往同一種編碼風格靠攏。
安裝 ESLint
可以全局或者本地安裝,作者推薦本地安裝(只在當前項目中安裝)。
npm i eslint -D
配置 ESLint
ESLint 安裝成功后,執行 npx eslint --init
,然后按照終端操作提示完成一系列設置來創建配置文件。
-
How would you like to use ESLint? (你想如何使用 ESLint?)
選擇 To check syntax, find problems, and enforce code style(檢查語法、發現問題並強制執行代碼風格)
-
What type of modules does your project use?(你的項目使用哪種類型的模塊?)
選擇 JavaScript modules (import/export)
-
Which framework does your project use? (你的項目使用哪種框架?)
選擇 Vue.js
-
Does your project use TypeScript?(你的項目是否使用 TypeScript?)
選擇 Yes
-
Where does your code run?(你的代碼在哪里運行?)
選擇 Browser 和 Node(按空格鍵進行選擇,選完按回車鍵確定)
-
How would you like to define a style for your project?(你想怎樣為你的項目定義風格?)
選擇 Use a popular style guide(使用一種流行的風格指南)
-
Which style guide do you want to follow?(你想遵循哪一種風格指南?)
選擇 Airbnb: github.com/airbnb/java…
ESLint 為我們列出了三種社區流行的 JavaScript 風格指南,分別是 Airbnb、Standard、Google。
這里作者不建議大家去自由配置 ESLint 規則,相信我,這三份 JavaScript 代碼風格指南值得我們反復學習,掌握后,編程能力能上一大台階。
-
What format do you want your config file to be in?(你希望你的配置文件是什么格式?)
選擇 JavaScript
-
Would you like to install them now with npm?(你想現在就用 NPM 安裝它們嗎?)
ESLint 會自動去查找缺失的依賴,我們這里選擇 Yes,使用 NPM 下載安裝這些依賴包。
注意:如果自動安裝依賴失敗,那么需要手動安裝
npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue -D
ESLint 配置文件 .eslintrc.js
會在項目根目錄下自動生成 .eslintrc.js
配置文件:
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ['plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 13,
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'no-console': 'off',
'import/no-unresolved': 'off',
'import/extensions': 'off',
'import/no-absolute-path': 'off',
'import/no-extraneous-dependencies': 'off',
'vue/no-v-model-argument': 'off'
},
globals: {
defineProps: true,
defineEmits: true
}
}
根據項目實際情況,如果我們有額外的 ESLint 規則,也在此文件中追加。
注意:
- VSCode 使用 ESLint 配置文件需要去插件市場下載插件 ESLint 。
雖然,現在編輯器已經給出錯誤提示和修復方案,但需要我們一個一個去點擊修復,還是挺麻煩的。很簡單,我們只需設置編輯器保存文件時自動執行 eslint --fix
命令進行代碼風格修復。
-
VSCode 在
settings.json
設置文件中,增加以下代碼:"editor.codeActionsOnSave": { "source.fixAll.eslint": true }
解決 Prettier 和 ESLint 的沖突
通常大家會在項目中根據實際情況添加一些額外的 ESLint 和 Prettier 配置規則,難免會存在規則沖突情況。
本項目中的 ESLint 配置中使用了 Airbnb JavaScript 風格指南校驗,其規則之一是代碼結束后面要加分號,而我們在 Prettier 配置文件中加了代碼結束后面不加分號的配置項,這樣就有沖突了,會出現用 Prettier 格式化后的代碼,ESLint 檢測到格式有問題的,從而拋出錯誤提示。
解決兩者沖突問題,需要用到 eslint-plugin-prettier 和 eslint-config-prettier。
eslint-plugin-prettier
將 Prettier 的規則設置到 ESLint 的規則中。eslint-config-prettier
關閉 ESLint 中與 Prettier 中會發生沖突的規則。
最后形成優先級:Prettier 配置規則
> ESLint 配置規則
。
-
安裝插件
npm i eslint-plugin-prettier eslint-config-prettier -D
-
在
.eslintrc.js
添加 prettier 插件module.exports = { ... extends: [ 'plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended' // 添加 prettier 插件 ], ... }
這樣,我們在執行 eslint --fix
命令時,ESLint 就會按照 Prettier 的配置規則來格式化代碼,輕松解決二者沖突問題。