參考網站
參考網站
這是一篇完整的 2022 年連結優化指南。
因此,如果你想要取得權威網站的反向連結。
你將會在這篇新指南中,享受本文可操作的技巧。
讓我們往深入其中吧。
Vben Admin
點我跳過 Vben Admin,直接前往配置Vite
介紹
Vue-Vben-Admin 是一個基於 Vue3.0、Vite、Ant-Design-Vue、TypeScript 的後台解決方案,目標是為開發中大型項目提供開箱即用的解決方案。包括二次封裝組件、utils、hooks、動態菜單、權限校驗、按鈕級別權限控制等功能。項目會使用前端較新的技術棧,可以作為項目的啟動模版,以幫助你快速搭建企業級中後台產品原型。也可以作為一個示例,用於學習 vue3
、vite
、ts
等主流技術。該項目會持續跟進最新技術,並將其應用在項目中。
文檔
本地運行文檔
如需本地運行文檔,請拉取代碼到本地。
1
2
3
4
5
6
7
8
| # 拉取代碼
git clone https://github.com/vbenjs/vue-vben-admin-doc
# 安裝依賴
yarn
# 運行項目
yarn dev
|
需要掌握的基礎知識
本項目需要一定前端基礎知識,請確保掌握Vue 的基礎知識,以便能處理一些常見的問題。建議在開發前先學一下以下內容,提前了解和學習這些知識,會對項目理解非常有幫助:
模版
vue-vben-admin
完整版本。該版本主要是提供一些Demo示例及插件的使用集成方式,主要用於參考。如果對項目不是很熟悉,不建議在此基礎上進行開發,請使用下方提供的精簡版本。
vue-vben-admin
精簡版本。刪除了相關示例、無用文件及功能、依賴。可以根據自身需求安裝對應的依賴。因為使用的是 vite,依賴刪除不會導致相關組件或者 hook 發出警告。只在需要的時候安裝對應的庫即可。
vite 插件推薦
vite-plugin-mock - 用於本地及開發環境數據 mock
vite-plugin-html - 用於 html 模版轉換,可以在html文件內進行書寫模版語法
vite-plugin-style-import - 用於組件庫樣式按需引入
vite-plugin-imagemin - 用於打包壓縮圖片資源
vite-plugin-theme - 用於在線切換主題色/黑暗主題適配等主題相關配置
vite-plugin-compression - 用於打包輸出.gz|.br文件
vite-plugin-svg-icons - 快速生成 svg sprite
瀏覽器支持
本地開發推薦使用Chrome 最新版瀏覽器,不支持Chrome 80以下版本。
生產環境支持現代瀏覽器,不支持IE。
創建VbenLearn項目
使用Vite創建項目
vite官方文檔
1
2
3
4
5
6
7
8
| # 官網上的創建項目命令
yarn create @vitejs/app
# 名稱
vben-learn
# vue-ts的模板
√ vue-ts
|
yarn報錯
文件名、目錄名或卷標語法不正確。報錯,參考鏈接
安裝 vscode 插件
安裝插件
VbenAdmin 官方推荐
獲取 Vben Admin
從 GitHub 獲取代碼
1
| git clone https://github.com/vbenjs/vue-vben-admin.git
|
從 Gitee 獲取代碼
1
| git clone https://gitee.com/annsion/vue-vben-admin.git
|
注意
Gitee 的代碼可能不是最新的
imagemin 依賴安裝失敗解決方法
由於 imagemin 在國內安裝困難,提供以下幾個解決方案:
- 使用 yarn 在 package.json 內配置(推薦,項目內已集成,前提是必須使用 yarn)
package.json
1
2
3
4
5
| // ...
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
}
// ...
|
- 使用 npm,在電腦 host 文件加上如下配置即可
etc/hosts
1
| 199.232.4.133 raw.githubusercontent.com
|
npm script
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| // ...
"scripts": {
# 安裝依賴
"bootstrap": "yarn install",
# 運行項目
"serve": "npm run dev",
# 運行項目
"dev": "vite",
# 構建項目
"build": "vite build && esno ./build/script/postBuild.ts",
# 清空緩存後構建項目
"build:no-cache": "yarn clean:cache && npm run build",
# 生成打包分析,在 `Mac OS` 電腦上執行完成後會自動打開界面,在 `Window` 電腦上執行完成後需要打開 `./build/.cache/stats.html` 查看
"report": "cross-env REPORT=true npm run build",
# 類型檢查
"type:check": "vue-tsc --noEmit --skipLibCheck",
# 預覽打包後的內容(先打包在進行預覽)
"preview": "npm run build && vite preview",
# 直接預覽本地 dist 文件目錄
"preview:dist": "vite preview",
# 生成 ChangeLog
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
# 刪除緩存
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
# 刪除 node_modules (`window` 系統手動刪除該目錄較慢,可以使用該命令來進行刪除)
"clean:lib": "rimraf node_modules",
# 執行 eslint 校驗,並修復部分問題
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
# 執行 prettier 格式化(該命令會對項目所有代碼進行 prettier 格式化,請謹慎執行)
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
# 執行 stylelint 格式化
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged",
# 對打包結果進行 gzip 測試
"test:gzip": "http-server dist --cors --gzip -c-1",
# 對打包目錄進行 brotli 測試
"test:br": "http-server dist --cors --brotli -c-1",
# 重新安裝依賴,見下方說明
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
"install:husky": "is-ci || husky install",
# 生成圖標集,見下方說明
"gen:icon": "esno ./build/generate/icon/index.ts",
"postinstall": "npm run install:husky"
},
// ...
|
目錄說明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| .
├── build # 打包腳本相關
│ ├── config # 配置文件
│ ├── generate # 生成器
│ ├── script # 腳本
│ └── vite # vite配置
├── mock # mock文件夾
├── public # 公共靜態資源目錄
├── src # 主目錄
│ ├── api # 接口文件
│ ├── assets # 資源文件
│ │ ├── icons # icon sprite 圖標文件夾
│ │ ├── images # 項目存放圖片的文件夾
│ │ └── svg # 項目存放svg圖片的文件夾
│ ├── components # 公共組件
│ ├── design # 樣式文件
│ ├── directives # 指令
│ ├── enums # 枚舉/常量
│ ├── hooks # hook
│ │ ├── component # 組件相關hook
│ │ ├── core # 基礎hook
│ │ ├── event # 事件相關hook
│ │ ├── setting # 配置相關hook
│ │ └── web # web相關hook
│ ├── layouts # 佈局文件
│ │ ├── default # 默認佈局
│ │ ├── iframe # iframe佈局
│ │ └── page # 頁面佈局
│ ├── locales # 多語言
│ ├── logics # 邏輯
│ ├── main.ts # 主入口
│ ├── router # 路由配置
│ ├── settings # 項目配置
│ │ ├── componentSetting.ts # 組件配置
│ │ ├── designSetting.ts # 樣式配置
│ │ ├── encryptionSetting.ts # 加密配置
│ │ ├── localeSetting.ts # 多語言配置
│ │ ├── projectSetting.ts # 項目配置
│ │ └── siteSetting.ts # 站點配置
│ ├── store # 數據倉庫
│ ├── utils # 工具類
│ └── views # 頁面
├── test # 測試
│ └── server # 測試用到的服務
│ ├── api # 測試服務器
│ ├── upload # 測試上傳服務器
│ └── websocket # 測試ws服務器
├── types # 類型文件
├── vite.config.ts # vite配置文件
└── windi.config.ts # windcss配置文件
|
Vite配置多環境
說明:
就好像之前在Vue-Cli中的env一樣。
Vite官網說明
VbenAdmin官方說明
項目的環境變量配置位於項目根目錄下的 .env、.env.development、.env.production
1
2
3
4
| .env # 在所有的環境中被載入
.env.local # 在所有的環境中被載入,但會被 git 忽略
.env.[mode] # 只在指定的模式中被載入
.env.[mode].local # 只在指定的模式中被載入,但會被 git 忽略
|
創建默認配置文件
根目錄下創建:.env
文件
1
2
3
4
5
6
7
8
| # 運行的端口
VITE_PORT = 3100
# 應用名稱
VITE_GLOB_APP_TITLE = Vben Admin
# 應用短名稱
VITE_GLOB_APP_SHORT_NAME = vue_vben_admin
|
創建生產配置文件
根目錄下創建:.env.production
文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # 是否使用mock
VITE_USE_MOCK = true
# 公共路徑
VITE_PUBLIC_PATH = /
# 是否刪除所有日誌打印
VITE_DROP_CONSOLE = true
# 是否啟用gzip或brotli壓縮。
# 可選:gzip | brotli | none
# 如果你需要多個表格,你可以使用`,`來分隔。
VITE_BUILD_COMPRESS = 'none'
# 應用基本接口地址
VITE_GLOB_API_URL=/api
# 文件上傳地址,可選
# 可以通過nginx轉發或直接寫入實際地址。
VITE_GLOB_UPLOAD_URL=/upload
# 接口前綴
VITE_GLOB_API_URL_PREFIX=
# 是否啟用圖像壓縮
VITE_USE_IMAGEMIN= true
#使用PWA
VITE_USE_PWA = false
# 是否與舊版瀏覽器兼容
VITE_LEGACY = false
|
創建開發配置文件
根目錄下創建:.env.development
文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| VITE_PORT = 3100
# 是否使用mock
VITE_USE_MOCK = true
# 公共路徑
VITE_PUBLIC_PATH = /
# 跨域代理,你可以配置多個代理。
VITE_PROXY=[["/api","http://localhost:3000"],["/upload","http://localhost:3001/upload"]]
# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
# 是否刪除所有日誌打印
VITE_DROP_CONSOLE = false
# 應用基本接口地址
VITE_GLOB_API_URL=/api
# 文件上傳地址,可選
VITE_GLOB_UPLOAD_URL=/upload
# 接口前綴
VITE_GLOB_API_URL_PREFIX=
|
測試配置
src\App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <template>
<div>{{ vitePort }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "App",
setup() {
const vitePort = ref(import.meta.env.VITE_PORT);
return {
vitePort,
};
},
});
</script>
|
生產環境動態配置,可參考 Vben Admin 官方說明
配置TS
說明
我們使用Vite
創建的項目時使用的是vue-ts
模板,所以在創建項目的時候package.json
就自帶了typescript
。該依賴會編譯我們的ts
文件。那麼是依賴我們就可以配置它。配置內容還是照搬Vben
的,加上註釋和規則來源。
創建配置文件
根目錄下創建:tsconfig.json文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
| {
"compilerOptions": {
// ↓ 指定ECMAScript目標版本,esnext為最新版本
"target": "esnext",
// ↓ 指定生成哪個模塊系統代碼,esnext為最新版本
"module": "esnext",
// ↓ 決定如何處理模塊。
"moduleResolution": "node",
// ↓ 啟用所有嚴格類型檢查選項。
"strict": true,
// ↓ 禁止對同一個文件的不一致的引用。
"forceConsistentCasingInFileNames": true,
// ↓ 允許從沒有設置默認導出的模塊中默認導入。這並不影響代碼的輸出,僅為了類型檢查。
"allowSyntheticDefaultImports": true,
// ↓ 禁用函數參數雙向協變檢查。
"strictFunctionTypes": false,
// ↓ 在 .tsx文件裡支持JSX
"jsx": "preserve",
// ↓ 解析非相對模塊名的基準目錄。查看 模塊解析文檔了解詳情。
"baseUrl": ".",
// ↓ 允許編譯javascript文件。
"allowJs": true,
// ↓ 生成相應的 .map文件。
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
// ↓ 若有未使用的局部變量則拋錯。
"noUnusedLocals": true,
// ↓ 若有未使用的參數則拋錯。
"noUnusedParameters": true,
// ↓ 啟用實驗性的ES裝飾器。
"experimentalDecorators": true,
// ↓ 編譯過程中需要引入的庫文件的列表。
"lib": ["dom", "esnext"],
// ↓ 要包含的類型聲明文件名列表。
"types": ["vite/client"],
// ↓ 要包含的類型聲明文件路徑列表。
"typeRoots": ["./node_modules/@types/", "./types"],
"incremental": true,
// ↓ 在表達式和聲明上有隱含的 any類型時報錯。
"noImplicitAny": false,
// ↓ 忽略所有的聲明文件( *.d.ts)的類型檢查。
"skipLibCheck": true,
// ↓ 模塊名到基於 baseUrl的路徑映射的列表。查看 模塊解析文檔了解詳情。
"paths": {
"/@/*": ["src/*"],
"/#/*": ["types/*"]
}
},
// ↓ 指定一個匹配列表(屬於自動指定該路徑下的所有ts相關文件)
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"types/**/*.d.ts",
"types/**/*.ts",
"mock/**/*.ts"
],
// ↓ 指定一個排除列表(include的反向操作)
"exclude": ["node_modules", "dist", "**/*.js"]
}
|
這裡配置完ESLint之後會報錯,說找不到vite/client。不用管它,當你執行完後面的ESLint-TS的時候就不會報錯了。
安裝 ESLint
說明
ESLint
簡單的來說就是去判斷你的JS
代碼寫的格式對不對的一個依賴。沒有它你的代碼也能運行,有了它你的代碼可以寫的更漂亮。 ESLint
還支持插件,第三方框架會基於ESLint
寫出自己的代碼檢查插件。比如Vue3
對應eslint-plugin-vue
。
安裝 ESLint
配置ESLint:根目錄下創建:.eslintrc.js
文件。
這裡可以使用eslint的init。但是我不清楚Vben的初始化操作是怎麼選的,所以直接拷貝Vben的配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| module.exports = {
// ↓ 默認情況下,ESLint 會在所有父級目錄裡尋找配置文件,一直到根目錄。如果你想要你所有項目都遵循一個特定的約定時,這將會很有用,但有時候會導致意想不到的結果。為了將 ESLint 限製到一個特定的項目,在你項目根目錄下的 package.json 文件或者 .eslintrc.* 文件裡的 eslintConfig 字段下設置 "root": true。 ESLint 一旦發現配置文件中有 "root": true,它就會停止在父級目錄中尋找。
// ↓ 此項是用來告訴eslint找當前配置文件不能往父級查找
root: true,
// ↓ 指定你想啟用的環境
env: {
browser: true,
node: true,
es6: true,
},
// ↓ 設置解析器
parser: "",
// ↓ 解析器選項
parserOptions: {},
// ↓ 擴展項
extends: [],
// ↓ 自定義規則配置
rules: {}
}
|
配置ESLint忽略文件:根目錄下創建:.eslintignore
文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| *.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile
|
安裝 eslint-plugin-vue
說明
ESLint
是檢查JS代碼的依賴,那麼它怎麼去檢查Vue
語法的文件呢?要完成這件事,我們就需要安裝vue
官方開發的ESLint
插件eslint-plugin-vue
。這樣ESLint
就知道該怎麼檢查vue
的文件了。
怎麼理解eslint-plugin-vue
和vue-eslint-parser
的關係呢?
ESLint
會對我們的代碼進行校驗,而 parser
的作用是將我們寫的代碼轉換為 ESTree
,ESLint
會對 ESTree
進行校驗。
vue-eslint-parser
文檔上說是vue
的模板解析器。 vue-eslint-parser
的文檔中強調<template>
標籤中的內容進行檢查。
那麼我的理解是vue-eslint-parser
將vue
文件轉換成ESTree
。然後使用eslint-plugin-vue
來檢查這個ESTree
。查出的結果交給ESLint
。
vue-eslint-parser
的官方說明:
This parser allows us to lint the <template>
of .vue files. We can make mistakes easily on <template>
if we use complex directives and expressions in the template. This parser and the rules of eslint-plugin-vue would catch some of the mistakes.
這個解析器允許我們對.vue
文件的<template>
進行檢查。如果我們在模板中使用了複雜的指令和表達式,我們很容易在<template>
上出錯。這個解析器和eslint-plugin-vue的規則可以發現一些錯誤。
總之eslint-plugin-vue
和vue-eslint-parser
需要一起使用,而且官網也有標示怎麼操作。
安裝兩個 npm 插件
1
| yarn add eslint-plugin-vue vue-eslint-parser --dev
|
將插件配置進ESLint
配置ESLint:根目錄下修改:.eslintrc.js
文件
.eslintrc.js
1
2
3
4
5
6
7
8
9
| module.exports = {
// ...
parser: 'vue-eslint-parser',
// ...
extends: [
'plugin:vue/vue3-recommended',
],
// ...
}
|
安裝 @typescript-eslint
說明
這裡同理需要安裝 @typescript-eslint/eslint-plugin
和 @typescript-eslint/parser
。
安裝TS的Lint
1
| yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser --dev
|
將插件配置進ESLint
配置ESLint:根目錄下修改:.eslintrc.js
文件
.eslintrc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| module.exports = {
// ...
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
ecmaFeatures: {
jsx: true,
tsx: true,
},
},
extends: ["plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended"],
// ...
}
|
安裝 eslint-plugin-prettier
說明
prettier
用來格式化代碼的。一般IDE都有prettier
的插件,在保存的時候格式化代碼。那麼我們eslint-plugin-prettier
就是通過JS來判斷代碼格式是否正確。
一般這很必要,因為不同的程序員有不同的IDE,不同的IDE有不同的prettier
插件。但對於項目來說只有一個eslint-plugin-prettier
。
而eslint-plugin-prettier
插件依賴於prettier
依賴。那麼eslint-config-prettier
插件又是幹嘛的?
prettier
和ESLint
之間有些規則不一樣,因此是用於解決衝突得。eslint-config-prettier
將prettier
一些規則默認關閉了。
安裝
1
| yarn add prettier eslint-plugin-prettier eslint-config-prettier --dev
|
配置prettier
prettier的配置項
根目錄下創建:prettier.config.js
文件
prettier.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
vueIndentScriptAndStyle: true,
singleQuote: true,
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'es5',
jsxBracketSameLine: false,
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf',
rangeStart: 0
}
|
配置prettier忽略文件
根目錄下創建:.prettierignore
文件
.prettierignore
1
2
3
4
5
6
7
8
9
| /dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
|
配置進ESLint
配置ESLint:修改根目錄下:.eslintrc.js
文件
.eslintrc.js
1
2
3
4
5
6
7
8
9
10
| module.exports = {
// ...
extends: [
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended",
],
// ...
}
|
自定義ESLint檢查規則
說明
ESLint
是用來檢查代碼的。又安裝了那麼多的插件。但是官方默認的方案有時不一樣符合我們的要求。我們需要自定義自己的規則。
修改規則主要是修改根目錄下.eslintrc.js
文件的rules
字段。
修改規則
配置ESLint:根目錄下:.eslintrc.js
文件
.eslintrc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
| module.exports = {
// ...
rules: {
// ↓ 禁止使用@ts-ignore來消除ESLint檢查
'@typescript-eslint/ban-ts-ignore': 'off',
// ↓ 在函數和類方法上需要顯式的返回類型
'@typescript-eslint/explicit-function-return-type': 'off',
// ↓ 禁止使用any類型
'@typescript-eslint/no-explicit-any': 'off',
// ↓ 除導入語句外,禁止使用require語句
'@typescript-eslint/no-var-requires': 'off',
// ↓ 禁止使用空函數
'@typescript-eslint/no-empty-function': 'off',
// ↓ 對自定義事件名稱強制使用特定的大小寫
'vue/custom-event-name-casing': 'off',
// ↓ 禁止定義前使用
'no-use-before-define': 'off',
// ↓ 在定義變量之前不允許使用變量
'@typescript-eslint/no-use-before-define': 'off',
// ↓ 禁止使用@ts-註解
'@typescript-eslint/ban-ts-comment': 'off',
// ↓ 禁止使用特定類型
'@typescript-eslint/ban-types': 'off',
// ↓ 禁止使用!後綴運算符進行非null斷言
'@typescript-eslint/no-non-null-assertion': 'off',
// ↓ 在導出的函數和類的公共類方法上需要顯式的返回值和參數類型
'@typescript-eslint/explicit-module-boundary-types': 'off',
// ↓ 禁止使用未使用的變量
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$',
},
],
// ↓ 禁止使用未使用的變量
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$',
},
],
// ↓ 在函數括號前需要或不允許有空格
'space-before-function-paren': 'off',
// ↓ 強制屬性順序
'vue/attributes-order': 'off',
// ↓ 強制每個組件應位於其自己的文件中
'vue/one-component-per-file': 'off',
// ↓ 在標籤的右括號之前要求或不允許換行
'vue/html-closing-bracket-newline': 'off',
// ↓ 強制每行的最大屬性數
'vue/max-attributes-per-line': 'off',
// ↓ 在多行元素的內容之前和之後需要換行
'vue/multiline-html-element-content-newline': 'off',
// ↓ 在單行元素的內容之前和之後需要換行
'vue/singleline-html-element-content-newline': 'off',
// ↓ 在模板中的自定義組件上實施屬性命名樣式
'vue/attribute-hyphenation': 'off',
// ↓ 需要道具的默認值
'vue/require-default-prop': 'off',
// ↓ 實施自我封閉的風格
// 'vue/html-self-closing': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always'
}
]
}
}
|
vben項目配置git忽略文件
說明
這個應該是項目一上來就要處理的,但是我手撕Vben的時候,按照配置文件的順序下來,到現在才處理了。
創建Git忽略文件
根目錄下創建:.gitignore
文件
.gitignore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| node_modules
.DS_Store
dist
dist-ssr
*.local
# other
.npmrc
.cache
test/upload-server/static
.local
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
# .vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
|
安裝style-lint
說明
這次需要安裝四個插件stylelint
、stylelint-config-standard
、stylelint-config-prettier
、stylelint-order
。
stylelint
是對我們編寫的樣式進行檢查的插件。stylelint-config-standard
是stylelint
擴展的檢查標準庫。stylelint-config-prettier
是用來解決衝突的(估計又和ESLint衝突了,參考之前的prettier
)。stylelint-order
是檢查我們樣式編寫順序的。
參考連結
安裝
1
| yarn add stylelint stylelint-config-standard stylelint-config-prettier stylelint-order --dev
|
配置stylelint
根目錄下創建:stylelint.config.js
文件
stylelint.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| module.exports = {
// ↓ 這個好像是沒有用的,但是VbenAdmin是這麼寫的
root: true,
// ↓ 插件
plugins: ['stylelint-order'],
// ↓ 擴展
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
// ↓ 自定義規則
rules: {},
// ↓ 忽略檢查的文件
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts']
}
|
配置自定義樣式規則
根目錄下修改:stylelint.config.js
文件
stylelint.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
| module.exports = {
// ...
rules: {
// ↓ 禁止使用未知的偽類選擇器。
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global']
}
],
// ↓ 禁止使用未知規則。
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin']
}
],
// ↓ 禁止空來源。
'no-empty-source': null,
// ↓ 禁止使用無效的命名網格區域。
'named-grid-areas-no-invalid': null,
// ↓ 要求或不允許使用Unicode字節順序標記。
'unicode-bom': 'never',
// ↓ 禁止較低特異性的選擇器在覆蓋較高特異性的選擇器之後出現。
'no-descending-specificity': null,
// ↓ 禁止在字體系列名稱列表中缺少通用系列。
'font-family-no-missing-generic-family-keyword': null,
// ↓ 在聲明的冒號後面需要一個空格或禁止空格。
'declaration-colon-space-after': 'always-single-line',
// ↓ 在聲明的冒號之前需要一個空格或禁止使用空格。
'declaration-colon-space-before': 'never',
// ↓ 在聲明塊內要求或不允許尾隨分號。
'declaration-block-trailing-semicolon': 'always',
// ↓ 在規則之前要求或禁止使用空行。
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested'],
}
],
// ↓ 禁止使用未知單位。
'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
// Specify the alphabetical order of the attributes in the declaration block
// ↓ 樣式順序
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'content',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak'
]
}
// ...
}
|
配置stylelint忽略文件
根目錄下創建:.stylelintignore
文件
.stylelintignore
配置Yarn自動清除功能
說明
實現每一次install
之後、add
之後、yarn autoclean --force
之後。從程序包依賴項中清除並刪除不必要的文件。
參考鏈接:yarn autoclean-官方說明
初始化
執行完命令之後,Yarn就會自動在根目錄下創建一個.yarnclean
文件,這樣就可以了。
安裝PostCSS
說明
我們需要安裝兩個庫postcss
、autoprefixer
。要現實的效果就是我們編寫一般的樣式,渲染頁面的dom
節點有面向各種瀏覽器廠商的樣式。
網上說postcss
類似一個平台,上面有很多插件,autoprefixer
就其中一個。
我以我的看法來解釋一下:postcss
就像一個管子,autoprefixer
是裡面的過濾器。你寫的css
從管子中流過,經過他們的處理就出現了不同的結果。
參考連結:
安裝
1
| yarn add postcss autoprefixer --dev
|
配置postcss
根目錄下:postcss.config.js
postcss.config.js
1
2
3
4
5
| module.exports = {
plugins: {
autoprefixer: {}
}
}
|
解析VbenAdmin中的script
說明
VbenAdmin官方文檔-npm Script
一下腳本直接添加在根目錄下的package.json
的scripts
對象中即可。也可以直接照搬VbenAdmin
的。
以bootstrap
為例。如下:
1
2
3
| yarn bootstrap
# or
# npm run bootstrap
|
bootstrap
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"bootstrap": "yarn install",
// ...
}
|
作用:使用yarn安裝所有依賴。
serve
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"serve": "npx --max_old_space_size=4096 vite",
// ...
}
|
作用:以開發環境運行項目,並設置node
的運行內存為4G
。
dev
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"dev": "npx --max_old_space_size=4096 vite",
// ...
}
|
作用:同serve
。
build
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"build": "vite build",
// ...
}
|
作用:這裡先不管。先使用原來創建項目的"build": "vite build"
。後面完成了vite
配置之後再處理這個腳本。
build:no-cache
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"build:no-cache": "yarn clean:cache && npm run build",
// ...
}
|
作用:清空緩存後構建。
report
作用:生成打包分析,這裡先不管,等後面配置vite再說。
preview
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"preview": "npm run build && vite preview",
// ...
}
|
作用:先打包在進行預覽,使用vite
訪問dist
目錄。
preview:dist
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"preview:dist": "vite preview",
// ...
}
|
作用:直接預覽本地 dist
文件目錄。
log
詳情查閱:使用conventional-changelog-cli生成變更記錄
作用:用來生產Git提交記錄的。
clean:cache
作用:刪除緩存,刪除node_modules/.cache/
和node_modules/.vite
下的文件,由於刪除命令依賴還沒安裝,所以後面再講。
clean:lib
作用:清除依賴,刪除node_modules
文件夾,由於刪除命令依賴還沒安裝,所以後面再講。
lint:eslint
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
// ...
}
|
作用:執行 eslint
校驗,該命令會對項目的src
、mock
目錄下的vue
、ts
、tsx
文件進行 eslint
校驗,並修復部分問題。
參考連結:eslint命令行說明
lint:prettier
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
// ...
}
|
作用:執行 prettier
格式化代碼。該命令會對項目所有代碼 進行 prettier
格式化。謹慎執行。
如果你的vsCode
安裝了prettier
插件。那麼prettier
插件就會讀取根目錄下的prettier.config.js
文件。當你保存的時候就會進行格式化文件。
但是過是別人提交上來的文件,你還去點開它的文件一個一個保存麼?所以執行這個命令,可以將src
目錄下,所有的文件格式化。
參考連結:prettier命令行說明
lint:stylelint
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
// ...
}
|
作用:校驗所有文件的樣式格式,並嘗試修復。
參考連結:stylelint命令行说明
lint:ls-lint
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:ls-lint": "ls-lint",
// ...
}
|
作用:校驗所有文件命名是否正確。
lint:lint-staged
需先創建lint-staged的配置文件
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
// ...
}
|
作用:對Git暫存的文件進行lint檢查。
lint:pretty
需先創建lint-staged的配置文件
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"lint:pretty": "pretty-quick --staged",
// ...
}
|
作用:對Git暫存文件進行pretty的操作。
test:gzip
作用:這個後面再說,http-server的npm首頁。
test:br
作用:這個後面再說,http-server的npm首頁。
reinstall
作用:重新安裝依賴,該命令會先刪除 node_modules、yarn.lock、package.lock.json後在進行依賴重新安裝,速度會明顯變慢。
由於刪除命令依賴還沒安裝,所以後面再講。
install:husky
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"install:husky": "is-ci || husky install",
// ...
}
|
作用:這個是用來初始化husky的。啟用Git掛鉤。
說明:如果不是CI服務器,就啟動Git鉤子。
gen:icon
作用:生成圖標的,後面再說。
postinstall
需先配置 husky
代碼:
package.json
1
2
3
4
5
| "scripts": {
// ...
"postinstall": "npm run install:husky",
// ...
}
|
作用:執行install:husky
命令。這個命令會自動啟動Git鉤子,該操作參考:手動初始化husky和Yarn2下怎麼自動啟動Git鉤子函數。
彙總
package.json
除腳本、運行依賴、開發依賴外的字段參考官方文檔。
yarn -v
的腳本是後面要說的腳本。
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| {
// ...
"scripts": {
"bootstrap": "yarn install",
"serve": "npx --max_old_space_size=4096 vite",
"dev": "npx --max_old_space_size=4096 vite",
"build": "vite build",
"build:no-cache": "yarn clean:cache && npm run build",
"report": "yarn -v",
"preview": "npm run build && vite preview",
"preview:dist": "vite preview",
"log": "yarn -v",
"clean:cache": "yarn -v",
"clean:lib": "yarn -v",
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:ls-lint": "ls-lint",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged",
"test:gzip": "yarn -v",
"test:br": "yarn -v",
"reinstall": "yarn -v",
"install:husky": "is-ci || husky install",
"gen:icon": "yarn -v",
"postinstall": "npm run install:husky"
},
// ...
}
|
安裝rimraf
說明
由於手動刪除文件夾太慢了,需要實現腳本刪除文件。
參考連結
rimraf的npm首頁
rimraf
的npm
首頁說明:
The UNIX command rm -rf
for node.
Install with npm
install rimraf
, or just drop rimraf.js somewhere.
翻譯:
node
版的Unix系統命令rm -rf
。
用npm
安裝rimraf
,或者直接把rimraf.js
放在某個地方。
雖然這裡可以通過import
使用函數刪除文件,但是我們主要是使用它的命令行蛤。
詳情查看:rimraf的npm首頁-Cil說明。
安裝
重寫腳本
就是根目錄下package.json
的scripts
字段。
clean:cache
package.json
1
2
3
4
5
| "scripts": {
// ...
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
// ...
}
|
目的:刪除緩存,刪除node_modules/.cache/
和node_modules/.vite
下的文件。
clean:lib
package.json
1
2
3
4
5
| "scripts": {
// ...
"clean:lib": "rimraf node_modules",
// ...
}
|
目的:清除依賴,刪除node_modules
文件夾。
reinstall
package.json
1
2
3
4
5
| "scripts": {
// ...
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
// ...
}
|
目的:重新安裝依賴,該命令會先刪除 node_modules
、yarn.lock
、package.lock.json
後在進行依賴重新安裝,速度會明顯變慢。
使用husky規范代碼提交
說明
使用Git提交代碼的時候,需要對暫存的代碼進行如下操作:
- 進行
lint
操作。 - 對
commit
的消息進行格式化檢查。 - 進行
prettier
操作。
需要安裝的依賴有:husky
、lint-staged
、@commitlint/cli
、@commitlint/config-conventional
、pretty-quick
、is-ci
。
husky
:可以在Git的鉤子函數中執行腳本。lint-staged
:針對暫存文件進行lint操作。@commitlint/cli
:對commit的消息進行格式檢查。@commitlint/config-conventional
:commit的消息檢查格式傳統配置,對應還有很多其他配置,比如angular的提交規範@commitlint/config-angular。pretty-quick
:針對暫存文件進行prettier操作。is-ci
:husky
好像不能在ci環境下執行,這個依賴是用來判斷是不是ci
環境的。
參考連結
安裝
1
| yarn add husky lint-staged @commitlint/cli @commitlint/config-conventional pretty-quick is-ci --dev
|
配置commitlint
在根目錄下創建commitlint.config.js
文件。
commitlint.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| module.exports = {
// ↓ 忽略包含init的提交消息
ignores: [(commit) => commit.includes('init')],
// ↓ 按照传统消息格式来验证
extends: ['@commitlint/config-conventional'],
// ↓ 这里是自定义解析器,看不太懂,直接搬代码吧
// ↓ https://commitlint.js.org/#/reference-configuration?id=parser-presets
parserPreset: {
parserOpts: {
headerPattern: /^(\w*|[\u4e00-\u9fa5]*)(?:[\(\(](.*)[\)\)])?[\:\:] (.*)/,
headerCorrespondence: ['type', 'scope', 'subject'],
referenceActions: [
'close',
'closes',
'closed',
'fix',
'fixes',
'fixed',
'resolve',
'resolves',
'resolved'
],
issuePrefixes: ['#'],
noteKeywords: ['BREAKING CHANGE', '不兼容变更'],
fieldPattern: /^-(.*?)-$/,
revertPattern: /^Revert\s"([\s\S]*)"\s*This reverts commit (\w*)\./,
revertCorrespondence: ['header', 'hash'],
warn() {},
mergePattern: null,
mergeCorrespondence: null
}
},
// ↓ 自定义提交消息规则
rules: {
// ↓ body以空白行开头
'body-leading-blank': [2, 'always'],
// ↓ footer以空白行开头
'footer-leading-blank': [1, 'always'],
// ↓ header的最大长度
'header-max-length': [2, 'always', 108],
// ↓ subject为空
'subject-empty': [2, 'never'],
// ↓ type为空
'type-empty': [2, 'never'],
// ↓ type的类型
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types'
]
]
}
};
|
配置husky
啟動Git鉤子
這個時候就在根目錄下創建了.husky
目錄了。 vben將裡面的一個文件夾刪除了。
修復YarnOnWindowsBug
官方參考連結
在帶有Git Bash(stdin is not a tty)的Windows上使用Yarn時,Git掛鉤可能會失敗。如果您有Windows用戶,則強烈建議添加以下解決方法。
- 創建
.husky/common.sh
:
1
2
3
4
5
6
7
8
| command_exists () {
command -v "$1" >/dev/null 2>&1
}
# Workaround for Windows 10, Git Bash and Yarn
if command_exists winpty && test -t 1; then
exec < /dev/tty
fi
|
- 在使用Yarn運行命令的地方將其來源:
1
2
3
4
5
| #!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
yarn ...
|
這裡是yarn命令的地方要這樣用,其他地方要不要無所謂。第二步可以先不管。
創建lint-staged的配置文件
- lint:lint-staged
package.json
1
2
3
| "scripts": {
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js"
}
|
作用:對Git暫存的文件進行lint檢查。
- lint:pretty
package.json
1
2
3
| "scripts": {
"lint:pretty": "pretty-quick --staged"
}
|
作用:對Git暫存文件進行pretty的操作。
- install:husky
package.json
1
2
3
| "scripts": {
"install:husky": "is-ci || husky install"
}
|
作用:如果不是CI服務器,就啟動Git鉤子。
- postinstall
package.json
1
2
3
| "scripts": {
"postinstall": "npm run install:husky"
}
|
作用:執行install:husky命令。這個命令會自動啟動Git鉤子,參考文檔。
給husky添加任務
1
2
3
4
5
6
7
8
9
10
| # 啟動husky
yarn install:husky
# 添加commit消息檢查任務到`.husky/commit-msg`文件中
# 這裡我一直執行不出來,是引號引其的問題,大家可以先創建空的然後把命令複製進去
# npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
npx husky add .husky/commit-msg
# 創建一個任務文件
npx husky add .husky/pre-commit
|
手動修改.husky/commit-msg
文件:
這裡面的$1
一定要用雙引號
,不然後面執行會報錯。
.husky/commit-msg
1
2
3
4
| #!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"
|
手動修改.husky/pre-commit
文件:
參考連結:
最後手動執行之前創建的腳本命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
[ -n "$CI" ] && exit 0
# Check the file name
# ! ls-lint cannot be used normally in mac pro of M1 system.
npm run lint:ls-lint
# Format and submit code according to lintstagedrc.js configuration
npm run lint:lint-staged
npm run lint:pretty
|
測試
失敗測試
最好先重啟你的ide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| git add .
git commit -m "隨便打一些消息"
> [email protected] lint:ls-lint F:\CodeLearn\vben\vben3
> ls-lint
> [email protected] lint:lint-staged F:\CodeLearn\vben\vben3
> lint-staged -c ./.husky/lintstagedrc.js
[STARTED] Preparing...
# 一系列檢查...
[SUCCESS] Cleaning up...
> [email protected] lint:pretty F:\CodeLearn\vben\vben3
> pretty-quick --staged
�🔍 Finding changed files since git revision 1346762.
�🎯 Found 2 changed files.
✅ Everything is awesome!
⧗ input: 隨便打一些消息
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
husky - commit-msg hook exited with code 1 (error)
|
格式
出錯了,我們規範的提交消息試試:
@commitlint/config-conventional的規範+我們自定義的規則。
type: some message
類型英文 | 類型中文 |
---|
build | 建立 |
chore | 雜事 |
ci | ci |
docs | 文檔 |
feat | 壯舉 |
fix | 修復 |
perf | 敷衍 |
refactor | 重構 |
revert | 恢復 |
style | 樣式 |
test | 測試 |
成功測試
提交成功,當然你不用擔心這樣手敲太麻煩了,後面還有插件可以自動幫你敲這些格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| git commit -m "feat: 我們成功的規範了Git提交!"
> [email protected] lint:pretty F:\CodeLearn\vben\vben3
> pretty-quick --staged
�🔍 Finding changed files since git revision 1346762.
�🎯 Found 2 changed files.
✅ Everything is awesome!
[master 6c61fc5] feat: 我們成功的規範了Git提交!
7 files changed, 104 insertions(+), 4 deletions(-)
create mode 100644 .husky/.gitignore
create mode 100644 .husky/commit-msg
create mode 100644 .husky/common.sh
create mode 100644 .husky/lintstagedrc.js
create mode 100644 .husky/pre-commit
# 推送出去
git push -u origin master
|
使用conventional-changelog-cli生成變更記錄
說明
在VbenAdmin
項目的腳本命令和根目錄中還有CHANGELOG.md
這麼個文件,這是記錄我們項目變更記錄的MD。明顯不是手寫出來的,那麼怎麼生成?
參考連結:
安裝
1
| yarn add global conventional-changelog-cli --dev
|
配置腳本
package.json
文件中的scripts
字段。
package.json
1
2
3
4
5
| "scripts": {
// ...
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
// ...
}
|
測試
可以看見生成了CHANGELOG.md
文件
1
2
3
4
5
6
|
# 0.0.0 (2022-10-13)
### Features
- 我们成功的规范了 Git 提交! ([6c61fc5](https://gitee.com/kuxiaoxin/vben3/commits/6c61fc5c0bc9d0ed551117eb00349ce82e35deea))
|
命令列參數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| > conventional-changelog --help
Generate a changelog from git metadata
Usage
conventional-changelog
Example
conventional-changelog -i CHANGELOG.md --same-file
Options
-i, --infile Read the CHANGELOG from this file
# 從這個文件中讀取CHANGELOG。
-o, --outfile Write the CHANGELOG to this file
If unspecified, it prints to stdout
# 將CHANGELOG寫入這個文件。
# 如果沒有指定,則打印到stdout。
-s, --same-file Outputting to the infile so you don\'t need to specify the same file as outfile
# 輸出到infile,所以你不需要指定與outfile相同的文件。
-p, --preset Name of the preset you want to use. Must be one of the following:
angular, atom, codemirror, ember, eslint, express, jquery, jscs or jshint
# 您要使用的預設名稱。必須是下列之一。
# angular, atom, codemirror,
# ember, eslint, express,
# jquery, jscs, jshint。
-k, --pkg A filepath of where your package.json is located
Default is the closest package.json from cwd
# 您的 package.json 所在的文件路徑。
# 默認為cwd中最接近的package.json。
-a, --append Should the newer release be appended to the older release
Default: \false
# 是否應將較新的版本附加到較舊的版本上?
# 默認:不追加
-r, --release-count How many releases to be generated from the latest
If 0, the whole changelog will be regenerated and the outfile will be overwritten
Default: 1
# 將從最新的版本中生成多少個版本。
# 如果為0,整個變更日誌將被重新生成,並且輸出文件將被覆蓋。
# 默認值: 1
--skip-unstable If given, unstable tags will be skipped, e.g., x.x.x-alpha.1, x.x.x-rc.2
# 如果給定,將跳過不穩定的標籤,例如,x.x.x-alpha.1, x.x.x-rc.2。
-u, --output-unreleased Output unreleased changelog
# 輸出未發布的變更日誌。
-v, --verbose Verbose output. Use this \for debugging
Default: \false
# Verbose 輸出。用來調試
# 默認:不輸出
-n, --config A filepath of your config script
Example of a config script: https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-cli/test/fixtures/config.js
# 配置腳本的文件路徑。
# 配置腳本的例子:
# https://github.com/conventional-changelog
# /conventional-changelog/blob/master/packages
# /conventional-changelog-cli/test/fixtures/config.js
-c, --context A filepath of a json that is used to define template variables
# 一個用於定義模板變量的json文件路徑。
-l, --lerna-package Generate a changelog \for a specific lerna package (:[email protected])
# 為一個特定的lerna包生成一個變更日誌(:[email protected])
-t, --tag-prefix Tag prefix to consider when reading the tags
# 讀取標籤時要考慮的標籤前綴。
--commit-path Generate a changelog scoped to a specific directory
# 生成一個特定目錄下的變更日誌。
|
配置Vite
說明
本章只是準備,大家可以直接把VbenAdmin的vite.config.ts
原代碼複製到我們的項目中。然後把報錯的地方註釋掉。接下來的章節會把這個文件一段一段的解析出來。章節做太長了大家應該也不喜歡看,至少我不喜歡看長文章。
代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
| import type { UserConfig, ConfigEnv } from 'vite';
import { loadEnv } from 'vite';
import { resolve } from 'path';
// import { generateModifyVars } from './build/config/themeConfig';
// import { createProxy } from './build/vite/proxy';
// import { wrapperEnv } from './build/utils';
// import { createVitePlugins } from './build/vite/plugin';
// import { OUTPUT_DIR } from './build/constant';
function pathResolve(dir: string) {
return resolve(__dirname, '.', dir);
}
export default ({ command, mode }: ConfigEnv): UserConfig => {
const root = process.cwd()
const env = loadEnv(mode, root)
// The boolean type read by loadEnv is a string. This function can be converted to boolean type
// const viteEnv = wrapperEnv(env);
// const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY, VITE_DROP_CONSOLE, VITE_LEGACY } = viteEnv;
// const isBuild = command === 'build';
return {
// base: VITE_PUBLIC_PATH,
root,
resolve: {
alias: [
{
// /@/xxxx => src/xxx
find: /^\/@\//,
replacement: pathResolve('src') + '/'
},
{
// /#/xxxx => types/xxx
find: /^\/#\//,
replacement: pathResolve('types') + '/'
}
]
},
server: {
// port: VITE_PORT,
// Load proxy configuration from .env
// proxy: createProxy(VITE_PROXY),
hmr: {
overlay: true
}
},
build: {
// minify: 'esbuild',
// outDir: OUTPUT_DIR,
// polyfillDynamicImport: VITE_LEGACY,
terserOptions: {
compress: {
keep_infinity: true,
// Used to delete console in production environment
// drop_console: VITE_DROP_CONSOLE,
},
},
// Turning off brotliSize display can slightly reduce packaging time
brotliSize: false,
chunkSizeWarningLimit: 1200
},
define: {
// setting vue-i18-next
// Suppress warning
__VUE_I18N_LEGACY_API__: false,
__VUE_I18N_FULL_INSTALL__: false,
__INTLIFY_PROD_DEVTOOLS__: false
},
css: {
preprocessorOptions: {
less: {
modifyVars: {
// Used for global import to avoid the need to import each style file separately
// reference: Avoid repeated references
hack: `true; @import (reference) "${resolve('src/design/config.less')}";`
// ...generateModifyVars(),
},
javascriptEnabled: true
}
}
},
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
// plugins: createVitePlugins(viteEnv, isBuild),
optimizeDeps: {
// @iconify/iconify: The dependency is dynamically and virtually loaded by @purge-icons/generated, so it needs to be specified explicitly
include: [
'@iconify/iconify',
'ant-design-vue/es/locale/zh_CN',
'moment/dist/locale/zh-cn',
'ant-design-vue/es/locale/en_US',
'moment/dist/locale/eu',
],
exclude: ['vue-demi']
}
}
}
|
Node的path.resolve方法
說明
psth.resolve([...paths])
方法是NodeApi
中path
模塊的方法。
參考連結
VbenAdmin原代碼
1
2
3
4
5
| import { resolve } from 'path'
function pathResolve(dir: string) {
return resolve(__dirname, '.', dir)
}
|
代碼在項目中報錯
此時引入項目會報錯:找不到 path
。
因為我們沒有安裝node
的聲明文件,ts裡面沒有。
1
| yarn add @types/node --dev
|
這個開發依賴在原VbenAdmin項目中沒有,是因為其他依賴中有這個依賴。
但是我們現在還沒有安裝包含@types/node的依賴。
剩下的依賴裡面有很多這樣的依賴,我們也不去猜是哪個依賴了。
反正是一個開發依賴,我們直接自己手動安裝了。
resolve方法
該方法百度上有兩種說法:
- 從後向前,生成絕對路徑。
- 若字符以
/
開頭,不會拼接到前面的路徑(因為拼接到此已經是一個絕對路徑)。 - 若以
../
開頭,拼接前面的路徑,且不含最後一節路徑。 - 若以
./
開頭 或者沒有符號 則拼接前面路徑。 - 需要注意的是:如果在處理完所有給定的 path 片段之後還未生成絕對路徑,則再加上當前工作目錄。
- 每一個參數都理解為一個cd命令。最終cd到哪個路徑。返回該絕對路徑。
__dirname
NodeApi
的__dirname
:當前模塊目錄名。
在線運行
1
2
3
4
5
6
| const path = require('path')
console.log(__dirname)
console.log(path.resolve(__dirname, '.', 'src'))
// /tmp/B3YQQ1
// /tmp/B3YQQ1/src
|
Vite的情景配置
說明
本章收集了一些Vite
如何根據不同環境進行不同配置的資料。
參考連結
代碼
總之不管使用什麼方式配置Vite
,最後返回的就只是一個對象
。對像中包含Vite
配置的細節。
1
2
3
4
5
6
7
8
9
10
11
| export default ({ command, mode }) => {
if (command === "serve") {
return {
// serve 獨有配置
}
} else {
return {
// build 獨有配置
}
}
}
|
參數說明
vite
目前只有vite
、vite build
、vite preview
這三種命令。以下是這三種命令的command
和mode
的值。
- vite
- command:
serve
- mode:
development
- vite build
- command:
build
- mode:
production
- vite preview
- command:
serve
- mode:
development
Node的process
說明
返回 Node.js 進程的當前工作目錄。
參考連結
代碼
Vite的loadEnv方法
說明
- 檢查
process.cwd()
路徑下.env.development.local
、.env.development
、.env.local
、.env
這四個環境文件。 - 輸出
NODE_ENV
和VITE_
開頭的鍵值對。 VITE_
開頭的鍵值對後面的不會覆蓋前面的。NODE_ENV
的值後面的會覆蓋前面的。
參考連結
原始碼
前置代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| // ↓ 加載node的path模塊
var path$1 = require('path');
var fs$2 = require('fs');
// ↓ 傳入的如果是一個包含'default'的key的對象,則輸出對象的'default'的key對應的value。
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e['default'] : e; }
// ↓ 結合上面二者
var path__default = /*#__PURE__*/_interopDefaultLegacy(path$1);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$2);
// ↓ 檢查dir下是否有formats中的路徑,返回有就返迴路徑或者文件
function lookupFile(dir, formats, pathOnly = false) {
for (const format of formats) {
// ↓ 輸出dir和formats連接後的路徑
const fullPath = path__default.join(dir, format);
// ↓ 同步的檢查該路徑是否存在,並且該路徑對應的是一個文件
if (fs__default.existsSync(fullPath) && fs__default.statSync(fullPath).isFile()) {
// ↓ 是否只要輸出路徑,否則輸出文件
return pathOnly ? fullPath : fs__default.readFileSync(fullPath, 'utf-8');
}
}
// ↓ 上面的路徑都不滿足輸出條件,那麼再檢查一遍檢查傳入的dir的目錄名
const parentDir = path__default.dirname(dir);
if (parentDir !== dir) {
return lookupFile(parentDir, formats, pathOnly);
}
}
|
代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| // 從傳參的root目錄下獲取
// 按順序 .env.${mode}.local、.env.${mode}、.env.local、.env這四個環境文件
// 輸出文件內配置的對象
function loadEnv(mode, root, prefix = 'VITE_') {
if (mode === 'local') {
// ↓ 如果第一個參數傳入'local',就報錯:
// ↓ "local "不能用作模式名稱,因為它與``.env文件的.local後綴衝突。
throw new Error(`"local" cannot be used as a mode name because it conflicts with ` +
`the .local postfix for .env files.`);
}
// ↓ 待輸出的環境變量對象
const env = {};
// ↓ 要讀取的四個文件名稱的字符串數組
const envFiles = [
/** mode local file */ `.env.${mode}.local`,
/** mode file */ `.env.${mode}`,
/** local file */ `.env.local`,
/** default file */ `.env`
];
// ↓ 檢查是否有實際的以VITE_*開頭的環境變量。
// ↓ 這些通常是Node內聯提供的env對象,並應優先考慮。
for (const key in process.env) {
if (key.startsWith(prefix) && env[key] === undefined) {
env[key] = process.env[key];
}
}
for (const file of envFiles) {
// ↓ 檢查根目錄下是否有指定配置文件
const path = lookupFile(root, [file], true);
if (path) {
// ↓ 以換行為單位輸出文件中KEY=VAL格式的到結果對像中
const parsed = main$2.parse(fs__default.readFileSync(path), {
debug: !!process.env.DEBUG || undefined
});
// ↓ 讓環境變量互相使用,這個方法我沒仔細研究。不是很懂
main$1({
parsed,
// ↓ 防止process.env修改
ignoreProcessEnv: true
});
// ↓ 只輸出以prefix開頭的key
for (const [key, value] of Object.entries(parsed)) {
// ↓ 只有這個key在前面沒有加載過才賦值
if (key.startsWith(prefix) && env[key] === undefined) {
env[key] = value;
}
else if (key === 'NODE_ENV') {
// ↓ 在.env文件中覆蓋NODE_ENV。
process.env.VITE_USER_NODE_ENV = value;
}
}
}
}
return env;
}
|
Vben中的wrapperEnv方法
說明
1
2
| // The boolean type read by loadEnv is a string. This function can be converted to boolean type
const viteEnv = wrapperEnv(env);
|
翻譯如下:
loadEnv
讀取的布爾類型是一個字符串。該函數可以轉換為布爾類型。
創建wrapperEnv方法
從這開始涉及到根目錄下的build
文件夾了。創建該文件夾。我們使用build
文件夾裝所有關於項目構建類的東西。
創建定義文件
根目錄下的build
文件夾創建typeing.d.ts
文件。
typeing.d.ts
1
2
3
4
5
6
| declare module '*.json' {
const src: any;
export default src;
}
declare type Recordable = Record<string, any>;
|
參考連結
定義Env對像類型
根目錄下的build
文件夾創建utils.ts
文件。
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| export interface ViteEnv {
VITE_PORT: number
VITE_USE_MOCK: boolean
VITE_USE_PWA: boolean
VITE_PUBLIC_PATH: string
VITE_PROXY: [string, string][]
VITE_GLOB_APP_TITLE: string
VITE_GLOB_APP_SHORT_NAME: string
VITE_USE_CDN: boolean
VITE_DROP_CONSOLE: boolean
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none'
VITE_LEGACY: boolean
VITE_USE_IMAGEMIN: boolean
}
|
導出的這個類型對應了我們配置文件中的Key。
格式化Env對象的wrapperEnv方法
在utils.ts
文件中添加以下方法:
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') {
realName = Number(realName);
}
if (envName === 'VITE_PROXY') {
try {
realName = JSON.parse(realName);
} catch (error) {}
}
ret[envName] = realName;
process.env[envName] = realName;
}
return ret;
}
|
代碼說明:
- 中間的正則替換就是將
\\n
替換成\n
,複製下面這些代碼可以去瀏覽器中體驗一下。console.log('asda\\\n\\n\n'.replace(/\n/g, '\n'))
=> asda\ [回車\n] \n [回車\n]
console.log('asda\\\n\\n\n'.replace(/\\n/g, '\n'))
=> asda\ [回車\n] [回車\n] [回車\n]
console.log('asda\\\n\\n\n'.replace(/\\\n/g, '\n'))
=> asda [回車\n] \n [回車\n]
vite.config.ts文件復盤
自此vite.config.ts
,除return
裡面的東西外,其他的都已經解析完了。以現在可以得到這樣一個代碼:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import { resolve } from 'path';
import { ConfigEnv, loadEnv, UserConfig } from 'vite';
import { wrapperEnv } from './build/util';
function pathResolve(dir: string) {
return resolve(__dirname, '.', dir);
}
export default ({ command, mode }: ConfigEnv): UserConfig => {
const root = process.cwd();
const env = loadEnv(mode, root);
const viteEnv = wrapperEnv(env);
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY, VITE_DROP_CONSOLE, VITE_LEGACY } = viteEnv;
const isBuild = command === 'build';
return {};
};
|
Vite配置-base
說明
Vite配置-base說明
官網原文:
開發或生產環境服務的 公共基礎路徑。合法的值包括以下幾種:
- 絕對 URL 路徑名,例如
/foo/
- 完整的 URL,例如
https://foo.com/
- 空字符串或
./
(用於開發環境)
更多信息詳見公共基礎路徑。
就是你的項目的BaseURL
。比如這裡我配置一個yiu
那麼我的項目訪問:http://localhost:3000/yiu/
當你訪問其他路徑就會報錯:http://localhost:3000/any/
1
| The server is configured with a public base URL of /yiu/ - did you mean to visit /yiu/any/ instead?
|
Vite配置-root
說明
Vite配置-root說明
官網原文:
- 類型:
string
- 默認:
process.cwd()
項目根目錄(index.html 文件所在的位置)。可以是一個絕對路徑,或者一個相對於該配置文件本身的路徑。
更多信息詳見項目根目錄。
用來找index.html
路徑的配置吧,亂寫啟動項目的時候會報錯。其他的也不用解釋了。
Vite配置-resolve.alias
說明
Vite配置-resolve.alias說明
官網原文:
- 類型:
1
| Record<string, string> | Array<{ find: string | RegExp, replacement: string }>
|
將會被傳遞到 @rollup/plugin-alias
作為它的 entries。也可以是一個對象,或一個 { find, replacement }
的數組。
當使用文件系統路徑的別名時,請始終使用絕對路徑。相對路徑作別名值將按原樣使用導致不會解析到文件系統路徑中。
更高級的自定義解析方法可以通過 插件 實現。
VbenAdmin
的示例:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| resolve: {
alias: [
{
// /@/xxxx => src/xxx
find: /^\/@\//,
replacement: pathResolve('src') + '/',
},
{
// /#/xxxx => types/xxx
find: /^\/#\//,
replacement: pathResolve('types') + '/',
},
],
},
|
Vite配置-server.port
說明
Vite配置-server.port說明
官網原文:
指定服務器端口。注意:如果端口已經被使用,Vite 會自動嘗試下一個可用的端口,所以這可能不是服務器最終監聽的實際端口。
Vite配置-server.proxy
說明
官網原文:
- 類型:
1
| Record<string, string | ProxyOptions>
|
為開發服務器配置自定義代理規則。期望接收一個 { key: options }
對象。如果 key 值以 ^
開頭,將會被解釋為 RegExp
。
使用 http-proxy
。完整選項詳見 此處。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| export default {
server: {
proxy: {
// 字符串簡寫寫法
'/foo': 'http://localhost:4567/foo',
// 選項寫法
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
// 正則表達式寫法
'^/fallback/.*': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/fallback/, '')
}
}
}
}
|
VbenAdmin代碼解析
我們在build
文件夾下創建一個vite
文件,這麼放置所有和vite
配置有關的文件。然後完成本章兩個文件的編寫。
proxy.ts
build\vite\proxy.ts
,注意,這裡帶註解的代碼和原代碼不一樣。因為原代碼我感覺有點問題。
Vite
官方文檔說代理配置接收的是一個Record<string, string | ProxyOptions>
對象。而這個文件的主要方法返回的是ProxyTargetList
類型的對象。
按理來說ProxyTargetList
應該直接等於Record<string, string | ProxyOptions>
。
但是VbenAmin
的代碼安裝了一個@types/http-proxy
(雖然Vite
本身的代理就是用http-proxy
實現的)的開發依賴。然後從這個開發依賴裡面導入一個ServerOptions
。讓ProxyTargetList
等於Record<string, ServerOptions & { rewrite: (path: string) => string }>
。
個人感覺可以直接使用vite
提供的類型即可,不用安裝@types/http-proxy
的聲明文件。
代碼
被代理之後,訪問的路徑是 代理至的路徑 + 訪問的路徑去掉根路徑後經過rewrite
處理的路徑。
build/vite/proxy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| /**
* .env.development 的 代理配置
*/
import type { ProxyOptions } from 'vite';
// ↓ 第一项被代理的路径,第二项代理至的路径
type ProxyItem = [string, string];
// ↓ 方法接收的参数
type ProxyList = ProxyItem[];
// ↓ Vite代理所接收对象类型
type ProxyTargetList = Record<string, string | ProxyOptions>;
// ↓ https类型的URL的匹配正则
const httpsRE = /^https:\/\//;
/**
* 生成Vite代理配置的方法
*/
export function createProxy(list: ProxyList = []) {
const ret: ProxyTargetList = {};
for (const [prefix, target] of list) {
const isHttps = httpsRE.test(target);
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
// ↓ 代理至的路径
target: target,
// ↓ 默认值:false-将主机标头的来源更改为目标URL
changeOrigin: true,
// ↓ 如果您想代理websocket
ws: true,
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
// https is require secure=false
...(isHttps ? { secure: false } : {}),
};
}
return ret;
}
|
vite.config.ts
直接將上面定義的生成代理配置的方法放到vite.config.ts
中使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // ...
import { createProxy } from './build/vite/proxy';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
server: {
// ...
// 從.env加載代理配置
proxy: createProxy(VITE_PROXY),
},
// ...
};
};
|
Vite配置-server.hmr
說明
Vite配置-server.hmr說明
官網原文:
- 類型:
1
| boolean | { protocol?: string, host?: string, port?: number, path?: string, timeout?: number, overlay?: boolean }
|
禁用或配置 HMR 連接(用於 HMR websocket 必須使用不同的 http 服務器地址的情況)。
設置 server.hmr.overlay
為 false
可以禁用服務器錯誤遮罩層。
這個就是配置Vite的熱更新的。文檔中說的服務器錯誤遮罩層,就是你在代碼中編寫編寫錯誤的代碼,編譯不通過的時候,瀏覽器頁面也會同時展示一個灰屏上面顯示你的代碼錯誤。
Vite配置-build.minify
說明
Vite配置-build.minify說明
官網原文:
- 類型:
1
| boolean | 'terser' | 'esbuild'
|
- 默認:
terser
設置為 false
可以禁用最小化混淆,或是用來指定使用哪種混淆器。默認為 Terser,雖然 Terser 相對較慢,但大多數情況下構建後的文件體積更小。 ESbuild
最小化混淆更快但構建後的文件相對更大。
Vite配置-build.outDir
說明
Vite配置-build.outDir說明
官網原文:
指定輸出路徑(相對於 項目根目錄)。
創建全局常數
這個輸出路徑我們把它寫成一個全局常量。全局常量保存在:build\constant.ts
build\constant.ts
1
2
3
4
5
6
| /**
* 在生产环境中输入的配置文件名称。
*/
export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
export const OUTPUT_DIR = 'dist';
|
然後在vite.config.ts
中引用:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ...
import { OUTPUT_DIR } from './build/constant';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
build: {
outDir: OUTPUT_DIR,
},
// ...
};
};
|
Vite配置-build.polyfillDynamicImport
說明
官網原文:
- 類型:
boolean
- 默認:
true
,除非build.target
是esnext
。
是否自動注入動態導入polyfill。
polyfill
會自動注入到每個index.html
條目的代理模塊中。如果通過build.rollupOptions.input
配置成使用非html
的自定義條目,那麼就需要手動導入自定義條目中的polyfill
:
1
| import 'vite/dynamic-import-polyfill'
|
注意:polyfill
不適用於庫模式
。如果你需要支持沒有本地動態導入的瀏覽器,你可能應該避免在你的庫中使用它。
Vite
使用ES
動態導入作為代碼分割點。生成的代碼還將使用動態導入來加載異步塊。然而,本機ESM
動態導入支持是在ESM
之後通過腳本標記實現的,並且這兩個特性在瀏覽器支持方面存在差異。 Vite
會自動注入一個輕量級的動態導入填充來消除這種差異。
如果你知道你的目標瀏覽器只支持本機動態導入,你可以通過build.polyfillDynamicImport
顯式禁用此特性。
總之VbenAdmin
中在生產環境將此項禁用了。如果打包後的項目有問題,而本地運行沒問題。那麼嘗試打開它,因為這個配置貌似和瀏覽器有關。
創建全局常數
這個輸出路徑我們把它寫成一個全局常量。全局常量保存在:build\constant.ts
build\constant.ts
1
2
3
4
5
6
| /**
* 在生产环境中输入的配置文件名称。
*/
export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
export const OUTPUT_DIR = 'dist';
|
然後在vite.config.ts
中引用:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ...
import { OUTPUT_DIR } from './build/constant';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
build: {
outDir: OUTPUT_DIR,
},
// ...
};
};
|
Vite配置-build.terserOptions
說明
Vite配置-build.terserOptions說明
官網原文:
傳遞給 Terser 的更多 minify 選項。
其他的配置項自行研究,這裡只註釋一下VbenAdmin
配置的代碼。
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
build: {
// ...
terserOptions: {
compress: {
keep_infinity: true,
// 用於刪除生產環境中的console
drop_console: VITE_DROP_CONSOLE,
},
},
},
// ...
};
};
|
compress
:(默認{}
)-通過false
以完全跳過壓縮。傳遞一個對像以指定自定義壓縮選項。compress.keep_infinity
:(默認值:false
)-通過true
以防止Infinity
被壓縮為1/0
,這可能會導致Chrome出現性能問題。compress.drop_console
:(默認值:false
)-傳遞true
以放棄對console.*
函數的調用 。如果您希望在刪除函數調用後刪除特定的函數調用,例如console.info
和/或保留函數自變量的副作用,請pure_funcs
改用。
Vite配置-build.brotliSize
說明
Vite配置-build.brotliSize說明
官網原文:
啟用/禁用 brotli 壓縮大小報告。壓縮大型輸出文件可能會很慢,因此禁用該功能可能會提高大型項目的構建性能。
禁用就好。
效果對比
打開壓縮報告:4.90s
關閉壓縮報告:3.50s
,少了後面一串壓縮大小,肯定更快啦。
Vite配置-build.chunkSizeWarningLimit
說明
Vite配置-build.chunkSizeWarningLimit說明
官網原文:
chunk 大小警告的限制(以 kbs
為單位)。
LimitChunkCountPlugin
在編寫代碼時,您可能已經添加了許多代碼拆分點以按需加載內容。編譯後,您可能會注意到一些塊太小了-造成更大的HTTP開銷。 LimitChunkCountPlugin
可以通過合併來對您的塊進行後處理。
Vite配置-define
說明
Vite配置-define說明
官網原文:
定義全局變量替換方式。每項在開發時會被定義為全局變量,而在構建時則是靜態替換。
- 從
2.0.0-beta.70
版本開始,字符串值將作為一個直接的表達式,所以如果定義為了一個字符串常量,它需要被顯式地引用(例如:通過 JSON.stringify
)。 - 替換只會在匹配到周圍是單詞邊界(
\b
)時執行。
使用
看一下vue-i18n的應用。
vue-i18n的全局變量替換
原文:
Bundler 構建功能標誌
esm-bundler builds
現在暴露了全局特性標誌,這些標誌可以在編譯時被覆蓋。
__VUE_I18N_FULL_INSTALL__
:啟用/禁用,除了vue-i18n APIs
,組件和指令都完全支持安裝:true
__VUE_I18N_LEGACY_API__
:啟用/禁用vue-i18n
傳統風格的API
支持,默認為true
__INTLIFY_PROD_DEVTOOLS__
:在生產中啟用/禁用intlify-devtools
和vue-devtools
支持,默認為false
在沒有配置這些標誌的情況下,編譯也能正常工作,但強烈建議正確配置這些標誌,以便在最終的bundle
中獲得正確的樹形搖動。
要配置這些標誌:
webpack
: 使用 DefinePlugin
Rollup
: 使用 @rollup/plugin-replace
Vite
:默認配置,但可以使用define
選項覆蓋
注意:替換值必須是布爾文,不能是字符串,否則bundler/minifier
將無法正確評估條件。
如此,就按照VbenAdmin的原代碼一樣配置吧。不然vue-i18n還會包警告,當然,現在我們還沒有安裝vue-i18n。後面配置國際化的時候再講。
Vite配置-css.preprocessorOptions
說明
Vite配置-css.preprocessorOptions說明
官網原文:
指定傳遞給 CSS 預處理器的選項。例如:
vite.config.ts
1
2
3
4
5
6
7
8
9
| export default {
css: {
preprocessorOptions: {
scss: {
additionalData: `$injectedColor: orange;`
}
}
}
}
|
安裝less
眾所周知Antd
框架使用的是less
。所以我們這裡研究less的配置。
安裝less:
VbenAdmin的全局樣式
在根目錄下的:src\design
文件夾中定義了VbenAdmin
所有 全局樣式 和 全局less
變量 。其中分為兩大類:
src\design\index.less
:全局樣式,在main.ts
中使用。src\design\config.less
:全局變量,在vite.config.ts
中使用。
至於該文件夾下的其他文件,都被直接或間接的引入到了index.less
、config.less
中。
我們這邊直接拷貝文件夾就行了,樣式也沒啥研究的。如果有能力的可以把和index.less相關的文件都刪除了,然後自己寫關於VbenAdmin的樣式。
但是變量不能動,很多都antd的變量,antd 的 less 變量參考連結。
複製完之後,順便將index.less在main.ts中引用一下。
Vite中的Less配置
代碼配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
css: {
preprocessorOptions: {
less: {
modifyVars: {
// 用於全局導入,以避免需要單獨導入每個樣式文件。
// reference: 避免重複引用
hack: `true; @import (reference) "${resolve('src/design/config.less')}";`,
// ↓ 這行代碼下一章講
// ...generateModifyVars(),
},
javascriptEnabled: true,
},
},
},
// ...
};
};
|
代碼解析
less對應的對象可以查看lessjs-配置項。
javascriptEnabled
: true這行代碼沒得說,畢竟antd
的less
文件中使用JS
寫法,比如:\node_modules\ant-design-vue\lib\style\color\bezierEasing.less
modifyVars
是在全局less文件後面添加變量的配置。 modifyVars對應的對象屬性名會加上@追加到less文件後。hack: true; @import (r...
:這一行代碼,在less文件中會被解析成:
1
| @hack=true; @import (reference) "${resolve('src/design/config.less')}";
|
那這個hack
變量是幹什麼的?我感覺啥也沒有,單純是為了將後面的@import
接上的@import
會報錯,只寫。這行代碼可以隨便寫,比如:
1
2
3
| // yiu: `red; @import (reference) "${resolve('src/design/config.less')}";`,
// ↓ 解析成,一樣的效果。
@yiu=red; @import (reference) "${resolve('src/design/config.less')}";`,
|
我個人感覺這個hack
和css hack
單純名字一樣。至於reference
,可以看一下關於Less js的@import實驗這篇博文即可理解。只是我個人疑惑為什麼這個import
為什麼不寫在globalVars中。
Vite的顏色
說明
解析build\config\themeConfig.ts
文件,直接創建這個文件。然後我們一段一段的分析:
1
| yarn add ant-design-vue@next
|
第一部分3-16行代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // ↓ 默認顏色
export const primaryColor = '#0960bd';
// ↓ 默認主題
export const themeMode = 'light';
// ↓ 主題類型
export type ThemeMode = 'dark' | 'light';
// ↓ 一個解決臨時檢查的方法類型
type Fn = (...arg: any) => any;
// ↓ Vite生成顏色組的方法參數,顏色這一塊有點深奧了,不做細的研究,感興趣的同學自己看看源碼吧
export interface GenerateColorsParams {
mixLighten: Fn;
mixDarken: Fn;
tinycolor: any;
color?: string;
}
|
第二部分18-22行代碼
1
2
3
4
5
6
7
| import { generate } from '@ant-design/colors';
export function generateAntColors(color: string, mode: ThemeMode) {
return generate(color, {
theme: mode == 'dark' ? 'dark' : 'default',
});
}
|
調用的@ant-design/colors
的顏色生成方法,將參數簡化了一下。
參考連結:
第三部分24-32行代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| export function getThemeColors(color?: string, theme?: ThemeMode) {
const tc = color || primaryColor;
const tm = theme || themeMode;
// ↓ 傳入的顏色根據主題生成10個顏色系列
const colors = generateAntColors(tc, tm);
// ↓ 取10個顏色的第6個作為主顏色
const primary = colors[5];
// ↓ 再使用主顏色根據主題生成10個顏色系列
const modeColors = generateAntColors(primary, tm === 'dark' ? 'light' : 'dark');
// ↓ 輸出這20個顏色
return [...colors, ...modeColors];
}
|
第四部分34-71行代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| export function generateColors({
color = primaryColor,
mixLighten,
mixDarken,
tinycolor,
}: GenerateColorsParams) {
const arr = new Array(19).fill(0);
const lightens = arr.map((t, i) => {
return mixLighten(color, i / 5);
});
const darkens = arr.map((t, i) => {
return mixDarken(color, i / 5);
});
const alphaColors = arr.map((t, i) => {
return tinycolor(color)
.setAlpha(i / 20)
.toRgbString();
});
const tinycolorLightens = arr
.map((t, i) => {
return tinycolor(color)
.lighten(i * 5)
.toHexString();
})
.filter((item) => item !== '#ffffff');
const tinycolorDarkens = arr
.map((t, i) => {
return tinycolor(color)
.darken(i * 5)
.toHexString();
})
.filter((item) => item !== '#000000');
return [...lightens, ...darkens, ...alphaColors, ...tinycolorDarkens, ...tinycolorLightens];
}
|
這裡直接根據顏色生成了98中顏色。我確實不太清楚這個麼多顏色幹什麼的。調用效果參考build\vite\plugin\theme.ts
(現在先不管這個文件):
build/vite/plugin/theme.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| /**
* Vite plugin for website theme color switching
* https://github.com/anncwb/vite-plugin-theme
*/
import { viteThemePlugin, mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme';
import { getThemeColors, generateColors } from '../../config/themeConfig';
export function configThemePlugin() {
const colors = generateColors({
mixDarken,
mixLighten,
tinycolor,
});
// ...
}
|
#0960bd
生成的顏色:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| console.log([...lightens, ...darkens, ...alphaColors, ...tinycolorDarkens, ...tinycolorLightens]);
// ↓ 輸出
[
'#0960bd', '#3a80ca', '#6ba0d7',
'#9dbfe5', '#cedff2', '#ffffff',
'#13011f10c', '#16113f119', '#19315e127',
'#1c417e134', '#1f519e141', '#2261be14e',
'#2571de15b', '#2891fd169', '#2ba21d176',
'#2eb23d183', '#31c25d190', '#34d27d19d',
'#37f29c1ab', '#0960bd', '#074d97',
'#053a71', '#04264c', '#021326',
'#000000', '#-2-13-26', '#-4-26-4c',
'#-5-3a-71', '#-7-4d-97', '#-9-60-bd',
'#-b-73-e3', '#-d-86-109', '#-e-9a-12e',
'#-10-ad-154', '#-12-c0-17a', '#-14-d3-1a0',
'#-16-e6-1c6', '#-17-fa-1eb', 'rgba(9, 96, 189, 0)',
'rgba(9, 96, 189, 0.05)', 'rgba(9, 96, 189, 0.1)', 'rgba(9, 96, 189, 0.15)',
'rgba(9, 96, 189, 0.2)', 'rgba(9, 96, 189, 0.25)', 'rgba(9, 96, 189, 0.3)',
'rgba(9, 96, 189, 0.35)', 'rgba(9, 96, 189, 0.4)', 'rgba(9, 96, 189, 0.45)',
'rgba(9, 96, 189, 0.5)', 'rgba(9, 96, 189, 0.55)', 'rgba(9, 96, 189, 0.6)',
'rgba(9, 96, 189, 0.65)', 'rgba(9, 96, 189, 0.7)', 'rgba(9, 96, 189, 0.75)',
'rgba(9, 96, 189, 0.8)', 'rgba(9, 96, 189, 0.85)', 'rgba(9, 96, 189, 0.9)',
'#0960bd', '#0854a5', '#07478c',
'#063b74', '#042f5c', '#032243',
'#02162b', '#010913', '#0960bd',
'#0a6cd5', '#0b79ee', '#1e86f4',
'#3793f5', '#4fa0f7', '#67adf8',
'#80baf9', '#98c7fa', '#b0d4fb',
'#c9e2fc', '#e1effe', '#f9fcff'
]
console.log(
[...lightens, ...darkens, ...alphaColors, ...tinycolorDarkens, ...tinycolorLightens].length
);
// ↓ 輸出
78
|
第五部分73-104行代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| /**
* less global variable
*/
export function generateModifyVars() {
const palettes = generateAntColors(primaryColor, themeMode);
const primary = palettes[5];
const primaryColorObj: Record<string, string> = {};
for (let index = 0; index < 10; index++) {
primaryColorObj[`primary-${index + 1}`] = palettes[index];
}
return {
'primary-color': primary,
...primaryColorObj,
'info-color': primary,
'processing-color': primary,
'success-color': '#55D187', // Success color
'error-color': '#ED6F6F', // False color
'warning-color': '#EFBD47', // Warning color
'disabled-color': 'rgba(0, 0, 0, 0.25)', // Failure color
'heading-color': 'rgba(0, 0, 0, 0.85)', // Title color
'text-color': 'rgba(0, 0, 0, 0.85)', // Main text color
'text-color-secondary': 'rgba(0, 0, 0, 0.45)', // Subtext color
'font-size-base': '14px', // Main font size
'box-shadow-base': '0 2px 8px rgba(0, 0, 0, 0.15)', // Floating shadow
'border-color-base': '#d9d9d9', // Border color,
'border-radius-base': '2px', // Component/float fillet
'link-color': primary, // Link color
};
}
|
這個方法就是生成antd
主題變量對象的。將該對象的結果放到Vite
的less
配置中:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // ...
import { generateModifyVars } from './build/config/themeConfig';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
css: {
preprocessorOptions: {
less: {
modifyVars: {
// ...
...generateModifyVars(),
},
// ...
},
},
},
// ...
};
};
|
Vite配置-plugin
說明
Vite配置-plugin說明
官網原文:
將要用到的插件數組。查看 插件 API 獲取 Vite
插件的更多細節。
配置插件
它接收一個插件對象,或者一個插件數組。由於我們需要配置很多插件,而每一個插件的配置也不一樣,所以我們把插件數組封裝到一個方法中去。統一配置VbenAdmin
的插件。
創建生成插件數組方法
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
| import type { Plugin } from 'vite';
import type { ViteEnv } from '../../utils';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS } = viteEnv;
const vitePlugins: (Plugin | Plugin[])[] = [];
return vitePlugins;
}
|
後續配置vitePlugins
這個數組。
配置Vite插件
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ...
import { createVitePlugins } from './build/vite/plugin';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
// 項目使用的vite插件。數量大,單獨提取管理
plugins: createVitePlugins(viteEnv, isBuild),
// ...
};
};
|
Vite插件-@vitejs-plugin-vue
說明
@vitejs/plugin-vue的npm首頁
要編寫Vue程序,這個不用解釋了吧,在使用Vite創建應用程序的時候,這個依賴就已經默認加上了。
引入組件
vue插件不用配置,可以直接放進去。
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
| // ...
import vue from '@vitejs/plugin-vue';
// ...
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
const vitePlugins: (Plugin | Plugin[])[] = [vue()];
// ...
return vitePlugins;
}
|
Vite插件-@vitejs-plugin-vue-jsx
說明
為什麼使用JSX
?
- 其實
JSX
的效果和我們在vue
的template
中寫的代碼效果是一樣的。最終都會被渲染成createElement
。 - 區別是
template
的標籤是不可變的
,我們要實現動態標籤,只能使用v-if
。而JSX
的最大特點就是靈活,我們可以隨意組裝HTML
代碼。
假如我們要實現一個組件渲染<hn></hn>
標籤,n
是我們傳入的參數。如果用template
,那麼我們要寫6
個v-if
。但是如果使用JSX
,我們就可以直接將n
放到標籤中去。
參考連結
安裝
1
| yarn add @vitejs/plugin-vue-jsx --dev
|
vue-jsx插件不用配置,可以直接放進去。
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
| // ...
import vueJsx from '@vitejs/plugin-vue-jsx';
// ...
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
const vitePlugins: (Plugin | Plugin[])[] = [vue(),vueJsx()];
// ...
return vitePlugins;
}
|
Vite插件-@vitejs-plugin-legacy
說明
原文描述:
注:此插件需要vite@^2.0.0-beta.12
。
Vite
默認的瀏覽器支持基線是原生ESM。本插件為不支持原生ESM的傳統瀏覽器提供支持。
默認情況下,該插件將為最終bundle
中的每個chunk
生成一個相應的legacy chunk
,用@babel/reset-env進行轉換,並以SystemJS模塊的形式發布(仍然支持代碼分割!)。
生成一個包含SystemJS
運行時的polyfill chunk
,以及由指定的瀏覽器目標和捆綁包中的實際使用情況決定的任何必要的polyfills
。
在生成的HTML中註入<script nomodule>
標籤,以便在沒有本地ESM支持的瀏覽器中有條件地加載polyfills
和legacy bundle
。
注入 import.meta.env.LEGACY env
變量,該變量僅在 legacy
生產構建中為真,而在所有其他情況下為假。(需要vite@^2.0.0-beta.69
)。
所以這個是一個瀏覽器兼容的插件。我們直接安裝使用,個人感覺學習的話也用不上。
參考連結
安裝
1
| yarn add @vitejs/plugin-legacy --dev
|
VbenAdmin配置build/vite/plugin/index.ts
:
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import legacy from '@vitejs/plugin-legacy';
// ...
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
// @vitejs/plugin-legacy
VITE_LEGACY && isBuild && vitePlugins.push(legacy());
// ...
return vitePlugins;
}
|
Vite插件-vite-plugin-html
說明
原文描述:
一個為index.html提供minify和基於EJS模板功能的Vite插件。
- minify:壓縮
index.html
代碼。 - EJS:給
index.html
提供訪問變量的能力。
參考連結
安裝
1
| yarn add vite-plugin-html --dev
|
創建配置文件
build/vite/plugin/html.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| /**
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/
import type { Plugin } from 'vite';
import type { ViteEnv } from '../../utils';
import html from 'vite-plugin-html';
import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
// ↓ 這里後續章動態配置講解
// const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
// const getAppConfigSrc = () => {
// return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
// };
const htmlPlugin: Plugin[] = html({
minify: isBuild,
inject: {
// Inject data into ejs template
injectData: {
title: VITE_GLOB_APP_TITLE,
},
// ↓ 這里後續章動態配置講解
// tags: isBuild
// ? [
// {
// tag: 'script',
// attrs: {
// src: getAppConfigSrc(),
// },
// },
// ]
// : [],
},
});
return htmlPlugin;
}
|
配置Vite插件
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
| // ...
import { configHtmlPlugin } from './html';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
// vite-plugin-html
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
// ...
}
|
補充
做完本章操作之後,我突然發現項目不能編譯了。
總是報package.json
找不到。我看了一下代碼,主要就是build\vite\plugin\html.ts
中的導入無法找到。
我反復對比了一下VbenAdmin
的源碼。也沒有發現有什麼不同的地方。
然後我又試了一下,當執行vite
命令的時候,build
文件夾下始終不能訪問package.json
。但是src
和vite.config.ts
中都可以導入訪問。
那麼這裡有兩種講解方案:
去掉導入
這裡的導入只是我們在使用_app.config.js
的時候加上一個版本信息。可以讓我們知道這個項目是多久打包的,打包的時候版本是多少。所以去掉也無妨,_app.config.js
還是一樣可以訪問的。
從vite.config.ts
中傳參下去
我使用了這個方案,因為萬一build
文件夾其他地方要用package.json
呢。不過用的地方應該也很少吧。
修改vite.config.ts
:
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ...
import pkg from './package.json';
// ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
plugins: createVitePlugins(viteEnv, isBuild, pkg),
// ...
};
};
|
修改build/vite/plugin/index.ts
:
build/vite/plugin/index.ts
1
2
3
4
5
6
7
| // ...
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
vitePlugins.push(configHtmlPlugin(viteEnv, isBuild, pkg));
return vitePlugins;
}
|
修改build/vite/plugin/html.ts
:
build/vite/plugin/html.ts
1
2
3
4
5
6
7
8
9
10
11
| // ...
import html from 'vite-plugin-html';
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean, pkg: any) {
// ...
const getAppConfigSrc = () => {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
// ...
return htmlPlugin;
}
|
完善build的utils.ts
說明
完善build\utils.ts
文件。
第一部分
除了這個process,應該也沒有要解釋的吧。
Node中文文檔-process
build/utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| export function isDevFn(mode: string): boolean {
return mode === 'development';
}
export function isProdFn(mode: string): boolean {
return mode === 'production';
}
/**
* Whether to generate package preview
*/
export function isReportMode(): boolean {
return process.env.REPORT === 'true';
}
|
安裝dotenv
dotenv
依賴的作用:從.evn
文件流Buffer
中讀取屬性輸出一個對象。
那麼和我們之前的加載環境變量的區別:
Vite
的loadEnv()
:這個方法Vite的官網上貌似沒有開放,而且獲取的文件是指定的,獲取的對象需要我們自己格式化一下。Vben
的wrapperEnv()
:這是一個格式化環境變量的方法。dotenv
:是一個依賴,其功能不僅僅是獲取環境變量,當然我們目前只用它來獲取指定的.env文件。
dotenv的npm首頁
第二部分
build/utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';
/**
* Get the environment variables starting with the specified prefix
* @param match prefix
* @param confFiles ext
*/
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
let envConfig = {};
// ↓ 循環文件名
confFiles.forEach((item) => {
try {
// ↓ 同步讀取根目錄下指定的文件,將讀取的六傳入dotenv輸出對象
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
// ↓ 將新獲取的對象追加到結果中,相同的覆蓋
envConfig = { ...envConfig, ...env };
} catch (error) {}
});
// ↓ 檢查輸出結果
Object.keys(envConfig).forEach((key) => {
// ↓ 如果不是以傳入的match變量開的就直接刪除
const reg = new RegExp(`^(${match})`);
if (!reg.test(key)) {
Reflect.deleteProperty(envConfig, key);
}
});
return envConfig;
}
/**
* Get user root directory
* @param dir file path
*/
export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir);
}
|
Vben的獲取配置文件名的方法
build/getConfigFileName.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
| /**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
return (
`__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
// ↓ 換成大寫
.toUpperCase()
// ↓ 將空白換成空字符串
.replace(/\s/g, '')
);
};
|
Vben動態配置環境變量
說明
前面章節中,我們在build\vite\plugin
中註釋一段index.html
中插入的script
段。這一段script
是用於Vben
的動態配置環境變量的。
當你打包完項目之後,你的項目的環境變量就不可以變了。此時如果你想修改一下項目的基礎API路徑,那麼你需要修改.env
文件,然後再次打包一遍。這麼做很麻煩。所以VbenAdmin
將環境變量抽離出來。
當你的項目打包完成之後,如果你還想修改環境變量,那麼可以直接修改dist
包下的_app.config.js
文件。
下面看實現。
1
| yarn add fs-extra @types/fs-extra --dev
|
fs-extra的npm首頁:fs-extra
添加本機fs
模塊中未包含的文件系統方法。
編寫生成配置文件的程序
Node-Api-fs
build/script/buildConf.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| /**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
import fs, { writeFileSync } from 'fs-extra';
import chalk from 'chalk';
import { getRootPath, getEnvConfig } from '../utils';
import { getConfigFileName } from '../getConfigFileName';
import pkg from '../../package.json';
function createConfig(
{
// ↓ windows對像下的存儲配置的屬性名
configName,
// ↓ 存儲配置的對象
config,
// ↓ JS文件名
configFileName = GLOB_CONFIG_FILE_NAME,
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
) {
try {
// ↓ 變量字符串
const windowConf = `window.${configName}`;
// Ensure that the variable will not be modified
// ↓ 將配置的對像以JSON字符串格式拼接到變量字符串後面,最後用正則將空格去掉
const configStr = `${windowConf}=${JSON.stringify(config)};
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
// ↓ 創建dist文件夾
fs.mkdirp(getRootPath(OUTPUT_DIR));
// ↓ 將字符串寫入到dist文件下的指定JS文件名的文件中
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
} catch (error) {
console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
}
}
export function runBuildConfig() {
// ↓ 獲取我們可以配置的環境變量對象
const config = getEnvConfig();
// ↓ 獲取配置文件的JS名
const configFileName = getConfigFileName(config);
// ↓ 創建文件
createConfig({ config, configName: configFileName });
}
|
安裝yargs
yargs的npm首頁:讀取你執行的命令行命令中的參數選項。
1
2
| yarn add yargs --dev
yarn add @types/yargs --dev
|
編寫腳本觸發文件
chalk的npm首頁:一個彩色console.log的工具,這個依賴被其他依賴作為生產依賴安裝在本項目中了,所以也不去猜是哪個依賴的了,直接使用。
build/script/postBuild.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // #!/usr/bin/env node
import { argv } from 'yargs';
import { runBuildConfig } from './buildConf';
import chalk from 'chalk';
import pkg from '../../package.json';
// ↓ 定義創建配置文件的方法,最條件判斷
export const runBuild = async () => {
try {
// 獲取命令中不以'-'開頭的參數
const argvList = argv._;
// Generate configuration file
// ↓ 如果參數中包含'no-conf',那麼就不創建配置文件
if (!argvList.includes('no-conf')) {
// ↓ 同步創建配置文件
await runBuildConfig();
}
console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};
// ↓ 執行方法
runBuild();
|
安裝esno
esno的npm首頁:命令行執行一個TS文件
編寫腳本
在編譯的時候調用TS文件:
package.json
1
2
3
4
5
6
7
| {
"scripts": {
// ...
"build": "vite build && esno ./build/script/postBuild.ts",
"build-noconf": "vite build && esno ./build/script/postBuild.ts no-conf"
}
}
|
index.html注入配置文件
build/vite/plugin/html.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| /**
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/
import type { Plugin } from 'vite';
import type { ViteEnv } from '../../utils';
import html from 'vite-plugin-html';
import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
const getAppConfigSrc = () => {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
const htmlPlugin: Plugin[] = html({
minify: isBuild,
inject: {
// Inject data into ejs template
injectData: {
title: VITE_GLOB_APP_TITLE,
},
// Embed the generated app.config.js file
// ↓ 在此處插入
tags: isBuild
? [
{
tag: 'script',
attrs: {
src: getAppConfigSrc(),
},
},
]
: [],
},
});
return htmlPlugin;
}
|
注意
之後在業務邏輯中獲取環境變量,就需要從這個JS
文件中獲取了。從而達到在生產環境改配置的需求。
參考:src\utils\env.ts
,但是這個後面再說。
Vite插件-vite-plugin-svg-icon
說明
當你使用該插件的時候,指定好存放svg
的文件夾。再按照指定的方式去訪問svg
圖片。就可以再不產生http請求的情況下渲染出svg
圖片。
使用該插件時,插件會自動將所有svg
圖片加載到HTML
中。並且每一個svg
將會被過濾去無用的信息數據。讓svg
達到最小的值。之後使用svg
圖片就只需要操作DOM即可,而不需要發送http
請求。
安裝
1
| yarn add vite-plugin-svg-icons --dev
|
創建配置
build/vite/plugin/svgSprite.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| /**
* Vite Plugin for fast creating SVG sprites.
* https://github.com/anncwb/vite-plugin-svg-icons
*/
import SvgIconsPlugin from 'vite-plugin-svg-icons';
import path from 'path';
export function configSvgIconsPlugin(isBuild: boolean) {
const svgIconsPlugin = SvgIconsPlugin({
// ↓ 本地svg圖片地址
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
svgoOptions: isBuild,
// 圖標ID的樣式
symbolId: 'icon-[dir]-[name]',
});
return svgIconsPlugin;
}
|
選項svgOptions的boolean類型不太清楚是乾什麼的。但是對像類型是控制svg過濾無用信息的選項。使用true是使用默認選項,false時不知道做什麼的但是也沒什麼影響。
用於配置
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import { configSvgIconsPlugin } from './svgSprite';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
// vite-plugin-svg-icons
vitePlugins.push(configSvgIconsPlugin(isBuild));
// ...
return vitePlugins;
}
|
main導入
src/main.ts
1
2
3
| // ...
import 'vite-plugin-svg-icons/register';
// ...
|
創建Svg組件
這裡有一個樣式,是全局上下文注入的。這個後面再講,先寫成string
。
src/components/Icon/src/SvgIcon.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| <template>
<svg class="vben-svg-icon" :class="[$attrs.class]" :style="getStyle" aria-hidden="true">
<use :xlink:href="symbolId" />
</svg>
</template>
<script lang="ts">
import type { CSSProperties } from 'vue';
import { defineComponent, computed } from 'vue';
// import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'SvgIcon',
props: {
prefix: {
type: String,
default: 'icon',
},
name: {
type: String,
required: true,
},
size: {
type: [Number, String],
default: 16,
},
},
setup(props) {
// const { prefixCls } = useDesign('svg-icon');
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
const getStyle = computed(
(): CSSProperties => {
const { size } = props;
let s = `${size}`;
s = `${s.replace('px', '')}px`;
return {
width: s,
height: s,
};
}
);
// prefixCls,
return { symbolId, getStyle };
},
});
</script>
<style lang="less" scoped>
// @prefix-cls: ~'@{namespace}-svg-icon';
// .@{prefix-cls} {
.vben-svg-icon {
overflow: hidden;
vertical-align: -0.15em;
fill: currentColor;
}
</style>
|
Vite插件-vite-plugin-windicss
說明
在Vite上單獨使用Tailwind
時,渲染速度很慢。
vite-plugin-windicss
比Tailwind
快20到100倍。
安裝
1
| yarn add vite-plugin-windicss --dev
|
創建Windicss配置文件
tailwind.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| import lineClamp from 'windicss/plugin/line-clamp';
import colors from 'windicss/colors';
import { defineConfig } from 'vite-plugin-windicss';
import { primaryColor } from './build/config/themeConfig';
export default defineConfig({
darkMode: 'class',
plugins: [lineClamp, createEnterPlugin()],
theme: {
extend: {
colors: {
...colors,
primary: primaryColor,
},
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
'2xl': '1600px',
},
},
},
});
/**
* Used for animation when the element is displayed
* @param maxOutput The larger the maxOutput output, the larger the generated css volume
*/
function createEnterPlugin(maxOutput = 10) {
const createCss = (index: number, d = 'x') => {
const upd = d.toUpperCase();
return {
[`*> .enter-${d}:nth-child(${index})`]: {
transform: `translate${upd}(50px)`,
},
[`*> .-enter-${d}:nth-child(${index})`]: {
transform: `translate${upd}(-50px)`,
},
[`* > .enter-${d}:nth-child(${index}),* > .-enter-${d}:nth-child(${index})`]: {
'z-index': `${10 - index}`,
opacity: '0',
animation: `enter-${d}-animation 0.4s ease-in-out 0.3s`,
'animation-fill-mode': 'forwards',
'animation-delay': `${(index * 1) / 10}s`,
},
};
};
const handler = ({ addBase }) => {
const addRawCss = {};
for (let index = 1; index < maxOutput; index++) {
Object.assign(addRawCss, {
...createCss(index, 'x'),
...createCss(index, 'y'),
});
}
addBase({
...addRawCss,
[`@keyframes enter-x-animation`]: {
to: {
opacity: '1',
transform: 'translateX(0)',
},
},
[`@keyframes enter-y-animation`]: {
to: {
opacity: '1',
transform: 'translateY(0)',
},
},
});
};
return { handler };
}
|
創建配置文件
build/vite/plugin/windicss.ts
1
2
3
4
5
6
7
8
9
10
11
12
| import type { Plugin } from 'vite';
import windiCSS from 'vite-plugin-windicss';
export function configWindiCssPlugin(): Plugin[] {
return windiCSS({
safelist: 'no-select',
preflight: {
enableAll: true,
},
});
}
|
應用配置
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
| // ...
import { configWindiCssPlugin } from './windicss';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
// vite-plugin-windicss
vitePlugins.push(configWindiCssPlugin());
return vitePlugins;
}
|
導入樣式
src/main.ts
1
2
3
| // ...
import '@virtual/windi.css';
// ...
|
Vite插件-vite-plugin-mock
說明
vite-plugin-mock的npm首頁
根據我自己的實驗:
localEnabled
控制mock開發環境是否啟動。
如果生產環境想要使用mock
,只有prodEnabled
為true,injectCode
注入指定代碼時才會生效。
安裝
1
| yarn add mockjs vite-plugin-mock --dev
|
編寫Mock用例
這裡直接將根目錄下的mock
目錄拷貝出來就行。
mock\_util.ts
:裡面封裝的是數據請求結構類型。mock\_createProductionServer.ts
:用於配置生產環境動態Mock的js文件,文檔中有說。其他的
:都是Mock用例,每一個js
、ts
,都要默認導出一個MockMethod
類型的數組。每一項MockMethod
就是攔截的一個方法。MockMethod
的response
對應方法的return
將會被Mock
實例處理。也就是說,你可以在return
的對像中使用Mock規則。
不過_createProductionServer.ts
中使用了Glob 導入。
配置Mock
build/vite/plugin/mock.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /**
* Mock plugin for development and production.
* https://github.com/anncwb/vite-plugin-mock
*/
import { viteMockServe } from 'vite-plugin-mock';
export function configMockPlugin(isBuild: boolean) {
return viteMockServe({
// ↓ 忽略以_開頭的文件
ignore: /^\_/,
// ↓ 解析根目錄下的mock文件夾
mockPath: 'mock',
localEnabled: !isBuild,
prodEnabled: isBuild,
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer';
setupProdMockServer();
`,
});
}
|
配置進Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import { configMockPlugin } from './mock';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
// vite-plugin-mock
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
return vitePlugins;
}
|
Vite插件-vite-plugin-purge-icons
說明
本章的插件是可以讓我們很方便高效的使用Iconify中所有的圖標。
這裡要講的是Iconify
各個版本插件的區別:
vite-plugin-purge-icons的npm首頁
安裝
1
2
| yarn add @iconify/iconify
yarn add vite-plugin-purge-icons @iconify/json --dev
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
11
| // ...
import PurgeIcons from 'vite-plugin-purge-icons';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// ...
// vite-plugin-purge-icons
vitePlugins.push(PurgeIcons());
// ...
return vitePlugins;
}
|
注意
Icon
組件後面再封裝,由於我們main.ts
中沒有引入vite-plugin-purge-icons
。所以,這裡還不能使用。後面會動態生成圖標。
Vite插件-vite-plugin-style-import
說明
vite-plugin-style-import的npm首頁
該插件可按需導入組件庫樣式,由於 vite
本身已按需導入了組件庫,因此僅樣式不是按需導入的,因此只需按需導入樣式即可。
安裝
1
| yarn add vite-plugin-style-import --dev
|
配置插件
build/vite/plugin/styleImport.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /**
* Introduces component library styles on demand.
* https://github.com/anncwb/vite-plugin-style-import
*/
import styleImport from 'vite-plugin-style-import';
export function configStyleImportPlugin() {
// if (!isBuild) return [];
const pwaPlugin = styleImport({
libs: [
{
libraryName: 'ant-design-vue',
esModule: true,
resolveStyle: (name) => {
return `ant-design-vue/es/${name}/style/index`;
},
},
],
});
return pwaPlugin;
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import { configStyleImportPlugin } from './styleImport';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
// vite-plugin-style-import
vitePlugins.push(configStyleImportPlugin());
return vitePlugins;
}
|
Vite插件-rollup-plugin-visualizer
說明
安裝
1
| yarn add rollup-plugin-visualizer @types/rollup-plugin-visualizer --dev
|
配置插件
build/vite/plugin/visualizer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| /**
* Package file volume analysis
*/
import visualizer from 'rollup-plugin-visualizer';
import { isReportMode } from '../../utils';
import type { Plugin } from 'vite';
export function configVisualizerConfig() {
if (isReportMode()) {
return visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
// @ts-ignore
gzipSize: true,
// @ts-ignore
brotliSize: true,
}) as Plugin;
}
return [];
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import { configVisualizerConfig } from './visualizer';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
// rollup-plugin-visualizer
vitePlugins.push(configVisualizerConfig());
return vitePlugins;
}
|
安裝cross-env
1
| yarn add cross-env --dev
|
編輯腳本
package.json
1
2
3
4
5
6
7
8
9
| {
// ...
"scripts": {
// ...
"report": "cross-env REPORT=true npm run build"
// ...
}
// ...
}
|
Vite插件-vite-plugin-theme
說明
vite-plugin-theme的npm首頁:Git站點,npm的失效了。
用於動態更改界面主題色的 vite 插件。
配置插件
build/vite/plugin/theme.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| /**
* Vite plugin for website theme color switching
* https://github.com/anncwb/vite-plugin-theme
*/
import { viteThemePlugin, mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme';
import { getThemeColors, generateColors } from '../../config/themeConfig';
export function configThemePlugin() {
const colors = generateColors({
mixDarken,
mixLighten,
tinycolor,
});
const plugin = viteThemePlugin({
// ↓ 之前生成的很多個顏色
colorVariables: [...getThemeColors(), ...colors],
});
return plugin;
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
| // ...
import { configThemePlugin } from './theme';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
//vite-plugin-theme
vitePlugins.push(configThemePlugin());
return vitePlugins;
}
|
修改主題方法
之後要修改主題,直接調用一下這個方法即可。
src/logics/theme/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import { getThemeColors, ThemeMode, generateColors } from '../../../build/config/themeConfig';
import { replaceStyleVariables } from 'vite-plugin-theme/es/client';
import { mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme/es/colorUtils';
export async function changeTheme(color: string, theme?: ThemeMode) {
const colors = generateColors({
mixDarken,
mixLighten,
tinycolor,
color,
});
return await replaceStyleVariables({
colorVariables: [...getThemeColors(color, theme), ...colors],
});
}
|
Vite插件-vite-plugin-imagemin
說明
vite-plugin-imagemin的npm首頁:一個壓縮圖片資源的 vite 插件。
配置鏡像(用於安裝imagemin的依賴,因為中國可能沒有安裝imagemin。)
若非中國用戶
,可跳過配置鏡像
package.json
1
2
3
4
| "resolutions": {
"//": "用於安裝imagemin的依賴關係,因為中國可能沒有安裝imagemin。",
"bin-wrapper": "npm:bin-wrapper-china"
},
|
安裝
1
| yarn add vite-plugin-imagemin --dev
|
配置插件
build/vite/plugin/imagemin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| // Image resource files used to compress the output of the production environment
// https://github.com/anncwb/vite-plugin-imagemin
import viteImagemin from 'vite-plugin-imagemin';
export function configImageminPlugin() {
const plugin = viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
webp: {
quality: 75,
},
mozjpeg: {
quality: 8,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
svgo: {
plugins: [
{
removeViewBox: false,
},
{
removeEmptyAttrs: false,
},
],
},
});
return plugin;
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
| // ...
import { configImageminPlugin } from './imagemin';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
if (isBuild) {
//vite-plugin-imagemin
VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin());
}
return vitePlugins;
}
|
Vite插件-vite-plugin-compression
說明
vite-plugin-compression的npm首頁:vite-plugin-compress
的增強版,壓縮用的。
安裝
1
| yarn add vite-plugin-compression --dev
|
配置插件
build/vite/plugin/compress.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| /**
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* https://github.com/anncwb/vite-plugin-compression
*/
import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none'): Plugin | Plugin[] {
const compressList = compress.split(',');
const plugins: Plugin[] = [];
if (compressList.includes('gzip')) {
plugins.push(
compressPlugin({
ext: '.gz',
})
);
}
if (compressList.includes('brotli')) {
plugins.push(
compressPlugin({
ext: '.br',
algorithm: 'brotliCompress',
})
);
}
return plugins;
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ...
import { configCompressPlugin } from './compress';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
// The following plugins only work in the production environment
if (isBuild) {
// ...
// rollup-plugin-gzip
vitePlugins.push(configCompressPlugin(VITE_BUILD_COMPRESS));
}
return vitePlugins;
}
|
Vite插件-vite-plugin-pwa
說明
安裝
1
| yarn add vite-plugin-pwa --dev
|
配置插件
build/vite/plugin/pwa.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| /**
* Zero-config PWA for Vite
* https://github.com/antfu/vite-plugin-pwa
*/
import type { ViteEnv } from '../../utils';
import { VitePWA } from 'vite-plugin-pwa';
export function configPwaConfig(env: ViteEnv) {
const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env;
if (VITE_USE_PWA) {
// vite-plugin-pwa
const pwaPlugin = VitePWA({
manifest: {
name: VITE_GLOB_APP_TITLE,
short_name: VITE_GLOB_APP_SHORT_NAME,
icons: [
{
// ./表示public文件夾
src: './resource/img/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: './resource/img/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
},
});
return pwaPlugin;
}
return [];
}
|
配置Vite
build/vite/plugin/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ...
import { configPwaConfig } from './pwa';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, pkg: any) {
// ...
// The following plugins only work in the production environment
if (isBuild) {
// ...
// vite-plugin-pwa
vitePlugins.push(configPwaConfig(viteEnv));
}
return vitePlugins;
}
|
Vite配置-optimizeDeps.include
說明
Vite配置-optimizedeps-include說明
原文:
類型: string[]
默認情況下,不在 node_modules
中的,鏈接的包不會被預構建。使用此選項可強制預構建鏈接的包。
安裝moment
antd
中需要使用moment
。而moment
配置和本章有關,所以就順便安裝一下:
配置Vite
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // ...
function pathResolve(dir: string) {
return resolve(__dirname, '.', dir);
}
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
optimizeDeps: {
// @iconify/iconify: The dependency is dynamically and virtually loaded by @purge-icons/generated, so it needs to be specified explicitly
include: [
'@iconify/iconify',
'ant-design-vue/es/locale/zh_CN',
'moment/dist/locale/zh-cn',
'ant-design-vue/es/locale/en_US',
'moment/dist/locale/eu',
],
// ...
},
};
};
|
Vite配置-optimizeDeps.exclude
說明
Vite配置-optimizedeps-exclude說明
原文:
類型: string[]
在預構建中強制排除的依賴項。
配置Vite
vite.config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ...
export default ({ command, mode }: ConfigEnv): UserConfig => {
// ...
return {
// ...
optimizeDeps: {
// ...
exclude: ['vue-demi'],
},
};
};
|
http-server
說明
項目編譯之後的靜態文件是不能直接本地訪問的。因為本地訪問使用的是file:///
協議。而file:///
不支持跨域和一些其他特性。比如JavaScript模塊、PWA等等。
那麼此時就需要換一種訪問本地文件的方式了,就是讓本地成為一個服務器。通過http
來訪問。
http-server就可以實現以http
形式訪問本地文件的目的,但是這個依賴不保證安全,只用於本地測試。
http-server的npm首頁:命令行啟動一個本地服務器。不安全,但是可以用來測試。
安裝
1
| yarn add http-server --dev
|
配置腳本
package.json
1
2
3
4
5
6
7
8
9
10
| {
// ...
"scripts": {
// ...
"test:gzip": "http-server dist --cors --gzip -c-1",
"test:br": "http-server dist --cors --brotli -c-1",
// ...
},
// ...
}
|