Browse Source

feat: 基础

master
xiangheng 2 years ago
commit
001be05d59
  1. 3
      .commitlintrc.js
  2. 12
      .editorconfig
  3. 5
      .env
  4. 2
      .env.development
  5. 5
      .env.production
  6. 2
      .eslintignore
  7. 32
      .eslintrc.js
  8. 25
      .gitignore
  9. 4
      .husky/commit-msg
  10. 4
      .husky/pre-commit
  11. 5
      .postcssrc.js
  12. 7
      .prettierrc.js
  13. 2
      .stylelintignore
  14. 36
      .stylelintrc.js
  15. 8
      .vscode/extensions.json
  16. 10
      .vscode/settings.json
  17. 70
      README.md
  18. 37
      build/proxy.js
  19. 28
      build/utils.js
  20. 159
      docs/1.工程化实践/1. eslint.md
  21. 102
      docs/1.工程化实践/2. stylelint.md
  22. 83
      docs/1.工程化实践/3. husky.md
  23. 96
      docs/1.工程化实践/4. commitlint.md
  24. 58
      docs/2.全家桶/1. 路由.md
  25. 30
      docs/2.全家桶/2. 全局状态管理.md
  26. 5
      docs/2.全家桶/3. 组件库.md
  27. 20
      docs/3.更多功能/1. Axios 封装及接口管理.md
  28. 103
      docs/3.更多功能/2. Css 样式处理.md
  29. 176
      docs/3.更多功能/3. Vite 基础配置.md
  30. 13
      index.html
  31. 9
      jsconfig.json
  32. 64
      package.json
  33. BIN
      public/favicon.ico
  34. 5
      src/App.vue
  35. 11
      src/api/demo.js
  36. BIN
      src/assets/logo.png
  37. 45
      src/components/HelloWorld.vue
  38. 35
      src/hooks/useRouter.js
  39. 106
      src/layouts/BaseLayout.vue
  40. 20
      src/main.js
  41. 5
      src/router/constants.js
  42. 9
      src/router/guard/index.js
  43. 14
      src/router/guard/permission.guard.js
  44. 24
      src/router/index.js
  45. 29
      src/router/modules/demo.modules.js
  46. 3
      src/router/modules/index.js
  47. 8
      src/store/index.js
  48. 12
      src/store/modules/demo.js
  49. 18
      src/styles/global/utils.less
  50. 5
      src/styles/global/variables.less
  51. 8
      src/styles/index.less
  52. 351
      src/styles/normalize.css
  53. 133
      src/styles/reset.css
  54. 15
      src/utils/env.js
  55. 57
      src/utils/request.js
  56. 33
      src/views/demo/components/StyleTxt.vue
  57. 59
      src/views/demo/pinia.vue
  58. 65
      src/views/demo/route.vue
  59. 101
      src/views/demo/style.vue
  60. 71
      vite.config.js
  61. 6187
      yarn.lock

3
.commitlintrc.js

@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional']
}

12
.editorconfig

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

5
.env

@ -0,0 +1,5 @@
# prot
VITE_PORT = 8000
# output dir
VITE_OUTPUT_DIR = dist

2
.env.development

@ -0,0 +1,2 @@
# public path
VITE_PUBLIC_PATH = /

5
.env.production

@ -0,0 +1,5 @@
# public path
VITE_PUBLIC_PATH = /
# 是否兼容传统浏览器
VITE_LEGACY = false

2
.eslintignore

@ -0,0 +1,2 @@
public
dist

32
.eslintrc.js

@ -0,0 +1,32 @@
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
extends: [
'eslint:recommended',
/**
* 继承 eslint-plugin-vue 插件的规则
* @link https://eslint.vuejs.org/user-guide/#installation
*/
'plugin:vue/recommended',
/**
* 继承 eslint-plugin-prettier 插件的规则
* @link https://github.com/prettier/eslint-plugin-prettier
*/
'plugin:prettier/recommended'
],
rules: {
'vue/multi-word-component-names': 'off'
}
})

25
.gitignore

@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

4
.husky/commit-msg

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm commitlint --edit

4
.husky/pre-commit

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm lint-staged

5
.postcssrc.js

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

7
.prettierrc.js

@ -0,0 +1,7 @@
module.exports = {
semi: false,
singleQuote: true,
printWidth: 80,
trailingComma: 'none',
arrowParens: 'avoid'
}

2
.stylelintignore

@ -0,0 +1,2 @@
public
dist

36
.stylelintrc.js

@ -0,0 +1,36 @@
module.exports = {
root: true,
extends: [
'stylelint-config-standard',
'stylelint-config-rational-order',
'stylelint-config-prettier',
'stylelint-config-html/vue' // 需要放在最后一位
],
defaultSeverity: 'warning',
plugins: ['stylelint-order'],
rules: {
'no-empty-source': null,
'selector-class-pattern': null,
'value-keyword-case': null,
'font-family-no-missing-generic-family-keyword': null
},
overrides: [
{
files: ['*.vue', '**/*.vue'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
}
]
}
}
]
}

8
.vscode/extensions.json

@ -0,0 +1,8 @@
{
"recommendations": [
"Vue.vscode-typescript-vue-plugin",
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint"
]
}

10
.vscode/settings.json

@ -0,0 +1,10 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true
},
"vetur.format.enable": false,
"stylelint.validate": ["vue"], // Add "vue" language.
"editor.tabSize": 2,
"files.eol": "\n",
}

70
README.md

