这是一个学习github项目:vue-vben-admin的专栏,我会一步一步从零开始记录我的学习过程 vue-vben-admin源码:github.com/vbenjs/vue-… 本专栏对应的github:github.com/qq975036719…
1. 创建项目
pnpm create vite
1.1 安装@types/node提供node内置API的类型提示
pnpm i @types/node -D
1.2 安装@vitejs/plugin-vue-jsx支持jsx
pnpm i @vitejs/plugin-vue-jsx -D
1.2 修改tsconfig.json开启同时支持ESM和CJS方式导入
"allowSyntheticDefaultImports": true
1.3 添加类型声明文件的目录
项目中会用到很多的类型声明文件*.d.ts,我们需要有一个公共的入口存放它们,放在项目根目录下的types目录中,因此需要在tsconfig.json中配置一下
"typeRoots": ["./node_modules/@types", "./types"]
还要记得在**tsconfig.json**的**include**中包含**types**目录下的所有**ts**和**dts**文件
"types/**/*.d.ts",
"types/**/*.ts",
1.4 ts配置@路径别名
修改tsconfig.json
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}
修改配置后需要重启vscode才会生效
1.5 vite配置@路径别名
修改vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [vue()],
})
1.6 css相关
1.6.1 配置less全局变量
新建build目录,用来存放和项目构建有关的信息,包括vite.config.ts中的配置也分模块放到这里。
在build目录下新建generate/generateModifyVars.ts,less中用到的变量在这里配置
// build/generate/generateModifyVars.ts
/**
* less 全局变量
*/
export function generateModifyVars() {
return {
'success-color': '#55D187',
'error-color': '#ED6F6F',
'warning-color': '#EFBD47',
'font-size-base': '14px',
'border-radius-base': '2px',
'app-content-background': '#fafafa',
}
}
还需要在tsconfig.node.json中包含build目录下的文件
{
"include": ["vite.config.ts", "build/**/*"]
}
1.6.2 使用less/sass以及postcss
pnpm i less postcss postcss-html postcss-less autoprefixer -D
配置postcss,在src目录下创建postcss.config.js
module.exports = {
plugins: {
autoprefixer: {},
},
}
vite.config.ts中配置css预处理器
css: {
preprocessorOptions: {
less: {
modifyVars: generateModifyVars(),
javascriptEnabled: true,
},
},
}
generateModifyVars()函数用于生成less的全局变量
/**
* less 全局变量
*/
export default function generateModifyVars() {
return {
'success-color': '#55D187',
'error-color': '#ED6F6F',
'warning-color': '#EFBD47',
'font-size-base': '14px',
'border-radius-base': '2px',
'app-content-background': '#fafafa',
};
}
1.6.3 整合windicss
- 安装
windicss以及vite-config-windicss
pnpm i windicss vite-plugin-windicss -D
- 加载到
vite.config.ts中
// vite.config.ts
plugins: [WindiCSS()],
- 导入
windi.css
// main.ts
import 'virtual:windi.css'
1.6.4 配置全局样式入口
创建src/design/index.less,并写入一些全局样式
input:-webkit-autofill {
box-shadow: 0 0 0 1000px white inset !important;
}
:-webkit-autofill {
transition: background-color 5000s ease-in-out 0s !important;
}
html {
overflow: hidden;
text-size-adjust: 100%;
}
html,
body {
width: 100%;
height: 100%;
overflow: visible !important;
overflow-x: hidden !important;
&.color-weak {
filter: invert(80%);
}
&.gray-mode {
filter: grayscale(100%);
filter: progid:dximagetransform.microsoft.basicimage(grayscale=1);
}
}
a:focus,
a:active,
button,
div,
svg,
span {
outline: none !important;
}
2. 代码规范
2.1 eslint
- 安装
pnpm i eslint eslint-plugin-vue -D
# 让 eslint 识别 TypeScript 语法
pnpm i @typescript-eslint/parser -D
# 提供额外的适合 ts 的语法规则
pnpm i @typescript-eslint/eslint-plugin -D
# 用于编写 .eslintrc.js 时提供类型检查和语法提示
pnpm i eslint-define-config -D
.eslintrc.js配置
// @ts-check
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
root: true, // 指定为根配置,防止有上级的 eslint 继续向上查找配置文件
env: {
browser: true,
node: true,
es6: true, // 开启 es6 支持
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true, // 开启 jsx 支持
},
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
],
rules: {
// 遇到不需要的 lint 检查的时候在这里配置关闭
'@typescript-eslint/no-var-requires': 'off',
},
})
.eslintignore忽略文件
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile
package.json中添加脚本
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
2.2 prettier
- 安装
pnpm i prettier eslint-config-prettier eslint-plugin-prettier -D
- 配置文件
.prettierrc
{
"printWidth": 100,
"semi": true,
"vueIndentScriptAndStyle": true,
"singleQuote": true,
"trailingComma": "all",
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "auto"
}
semi:行尾分号trailingComma:行尾加逗号proseWrap:不折行
- 配置忽略文件
.prettierignore
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
- 添加
package.json脚本
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
2.3 stylelint
样式表文件linter
- 安装
pnpm i stylelint stylelint-config-html stylelint-config-prettier stylelint-config-standard stylelint-order -D
- 配置文件
stylelint.config.js
module.exports = {
root: true,
plugins: ['stylelint-order'],
customSyntax: 'postcss-less',
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
rules: {
'selector-class-pattern': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep'],
},
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin',
],
},
],
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'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'] }],
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports',
},
{
type: 'at-rule',
name: 'media',
},
'rules',
],
{ severity: 'warning' },
],
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
overrides: [
{
files: ['*.vue', '**/*.vue'],
extends: ['stylelint-config-recommended', 'stylelint-config-html'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'],
},
],
},
},
],
};
package.json中添加脚本
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"
3. Git规范
3.1 commitizen/commitlint
- 安装依赖
pnpm install -D commitizen cz-conventional-changelog @commitlint/config-conventional @commitlint/cli commitlint-config-cz cz-customizable
- cz-customizable 用于自定义提示文案
- 配置
package.json
{
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
}
}
- 配置
commitlint.config.js
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 108],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
'subject-case': [0],
'type-enum': [
2,
'always',
[
'feat', // 增加新功能
'fix', // 修复问题/BUG
'perf', // 优化/性能提升
'style', // 代码风格相关无影响运行结果的
'docs', // 文档/注释
'test', // 测试相关
'refactor', // 重构
'build', // 构建相关
'ci', // 持续集成
'chore', // 依赖更新/脚手架配置修改等
'revert', // 撤销修改
'wip', // 开发中
'workflow', // 工作流改进
'types', // 类型定义文件更改
'release', // 代码发布
],
],
},
};
- 配置自定义提示文案:
.cz-config.js
module.exports = {
types: [
{ value: 'feat', name: 'feat: 增加新功能' },
{ value: 'fix', name: 'fix: 修复问题/BUG' },
{ value: 'perf', name: 'perf: 优化/性能提升' },
{ value: 'style', name: 'style: 代码风格相关无影响运行结果的' },
{ value: 'docs', name: 'docs: 文档/注释' },
{ value: 'test', name: 'test: 测试相关' },
{ value: 'refactor', name: 'refactor: 重构' },
{ value: 'build', name: 'build: 构建相关' },
{ value: 'ci', name: 'ci: 持续集成' },
{ value: 'release', name: 'release: 发布' },
{ value: 'deploy', name: 'deploy: 部署' },
{ value: 'test', name: 'test: 增加测试' },
{ value: 'chore', name: 'chore: 依赖更新/脚手架配置修改等' },
{ value: 'revert', name: 'revert: 撤销修改' },
{ value: 'wip', name: 'wip: 开发中' },
{ value: 'workflow', name: 'workflow: 工作流改进' },
{ value: 'types', name: 'types: 类型定义文件更改' },
{ value: 'release', name: 'release: 代码发布' },
],
// override the messages, defaults are as follows
messages: {
type: '请选择提交类型:',
customScope: '请输入修改的范围(可选):',
subject: '请简要描述提交 message (必填):',
body: '请输入详细描述(可选):',
footer: '请输入要关闭的issue(可选):',
confirmCommit: '确认使用以上信息提交?(y/n/e/h)',
},
allowCustomScopes: true,
skipQuestions: ['body', 'footer'],
subjectLimit: 72,
};
3.2 husky
- 安装
pnpm i husky lint-staged -D
- 添加
package.json脚本
"prepare": "husky install"
- 添加
hooks
npx husky add .husky/pre-commit "pnpm run lint:lint-staged"
- 添加
commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
- 添加
lint-staged配置文件./.husky/lintstagedrc.js
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
'package.json': ['prettier --write'],
'*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'],
'*.{scss,less,styl,html}': ['stylelint --fix', 'prettier --write'],
'*.md': ['prettier --write'],
};
package.json中添加lint脚本
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
4. 集成pinia
4.1 安装pinia
pnpm i pinia
4.2 pinia目录结构
- 创建
src/store/index.ts
import { createPinia } from 'pinia'
const store = createPinia()
export default store
5. 集成vue-router
5.1 安装vue-router
pnpm i vue-router@4
5.2 配置路由
创建src/router/router.config.ts
5.2.1 静态路由constantRoutes
// src/router/router.config.ts
// 静态路由
const constantRoutes: RouteRecordRaw[] = [
{
path: '/login',
component: () => import('/@/views/login/index.vue'),
name: 'login',
meta: { title: '登录' },
},
{
path: '/',
name: 'App',
redirect: '/app',
meta: {
title: 'App',
},
},
]
5.2.2 publicRoutes
配置403、404等路由信息
// src/router/router.config.ts
// 错误页面路由
export const publicRoutes: RouteRecordRaw[] = [
{
path: '/:pathMatch(.*)',
redirect: '/404',
},
{
path: '/404',
component: () => import('@/views/404.vue'),
},
]
5.2.3 accessRoutes
需要登录后才能访问的路由,先只配置一个首页,后面写业务的时候再完善。
// src/router/router.config.ts
// 需要登录以及相关权限才能访问的页面路由
export const accessRoutes: RouteRecordRaw[] = [
{
path: '/app',
name: 'app',
component: BasicLayout,
redirect: '/app/home',
meta: { title: '后台管理系统' },
children: [
{
path: '/app/home',
component: () => import('@/views/home/index.vue'),
name: 'home',
meta: {
title: '首页',
icon: 'liulanqi',
auth: ['home'],
},
},
],
},
]
5.3 导出router
在src/router/index.ts中将router实例导出
import { createRouter, createWebHashHistory } from 'vue-router'
import routes from './router.config'
const router = createRouter({
history: createWebHashHistory(),
routes,
strict: true,
scrollBehavior: () => ({ left: 0, top: 0 }),
})
export default router
6. 集成ant-design-vue
6.1 安装ant-design-vue
pnpm i ant-design-vue
6.2 配置按需加载和自动导入
- 按需加载要用到
unplugin-vue-components,自动导入要用到unplugin-auto-import,这样在使用vue的ref之类的函数时不需要导入可以直接使用(需要配置)。
pnpm i unplugin-vue-components unplugin-auto-import -D
- 修改
vite.config.ts加载插件
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
vue(),
Components({
dts: 'src/components.d.ts',
resolvers: [AntDesignVueResolver()],
}),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dts: 'src/auto-imports.d.ts',
}),
],
})
配置好后组件不用导入,也不用在components中注册了,直接在template中使用就行,不只是对于UI库的组件,即便是我们自己编写的组件也是可以的。
6.3 安装图标包
pnpm i @ant-design/icons-vue
7. Vite环境变量
.env:所有情况下都会加载的配置文件
# port
VITE_PORT = 3100
# spa-title
VITE_GLOB_APP_TITLE = Plasticine Admin
# spa shortname
VITE_GLOB_APP_SHORT_NAME = vue_plasticine_admin
.env.development:开发环境下才会加载的配置文件
# public path
VITE_PUBLIC_PATH = /
.env.production:生产环境下才会加载的配置文件
# public path
VITE_PUBLIC_PATH = /
# Delete console
VITE_DROP_CONSOLE = true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
# Is it compatible with older browsers
VITE_LEGACY = false
.env.test:测试环境下才会加载的配置文件
NODE_ENV=production
# Whether to open mock
VITE_USE_MOCK = true
# public path
VITE_PUBLIC_PATH = /
# Delete console
VITE_DROP_CONSOLE = true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
# Is it compatible with older browsers
VITE_LEGACY = false
vite默认是没有测试环境的,如果需要运行测试环境,应当加上--mode参数,比如
vite build --mode test
8. 配置构建脚本
该步骤主要是为了实现动态修改配置
构建项目之后,会自动生成 _app.config.js 文件并插入 index.html
_app.config.js 用于项目在打包后,需要动态修改配置的需求,如接口地址。不用重新进行打包,可在打包后修改 /dist/_app.config.js 内的变量,刷新即可更新代码内的局部变量
8.1 构建脚本
package.json
"build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
"build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
8.2 esno
esno:用于直接运行ts代码,类似的库还有ts-node
pnpm i esno cross-env -D
8.3 cross-env
cross-env makes it so you can have a single command without worrying about setting or using the environment variable properly for the platform. Just set it like you would if it's running on a POSIX system, and cross-env will take care of setting it properly.
允许我们跨平台设置和使用环境变量,比如当我们使用 NODE_ENV = production 来设置环境变量的时候,大多数windows命令会提示将会阻塞或者异常,或者,windows不支持NODE_ENV=development的这样的设置方式,会报错。
8.4 postBuild.ts
build/script/postBuild.ts
// #!/usr/bin/env node
import { runBuildConfig } from './buildConf';
import chalk from 'chalk';
import pkg from '../../package.json';
export const runBuild = async () => {
try {
// 获取参数
const argvList = process.argv.splice(2);
// Generate configuration file
if (!argvList.includes('disabled-config')) {
runBuildConfig();
}
console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};
runBuild();
argvList获取命令参数,当我们运行脚本时,比如npx esno ./build/script/postBuild.ts a 1 2 3,得到的argvList === ['s', 'a', '1', '2', '3']
获取到参数后判断是否要读取配置文件,如果有参数disabled-config则直接build,不读取配置。
import pkg from '../../package.json'需要在ts.config.json中开启resolveJsonModule选项,并且删除ts.config.node.json,全部的include都放在ts.config.json中管理即可,不需要分开管理。
接下来看看runBuildConfig的原理。
8.5 runBuildConfig
创建build/script/buildConf.ts,该文件中导出一个函数runBuildConfig,用于读取配置信息,为什么要这样设计呢?这是为了在修改一些配置的时候不用重新构建,直接修改配置文件即可生效
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
先看看主要的函数runBuildConfig
build/script/buildConf.ts
export function runBuildConfig() {
// 读取指定前缀的环境变量 -- 默认前缀为 VITE_GLOB_
// 如 .env 中有 VITE_GLOB_PLASTICINE = plasticine
// 则 config.VITE_GLOB_PLASTICINE === 'plasticine'
const config = getEnvConfig();
// 读取配置文件变量名 -- 会被挂载到 window 对象上,并加上前后缀
// 如读取到配置 VITE_GLOB_APP_SHORT_NAME = plasticine_admin
// 则会有 window.__PRODUCTION__PLASTICINE_ADMIN__CONF__ 作为整个项目的配置
const configFileName = getConfigFileName(config);
// 根据配置文件的配置创建配置信息
createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME });
}
一步一步来,先看看用到的第一个函数getEnvConfig
8.5.1 getEnvConfig
build/utils.ts
/**
* 获取当前环境下生效的配置文件名
*/
function getConfFiles() {
// 1. 获取执行的脚本 如运行 npx esno ./build/utils.ts --> esno .\\\\build\\\\utils.ts
const script = process.env.npm_lifecycle_script;
// 2. 用正则表达式匹配运行环境
const reg = new RegExp('--mode ([a-z_\\d]+)');
const result = reg.exec(script as string) as any;
if (result) {
// 传入了 mode 参数则使用对应的环境
const mode = result[1] as string;
return ['.env', `.env.${mode}`];
}
// 否则默认是生产环境
return ['.env', '.env.production'];
}
/**
* 获取指定前缀的环境变量
* @param match 前缀
* @param confFiles ext
*/
export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) {
let envConfig = {};
// 读取 confFiles 中的键值对配置项到 envConfig 对象中
confFiles.forEach((item) => {
try {
// 读取当前脚本执行目录下的 confFiles 中的文件
// 得到的 env 就是配置文件中的键值对
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
envConfig = { ...envConfig, ...env };
} catch (e) {
console.error(`Error in parsing ${item}`, e);
}
});
// 只匹配 match 开头的配置项
const reg = new RegExp(`^(${match})`);
Object.keys(envConfig).forEach((key) => {
if (!reg.test(key)) {
// 不匹配的项利用 Reflect 反射将其从 envConfig 对象中删除
Reflect.deleteProperty(envConfig, key);
}
});
return envConfig;
}
8.5.2 getConfigFileName
build/utils.ts
/**
* 获取配置文件的变量名
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
// 读取配置项 VITE_GLOB_APP_SHORT_NAME 进行拼接,没有配置该项则默认用 __APP
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
.toUpperCase()
.replace(/\s/g, ''); // 去除空白字符
};
8.5.3 createConfig
这个才是重点,用于从读取到的配置项信息中动态注入一个配置项对象到window中
build/script/buildConf.ts
/**
* 根据配置参数去动态生成 window.项目配置属性 = { 配置项 }
* 然后生成 _app.config.js 到打包输出目录下
* @param params 配置参数
*/
function createConfig(params: CreateConfigParams) {
const { configName, config, configFileName } = params;
try {
// 1. 生成注入代码
const windowConf = `window.${configName}`; // 生成 项目配置属性名
// 将配置项对象挂载到 windowConf 属性名上
// 利用 Object.defineProperty 保证配置项对象不会被修改
const configStr = `${windowConf}=${JSON.stringify(config)}
Object.freeze(${windowConf});
Object.defineProperty(window, ${windowConf}, {
configurable: false,
writable: false
});`.replace(/\s/g, ''); // 去除空白字符
// 2. 检查构建目录是否存在,不存在则会创建 -- 注意:用的是 fs-extra 而不是 fs
fs.mkdirp(getRootPath(OUTPUT_DIR));
// 3. 创建动态生成的配置文件到 OUTPUT_DIR 目录下,并且将注入的代码写入该文件中
writeFileSync(getRootPath(OUTPUT_DIR, configFileName ?? '_app.config.js'), configStr);
// 4. 输出配置文件生成成功信息
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));
}
}
- 这里的
fs不是node自带的fs,而是一个第三方库fs-extra,mkdirp会在目录不存在的时候创建目录 writeFileSync,从函数名就可以直到它是会以阻塞的方式创建文件的chalk是一个用于控制台中输出有颜色文字的库
getRootPath能够获取项目根目录路径,并将传入的参数拼接到项目根目录路径后面,生成最终的绝对路径
/**
* 获取 dir 相对于项目根目录的绝对路径
* @param dir file path
*/
export function getRootPath(...dir: string[]) {
return path.resolve(process.cwd(), ...dir);
}
本质上就是封装了一下path.resolve用于生成绝对路径
process.cwd()获取的是node命令执行时所在的路径
8.6 将生成的js加载到打包的html中
现在我们运行pnpm run build确实是可以生成_app.config.js这么一个配置文件了
window.__PRODUCTION__VUE_PLASTICINE_ADMIN__CONF__={"VITE_GLOB_APP_TITLE":"PlasticineAdmin","VITE_GLOB_APP_SHORT_NAME":"vue_plasticine_admin"};Object.freeze(window.__PRODUCTION__VUE_PLASTICINE_ADMIN__CONF__);Object.defineProperty(window,"__PRODUCTION__VUE_PLASTICINE_ADMIN__CONF__",{configurable:false,writable:false});
但是有一个问题,这个配置文件并没有被放到最终打包好的html文件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script type="module" crossorigin src="/assets/index.e08c9cc4.js"></script>
<link rel="stylesheet" href="/assets/index.b30a997d.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
因此还差一步,就是让生成的js配置文件加载到html文件中,手动放上去太不靠谱了,这时候就要让vite的plugin大显身手了,有一个插件vite-plugin-html,它可以帮我们实现注入代码到最终打包的html文件中的功能。
先安装vite-plugin-html
pnpm i vite-plugin-html -D
然后按该插件的文档进行如下配置
import { createHtmlPlugin } from 'vite-plugin-html';
export default defineConfig({
plugins: [
createHtmlPlugin({
minify: true,
inject: {
data: {
title: 'plasticine',
},
tags: [
{
tag: 'script',
attrs: {
src: '/_app.config.js',
},
},
],
},
}),
],
});
文档链接:vite-plugin-html文档
现在再进行打包,得到的html如下
<!doctype html><html lang="en"><head><script src="/_app.config.js"></script><meta charset="UTF-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Vite App</title><script type="module" crossorigin src="/assets/index.e08c9cc4.js"></script><link rel="stylesheet" href="/assets/index.b30a997d.css"></head><body><div id="app"></div></body></html>
可以看到/_app.config.js被成功注入了
虽然现在是能够正常构建了,但是有一个问题,很多东西都是可配置的,但我们却把它们写死了,这十分不友好,接下来我们会进行一次代码重构,主要是搭建一个可配置的框架,将各个插件的配置分离到单个文件中去进行,再由一个统一的出口文件导出。这个我们放到下一篇文章讲解吧。