创建基础框架
- 使用vite创建基础框架 vite 官网
-
- 使用你常用的包管理器
-
-
npm
pnpm
yarn
bun
-
perl
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app --template vue
# bun
bun create vite my-vue-app --template vue
-
-
- 更多的基础框架模版
-
-
-
-
-
vue
vue-ts
react
react-ts
-
-
-
-
- 我们是ts项目,并且我选择的包管理器是
pnpm
所以我们的构建模版命令是
- 我们是ts项目,并且我选择的包管理器是
-
-
pnpm create vite my-vue-app --template vue-ts
-
关联git仓库
- 打开 gitee官网 ,登陆之后选择新建项目
-
- gitee 新建项目步骤
-
-
- 点击新建
-
-
-
- 填写仓库信息
-
-
-
- 创建完之后出来本地项目关联仓库步骤
-
- 使用
vscode
打开项目,然后打开终端,输入以下命令
csharp
git init
git add .
git commit -m "first commit"
git remote add origin 你的git仓库地址
git push -u origin "master"
- 成功如下
-
- vscode
- gitee
约束和统一代码规范
- 多人协作项目中,代码规范约束是必不可少的
- 使用的vscode插件列表
-
- .vscode目录下新增
extensions.json
文件,填入以下插件列表,然后点击vscode插件图标,搜索框右边点击漏斗图标,点击推荐,安装相应插件
- .vscode目录下新增
json
{
"recommendations": ["Vue.volar","dbaeumer.vscode-eslint","rvest.vs-code-prettier-eslint","esbenp.prettier-vscode","editorconfig.editorconfig","stylelint.vscode-stylelint"]
}
- 使用的插件如下
sql
pnpm i eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue prettier stylelint@15.11 stylelint-config-html@1.1 stylelint-config-recess-order@4.3 stylelint-config-recommended-scss@13 stylelint-config-recommended-vue@1.5 stylelint-config-standard@34 stylelint-config-standard-scss@13 @typescript-eslint/eslint-plugin -D
- 步骤如下
-
- 项目根目录下创建如下文件 -- 跟
package.json
同级
- 项目根目录下创建如下文件 -- 跟
bash
.prettierrc.cjs # prettier 格式化
.prettierignore # prettier 忽略文件
.eslintrc.cjs # eslint 格式化
.eslintignore #eslint 忽略文件
.stylelintrc.cjs # css 格式化
.stylelintignore # css 忽略文件
.editorconfig # 编译器配置 用于抹平各个编译器的配置 统一设置
.gitignore # git 忽略文件
-
-
- 各个文件的配置内容如下
-
eslint prettier stylelintrc editorconfig gitignore 详细配置
- eslint
perl
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: "vue-eslint-parser",
extends: [
// https://eslint.vuejs.org/user-guide/#usage
"plugin:vue/vue3-recommended",
// "./.eslintrc-auto-import.json",
"prettier",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
// "@unocss",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: "@typescript-eslint/parser",
project: "./tsconfig.*?.json",
createDefaultProgram: false,
extraFileExtensions: [".vue"],
},
plugins: ["vue", "@typescript-eslint"],
rules: {
// https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention
"vue/multi-word-component-names": 0,
"vue/no-v-model-argument": "off",
"vue/script-setup-uses-vars": "error",
"vue/no-reserved-component-names": "error",
"vue/custom-event-name-casing": "error",
"vue/attributes-order": "warn",
"vue/one-component-per-file": "off",
"vue/max-attributes-per-line": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/attribute-hyphenation": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "warn",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
"@typescript-eslint/no-empty-function": "error", // 关闭空方法检查
"@typescript-eslint/no-explicit-any": "error", // 关闭any类型的警告
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": 2,
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": 2,
// "no-debugger": import.meta.env.mode === "production" ? "error" : "off",
"prettier/prettier": [
"error",
{
useTabs: false, // 不使用制表符
},
],
"vue/html-closing-bracket-newline": "off",
},
// eslint不能对html文件生效
overrides: [
{
files: ["*.html"],
processor: "vue/.vue",
},
],
// https://eslint.org/docs/latest/use/configure/language-options#specifying-globals
globals: {
DialogOption: "readonly",
OptionType: "readonly",
},
};
- eslintignore
arduino
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
*.woff
*.ttf
.idea
src/assets
.eslintrc.cjs
.prettierrc.cjs
.stylelintrc.cjs
- prettierrc
java
module.exports = {
// (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always)
arrowParens: "always",
// 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false
bracketSameLine: false,
// 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar})
bracketSpacing: true,
// 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto)
embeddedLanguageFormatting: "auto",
// 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css)
htmlWhitespaceSensitivity: "css",
// 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false
insertPragma: false,
// 在 JSX 中使用单引号替代双引号,默认false
jsxSingleQuote: false,
// 每行最多字符数量,超出换行(默认80)
printWidth: 80,
// 超出打印宽度 (always | never | preserve )
proseWrap: "preserve",
// 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;)
quoteProps: "as-needed",
// 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false
requirePragma: false,
// 结尾添加分号
semi: true,
// 使用单引号 (true:单引号;false:双引号)
singleQuote: false,
// 缩进空格数,默认2个空格
tabWidth: 2,
// 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号
trailingComma: "es5",
// 指定缩进方式,空格或tab,默认false,即使用空格
useTabs: false,
// vue 文件中是否缩进 <style> 和 <script> 标签,默认 false
vueIndentScriptAndStyle: false,
endOfLine: "auto",
overrides: [
{
files: "*.html",
options: {
parser: "html",
},
},
],
};
- prettierignore
arduino
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
- stylelintrc
java
module.exports = {
// 继承推荐规范配置
extends: [
'stylelint-config-standard',
'stylelint-config-recommended-scss',
'stylelint-config-recommended-vue/scss',
'stylelint-config-html/vue',
'stylelint-config-recess-order',
],
// 指定不同文件对应的解析器
overrides: [
{
files: ['**/*.{vue,html}'],
customSyntax: 'postcss-html',
},
{
files: ['**/*.{css,scss}'],
customSyntax: 'postcss-scss',
},
],
// 自定义规则
rules: {
// 允许 global 、export 、v-deep等伪类
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
},
],
// scss 语法提示
// 参考 https://github.com/stylelint/stylelint/issues/3190
'at-rule-no-unknown': null,
'scss/at-rule-no-unknown': true,
// css书写顺序
'order/order': ['declarations', 'custom-properties', 'dollar-variables', 'rules', 'at-rules'],
'order/properties-order': [
'position',
'z-index',
// 其他样式的顺序
],
// 其他规则
'no-empty-source': null,
},
}
- stylelintignore
arduino
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
- editorconfig
ini
# http://editorconfig.org
root = true
# 表示所有文件适用
[*]
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = tab # 缩进风格(tab | space)
insert_final_newline = true # 始终在文件末尾插入一个新行
max_line_length = 80 # 设置最大行长度为 80 字符
# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
- gitignore
bash
node_modules
.DS_Store
dist
dist-ssr
*.local
.history
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
package-lock.json
pnpm-lock.yaml
stats.html
# typings
src/typings/auto-imports.d.ts
src/typings/components.d.ts
-
packge.json
文件内的scripts
内新增
swift
"lint:eslint": "eslint "src/**/*.{vue,ts,js}" --fix",
"lint:prettier": "prettier --write "**/*.{js,ts,json,css,less,scss,vue,html,md}"",
"lint:stylelint": "stylelint "**/*.{css,scss,vue,html}" --fix",
"lint:fix": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
- .vscode 目录下新增
settings.json
文件,填入以下内容
json
{
"editor.formatOnSave": true, //选择性开启
"eslint.enable": true, // 开启eslint检查
"prettier.useEditorConfig": true,
"prettier.requireConfig": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", // 指定 prettier 为所有文件默认格式化器
//针对共用的语言如JS、TS和JSX关闭文件保存自动格式化功能,通过eslint来做这件事
"[javascript]": {
"editor.formatOnSave": false
},
"[javascriptreact]": {
"editor.formatOnSave": false
},
"[typescript]": {
"editor.formatOnSave": false
},
"editor.codeActionsOnSave": {
"source.fixAll": "always",
"source.fixAll.eslint": "always", // 开启eslint自动检测
"source.fixAll.stylelint": "always", // 开启 Stylelint 保存自动检测
},
// Stylelint 校验文件
"stylelint.validate": [
"css",
"scss",
"vue",
"less",
"html"
],
"cSpell.words": [
"Attributify",
"breakline",
"brotliccompress",
"commitlint",
"consola",
"ERUDA",
"flac",
"Pausable",
"recyclabledev",
"Triggerable",
"updatepassword",
"vuejsx"
],
"css.customData": [".vscode/tailwindcss.json"],
"editor.quickSuggestions": {
"strings": true,
"other": true,
"comments": true,
},
// "unocss.root": "packages/client"
}
- 测试校验 警告或者报错之类的不用管,稍后增加自动修复
-
- 运行 eslint 检查命令
pnpm lint:eslint
- 运行 eslint 检查命令
-
-
- 结果如下
-
-
- 运行 prettier 检查命令
pnpm lint:prettier
- 运行 prettier 检查命令
-
-
- 结果如下
-
-
- 运行 stylelint 检查命令
pnpm lint:stylelint
- 运行 stylelint 检查命令
-
-
- 结果如下
-
-
- 以上三条命令合并为一条
-
-
pnpm lint:fix
运行起来后效果一样
-
添加代码提交规范校验-husky
- 所需的插件列表
-
- husky
- lint-staged
- @commitlint
- @commitlint/config-conventional
- commitizen
- cz-git
- 安装husky
csharp
pnpm dlx husky-init && pnpm install
-
- 上一个步骤完成后,
packge.json
内的scripts
内应该有"prepare": "husky install"
这条命令 - 执行创建钩子命令
npx husky add .husky/pre-commit "npm test"
- 添加一条记录
git add .husky/pre-commit
- 上一个步骤完成后,
- 安装 lint-staged
css
pnpm i lint-staged -D
-
package.json
文件内 新增如下内容,注意需要和scripts
同级
json
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json}": [
"prettier --write"
],
"*.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,css}": [
"stylelint --fix --allow-empty-input",
"prettier --write"
],
"*.md": [
"prettier --write"
]
},
-
package.json
的scripts
内新增一条命令:"lint:lint-staged": "lint-staged"
- 修改提交前钩子触发命令
-
-
- 根目录
.husky
目录下的pre-commit
文件中的npm test
修改为pnpm lint:lint-staged
- 根目录
-
-
- git提交一次记录试试 出现如下表示成功
- Commitlint 检查您的提交消息是否符合 Conventional commit format.
css
pnpm i @commitlint @commitlint/config-conventional -D
-
- 根目录创建配置文件
commitlint.config.cjs
,内容如下 参考配置
- 根目录创建配置文件
perl
module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// @see: https://commitlint.js.org/#/reference-rules
rules: {
"subject-case": [0], // subject大小写不做校验
// 类型枚举,git提交type必须是以下类型
"type-enum": [
2,
"always",
[
'feat', // 新增功能
'fix', // 修复缺陷
'docs', // 文档变更
'style', // 代码格式(不影响功能,例如空格、分号等格式修正)
'refactor', // 代码重构(不包括 bug 修复、功能新增)
'perf', // 性能优化
'test', // 添加疏漏测试或已有测试改动
'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
'ci', // 修改 CI 配置、脚本
'revert', // 回滚 commit
'chore', // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
},
};
-
- 终端运行如下命令,添加提交信息钩子
bash
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
-
- 验证
-
-
- 正确的提交格式:
<type>(<scope>): <subject>
,type 和 subject 默认必填
- 正确的提交格式:
-
注意: :
是英文冒号,冒号后面需要一个空格
- Commitizen & cz-git
-
- commitizen : 基于Node.js的
git commit
命令行工具,辅助生成标准化规范化的 commit message
- commitizen : 基于Node.js的
-
- cz-git: 一款工程性更强,轻量级,高度自定义,标准输出格式的 commitizen 适配器
-
- 安装
css
pnpm i commitizen cz-git -D
-
- cz-git 配置
-
-
- 在
package.json
中scripts
同级的地方添加如下命令
- 在
-
json
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
-
-
-
- cz-git 与 commitlint 进行联动给予校验信息,所以可以编写于 commitlint 配置文件之中
-
-
-
- 添加提交指令,执行git 操作和校验
swift
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint:eslint": "eslint "src/**/*.{vue,ts,js}" --fix",
"lint:prettier": "prettier --write "**/*.{js,ts,json,css,less,scss,vue,html,md}"",
"lint:stylelint": "stylelint "**/*.{css,scss,vue,html}" --fix",
"lint:fix": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install",
"lint:lint-staged": "lint-staged",
"commit": "git pull && git add -A && git-cz && git push" // 添加这一行
},
-
- 修改
commitlint.config.cjs
文件内容
- 修改
java
module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// @see https://commitlint.js.org/#/reference-rules
// 提交类型枚举,git提交type必须是以下类型
"type-enum": [
2,
"always",
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
"refactor", // 代码重构(不包括 bug 修复、功能新增)
"perf", // 性能优化
"test", // 添加疏漏测试或已有测试改动
"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
"subject-case": [0], // subject大小写不做校验
},
prompt: {
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀(可选):",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: "是否提交或修改commit ?",
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝 文档变更", emoji: ":memo:" },
{ value: "style", name: "格式: 🌈 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: 🔄 代码重构(不包括 bug 修复、功能新增)", emoji: ":recycle:" },
{ value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [
{ value: "closed", name: "closed: ISSUES has been processed" },
],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
};
-
- 测试提交
- release-it自动生成changelog
-
- release-it 是一个命令行工具,用于在发布新版本时自动化处理一系列任务。它可以帮助您自动化执行以下任务:
-
-
- 增加版本号并提交 Git
- 生成变更日志(Changelog)并提交到 Git
- 创建 Git 标签并推送到远程仓库
- 发布到 npm 等软件仓库
- 在 GitHub、GitLab 等平台创建发行版
-
-
- 安装插件
-
-
pnpm add release-it @release-it/conventional-changelog -D
-
-
- 在根目录下添加脚本文件
release-it.json
- 在根目录下添加脚本文件
perl
{
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
},
"git": {
"commitMessage": "chore: Release v${version}"
}
}
-
- 添加执行命令
swift
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint:eslint": "eslint "src/**/*.{vue,ts,js}" --fix",
"lint:prettier": "prettier --write "**/*.{js,ts,json,css,less,scss,vue,html,md}"",
"lint:stylelint": "stylelint "**/*.{css,scss,vue,html}" --fix",
"lint:fix": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install",
"lint:lint-staged": "lint-staged",
"commit": "git pull && git add -A && git-cz && git push",
"release": "release-it" // 这一行
},
-
-
-
- 注意: 运行
pnpm release
时, 工作目录必须是干净的, 也就是说你需要把修改了的文件提交, 或者使用git stash
放入缓存中, 运行完了, 别忘了在git stash pop
取出来
- 注意: 运行
-
-
-
-
-
-
- 以下是我没有提交就运行
pnpm release
的报错
- 以下是我没有提交就运行
-
-
-
-
- npm 版本号
-
-
- 先补充一下npm版本号相关知识,npm的版本号遵循
SemVer
规范,版本号格式必须采用X.Y.Z
的格式,其中X、Y
和Z
为非负的整数。X
是主版本号、Y
是次版本号、而Z
为修订号,英文对应表示为major
、minor
、patch
,每个号必须采用递增。
- 先补充一下npm版本号相关知识,npm的版本号遵循
-
-
-
-
- 例如
-
-
ini
X.Y.Z => {major}.{minor}.{patch}
0.0.1
# 更新主版本号
pnpm release major
# 更新次版本号
pnpm release minor
# 更新修订号
pnpm release patch
-
-
-
- 了解更多,请看官网
-
-
-
- 执行过程如下
-
-
- release-it会自动执行配置的任务,更新版本号码和变更日志的描述。完成后,它会自动提交代码(Commit)、打标签(Tag)、推送(Push)到仓库等等。
- 查看 changelog: 生成的 changelog 将会保存在 CHANGELOG.md 文件中。你可以查看这个文件来了解新版本的变化。
-
添加unocss 原子化插件
- 安装
-
pnpm i unocss -D
- 配置 按照官网进行配置
-
- 配置vite
javascript
// vite.config.ts
import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
UnoCSS(),
],
})
-
- 根目录创建
uno.config.ts
- 根目录创建
javascript
// uno.config.ts
import { defineConfig, presetUno, presetAttributify } from "unocss";
export default defineConfig({
// ...UnoCSS options
// 自定义简写以提供自动补齐建议。 如果 values 是一个数组,它将与 | 连接并用 () 封装。
// 以下是我的部分配置
shortcuts: [
{
"flex-center-center": "flex justify-center items-center",
"flex-between-center": "flex justify-between items-center",
"absolute-center":
"absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]",
"border-s-w": "border-[1px] border-solid border-white",
"hw-full": "h-full w-full",
"hw-100": "h-[calc(100vh-70px)] w-screen",
"t-3c": "text-[#3cbdc5]",
"t-c": "text-center",
"bg-3c": "bg-[#3cbdc5]",
"border-b-solid": "border-b-1px border-b-solid border-b-[#e5e5e5]",
},
],
// 自定义css类
rules: [
[
"bg-no-repeat-cover",
{ "background-repeat": "no-repeat", "background-size": "cover" },
],
[/^br-(\d+)$/, ([, d]) => ({ "border-radius": `${d}px` })],
[
/^base-shadow-(\d+)$/,
([, d]) => ({ "box-shadow": `0 0 ${d}px 0 rgba(0, 0, 0, 0.05)` }),
],
],
presets: [presetUno(), presetAttributify()],
});
-
- 安装 css 样式重置文件
-
-
pnpm add @unocss/reset -D
-
-
main.ts
入口引入 样式重置文件 和 unocss
arduino
// main.ts
/** 重置样式 这里引入自定义的重置样式也可 */
import '@unocss/reset/tailwind-compat.css'
// 引入项目的自定义样式,需要在引入unocss之前
import "./style.css";
import 'virtual:uno.css'
-
- 安装 unocss eslint 插件
-
-
pnpm i @unocss/eslint-config -D
- 更新
eslintrc.cjs
文件
-
perl
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: "vue-eslint-parser",
extends: [
// https://eslint.vuejs.org/user-guide/#usage
"plugin:vue/vue3-recommended",
// "./.eslintrc-auto-import.json",
"prettier",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"@unocss",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: "@typescript-eslint/parser",
project: "./tsconfig.*?.json",
createDefaultProgram: false,
extraFileExtensions: [".vue"],
},
plugins: ["vue", "@typescript-eslint"],
rules: {
// https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention
"vue/multi-word-component-names": 0,
"vue/no-v-model-argument": "off",
"vue/script-setup-uses-vars": "error",
"vue/no-reserved-component-names": "error",
"vue/custom-event-name-casing": "error",
"vue/attributes-order": "warn",
"vue/one-component-per-file": "off",
"vue/max-attributes-per-line": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/attribute-hyphenation": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "warn",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
"@typescript-eslint/no-empty-function": "error", // 关闭空方法检查
"@typescript-eslint/no-explicit-any": "error", // 关闭any类型的警告
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": 2,
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": 2,
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"prettier/prettier": [
"error",
{
useTabs: false, // 不使用制表符
},
],
"vue/html-closing-bracket-newline": "off",
},
// eslint不能对html文件生效
overrides: [
{
files: ["*.html"],
processor: "vue/.vue",
},
],
// https://eslint.org/docs/latest/use/configure/language-options#specifying-globals
globals: {
DialogOption: "readonly",
OptionType: "readonly",
},
};
-
- 运行效果如下
- 安装vscode unocss插件,获得更好的unocss书写体验
-
-
- unocss
- vscode-unocss-highlight
- vscode-uno-magic
-
自动导入vue的相关函数
- 如
ref
watch
等等 - 步骤
-
- 安装自动导入插件
-
-
pnpm i unplugin-auto-import -D
-
-
- vite.config.ts 配置
javascript
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
import AutoImport from "unplugin-auto-import/vite";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
console.log(env);
return {
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
],
};
});
-
- 重新运行项目,尝试不导入vue的函数,直接使用
-
- 如果eslint有爆红,重启vscode即可解决
自动导入 第三方组件库和自定义组件文件
以下使用vant
组件库示例,别的组件库也是如此,不再过多赘述 参照各个组件库官网即可
- 安装 vant 和自动导入插件
-
pnpm i vant unplugin-vue-components @vant/auto-import-resolver -D
- vite.config.ts 导入
javascript
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// 自动导入 vue 等的 相关的函数
import AutoImport from "unplugin-auto-import/vite";
// 自动导入第三方组件 和项目组件
import components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
console.log(env);
return {
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
resolvers: [VantResolver()],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
components({
resolvers: [VantResolver()], //第三方组件库 例如 vant element 等
dirs: ["src/components", "src/**/components"], //自定义的组件位置
dts: mode === "development" ? "src/typings/components.d.ts" : false, //生成声明文件位置
}),
],
};
});
- main.ts 引入 css文件
javascript
import { createApp } from "vue";
/** 重置样式 这里引入自定义的重置样式也可 */
import "@unocss/reset/tailwind-compat.css";
// 引入项目的自定义样式,需要在引入unocss之前
import "./style.css";
import "virtual:uno.css";
import "vant/es/toast/style";
import "vant/es/dialog/style";
import "vant/es/notify/style";
import "vant/es/image-preview/style";
import App from "./App.vue";
createApp(App).mount("#app");
- 体验
xml
<script setup lang="ts">
const num = ref("100");
onMounted(() => {
console.log(num.value);
});
</script>
<template>
<div class="uno-css-demo-box">
<div class="title text-center text-[30px] text-warm-gray-200 font-bold">
体验uno-css
</div>
<div class="url text-center text-[16px] text-[pink]">
https://unocss.nodejs.cn/
</div>
<van-button text="vant-button" type="primary" />
<HelloWorld :msg="num" />
</div>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
transition: filter 300ms;
will-change: filter;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
路由自动导入和布局组件自动导入
- 使用到的插件是
-
vite-plugin-vue-layouts
vite-plugin-pages
github
- 安装
-
pnpm i vite-plugin-pages vite-plugin-vue-layouts nprogress @types/nprogress vue-router -D
-
-
- nprogress 是顶部进度条展示,根据项目要求来,可装可不装
-
- vite.config.ts 配置
-
- 顺便一起配置
src
--@
映射
- 顺便一起配置
javascript
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// 自动导入 vue 等的 相关的函数
import AutoImport from "unplugin-auto-import/vite";
// 自动导入第三方组件 和项目组件
import components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
// 路由自动导入和布局组件自动合成
import { resolve } from "path";
import Pages from "vite-plugin-pages";
import Layouts from "vite-plugin-vue-layouts";
const pathSrc = resolve(__dirname, "src");
const pathComp = resolve(__dirname, "src/components");
const pathAssets = resolve(__dirname, "src/assets");
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
console.log(env);
return {
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
resolvers: [VantResolver()],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
components({
resolvers: [VantResolver()], //第三方组件库 例如 vant element 等
dirs: ["src/components", "src/**/components"], //自定义的组件位置
dts: mode === "development" ? "src/typings/components.d.ts" : false, //生成声明文件位置
}),
Pages({
pagesDir: "src/views", //需要生成路由的文件目录,默认就是识别src下面的pages文件
extensions: ["vue"],
exclude: ["**/components/*.vue"], // 忽略的文件夹
importMode: "async", // 是否是异步路由
}),
Layouts({
// 如果是默认 layouts文件夹,默认 default.vue文件,则不需要配置
layoutsDirs: "src/layouts", // 布局文件存放目录
defaultLayout: "default", //对应 src/layouts/default.vue
}),
],
resolve: {
alias: {
"@": pathSrc,
comp: pathComp,
// 配置图片要这样引用也可以自定义方法引入图片静态资源
assets: pathAssets,
},
},
};
});
- tsconfig.json
json
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"noLib": false,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"allowJs": true,
"paths": {
"@/*": ["src/*"],
"comp/*": ["src/components/*"],
"assets/*": ["src/assets/*"]
},
"types": [
"vite/client",
// "unplugin-icons/types/vue",
"vue"
// "vite-plugin-vue-layouts/client"
],
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
"allowSyntheticDefaultImports": true /* 允许默认导入 */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
},
"include": [
"src/**/*.ts",
"src/**/*.vue",
"src/typings/**/*.d.ts",
"mock/**/*.ts",
"vite.config.ts"
],
"exclude": ["node_modules", "dist", "**/*.js"]
}
- 步骤
- 自动导入路由文件
-
- src 下创建
router
目录,然后目录下新建index.ts
,内容入下
- src 下创建
javascript
// vue3:
// createRouter:创建路由实例
// 路由模式:history(localhost:5173/login) hash(localhost:5173/#/login)
// createWebHistory:设置 history 模式
// createWebHashHistory:设置 hash 模式
// import.meta.env.BASE_URL:去配置文件(vite.config.ts)中找到设置 base 路径
// 设置之后,开启服务时,会在服务前添加 base 对应的路径(默认值:/)
import { createRouter, createWebHistory } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({
showSpinner: false, // 关闭右侧加载动画
});
// 创建路由对象
const router = createRouter({
// 设置模式:history 模式
history: createWebHistory(import.meta.env.BASE_URL),
routes: [],
});
//导航守卫
router.beforeEach((to, from, next) => {
//进度条
NProgress.start();
//动态修改标题
if (to.meta.title) {
document.title = `${to.meta.title || ""}`;
}
next();
});
//后置守卫
router.afterEach((to) => {
document.title = `${to.meta?.title || ""}`;
NProgress.done();
});
export default router;
-
- man.ts 导入
javascript
import { createApp } from "vue";
/** 重置样式 这里引入自定义的重置样式也可 */
import "@unocss/reset/tailwind-compat.css";
// 引入项目的自定义样式,需要在引入unocss之前
// import "./style.css";
import "virtual:uno.css";
import "vant/es/toast/style";
import "vant/es/dialog/style";
import "vant/es/notify/style";
import "vant/es/image-preview/style";
import App from "./App.vue";
//挂载路由
import router from "./router";
const app = createApp(App);
app.use(router);
app.mount("#app");
-
- typings 目录下新建
env.d.ts
文件
- typings 目录下新建
php
/// <reference types="vite/client" />
/// <reference types="vite-plugin-pages/client" /> // 去除 ts告警
/// <reference types="vite-plugin-vue-layouts/client" />// 去除 ts告警
declare module "*.vue" {
import { DefineComponent } from "vue";
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>;
export default component;
}
interface ImportMetaEnv {
/** @description 网站标题 */
readonly VITE_APP_TITLE: string;
/** @description 开发环境 端口 : 9527 */
readonly VITE_APP_PORT: number;
/** @description 代理前缀 : /api */
readonly VITE_APP_BASE_API: string;
/** @description 代理网站地址 也是正式环境的网站地址 */
readonly VITE_APP_BASE_URL: string;
/** @description 打包访问目录 默认 : / */
readonly VITE_PUBLIC_PATH: string;
/** @description 本地存储token的标识 : token */
readonly VITE_TOKEN: string;
/** @description oss 图片地址 */
readonly VITE_APP_IMG_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
-
- src 下创建
views
目录,目录下创建2个文件夹,目录结构如下
- src 下创建
-
-
- 释义
-
-
-
-
- [id].vue,是指的是动态路由,访问这个路由需要传递一个id的参数
- [...all].vue,是适配404的页面
-
-
-
- 去手动创建路由
router/index
内
- 去手动创建路由
javascript
// vue3:
// createRouter:创建路由实例
// 路由模式:history(localhost:5173/login) hash(localhost:5173/#/login)
// createWebHistory:设置 history 模式
// createWebHashHistory:设置 hash 模式
// import.meta.env.BASE_URL:去配置文件(vite.config.ts)中找到设置 base 路径
// 设置之后,开启服务时,会在服务前添加 base 对应的路径(默认值:/)
import { createRouter, createWebHistory } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({
showSpinner: false, // 关闭右侧加载动画
});
// 创建路由对象
const router = createRouter({
// 设置模式:history 模式
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/home",
name: "home",
meta: {
title: "首页",
},
component: () => import("@/views/home/index.vue"),
},
{
path: "/home/test",
name: "home-test",
meta: {
title: "首页-test",
},
component: () => import("@/views/home/test.vue"),
},
{
path: "/login",
name: "login",
meta: {
title: "登录页",
},
component: () => import("@/views/login/index.vue"),
},
],
});
//导航守卫
router.beforeEach((to, from, next) => {
//进度条
NProgress.start();
//动态修改标题
if (to.meta.title) {
document.title = `${to.meta.title || ""}`;
}
next();
});
//后置守卫
router.afterEach((to) => {
document.title = `${to.meta?.title || ""}`;
NProgress.done();
});
export default router;
-
- App.vue文件内删除内容,填充以下内容
xml
<script setup lang="ts"></script>
<template>
<router-view />
</template>
<style scoped></style>
-
- 以上创建路由就是常规的创建,现在,改造
router/index.ts
,使用插件自动生成,只需要2个步骤,导入和使用即可,内容如下
- 以上创建路由就是常规的创建,现在,改造
javascript
// vue3:
// createRouter:创建路由实例
// 路由模式:history(localhost:5173/login) hash(localhost:5173/#/login)
// createWebHistory:设置 history 模式
// createWebHashHistory:设置 hash 模式
// import.meta.env.BASE_URL:去配置文件(vite.config.ts)中找到设置 base 路径
// 设置之后,开启服务时,会在服务前添加 base 对应的路径(默认值:/)
import { createRouter, createWebHistory } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import routes from "~pages"; // 导入插件自动生成的路由
// 如果这里导入报错,请查看上面的env.d.ts 文件内有没有导入 第二行代码
console.log(routes);
NProgress.configure({
showSpinner: false, // 关闭右侧加载动画
});
// 创建路由对象
const router = createRouter({
// 设置模式:history 模式
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
redirect: "/home",
},
// {
// path: "/home",
// name: "home",
// meta: {
// title: "首页",
// },
// component: () => import("@/views/home/index.vue"),
// },
// {
// path: "/home/test",
// name: "home-test",
// meta: {
// title: "首页-test",
// },
// component: () => import("@/views/home/test.vue"),
// },
// {
// path: "/login",
// name: "login",
// meta: {
// title: "登录页",
// },
// component: () => import("@/views/login/index.vue"),
// },
...routes,//使用
],
});
//导航守卫
router.beforeEach((to, from, next) => {
//进度条
NProgress.start();
//动态修改标题
if (to.meta.title) {
document.title = `${to.meta.title || ""}`;
}
next();
});
//后置守卫
router.afterEach((to) => {
document.title = `${to.meta?.title || ""}`;
NProgress.done();
});
export default router;
-
- 关于需要添加meta信息和自定义路由的名字,需要在响应的页面下写一个
route
标签 格式与常规书写路由的格式一致,可用语言有json
yaml
json5
,默认json5
- 关于需要添加meta信息和自定义路由的名字,需要在响应的页面下写一个
xml
<script setup lang="ts">
import {} from "vue";
</script>
<template>
<div class="">login-index</div>
</template>
<style scoped lang="scss"></style>
<route lang="json5">
{
meta: {
title: "自定义登录页标题",
},
name: "c-login",
path: "/c-login",
}
</route>
- 自动导入和使用layouts布局组件
-
- src下创建
layouts
目录,目录下创建default.vue
文件,常规的手机端布局,上中下
- src下创建
xml
<script setup lang="ts">
import {} from "vue";
</script>
<template>
<div class="h-[100vh] flex flex-col ">
<div class="header h-[50px] bg-pink text-center">Header</div>
<div class="main flex flex-1 bg-[lavenderblush]">
<router-view />
</div>
<div class="footer h-[50px] bg-[paleturquoise] text-center">Footer</div>
</div>
</template>
<style></style>
-
- 然后改造下router/index.ts文件
javascript
// vue3:
// createRouter:创建路由实例
// 路由模式:history(localhost:5173/login) hash(localhost:5173/#/login)
// createWebHistory:设置 history 模式
// createWebHashHistory:设置 hash 模式
// import.meta.env.BASE_URL:去配置文件(vite.config.ts)中找到设置 base 路径
// 设置之后,开启服务时,会在服务前添加 base 对应的路径(默认值:/)
import { createRouter, createWebHistory } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import routes from "~pages";
import { setupLayouts } from "virtual:generated-layouts";
const route = setupLayouts(routes);
NProgress.configure({
showSpinner: false, // 关闭右侧加载动画
});
// 创建路由对象
const router = createRouter({
// 设置模式:history 模式
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
redirect: "/home",
},
...route,
],
});
//导航守卫
router.beforeEach((to, from, next) => {
//进度条
NProgress.start();
//动态修改标题
if (to.meta.title) {
document.title = `${to.meta.title || ""}`;
}
next();
});
//后置守卫
router.afterEach((to) => {
document.title = `${to.meta?.title || ""}`;
NProgress.done();
});
export default router;
-
- 如果某个页面不需要布局组件或者使用别的布局组件,那么同样可以在对应的文件内的route标签内填写
bash
<route lang="json5">
{
meta: {
title: "test",
layout: "headers", // layouts/下面需要有这个布局组件 ,如果不需要布局组件,给个false/或者给个不存在的布局组件即可
},
}
</route>
增加打包文件标题管理,入口文件管理
vite-plugin-html
更多的是用来处理多页应用程序,但是现在我们没有这个场景,只需要用来根据环境变量修改标题,引入入口js文件即可。- 安装以及导入使用
-
pnpm i vite-plugin-html -D
- vite.config.ts 中导入以及使用
php
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// 自动导入 vue 等的 相关的函数
import AutoImport from "unplugin-auto-import/vite";
// 自动导入第三方组件 和项目组件
import components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
// 路由自动导入和布局组件自动合成
import { resolve } from "path";
import Pages from "vite-plugin-pages";
import Layouts from "vite-plugin-vue-layouts";
// 导入 vite-plugin-html 根据环境变量修改标题
import { createHtmlPlugin } from "vite-plugin-html";
// 配置路径简写
const pathSrc = resolve(__dirname, "src");
const pathComp = resolve(__dirname, "src/components");
const pathAssets = resolve(__dirname, "src/assets");
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
console.log(env);
return {
base: env.VITE_PUBLIC_PATH,
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
resolvers: [VantResolver()],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
components({
resolvers: [VantResolver()], //第三方组件库 例如 vant element 等
dirs: ["src/components", "src/**/components"], //自定义的组件位置
dts: mode === "development" ? "src/typings/components.d.ts" : false, //生成声明文件位置
}),
Pages({
pagesDir: "src/views", //需要生成路由的文件目录,默认就是识别src下面的pages文件
extensions: ["vue"],
exclude: ["**/components/*.vue"], // 忽略的文件夹
importMode: "async", // 是否是异步路由
}),
Layouts({
// 如果是默认 layouts文件夹,默认 default.vue文件,则不需要配置
layoutsDirs: "src/layouts", // 布局文件存放目录
defaultLayout: "default", //对应 src/layouts/default.vue
}),
createHtmlPlugin({
minify: true,
/**
* 在这里写entry后,你将不需要在`index.html`内添加 script 标签,原有标签需要删除
* @default src/main.ts
*/
entry: "/src/main.ts",
/**
* 需要注入 index.html ejs 模版的数据
*/
inject: {
data: {
// 查找.env 等环境变量文件里面的VITE_PROJECT_TITLE,请以VITE_标识开头
title: loadEnv(mode, process.cwd()).VITE_PROJECT_TITLE,
injectScript: `<script src="/inject.js"></script> `,
ENABLE_ERUDA: env.VITE_ENABLE_ERUDA || "false",
},
},
}),
],
resolve: {
alias: {
"@": pathSrc,
comp: pathComp,
// 配置图片要这样引用也可以自定义方法引入图片静态资源
assets: pathAssets,
},
},
};
});
-
- 改造
index.html
文件
- 改造
xml
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
/>
<title><%- title %></title>
</head>
<body>
<div id="app"></div>
<%- injectScript %>
<script>
global = globalThis;
</script>
<!-- <script type="module" src="/src/main.ts"></script> -->
</body>
</html>
配置gzip压缩
- 安装
vite-plugin-compression
插件 github
-
pnpm i vite-plugin-compression -D
- 参数说明:
| 参数 | 类型 | 默认值 | 说明 |
|--------------------|--------------------------------------|---------------|------------------------------------------------------------------|-----|------|-----|----------|
| verbose | boolean | true | 是否在控制台输出压缩结果 |
| filter | RegExp or (file: string) => boolean | DefaultFilter | 指定哪些资源不压缩DefaultFilter:/.(js | mjs | json | css | html)$/i |
| disable | boolean | false | 是否禁用 |
| threshold | number | - | 体积大于 threshold 才会被压缩,单位 b |
| algorithm | string | gzip | 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw'] |
| ext | string | .gz | 生成的压缩包后缀 |
| compressionOptions | Object | - | 对应的压缩算法参数 |
| deleteOriginFile | boolean | - | 压缩后是否删除文件 |
vite.config.ts
导入使用
php
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// 自动导入 vue 等的 相关的函数
import AutoImport from "unplugin-auto-import/vite";
// 自动导入第三方组件 和项目组件
import components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
// 路由自动导入和布局组件自动合成
import { resolve } from "path";
import Pages from "vite-plugin-pages";
import Layouts from "vite-plugin-vue-layouts";
// 导入 vite-plugin-html 根据环境变量修改标题
import { createHtmlPlugin } from "vite-plugin-html";
// 配置gzip压缩插件
import viteCompression from "vite-plugin-compression";
// 配置路径简写
const pathSrc = resolve(__dirname, "src");
const pathComp = resolve(__dirname, "src/components");
const pathAssets = resolve(__dirname, "src/assets");
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
console.log(env);
return {
base: env.VITE_PUBLIC_PATH,
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
resolvers: [VantResolver()],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
components({
resolvers: [VantResolver()], //第三方组件库 例如 vant element 等
dirs: ["src/components", "src/**/components"], //自定义的组件位置
dts: mode === "development" ? "src/typings/components.d.ts" : false, //生成声明文件位置
}),
Pages({
pagesDir: "src/views", //需要生成路由的文件目录,默认就是识别src下面的pages文件
extensions: ["vue"],
exclude: ["**/components/*.vue"], // 忽略的文件夹
importMode: "async", // 是否是异步路由
}),
Layouts({
// 如果是默认 layouts文件夹,默认 default.vue文件,则不需要配置
layoutsDirs: "src/layouts", // 布局文件存放目录
defaultLayout: "default", //对应 src/layouts/default.vue
}),
createHtmlPlugin({
minify: true,
/**
* 在这里写entry后,你将不需要在`index.html`内添加 script 标签,原有标签需要删除
* @default src/main.ts
*/
entry: "/src/main.ts",
/**
* 需要注入 index.html ejs 模版的数据
*/
inject: {
data: {
// 查找.env 等环境变量文件里面的VITE_PROJECT_TITLE,请以VITE_标识开头
title: loadEnv(mode, process.cwd()).VITE_PROJECT_TITLE,
injectScript: `<script src="/inject.js"></script> `,
ENABLE_ERUDA: env.VITE_ENABLE_ERUDA || "false",
},
},
}),
viteCompression(),
],
resolve: {
alias: {
"@": pathSrc,
comp: pathComp,
// 配置图片要这样引用也可以自定义方法引入图片静态资源
assets: pathAssets,
},
},
};
});
配置svg-icon图标
- 新增一个既可以使用本地svg,又可以自动导入网上的svg-icon使用的组件
- 用到的插件
-
- @iconify/vue 图标大全网址
- unplugin-icons
- vite-plugin-svg-icons
- 安装
-
pnpm i vite-plugin-svg-icons unplugin-icons @iconify/vue -D
- src/assets/ 目录下新建svg-icons目录,用于保存本地svg-icon资源
-
- 创建一个
18.svg
和svg-error.svg
2个文件 - 18.svg 用于测试,svg-error.svg 用于使用本地icon时,没有传的时候的报错显示,并且控制台也会输出错误提醒
- 创建一个
arduino
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M8.5 15H10V9H7v1.5h1.5zm3 0H16V9h-4.5zm1.5-1v-1.5h1.5V14zm0-2.5V10h1.5v1.5zM3 21V3h18v18z"/></svg>
arduino
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="red" d="m12 1l11.951 20.7H.05zM3.513 19.7h16.974L12 5zM13 9.5V15h-2V9.5zm-2 7h2.004v2.004H11z"/></svg>
- 根目录下新增
.env
配置文件
ini
VITE_ICON_LOCAL_PREFIX=Icon-local
VITE_ICON_PREFIX=Icon
- viteconfig.ts 配置
php
import { defineConfig, loadEnv, UserConfig, ConfigEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import UnoCSS from "unocss/vite";
// 自动导入 vue 等的 相关的函数
import AutoImport from "unplugin-auto-import/vite";
// 自动导入第三方组件 和项目组件
import components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
// 路由自动导入和布局组件自动合成
import { resolve } from "path";
import Pages from "vite-plugin-pages";
import Layouts from "vite-plugin-vue-layouts";
// 导入 vite-plugin-html 根据环境变量修改标题
import { createHtmlPlugin } from "vite-plugin-html";
// 配置gzip压缩插件
import viteCompression from "vite-plugin-compression";
// 配置icon相关的
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import { FileSystemIconLoader } from "unplugin-icons/loaders";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
// 配置路径简写
const pathSrc = resolve(__dirname, "src");
const pathComp = resolve(__dirname, "src/components");
const pathAssets = resolve(__dirname, "src/assets");
const pathSvgIcon = resolve(__dirname, "src/assets/svg-icons");
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd());
// console.log(env);
const { VITE_ICON_LOCAL_PREFIX, VITE_ICON_PREFIX } = env;
/** 本地svg图标集合名称 */
const collectionName = VITE_ICON_LOCAL_PREFIX.replace(
`${VITE_ICON_PREFIX}-`,
""
);
return {
base: env.VITE_PUBLIC_PATH,
plugins: [
vue(),
UnoCSS(),
AutoImport({
// 自动导入参考: https://github.com/sxzz/element-plus-best-practices/blob/main/vite.config.ts
imports: ["vue", "vue-router"],
resolvers: [VantResolver(), IconsResolver({ prefix: "Icon" })],
eslintrc: {
enabled: true,
globalsPropValue: true,
filepath: "./.eslintrc-auto-import.json",
},
vueTemplate: true,
// 配置文件生成位置(false:关闭自动生成)
dts: mode === "development" ? "src/typings/auto-imports.d.ts" : false,
}),
components({
resolvers: [
VantResolver(), // 自动注册图标组件
IconsResolver({
// 修改Icon组件前缀,不设置则默认为i,禁用则设置为false
prefix: "icon",
// 指定collection,默认为全部
// enabledCollections: [],
}),
], //第三方组件库 例如 vant element 等
dirs: ["src/components", "src/**/components"], //自定义的组件位置
dts: mode === "development" ? "src/typings/components.d.ts" : false, //生成声明文件位置
}),
Pages({
pagesDir: "src/views", //需要生成路由的文件目录,默认就是识别src下面的pages文件
extensions: ["vue"],
exclude: ["**/components/*.vue"], // 忽略的文件夹
importMode: "async", // 是否是异步路由
}),
Layouts({
// 如果是默认 layouts文件夹,默认 default.vue文件,则不需要配置
layoutsDirs: "src/layouts", // 布局文件存放目录
defaultLayout: "default", //对应 src/layouts/default.vue
}),
createHtmlPlugin({
minify: true,
/**
* 在这里写entry后,你将不需要在`index.html`内添加 script 标签,原有标签需要删除
* @default src/main.ts
*/
entry: "/src/main.ts",
/**
* 需要注入 index.html ejs 模版的数据
*/
inject: {
data: {
// 查找.env 等环境变量文件里面的VITE_PROJECT_TITLE,请以VITE_标识开头
title: loadEnv(mode, process.cwd()).VITE_PROJECT_TITLE,
injectScript: `<script src="/inject.js"></script> `,
ENABLE_ERUDA: env.VITE_ENABLE_ERUDA || "false",
},
},
}),
viteCompression(),
Icons({
autoInstall: true, // 自动安装
compiler: "vue3", // 支持vue3
scale: 1, // 图标大小
defaultClass: "inline-block", // 插入icon的类名
defaultStyle: "marginTop:-0.1875em", // 插入icon的样式
customCollections: {
[collectionName]: FileSystemIconLoader(pathSvgIcon, (svg) =>
svg.replace(/^<svg\s/, '<svg width="1em" height="1em" ')
),
},
}),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [resolve(pathSvgIcon)],
// 指定symbolId格式
symbolId: `${VITE_ICON_LOCAL_PREFIX}-[name]`,
}),
],
resolve: {
alias: {
"@": pathSrc,
comp: pathComp,
// 配置图片要这样引用也可以自定义方法引入图片静态资源
assets: pathAssets,
},
},
};
});
- main.ts 引入
import "virtual:svg-icons-register";
src/components
目录下新建BaseIcon/SvgIcon.vue
组件
xml
<template>
<template v-if="renderLocalIcon">
<svg aria-hidden="true" width="1em" height="1em" v-bind="bindAttrs">
<use :xlink:href="symbolId" fill="currentColor" />
</svg>
</template>
<template v-else>
<Icon v-if="icon" :icon="icon" v-bind="bindAttrs" />
</template>
</template>
<script setup lang="ts">
import { computed, useAttrs } from "vue";
import { Icon } from "@iconify/vue";
/**
* 图标组件
* - 支持iconify和本地svg图标
* - 同时传递了icon和localIcon,localIcon会优先渲染
*/
interface Props {
/** 图标名称 */
icon?: string;
/** 本地svg的文件名 */
localIcon?: string;
}
const props = defineProps<Props>();
const attrs = useAttrs();
const bindAttrs = computed<{ class: string; style: string }>(() => ({
class: (attrs.class as string) || "",
style: (attrs.style as string) || "",
}));
const symbolId = computed(() => {
const { VITE_ICON_LOCAL_PREFIX: prefix } = import.meta.env;
if (!props.localIcon) {
window.console.error(
`SvgIcon: 没有传递图标名称,使用项目图标请确保给localIcon传递有效值!
- localIcon: ${props.localIcon}
- now error-icon-name: svg-error
`
);
return `#${prefix}-svg-error`;
}
const icon = props.localIcon;
return `#${prefix}-${icon}`;
});
/** 渲染本地icon */
const renderLocalIcon = computed(() => props.localIcon || !props.icon);
</script>
<style scoped></style>
-
- 第14行代码 ts 可能会校验不过,解决办法,新增
declare
文件
- 第14行代码 ts 可能会校验不过,解决办法,新增
-
-
typings
目录下新建plugins-decalare.d.ts
文件,- 填入
declare module "@iconify/vue";
-
- 使用组件的2中方式
xml
<!-- 使用在线icon时,请复制 icon名字 下图 说明 -->
<SvgIcon icon="material-symbols-light:10k" class="color-pink" />
<!-- 使用本地svg时,需要把文件放入src/assets/svg-icons目录下,使用的时候直接书写svg文件名即可 -->
<SvgIcon local-icon="18" class="color-red" />
- 使用在线icon时,只需要复制icon的名字,插件会自动下载导入使用 示例网址
- 效果如下可正常展示
- 另一种使用方式
-
- 使用vue的
h函数
处理一遍 - src下新建
utils/icons.ts
文件
- 使用vue的
typescript
import { h } from "vue";
import SvgIcon from "comp/BaseIcon/SvgIcon.vue";
export const useIconRender = () => {
interface IconConfig {
/**
* 图标名称(iconify图标的名称)
* - 例如:mdi-account 或者 mdi:account
*/
icon?: string;
/**
* 本地svg图标文件名(assets/svg文件夹下)
*/
localIcon?: string;
/** 图标颜色 */
color?: string;
/** 图标大小 */
fontSize?: number;
}
interface IconStyle {
color?: string;
fontSize?: string;
}
/**
* 图标渲染
* @param config
* @property icon - 图标名称(iconify图标的名称), 例如:mdi-account 或者 mdi:account
* @property localIcon - 本地svg图标文件名(assets/svg文件夹下)
* @property color - 图标颜色
* @property fontSize - 图标大小
*/
const iconRender = (config: IconConfig) => {
const { color, fontSize, icon, localIcon } = config;
const style: IconStyle = {};
if (color) {
style.color = color;
}
if (fontSize) {
style.fontSize = `${fontSize}px`;
}
if (!icon && !localIcon) {
window.console.warn(
"没有传递图标名称,请确保给icon或localIcon传递有效值!"
);
}
return () => h(SvgIcon, { icon, localIcon, style });
};
return {
iconRender,
};
};
-
- 在需要使用的地方导入,创建,然后使用
component
标签使用,效果一样
- 在需要使用的地方导入,创建,然后使用
xml
<script setup lang="ts">
import {} from "vue";
import { useIconRender } from "@/utils/icons";
const { iconRender } = useIconRender();
const icons = [
{
icon: iconRender({ localIcon: "18", color: "green", fontSize: 30 }),
},
{
icon: iconRender({
icon: "material-symbols-light:18mp-outline-rounded",
color: "red",
fontSize: 30,
}),
},
];
</script>
<template>
<div class="">
home-index
<template v-for="(item, index) in icons" :key="index">
<component :is="item.icon" />
</template>
</div>
</template>
<style scoped lang="scss"></style>
-
- 如图
引入控制台标识辅助打印插件
pnpm i vite-plugin-enhance-log -D
- vite.config.ts 内的 plugins 内使用
- 控制台报vite版本不对等的问题可以不管,因为我们只是在开发环境使用,为了方便我们调试而已
php
...
// 控制台标识符打印插件
import EnhanceLog from "vite-plugin-enhance-log";
...
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return {
base: env.VITE_PUBLIC_PATH,
plugins: [
...,
EnhanceLog({
splitBy: "\n",
preTip: "🤡🤡🤡🤡🤡🤡🤡🤡🤡🤡",
enableFileName: true,
endLine: true,
}),
],
resolve: {
alias: {
"@": pathSrc,
comp: pathComp,
// 配置图片要这样引用也可以自定义方法引入图片静态资源
assets: pathAssets,
},
},
};
});
配置px转rem/vhvw,scss,以及css降级和添加css-polyfill
- 以转rem为例
- 安装
postcss-pxtorem
- vite.config.ts 内添加css选项,内容如下
php
// px-rem
import postcssPxtorem from "postcss-pxtorem";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return {
css: {
postcss: {
plugins: [
postcssPxtorem({
rootValue: 16,
propList: ["*"],
unitPrecision: 6,
selectorBlackList: [],
replace: true,
mediaQuery: false,
minPixelValue: 0,
}),
],
},
},
};
});
- ts类型可能会提示报错,
typings/plugins-declare.d.ts
内添加如下内容
typescript
declare module "postcss-preset-env";//css 自动添加浏览器前缀 先加上
declare module "postcss-pxtorem";
- vite.config.ts内添加
javascript
// px-rem
import postcssPxtorem from "postcss-pxtorem";
//css 自动加前缀和polyfill
import postcssPresetEnv from "postcss-preset-env";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return {
css: {
postcss: {
plugins: [
postcssPresetEnv()
],
},
},
};
});
- 添加sass 编译器
-
pnpm i sass sass-loader -D
- src目录下 添加
styles/variables.scss
目录和文件,内容填写一个sass变量,即可随处引用
bash
$myScssColor: #a22e;
-
- vite.config.ts
javascript
// px-rem
import postcssPxtorem from "postcss-pxtorem";
//css 自动加前缀和polyfill
import postcssPresetEnv from "postcss-preset-env";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return {
css: {
// CSS 预处理器
preprocessorOptions: {
// 定义全局 SCSS 变量
scss: {
javascriptEnabled: true,
additionalData: `
@use "@/styles/variables.scss" as *;
`,
},
},
},
};
});
- 然后在页面上使用
xml
<script setup lang="ts">
import {} from "vue";
import { useIconRender } from "@/utils/icons";
const { iconRender } = useIconRender();
const icons = [
{
icon: iconRender({ localIcon: "18", color: "green", fontSize: 30 }),
},
{
icon: iconRender({
icon: "material-symbols-light:18mp-outline-rounded",
color: "red",
fontSize: 30,
}),
},
];
</script>
<template>
<div class="home-index">
home-index
<div>5555</div>
<template v-for="(item, index) in icons" :key="index">
<component :is="item.icon" />
</template>
</div>
</template>
<style scoped lang="scss">
.home-index {
div {
color: $myScssColor;
}
}
</style>
<route lang="json5">
{
meta: {
title: "首页",
layout: false,
},
}
</route>
创建axios副本
- 安装 axios
pnpm i axios -S
- utils 下创建
request
目录,然后目录下创建http.ts
和tools.ts
index.ts
typescript
import { showFailToast } from "vant";
export const handleNetworkError = (errStatus?: number): void => {
const networkErrMap: any = {
"400": "错误的请求", // token 失效
"401": "未授权,请重新登录",
"403": "拒绝访问",
"404": "请求错误,未找到该资源",
"405": "请求方法未允许",
"408": "请求超时",
"500": "服务器端出错",
"501": "网络未实现",
"502": "网络错误",
"503": "服务不可用",
"504": "网络超时",
"505": "http版本不支持该请求",
};
if (errStatus) {
showFailToast(networkErrMap[errStatus] ?? `其他连接错误 --${errStatus}`);
return;
}
showFailToast("无法连接到服务器!");
};
export const handleAuthError = (errno: string): boolean => {
const authErrMap: any = {
"10031": "登录失效,需要重新登录", // token 失效
"10032": "您太久没登录,请重新登录~", // token 过期
"10033": "账户未绑定角色,请联系管理员绑定角色",
"10034": "该用户未注册,请联系管理员注册用户",
"10035": "code 无法获取对应第三方平台用户",
"10036": "该账户未关联员工,请联系管理员做关联",
"10037": "账号已无效",
"10038": "账号未找到",
};
if (authErrMap.hasOwnProperty(errno)) {
showFailToast(authErrMap[errno]);
// 授权错误,登出账户
// logout();
return false;
}
return true;
};
export const handleGeneralError = (code: number, msg: string): boolean => {
if (code !== 200) {
showFailToast(msg);
return false;
}
return true;
};
kotlin
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig,
AxiosHeaders,
Method,
} from "axios";
import { showLoadingToast, closeToast } from "vant";
import { handleGeneralError, handleNetworkError } from "./tools";
// 定义接口
export interface HRequestInterceptors {
// 请求拦截器(成功与失败)
requestInterceptors?: (
config: InternalAxiosRequestConfig
) => InternalAxiosRequestConfig;
requestInterceptorsCatch?: (error: any) => any;
// 相应拦截器(成功与失败)
responseInterceptor?: (res: AxiosResponse) => AxiosResponse;
responseInterceptorCatch?: (error: any) => any;
}
/**
* @description
* 继承接口: 定义每个请求的拦截器并且设置请求状态显示
* axios config 需要传递的参数
* */
export interface HRequestConfig extends AxiosRequestConfig {
interceptors?: HRequestInterceptors;
// 是否展示请求加载状态
showLoading?: boolean;
needToken?: boolean;
// headers?: BaseHeaders;
}
/**
* @description 判断 axios 请求返回值是否属于 AxiosError
*/
function isErrorObject(error: any): error is AxiosError {
return (error as AxiosError).isAxiosError !== undefined;
}
// 请求基础配置
const baseConfig: HRequestConfig = {
baseURL: import.meta.env.PROD ? import.meta.env.VITE_APP_BASE_URL : "/",
timeout: 10000,
};
// 请求加载显示状态
const DEFAULT_LOADING = false;
const DEFAULT_NEED_TOKEN = true;
const TOKEN = import.meta.env.VITE_TOKEN;
class Http {
// 类型
instance: AxiosInstance;
interceptors?: HRequestInterceptors;
showLoading: boolean;
needToken: boolean;
// loading?: LoadingInstance;
constructor(config: HRequestConfig) {
this.instance = axios.create(config);
this.interceptors = config.interceptors;
this.showLoading = config.showLoading ?? DEFAULT_LOADING;
this.needToken = config.needToken ?? DEFAULT_NEED_TOKEN;
// 请求拦截器 类型
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptors,
this.interceptors?.requestInterceptorsCatch
);
// 响应拦截器 类型
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptor,
this.interceptors?.responseInterceptorCatch
);
// 所有实例的请求拦截
this.instance.interceptors.request.use(
(config) => {
if (this.showLoading) {
showLoadingToast({
duration: 0,
message: "加载中...",
forbidClick: true,
});
}
if (this.needToken) {
config.headers[TOKEN] = localStorage.getItem(TOKEN) || "";
}
// 测试环境的地址替换
if (import.meta.env.MODE === "test") {
config.url = config.url?.replace(
//recyclable//g,
"/recyclabledev/"
);
}
console.log(config);
return config;
},
(err: AxiosError) => {
return err;
// return Promise.reject(err);
}
);
// 所有实例的响应拦截
this.instance.interceptors.response.use(
(res) => {
closeToast();
return res;
},
(err) => {
return err;
}
);
}
server<T>(config: HRequestConfig): Promise<T> {
this.showLoading = config.showLoading ?? DEFAULT_LOADING;
this.needToken = config.needToken ?? DEFAULT_NEED_TOKEN;
return new Promise((resolve, reject) => {
this.instance
.request(config)
.then((res) => {
console.log(
isErrorObject(res),
!handleGeneralError(
res.data.data?.code || res.data.code,
res.data.data?.msg || res.data.msg
),
res
);
if (isErrorObject(res)) {
const err = res as AxiosError;
handleNetworkError(err.response?.status);
reject(err);
}
console.log(res.data);
if (!res) reject("请求失败");
if (
!handleGeneralError(
res.data.data?.code || res.data.code,
res.data.data?.msg || res.data.msg
)
) {
reject(res.data as T);
}
if (res.data.data?.code) {
resolve(res.data.data as T);
} else {
resolve(res.data as T);
}
})
.catch((err) => {
reject(err);
});
});
}
// 对request 二次封装
/**
* @param T 返回值data 的类型声明
* @param S 请求参数的类型声明
* @param url 请求路径 必填
* @param params 请求参数 可选
* @param headers 请求头 可选
* @param config 请求配置 可选
* @returns Promise 请求的响应参数
*/
get<T, S>(
url: string,
params?: S,
config?: HRequestConfig,
headers?: AxiosHeaders
): Promise<AxiosResponse<T>> {
return this.server<AxiosResponse<T>>({
...baseConfig,
...config,
headers: { ...baseConfig.headers, ...config?.headers, ...headers },
params,
method: "GET",
url,
});
}
/**
* @param T 返回值data 的类型声明
* @param S 请求参数的类型声明
* @param url 请求路径 必填
* @param data 请求参数 可选
* @param headers 请求头 可选
* @param config 请求配置 可选
* @returns Promise 请求的响应参数
*/
post<T, S>(
url: string,
data?: S,
config?: HRequestConfig,
headers?: AxiosHeaders
): Promise<AxiosResponse<T>> {
return this.server<AxiosResponse<T>>({
...baseConfig,
...config,
headers: { ...baseConfig.headers, ...config?.headers, ...headers },
data,
method: "POST",
url,
});
}
/**
* @param T 返回值data 的类型声明
* @param S 请求参数的类型声明
* @param url 请求路径 必填
* @param params 请求参数 可选
* @param headers 请求头 可选
* @param config 请求配置 可选
* @returns Promise 请求的响应参数
*/
put<T, S>(
url: string,
data?: S,
config?: HRequestConfig,
headers?: AxiosHeaders
): Promise<AxiosResponse<T>> {
return this.server<AxiosResponse<T>>({
...baseConfig,
...config,
headers: { ...baseConfig.headers, ...config?.headers, ...headers },
data,
method: "PUT",
url,
});
}
/**
* @param T 返回值data 的类型声明
* @param S 请求参数的类型声明
* @param url 请求路径 必填
* @param params 请求参数 可选
* @param headers 请求头 可选
* @param config 请求配置 可选
* @returns Promise 请求的响应参数
*/
delete<T, S>(
url: string,
data?: S,
headers?: AxiosHeaders,
config?: HRequestConfig
): Promise<AxiosResponse<T>> {
return this.server<AxiosResponse<T>>({
...baseConfig,
...config,
headers: { ...baseConfig.headers, ...config?.headers, ...headers },
data,
method: "DELETE",
url,
});
}
/**
* @param T 返回值data 的类型声明
* @param S 请求参数的类型声明
* @param url 请求路径 必填
* @param data 请求参数 可选
* @param method 请求方法 可选 不传默认为 GET 当如果需要自定义 headers,config 时 必传
* @param headers 请求头 可选
* @param config 请求配置 可选
* @returns Promise 请求的响应参数
* @example
* request.request<{name:string,id:number},{username:string,password:string}>('/api/login', { username: 'admin', password: '123456' }, 'POST')
*/
request<T, S>(
url: string,
data?: S,
method?: Method,
headers?: AxiosHeaders,
config?: HRequestConfig
): Promise<AxiosResponse<T>> {
return this.server<AxiosResponse<T>>({
...baseConfig,
...config,
headers: { ...baseConfig.headers, ...config?.headers, ...headers },
data,
method: method ? method : "GET",
url,
});
}
}
/**
* @description
* 使用 Http 类 new 一个 请求实例
*
* @param {HRequestConfig} config
*
* interceptors 当前实例的 自定义 请求 响应 拦截器 需要特殊处理的可以在这里配置
*/
const request = new Http({
...baseConfig,
interceptors: {
requestInterceptors: (config) => {
return config;
},
requestInterceptorsCatch(err) {
return err;
},
responseInterceptor(res) {
return res;
},
responseInterceptorCatch(error) {
return error;
},
},
});
export { Http, request };
javascript
export * from "./http";
创建pinia仓库实例
- src 下新建
stores
目录,目录下新建index.ts
文件和modules
文件夹,文件夹内新建home.ts
ini
export const useHomeStore = defineStore("home", () => {
const count = ref(0);
return {
count,
};
});
javascript
import { createPinia } from "pinia";
//数据持久化插件
import persist from "pinia-plugin-persistedstate";
const pinia = createPinia();
pinia.use(persist);
export default pinia;
export * from "./modules/home";
- 然后在
main.ts
内导入挂载
javascript
import { createApp } from "vue";
import App from "./App.vue";
// 导入pinia
import pinia from "./stores";
const app = createApp(App);
app.use(pinia);
app.mount("#app");