@ -0,0 +1,70 @@
# Vue 2.7 + Vite 脚手架
Vue2.7 已经发布正式版啦,不出意外的话这应该是 Vue2 的最后一个版本了,但是很多公司目前还没有升级 Vue3 的打算(比如我们)。
所以封装了这么一个开箱即用的脚手架模板,脚手架的功能可以看下面的功能列表小节,并且配备完整的技术文档。
可以直接使用,也可以当作学习源码。
## 教程目录
1. 工程化实践
- [Eslint 格式化 JS 代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/1.%20eslint.md)
- [Stylelint 格式化 CSS 代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/2.%20stylelint.md)
- [Husky 提交时自动格式化代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/3.%20husky.md)
- [Commitlint 校验 Commit Message](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/4.%20commitlint.md)
2. 全家桶
- [路由 vue-router](./docs/2.%E5%85%A8%E5%AE%B6%E6%A1%B6/1.%20%E8%B7%AF%E7%94%B1.md)
- [全局状态管理 pinia](./docs/2.%E5%85%A8%E5%AE%B6%E6%A1%B6/2.%20%E5%85%A8%E5%B1%80%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86.md)
- [组件库](./docs/2.%E5%85%A8%E5%AE%B6%E6%A1%B6/3.%20%E7%BB%84%E4%BB%B6%E5%BA%93.md)
3. 更多功能
- [Axios 封装及接口管理](./docs/3.%E6%9B%B4%E5%A4%9A%E5%8A%9F%E8%83%BD/1.%20Axios%20%E5%B0%81%E8%A3%85%E5%8F%8A%E6%8E%A5%E5%8F%A3%E7%AE%A1%E7%90%86.md)
- [Css 样式处理](./docs/3.%E6%9B%B4%E5%A4%9A%E5%8A%9F%E8%83%BD/2.%20Css%20%E6%A0%B7%E5%BC%8F%E5%A4%84%E7%90%86.md)
- [Vite 基础配置](./docs/3.%E6%9B%B4%E5%A4%9A%E5%8A%9F%E8%83%BD/3.%20Vite%20%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE.md)
## 基础搭建
在这里我们使用的是 [pnpm](https://pnpm.io/zh/) 来作为本项目的包管理工具。
```bash
# 安装依赖
pnpm i
# 运行
pnpm run dev
# 打包
pnpm run build
# 打包文件预览
pnpm run preview
```
> 本人开发环境
>
> node v16.13.1(大于 14 版本即可)
>
> pnpm v6.30.0
### 插件安装
此脚手架必须安以下依赖才能保证,代码自动格式化的有效运行:
- Vetur
- EditorConfig for VS Code
- ESLint
- Stylelint
## 功能列表
- [x] Vue2.7 + Vite
- [x] Eslint、Stylelint、Commitlint 统一开发规范
- [x] husky + lint-staged (git commit 时自动格式化代码)
- [x] Vue 全家桶集成
- [x] Axios 封装及接口管理
- [x] Css 样式处理
- [x] vite.config.js 基础配置
- [x] 跨域配置
- [x] 多环境变量配置
- [x] 浏览器构建兼容性

37
build/proxy.js

@ -0,0 +1,37 @@
/**
* { prefix: '/demo', target: 'http://localhost:3000/demo', removePrefix: true }
* 最终会被转化为
* {
* '/demo': {
* target: 'http://localhost:3000/demo',
* changeOrigin: true,
* ws: true,
* rewrite: path => path.replace(new RegExp('^/demo'), '')
* }
* }
*/
const proxyList = [
{ prefix: '/demo', target: 'http://localhost:3000/demo', removePrefix: true },
{ prefix: '/api', target: 'http://localhost:3000/api' }
]
export default proxyList.reduce((pre, cur) => {
const { prefix, target, removePrefix, ...res } = cur
// https://www.vitejs.net/config/#server-proxy
const proxyOpts = {
target,
changeOrigin: true,
ws: true,
...res
}
// 移除前缀
if (removePrefix) {
proxyOpts.rewrite = path => path.replace(new RegExp(`^${prefix}`), '')
}
pre[prefix] = proxyOpts
return pre
}, {})

28
build/utils.js

@ -0,0 +1,28 @@
import { resolve } from 'path'
/**
* env 对象数据类型进行转换
* @param {Object} envConf
* @returns
*/
export function wrapperEnv(envConf) {
const ret = {}
Object.keys(envConf).forEach(key => {
let val = envConf[key]
val = val === 'true' ? true : val === 'false' ? false : val
if (key === 'VITE_PORT') {
val = Number(val)
}
ret[key] = val
})
return ret
}
export function pathResolve(dir) {
return resolve(process.cwd(), '.', dir)
}

159
docs/1.工程化实践/1. eslint.md

@ -0,0 +1,159 @@
# Eslint 代码格式化
在工作中,一个项目往往是很多人共同开发,因为每个人的编码习惯都不一样,就会增加后期维护成本。
为了解决这个问题,我们一般会定义一个开发规范文档,使用约定的方式来统一项目的编码规范。
有了规范文档之后,我们就会在开发中小心翼翼的调整代码风格,每行、每列、每个双引号。这样无疑也会影响开发效率,而且忙的时候可能还会忘记某些规范条例。
为了更好的统一团队的编码规范,在这里我使用了 EditorConfig + ESLint + Prettier 这些工具来辅助解决规范性问题。
## EditorConfig
[EditorConfig](https://editorconfig.org/) 主要用于统一不同 IDE 编辑器的编码风格。
> 当然每个团队最好还是统一一个代码编辑器。
在根目录下添加 `.editorconfig` 文件:
```plain
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
```
> 很多 IDE 中会默认支持此配置,但是也有些不支持,如:VSCode、Atom、Sublime Text 等。
>
> 具体列表可以参考官网,如果在 VSCode 中使用需要安装 `EditorConfig for VS Code` 插件。
## ESLint
[ESLint](http://eslint.cn/) 是针对 EScript 的一款代码检测工具,它可以检测项目中编写不规范的代码,如果写出不符合规范的代码会被警告。
由此我们就可以借助于 ESLint 强大的功能来统一团队的编码规范。
1. 安装依赖
- [`eslint`](https://github.com/eslint/eslint) - Eslint 本体
- [`eslint-define-config`](https://github.com/Shinigami92/eslint-define-config)- 改善 ESLint 规范编写体验
- [`eslint-plugin-vue`](https://github.com/vuejs/eslint-plugin-vue)- 适用于 Vue 文件的 ESLint 插件
- `vue-eslint-parser`- 使用 `eslint-plugin-vue` 时必须安装的 eslint 解析器
```bash
pnpm add eslint eslint-define-config eslint-plugin-vue vue-eslint-parser -D
```
2. 添加 ESLint 配置文件
在根目录添加一个 `.eslintrc.js` 文件,内容如下:
```javascript
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
extends: [
/**
* 继承 eslint-plugin-vue 插件的规则
* @link https://eslint.vuejs.org/user-guide/#installation
*/
'plugin:vue/recommended'
],
rules: {
'vue/multi-word-component-names': 'off'
}
})
```
> 关于配置文件中的选项大家去看[官方文档](http://eslint.cn/docs/user-guide/configuring),已经写得很详细了。
3. 添加 ESLint 过滤规则
在根目录添加一个 `.eslintignore` 文件,内容如下:
```plain
public
dist
```
## Prettier
Prettier 是一款强大的代码格式化工具,这里我们使用 ESLint + Prettier 来格式化代码。
1. 安装依赖
- `prettier` - prettier 本体
- `eslint-config-prettier` - 关闭 ESLint 中与 Prettier 中发生冲突的规则
- `eslint-plugin-prettier` - 将 Prettier 的规则设置到 ESLint 的规则中
```bash
pnpm add prettier eslint-config-prettier eslint-plugin-prettier -D
```
2. 添加 Prettier 配置文件
在根目录添加一个 `.prettierrc.js` 文件,内容如下:
```javascript
module.exports = {
semi: false,
singleQuote: true,
printWidth: 80,
trailingComma: 'none',
arrowParens: 'avoid',
}
```
3. 修改 ESLint 配置,使 Eslint 兼容 Prettier 规则
```javascript
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
/// ...
extends: [
'plugin:vue/vue3-recommended',
/**
* 继承 eslint-plugin-prettier 插件的规则
* @link https://github.com/prettier/eslint-plugin-prettier
*/
'plugin:prettier/recommended'
],
// ...
})
```
## 自动格式化代码
做好以上配置之后,在编码时不符合规范的地方就会被编辑器标注出来,可以使我们更好的发现问题。
如果你用的是VScode,还可以工作区配置中,添加如下代码,之后就可以享受保存时自动格式化的功能了:
```json
{
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true
}
}
```

102
docs/1.工程化实践/2. stylelint.md

@ -0,0 +1,102 @@
# Stylelint CSS 格式化
Stylelint 是一个强大、先进的 CSS 代码检查器(linter),可以帮助你规避 CSS 代码中的错误并保持一致的编码风格。
1. 安装依赖
- [`stylelint`](https://stylelint.io/) - Stylelint 本体
- [`stylelint-config-prettier`](https://github.com/prettier/stylelint-config-prettier)- 关闭 Stylelint 中与 Prettier 中会发生冲突的规则。
- [`stylelint-config-rational-order`](https://github.com/constverum/stylelint-config-rational-order) - 对 CSS 声明进行排序
- [`stylelint-config-standard`](https://github.com/stylelint/stylelint-config-standard)- Stylelint 官方推荐规则
- `stylelint-order` 使用 `stylelint-config-rational-order` 时依赖的模块
```bash
pnpm add stylelint stylelint-config-prettier stylelint-config-rational-order stylelint-config-standard stylelint-order -D
```
2. 添加 StyleLint 配置文件
在根目录添加一个 `.stylelintrc.js` 文件,内容如下:
```javascript
module.exports = {
root: true,
extends: [
'stylelint-config-standard',
'stylelint-config-rational-order',
'stylelint-config-prettier'
],
defaultSeverity: 'warning',
plugins: ['stylelint-order'],
rules: {
'no-empty-source': null,
'selector-class-pattern': null
}
}
```
在根目录添加一个 `.stylelintignore` 文件,内容如下:
```plain
public
dist
```
3. 启用 Vue 文件支持
`stylelint` v14 版本默认是不支持 vue 文件中的 style 代码自动检测,详情我们可以查看[官方迁移指南](https://github.com/stylelint/stylelint/blob/main/docs/migration-guide/to-14.md),具体配置如下:
- `stylelint-config-html` 解析 vue 文件
- `postcss-html` 使用 `stylelint-config-html` 依赖的模块
- `postcss-less` 对 less 文件进行解析
```bash
pnpm add stylelint-config-html postcss-html postcss-less -D
```
4. 修改 `.stylelintrc.js` 文件:
```javascript
module.exports = {
root: true,
extends: [
'stylelint-config-standard',
'stylelint-config-rational-order',
'stylelint-config-prettier',
'stylelint-config-html/vue' // 需要放在最后一位
],
defaultSeverity: 'warning',
plugins: ['stylelint-order'],
rules: {
'no-empty-source': null,
'selector-class-pattern': null
},
overrides: [
{
files: ['*.vue', '**/*.vue'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
}
]
}
}
]
}
```
5. 在 VSCode 工作区配置中,添加如下代码:
```json
{
"stylelint.validate": ["vue"] // Add "vue" language.
}
```

83
docs/1.工程化实践/3. husky.md

@ -0,0 +1,83 @@
# Husky Git Hook 工具
husky Git Hook 工具,为 git 提供一系列钩子函数,在提交前(pre-commit)、提交消息(commit-msg)等钩子触发时可以为我们执行一些脚本。
我们可以使用 husky 工具来进行代码提交前的自动格式化,以及 commit message 的校验。
## 提交前代码格式化
1. 首先安装 husky
```bash
pnpm add husky -D
```
2. 初始化 husky
```bash
pnpm husky install
```
并在 package.json 中添加如下内容
```json
{
"scripts": {
//...
"prepare": "husky install"
}
}
```
3. 添加 git hook
```bash
pnpm husky add .husky/pre-commit
```
到这里之后我们还需要使用另外一个工具: [`lint-staged`](https://www.npmjs.com/package/lint-staged),它是对 git 暂存区文件进行 lint 检查的工具。
4. 安装 lint-staged
```bash
pnpm add lint-staged -D
```
5. 在 `package.json` 中添加如下配置
```json
{
//...
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"prettier --write",
"eslint --fix"
],
"*.vue": [
"stylelint --fix",
"prettier --write",
"eslint --fix"
],
"*.{less,css}": [
"stylelint --fix",
"prettier --write"
]
}
}
```
6. 在 `.husky/pre-commit` 文件中写入以下内容
```shell
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm lint-staged
```
经过以上配置之后,我们就可以在每次提交之前对所有代码进行格式化,保证线上代码的规范性。
> 在实际中如果遇见 `Use the --allow-empty option to continue, or check your task configuration` 这个问题。
>
> 我们可以修改 `pnpm lint-staged``pnpm lint-staged --allow-empty` 来暂时屏蔽这个问题。

96
docs/1.工程化实践/4. commitlint.md

@ -0,0 +1,96 @@
# Commitlint 提交信息校验
我们在使用 `git commit` 时,git 会记录每一次的 `commit message`(提交信息)。
正确的描述 `commit message` 在多人协同开发一个项目时,显得尤其重要。
这里我们可以看一下 `angular` 的 [`commit message`](https://github.com/angular/angular/commits/main),会发现它的描述特别的清晰明了。
而 [`commitlint`](https://commitlint.js.org/#/) 就是对 `commit message` 进行的检查的一个工具,当不规范时会终止提交。
1. 安装依赖
- `@commitlint/cli`- Commitlint 本体
- `@commitlint/config-conventional`- 通用的提交规范
```bash
pnpm add @commitlint/cli @commitlint/config-conventional -D
```
1. 创建 commitlint 配置
在根目录添加一个 `.commitlintrc.js` 文件,内容如下:
```javascript
module.exports = {
extends: ['@commitlint/config-conventional']
}
```
1. 在 git `commit-msg` 时进行检查
执行下面这条命令即可:
```bash
pnpm husky add .husky/commit-msg "pnpm commitlint --edit $1"
```
## Commit Message 格式
配置完成之后就可以在每次 `git commit` 时对 `commit message` 进行校验了,规范有两种格式:单行信息和多行信息。
1. 单行信息
用于业务代码提交时使用,业务代码一般来说更改比较多而且无法具体说明其信息,具体的还需要看产品文档,所以用单行信息即可。
```xml
<type>(<scope>): <subject>
```
1. 多行信息
用于提交一些不经常更改的功能型代码时使用,如:某个功能函数的新增、修改或重构,目录结构的调整(工程化调整),架构的更改等,这些我们需要进行详细说明防止出现遗忘。
```xml
<type>(<scope>): <subject>
<BLANK LINE> // 空行
<body>
<BLANK LINE>
<footer>
```
字段描述:
- `type` 类型
- `scope` 影响的范围, 比如: \*(全局),route, component, utils, build,readme,css 等,
- `subject` 概述, 建议符合 [50/72 formatting](https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions/2290016/git-commit-messages-50-72-formatting)
- `body` 具体修改内容,描述为什么修改, 做了什么样的修改, 以及开发的思路等等,可以分为多行, 建议符合 [50/72 formatting](https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions/2290016/git-commit-messages-50-72-formatting)
- `footer` 一些备注, 通常是 Breaking Changes(重要改动) 或 Closed Issues(修复 Bug 的链接)
`type` 类型有以下几种:
| 类型 | 描述 |
| :------- | :----------------------------------- |
| build | 发布版本 |
| chore | 改变构建流程、或者增加依赖库、工具等 |
| ci | 持续集成修改 |
| docs | 文档修改 |
| feat | 新特性 |
| fix | 修改问题 |
| perf | 优化相关,比如提升性能、体验 |
| refactor | 代码重构 |
| revert | 回滚到上一个版本 |
| style | 代码格式修改 |
| test | 测试用例修改 |
示例:
```
feat(eslint): 集成 eslint - xcder - 2022.07.01
1. Vscode 安装 Eslint 插件即可在保存时自动格式化
2. 运行 pnpm lint:eslint 可全局进行代码格式化
可以浏览 docs/1.工程化实践/1.eslint 文件了解详情。
```

58
docs/2.全家桶/1. 路由.md

@ -0,0 +1,58 @@
# VueRouter 路由
Vue2.x 中只能使用 vue-router3 版本。
## 目录说明
有关路由的功能都在以下的目录中。
```sh
.
├── src # 主目录
│ ├── main.js # 主入口
│ ├── router # 路由配置
│ │ ├── guard # 路由守卫
│ │ ├── modules # 路由模块
│ │ └── index.js
│ └── views # 页面
```
## route modules
关于路由表,建议根据功能的不同来拆分到 modules 文件夹中。
这样做的好处是:
1. 方便后期维护
2. 减少 Git 合并代码时的冲突的可能
## componsition-api 中使用
在 hooks/useRouter 中封装了两个方法:
1. `useRouter`
2. `useRoute`
```javascript
import { watch } from 'vue'
import { useRoute, useRouter } from '@/hooks/useRouter'
export default {
setup() {
const route = useRoute()
const router = useRouter()
watch(route, () => {
console.log('route 变化', route.value)
})
function routeChange() {
router.push({ path: '/home', query: { key: Date.now() } })
}
return {
routeChange
}
}
}
```

30
docs/2.全家桶/2. 全局状态管理.md

@ -0,0 +1,30 @@
# 全局状态管理
这里全局状态管理器选择了 [Pinia](https://pinia.vuejs.org/),之所以没有选择 Vuex,对于我来说最主要的原因有两点:
1. Vue3 官方文档中,已经将 Pinia 放入官方推荐的核心库位置中了。
2. 对 Composition-API 具有更好的支持。
接下来看一段官方的说明:
> Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分内容,并决定实现它 取而代之的是新的建议。
>
> 与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
## 目录说明
有关全局状态管理的功能都在以下的目录中。
```sh
.
├── src # 主目录
│ ├── main.js # 主入口
│ ├── store # store 配置
│ │ ├── modules # store 模块
│ │ └── index.js
│ └── views # 页面
```
## store modules
我们在开发中需要将不同功能所对应的状态,拆分到不同的 modules,好处在 route moduels 中已经描述过了。

5
docs/2.全家桶/3. 组件库.md

@ -0,0 +1,5 @@
# 组件库
关于组件库可选择的就有很多了,因为可能每个人需要的都不相同,这里就不集成到脚手架模板中了。
有一点需要注意的是,Vue2.7 还是只能使用 Vue2.x 相关的组件库。

20
docs/3.更多功能/1. Axios 封装及接口管理.md

@ -0,0 +1,20 @@
# Axios 封装及接口管理
这里接口请求使用的是基于 Promise 封装的请求库 [Axios](https://axios-http.com/zh/),也是现在使用很广泛的一个请求库。
## 目录说明
有关请求库的功能都在以下的目录中。
```sh
.
├── src # 主目录
│ ├── api # api 方法配置
│ │ └── demo.js # 演示方法
│ └── utils # 公共方法
│ └── request.js # axios 请求库二次封装
```
在开发时,我们先在 `utils/requset.js` 中配置好适合自己业务的请求拦截和响应拦截。
之后在 `api` 文件夹中定义请求方法,这里我一般会以功能进行拆分,同一个功能的请求方法封装在一个文件,之后在需要的地方进行调用。

103
docs/3.更多功能/2. Css 样式处理.md

@ -0,0 +1,103 @@
# Css 样式处理
## 统一浏览器默认样式
在 Web 开发时,因为浏览器内核的不同,所以会导致某些 Html 元素默认的渲染样式有所差别,虽然很细微,但是会影响我们在不同浏览器中的显示效果。
为了解决这个问题,聪明的开发者就将某个元素在各个浏览器中表现不同的样式,通过一段 CSS 进行重置,然后再进行开发,这样就可以保证每个浏览器显示效果的统一性。
目前常用的有两种解决方案:
1. Normalize - [normalize.css](https://necolas.github.io/normalize.css/) 偏向于修复浏览器的默认 BUG 和一致性,但是保留元素的默认样式。
2. Reset - [reset.css](https://meyerweb.com/eric/tools/css/reset/) 偏向于完全重置浏览器默认样式,可控性更高。
在本项目中,我们结合两种方案进行使用,代码在 `styles/normalize.css``styles/reset.css` 中。
## Less 预处理器
在 Vite 中使用 Less,我们只需要运行以下命令即可:
```bash
pnpm add less -D
```
> 如果需要使用 sass 则把 less 换成 sass 就行。
安装完成之后,无需多余配置,vite 即可对 less 样式进行解析。
在 Vue 文件中,我们如下,即可使用 less 进行样式的编写:
```vue
<style lang="less" scoped>
// less 样式代码
.card {
&-body {}
&-header {}
}
</style>
```
> 这里 CSS 命名规范推荐 BEM 命名规范,具体可以自行搜索了解。
### Less 全局变量/方法
一般在项目中会有自己的设计规范,比如像 Element、Antd,它就有一套自己的样式规范。
其中像颜色、边框、边距等都有统一的规范,当我们改一处,所有地方都会改变,这就用到了 less 的变量和 mixin。
然而我们每处都 `@import` 就比较麻烦,这个时候我们可以使用在 vite 中进行 less 的全局样式配置。
```js
import { defineConfig } from 'vite'
import { resolve } from 'path'
function pathResolve(dir) {
return resolve(process.cwd(), '.', dir)
}
// 全局变量
const variablesLessPath = pathResolve('./src/styles/global/variables.less')
// 全局方法
const utilsLessPath = pathResolve('./src/styles/global/utils.less')
export default defineConfig({
css: {
preprocessorOptions: {
less: {
modifyVars: {
hask: `
true;
@import (reference) "${variablesLessPath}";
@import (reference) "${utilsLessPath}";
`
},
javascriptEnabled: true
}
}
}
})
```
## Vue 样式穿透
[官方文档](https://vuejs.org/api/sfc-css-features.html#scoped-css)
在 Vue2.7 中,改变了以往样式穿透的语法,如果继续使用 `::v-deep`、`/deep/`、`>>>` 等语法的话,会出现一个警告,下面是新的语法:
```css
/* 深度选择器 */
:deep(selector) {
/* ... */
}
/* 插槽选择器 */
:slotted(selector) {
/* ... */
}
/* 全局选择器 */
:global(selector) {
/* ... */
}
```

176
docs/3.更多功能/3. Vite 基础配置.md

@ -0,0 +1,176 @@
# Vite 基础配置
## 目录说明
有关 vite 配置的功能都在以下的目录中。
```sh
.
├── build # 打包配置文件
├── src # 主目录
│ ├── utils
│ │ └── env.js # 环境变量判断方法
├── .env # 基础环境变量
├── .env.development # 开发环境变量
├── .env.production # 生产环境变量
└── vite.config.js # vite 配置
```
## 基础配置
完整的 vite 配置如下所示,配置了大概有以下的功能:
1. 环境变量
2. alias 别名
3. 跨域配置
4. 构建兼容性
5. less 全局样式
```javascript
import vue from '@vitejs/plugin-vue2'
import legacy from '@vitejs/plugin-legacy'
import serverProxy from './build/proxy'
import { defineConfig, loadEnv } from 'vite'
import { pathResolve, wrapperEnv } from './build/utils'
const variablesLessPath = pathResolve('./src/styles/global/variables.less')
const utilsLessPath = pathResolve('./src/styles/global/utils.less')
// https://vitejs.dev/config/
export default ({ mode, command }) => {
const root = process.cwd()
const isBuild = command === 'build'
// 在 vite 配置文件中使用环境变量,需要通过 loadEnv 来加载
const env = loadEnv(mode, root)
const { VITE_PUBLIC_PATH, VITE_OUTPUT_DIR, VITE_PORT, VITE_LEGACY } =
wrapperEnv(env)
const plugins = [vue()]
/**
* vite 默认打包文件带有 ES6 语法,在旧版浏览器中是不支持的。
* 为了支持旧版浏览器,可以在 .env.production 中开启 VITE_LEGACY 设置
*/
if (isBuild && VITE_LEGACY) {
plugins.push(legacy())
}
return defineConfig({
root,
base: VITE_PUBLIC_PATH,
plugins,
resolve: {
alias: {
// @/xxxx => src/xxxx
'@': pathResolve('./src')
}
},
server: {
host: true,
port: VITE_PORT,
proxy: serverProxy
},
build: {
/**
* 最终构建的浏览器兼容目标
* https://www.vitejs.net/config/#build-target
*/
target: 'es2015',
outDir: VITE_OUTPUT_DIR,
brotliSize: false, // 关闭 brotli 压缩大小报告,可提升构建速度
chunkSizeWarningLimit: 2000 // chunk 大小警告的限制(以 kbs 为单位)
},
css: {
preprocessorOptions: {
less: {
modifyVars: {
hask: `
true;
@import (reference) "${variablesLessPath}";
@import (reference) "${utilsLessPath}";
`
},
javascriptEnabled: true
}
}
}
})
}
```
## 跨域配置
这里我们使用的是 vite 自带的 [http-proxy](https://github.com/http-party/node-http-proxy) 来解决跨域,这也是我们在开发中比较常见的解决跨域的一种方式。
我们打开 `build/proxy.js` 文件可以看到其中有一个 `proxyList` 变量,我们的跨域配置写在这里即可。
```javascript
const proxyList = [
{ prefix: '/demo', target: 'http://localhost:3000/demo', removePrefix: true },
{ prefix: '/api', target: 'http://localhost:3000/api' }
]
// 以上配置最终会被转换为 vite 所需要的格式:
{
'/demo': {
target: 'http://localhost:3000/demo',
changeOrigin: true,
ws: true,
rewrite: path => path.replace(new RegExp('^/demo'), '')
},
'/api': {
target: 'http://localhost:3000/demo',
changeOrigin: true,
ws: true,
}
}
```
## 环境变量
[官方文档](https://www.vitejs.net/guide/env-and-mode.html)
在本脚手架中配置了2种环境:
1. development 开发环境
2. production 生产环境
环境变量数据在 vue 项目中可以直接使用 `import.meta.env` 这个变量获取,比如 `src/utils/env.js` 中:
```javascript
/**
* 是否为开发环境
* @returns {Boolean}
*/
export function isDev() {
return import.meta.env.DEV
}
/**
* 是否为生产环境
* @returns {Boolean}
*/
export function isProd() {
return import.meta.env.PROD
}
```
但是如果需要在 `vite.config.js` 中使用的话,需要用到 `loadEnv` 这个方法:
```javascript
import { defineConfig, loadEnv } from 'vite'
export default ({ mode }) => {
// 通过 loadEnv 获取环境变量数据
const env = loadEnv(mode, process.cwd())
return defineConfig({
/* vite 配置 */
})
}
```

13
index.html

@ -0,0 +1,13 @@
<!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>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

9
jsconfig.json

@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules"]
}

64
package.json

@ -0,0 +1,64 @@
{
"name": "vue2.7",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"prepare": "husky install",
"lint:eslint": "eslint \"src/**/*.{vue,js,jsx}\" --fix",
"lint:stylelint": "stylelint \"src/**/*.{vue,css,less,scss}\" --fix"
},
"dependencies": {
"ant-design-vue": "1.7.8",
"axios": "^1.3.4",
"pinia": "^2.0.33",
"vue": "^2.7.5",
"vue-router": "^3.5.4"
},
"devDependencies": {
"@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^17.0.3",
"@vitejs/plugin-legacy": "^4.0.2",
"@vitejs/plugin-vue2": "^2.2.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.5.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.1.1",
"husky": "^8.0.1",
"less": "^4.1.3",
"lint-staged": "^13.0.3",
"postcss-html": "^1.4.1",
"postcss-less": "^6.0.0",
"prettier": "^2.7.1",
"stylelint": "^14.9.1",
"stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^26.0.0",
"stylelint-order": "^5.0.0",
"vite": "^4.2.1",
"vue-eslint-parser": "^9.1.0"
},
"browserslist": [
"defaults"
],
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"prettier --write",
"eslint --fix"
],
"*.vue": [
"stylelint --fix",
"prettier --write",
"eslint --fix"
],
"*.{less,css}": [
"stylelint --fix",
"prettier --write"
]
}
}

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

5
src/App.vue

@ -0,0 +1,5 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>

11
src/api/demo.js

@ -0,0 +1,11 @@
import request from '@/utils/request'
export const demo = {
getList: params => {
return request({
url: '/demo',
method: 'get',
params
})
}
}

BIN
src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

45
src/components/HelloWorld.vue

@ -0,0 +1,45 @@
<script setup>
import { ref } from 'vue'
defineProps({
msg: {
type: String,
default: ''
}
})
const count = ref(0)
</script>
<template>
<div>
<h1>{{ msg }}</h1>
<p>
Recommended IDE setup:
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
+
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
</p>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Documentation
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a>
</p>
<button type="button" @click="count++">count is: {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</div>
</template>
<style scoped>
a {
color: #42b983;
}
</style>

35
src/hooks/useRouter.js

@ -0,0 +1,35 @@
import { getCurrentInstance, shallowRef } from 'vue'
import { router } from '@/router'
export function useRouter() {
const vm = getCurrentInstance()
if (vm) {
return router
}
console.warn('请在 setup 中调用。')
return undefined
}
let currentRoute = shallowRef()
export function useRoute() {
if (!currentRoute.value) {
const vm = getCurrentInstance()
if (!vm) {
console.warn('请在 setup 中调用。')
return
}
currentRoute.value = vm.proxy.$route
// 每次路由切换时,更新 route 参数
const router = useRouter()
router.afterEach(to => (currentRoute.value = to))
}
return currentRoute
}

106
src/layouts/BaseLayout.vue

@ -0,0 +1,106 @@
<template>
<div class="base-layout">
<ul class="base-layout__menu">
<li
v-for="route of routes"
:key="route.path"
:class="{ active: isActive(route.path) }"
@click="toPath(route.path)"
>
{{ route.meta.title }}
</li>
</ul>
<div class="base-layout__main">
<router-view></router-view>
</div>
<div class="base-layout__footer">
Vue2.7 + Vite + Less + Eslint + Stylelint + Commitlint 工程化脚手架
</div>
</div>
</template>
<script>
import { useRouter, useRoute } from '@/hooks/useRouter'
export default {
setup() {
const router = useRouter()
const route = useRoute()
const demoRoutes = router.options.routes[1].children
function isActive(path) {
return route.value.path === `/demo/${path}`
}
function toPath(path) {
router.push(`/demo/${path}`)
}
return {
/**
* data
*/
routes: demoRoutes,
/**
* methods
*/
isActive,
toPath
}
}
}
</script>
<style lang="less" scoped>
.base-layout {
height: 100%;
background-color: #f5f5f5;
&__menu {
height: 48px;
padding-top: 8px;
background-color: #fff;
border-bottom: 1px solid #eee;
li {
position: relative;
top: 1px;
display: inline-block;
height: 41px;
margin-left: 8px;
padding: 0 8px;
font-size: 14px;
line-height: 40px;
text-align: center;
border: 1px solid transparent;
cursor: pointer;
&.active,
&:hover {
background-color: #f5f5f5;
border-color: #eee;
border-bottom-color: #f5f5f5;
border-radius: 8px 8px 0 0;
}
}
}
&__main {
min-height: calc(100% - 45px - 48px);
padding: 16px;
overflow: auto;
}
&__footer {
padding: 16px 0;
color: #c3c3c3;
font-size: 12px;
text-align: center;
border-top: 1px solid #eee;
}
}
</style>

20
src/main.js

@ -0,0 +1,20 @@
import Vue from 'vue'
import App from './App.vue'
import { router } from './router'
import { setupGuard } from './router/guard'
import { store } from './store'
import antd from 'ant-design-vue'
Vue.use(antd)
import './styles/index.less'
import 'ant-design-vue/dist/antd.css'
setupGuard(router)
new Vue({
router,
pinia: store,
render: h => h(App)
}).$mount('#app')

5
src/router/constants.js

@ -0,0 +1,5 @@
import BaseLayout from '@/layouts/BaseLayout.vue'
export const LAYOUT = BaseLayout
export const HOME_PATH = '/'

9
src/router/guard/index.js

@ -0,0 +1,9 @@
import { setupPermissionGuard } from './permission.guard'
/**
* 设置路由守卫
* @param {import('vue-router/types/router').VueRouter} router
*/
export function setupGuard(router) {
setupPermissionGuard(router) // 权限守卫
}

14
src/router/guard/permission.guard.js

@ -0,0 +1,14 @@
/**
* 设置权限守卫
* @param {import('vue-router/types/router').VueRouter} router
*/
export function setupPermissionGuard(router) {
router.beforeEach((to, from, next) => {
console.log('beforeEach', to, from)
next()
})
router.afterEach((to, from) => {
console.log('afterEach', to, from)
})
}

24
src/router/index.js

@ -0,0 +1,24 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import routeModules from './modules'
import { HOME_PATH } from './constants'
Vue.use(VueRouter)
/**
* @type {import('vue-router').RouteConfig[]}
*/
const routes = [
{
path: '/',
redirect: HOME_PATH
},
...routeModules
]
export const router = new VueRouter({
mode: 'hash',
routes
})

29
src/router/modules/demo.modules.js

@ -0,0 +1,29 @@
import { LAYOUT } from '../constants'
/**
* @type {import('vue-router').RouteConfig[]}
*/
export default [
{
path: '/demo',
component: LAYOUT,
redirect: '/demo/route',
children: [
{
path: 'route',
component: () => import('@/views/demo/route.vue'),
meta: { title: '路由使用' }
},
{
path: 'pinia',
component: () => import('@/views/demo/pinia.vue'),
meta: { title: 'Pinia使用' }
},
{
path: 'style',
component: () => import('@/views/demo/style.vue'),
meta: { title: '公共样式' }
}
]
}
]

3
src/router/modules/index.js

@ -0,0 +1,3 @@
import demoRouteModules from './demo.modules'
export default [...demoRouteModules]

8
src/store/index.js

@ -0,0 +1,8 @@
import Vue from 'vue'
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
export const store = createPinia()
export * from './modules/demo'

12
src/store/modules/demo.js

@ -0,0 +1,12 @@
import { defineStore } from 'pinia'
export const useDemoStore = defineStore('demo', {
state: () => ({
count: 1
}),
actions: {
accumulate() {
this.count++
}
}
})

18
src/styles/global/utils.less

@ -0,0 +1,18 @@
// 禁止换行, 文本溢出省略号显示 (一行)
.ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
// 文本溢出省略号显示 (多行)
// 兼容大部分主流浏览器
.multi-ellipsis(@lines) {
display: flex;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: @lines;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
}

5
src/styles/global/variables.less

@ -0,0 +1,5 @@
@primary: #409eff;
@success: #67c23a;
@warning: #e6a23c;
@danger: #f56c6c;
@info: #909399;

8
src/styles/index.less

@ -0,0 +1,8 @@
@import './normalize.css';
@import './reset.css';
html,
body,
#app {
height: 100%;
}

351
src/styles/normalize.css

@ -0,0 +1,351 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
margin: 0.67em 0;
font-size: 2em;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-size: 1em; /* 2 */
font-family: monospace, monospace; /* 1 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
border-bottom: none; /* 1 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-size: 1em; /* 2 */
font-family: monospace, monospace; /* 1 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
margin: 0; /* 2 */
font-size: 100%; /* 1 */
font-family: inherit; /* 1 */
line-height: 1.15; /* 1 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
padding: 0;
border-style: none;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type='button']:-moz-focusring,
[type='reset']:-moz-focusring,
[type='submit']:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
display: table; /* 1 */
box-sizing: border-box; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
color: inherit; /* 2 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type='checkbox'],
[type='radio'] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type='search'] {
outline-offset: -2px; /* 2 */
appearance: textfield; /* 1 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type='search']::-webkit-search-decoration {
appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
font: inherit; /* 2 */
appearance: button; /* 1 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

133
src/styles/reset.css

@ -0,0 +1,133 @@
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
font: inherit;
font-size: 100%;
vertical-align: baseline;
border: 0;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
img,
video {
max-width: 100%;
height: auto;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

15
src/utils/env.js

@ -0,0 +1,15 @@
/**
* 是否为开发环境
* @returns {Boolean}
*/
export function isDev() {
return import.meta.env.DEV
}
/**
* 是否为生产环境
* @returns {Boolean}
*/
export function isProd() {
return import.meta.env.PROD
}

57
src/utils/request.js

@ -0,0 +1,57 @@
import axios from 'axios'
const services = axios.create({
baseURL: '/api',
timeout: 8000
})
// 请求拦截
services.interceptors.request.use(
config => {
/**
* 在这里一般会携带前台的参数发送给后台比如下面这段代码
* const token = getToken()
* if (token) {
* config.headers.token = token
* }
*/
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截
services.interceptors.response.use(
response => {
const res = response.data
/**
* 这里使用的是自定义 Code 码来做统一的错误处理
* code 等于 -1 则代表接口响应出错可根据自己的业务来进行修改
*/
if (res.code === -1) {
const msg = res.message || '未知错误,请联系管理员查看'
console.error('[api]', msg)
return Promise.reject(msg)
}
return res.data
},
error => {
const { response } = error
if (response && response.data) {
return Promise.reject(error)
} else {
const { message } = error
console.error('[api]', message)
return Promise.reject(error)
}
}
)
export default services

33
src/views/demo/components/StyleTxt.vue

@ -0,0 +1,33 @@
<template>
<div>
<h2 class="title">你买的什么书</h2>
<p class="answer">编程</p>
<p class="question">C++ 还是 Java</p>
<p class="answer">沈从文</p>
</div>
</template>
<script>
export default {}
</script>
<style lang="less" scoped>
.title {
font-weight: 700;
font-size: 16px;
}
.answer,
.question {
margin-top: 16px;
}
.title,
.question {
color: @primary;
}
.answer {
color: @info;
}
</style>

59
src/views/demo/pinia.vue

@ -0,0 +1,59 @@
<template>
<div>
<div v-if="hasOriginPath" style="margin-bottom: 16px">
<button @click="goBack">返回上一页</button>
</div>
<p>当前数量{{ count }}</p>
<button @click="accumulate">累加</button>
</div>
</template>
<script>
import { computed } from 'vue'
import { useRoute, useRouter } from '@/hooks/useRouter'
import { useDemoStore } from '@/store'
export default {
setup() {
const route = useRoute()
const router = useRouter()
const hasOriginPath = computed(() => !!route.value.query.originPath)
function goBack() {
const { path, query } = route.value
router.replace({ path: query.originPath, query: { originPath: path } })
}
const demoStore = useDemoStore()
const count = computed(() => demoStore.count) // state 使 computed
const accumulate = () => demoStore.accumulate()
return {
/**
* store
*/
count,
/**
* computed
*/
hasOriginPath,
/**
* methods
*/
goBack,
accumulate
}
}
}
</script>
<style lang="less" scoped>
p {
margin-bottom: 16px;
}
</style>

65
src/views/demo/route.vue

@ -0,0 +1,65 @@
<template>
<div>
<p>当前路由{{ routePath }}</p>
<p>上个页面的路径{{ originPath }}</p>
<div class="btns">
<button @click="handleRouteChange">改变路由</button>
<button @click="toPage('/demo/pinia')">跳转Pinia使用</button>
</div>
</div>
</template>
<script>
import { computed, watch } from 'vue'
import { useRoute, useRouter } from '@/hooks/useRouter'
export default {
setup() {
const route = useRoute()
const router = useRouter()
watch(route, () => {
console.log('route 变化', route.value)
})
//
const originPath = computed(() => route.value.query.originPath)
const routePath = computed(() => route.value.fullPath)
function handleRouteChange() {
router.push({ path: route.value.path, query: { key: Date.now() } })
}
function toPage(path) {
router.push({ path, query: { originPath: route.value.path } })
}
return {
/**
* computed
*/
routePath,
originPath,
/**
* methods
*/
handleRouteChange,
toPage
}
}
}
</script>
<style lang="less" scoped>
p {
margin-bottom: 16px;
}
.btns {
button {
margin-right: 16px;
}
}
</style>

101
src/views/demo/style.vue

@ -0,0 +1,101 @@
<template>
<div>
<div class="colors">
<div class="colors-item primary">primary</div>
<div class="colors-item success">success</div>
<div class="colors-item warning">warning</div>
<div class="colors-item danger">danger</div>
<div class="colors-item info">info</div>
<a-button type="primary" shape="circle" icon="search" />
<a-button type="primary" shape="circle"> A </a-button>
</div>
<p class="tips">Less 全局变量在 styles/global/variables.less 中定义</p>
<p class="one-line">
Less全局方法单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略
</p>
<p class="more-line">
Less全局方法多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略
</p>
<p class="tips">Less 全局方法在 styles/global/utils.less 中定义</p>
<StyleTxt></StyleTxt>
<p class="tips">
Vue2.7 样式穿透使用 ::v-deep(selector) :deep(selector) 选择器
</p>
</div>
</template>
<script>
import StyleTxt from './components/StyleTxt.vue'
export default {
components: {
StyleTxt
}
}
</script>
<style lang="less" scoped>
.colors {
margin-bottom: 16px;
&-item {
display: inline-block;
width: 120px;
margin-right: 16px;
padding: 16px;
color: #fff;
font-size: 14px;
text-align: center;
&.primary {
background-color: @primary;
}
&.success {
background-color: @success;
}
&.warning {
background-color: @warning;
}
&.danger {
background-color: @danger;
}
&.info {
background-color: @info;
}
}
}
.one-line {
width: 300px;
margin-bottom: 16px;
.ellipsis();
}
.more-line {
width: 300px;
.multi-ellipsis(3);
}
.tips {
margin: 16px 0;
padding: 8px 16px;
font-size: 12px;
background-color: #fff;
}
::v-deep(.title) {
color: @danger;
}
:deep(.answer) {
color: @success;
}
</style>

71
vite.config.js

@ -0,0 +1,71 @@
import vue from '@vitejs/plugin-vue2'
import legacy from '@vitejs/plugin-legacy'
import serverProxy from './build/proxy'
import { defineConfig, loadEnv } from 'vite'
import { pathResolve, wrapperEnv } from './build/utils'
const variablesLessPath = pathResolve('./src/styles/global/variables.less')
const utilsLessPath = pathResolve('./src/styles/global/utils.less')
// https://vitejs.dev/config/
export default ({ mode, command }) => {
const root = process.cwd()
const isBuild = command === 'build'
// 在 vite 配置文件中使用环境变量,需要通过 loadEnv 来加载
const env = loadEnv(mode, root)
const { VITE_PUBLIC_PATH, VITE_OUTPUT_DIR, VITE_PORT, VITE_LEGACY } =
wrapperEnv(env)
const plugins = [vue()]
/**
* vite 默认打包文件带有 ES6 语法在旧版浏览器中是不支持的
* 为了支持旧版浏览器可以在 .env.production 中开启 VITE_LEGACY 设置
*/
if (isBuild && VITE_LEGACY) {
plugins.push(legacy())
}
return defineConfig({
root,
base: VITE_PUBLIC_PATH,
plugins,
resolve: {
alias: {
// @/xxxx => src/xxxx
'@': pathResolve('./src')
}
},
server: {
host: true,
port: VITE_PORT,
proxy: serverProxy
},
build: {
/**
* 最终构建的浏览器兼容目标
* https://www.vitejs.net/config/#build-target
*/
target: 'es2015',
outDir: VITE_OUTPUT_DIR,
brotliSize: false, // 关闭 brotli 压缩大小报告,可提升构建速度
chunkSizeWarningLimit: 2000 // chunk 大小警告的限制(以 kbs 为单位)
},
css: {
preprocessorOptions: {
less: {
modifyVars: {
hask: `
true;
@import (reference) "${variablesLessPath}";
@import (reference) "${utilsLessPath}";
`
},
javascriptEnabled: true
}
}
}
})
}

6187
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save