├── .eslintignore
├── .stylelintignore
├── .env.development
├── .env
├── .postcssrc.js
├── .commitlintrc.js
├── .env.production
├── public
└── favicon.ico
├── src
├── assets
│ └── logo.png
├── App.vue
├── router
│ ├── modules
│ │ ├── index.js
│ │ └── demo.modules.js
│ ├── constants.js
│ ├── guard
│ │ ├── index.js
│ │ └── permission.guard.js
│ └── index.js
├── styles
│ ├── global
│ │ ├── variables.less
│ │ └── utils.less
│ ├── index.less
│ ├── reset.css
│ └── normalize.css
├── store
│ ├── index.js
│ └── modules
│ │ └── demo.js
├── api
│ └── demo.js
├── utils
│ ├── env.js
│ └── request.js
├── main.js
├── views
│ └── demo
│ │ ├── components
│ │ └── StyleTxt.vue
│ │ ├── pinia.vue
│ │ ├── route.vue
│ │ └── style.vue
├── hooks
│ └── useRouter.js
├── components
│ └── HelloWorld.vue
└── layouts
│ └── BaseLayout.vue
├── .husky
├── pre-commit
└── commit-msg
├── docs
├── 2.全家桶
│ ├── 3. 组件库.md
│ ├── 2. 全局状态管理.md
│ └── 1. 路由.md
├── 3.更多功能
│ ├── 1. Axios 封装及接口管理.md
│ ├── 2. Css 样式处理.md
│ └── 3. Vite 基础配置.md
└── 1.工程化实践
│ ├── 3. husky.md
│ ├── 4. commitlint.md
│ ├── 2. stylelint.md
│ └── 1. eslint.md
├── .prettierrc.js
├── jsconfig.json
├── .vscode
├── extensions.json
└── settings.json
├── .editorconfig
├── .gitignore
├── index.html
├── .eslintrc.js
├── .stylelintrc.js
├── package.json
├── vite.config.js
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | public
2 | dist
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | public
2 | dist
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | # public path
2 | VITE_PUBLIC_PATH = /
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # prot
2 | VITE_PORT = 8000
3 |
4 | # output dir
5 | VITE_OUTPUT_DIR = dist
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional']
3 | }
4 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | # public path
2 | VITE_PUBLIC_PATH = /
3 |
4 | # 是否兼容传统浏览器
5 | VITE_LEGACY = false
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Only-Xc/vue2.7-vite-cli/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Only-Xc/vue2.7-vite-cli/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | pnpm lint-staged
5 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | pnpm commitlint --edit
5 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/router/modules/index.js:
--------------------------------------------------------------------------------
1 | import demoRouteModules from './demo.modules'
2 |
3 | export default [...demoRouteModules]
4 |
--------------------------------------------------------------------------------
/docs/2.全家桶/3. 组件库.md:
--------------------------------------------------------------------------------
1 | # 组件库
2 |
3 | 关于组件库可选择的就有很多了,因为可能每个人需要的都不相同,这里就不集成到脚手架模板中了。
4 |
5 | 有一点需要注意的是,Vue2.7 还是只能使用 Vue2.x 相关的组件库。
--------------------------------------------------------------------------------
/src/styles/global/variables.less:
--------------------------------------------------------------------------------
1 | @primary: #409eff;
2 | @success: #67c23a;
3 | @warning: #e6a23c;
4 | @danger: #f56c6c;
5 | @info: #909399;
6 |
--------------------------------------------------------------------------------
/src/styles/index.less:
--------------------------------------------------------------------------------
1 | @import './normalize.css';
2 | @import './reset.css';
3 |
4 | html,
5 | body,
6 | #app {
7 | height: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/src/router/constants.js:
--------------------------------------------------------------------------------
1 | import BaseLayout from '@/layouts/BaseLayout.vue'
2 |
3 | export const LAYOUT = BaseLayout
4 |
5 | export const HOME_PATH = '/demo'
6 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | singleQuote: true,
4 | printWidth: 80,
5 | trailingComma: 'none',
6 | arrowParens: 'avoid'
7 | }
8 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["./src/*"]
6 | }
7 | },
8 | "exclude": ["node_modules"]
9 | }
10 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "octref.vetur",
4 | "editorconfig.editorconfig",
5 | "dbaeumer.vscode-eslint",
6 | "stylelint.vscode-stylelint"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { createPinia, PiniaVuePlugin } from 'pinia'
3 |
4 | Vue.use(PiniaVuePlugin)
5 |
6 | export const store = createPinia()
7 |
8 | export * from './modules/demo'
9 |
--------------------------------------------------------------------------------
/src/api/demo.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export const demo = {
4 | getList: params => {
5 | return request({
6 | url: '/demo',
7 | method: 'get',
8 | params
9 | })
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/store/modules/demo.js:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 |
3 | export const useDemoStore = defineStore('demo', {
4 | state: () => ({
5 | count: 1
6 | }),
7 | actions: {
8 | accumulate() {
9 | this.count++
10 | }
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/src/router/guard/index.js:
--------------------------------------------------------------------------------
1 | import { setupPermissionGuard } from './permission.guard'
2 |
3 | /**
4 | * 设置路由守卫
5 | * @param {import('vue-router/types/router').VueRouter} router
6 | */
7 | export function setupGuard(router) {
8 | setupPermissionGuard(router) // 权限守卫
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/env.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 是否为开发环境
3 | * @returns {Boolean}
4 | */
5 | export function isDev() {
6 | return import.meta.env.DEV
7 | }
8 |
9 | /**
10 | * 是否为生产环境
11 | * @returns {Boolean}
12 | */
13 | export function isProd() {
14 | return import.meta.env.PROD
15 | }
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll": true,
4 | "source.fixAll.eslint": true
5 | },
6 | "vetur.format.enable": false,
7 | "stylelint.validate": ["vue"], // Add "vue" language.
8 | "editor.tabSize": 2,
9 | "files.eol": "\n",
10 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = false
12 | insert_final_newline = false
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | import { router } from './router'
5 | import { setupGuard } from './router/guard'
6 |
7 | import { store } from './store'
8 |
9 | import './styles/index.less'
10 |
11 | setupGuard(router)
12 |
13 | new Vue({
14 | router,
15 | pinia: store,
16 | render: h => h(App)
17 | }).$mount('#app')
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/settings.json
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/router/guard/permission.guard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 设置权限守卫
3 | * @param {import('vue-router/types/router').VueRouter} router
4 | */
5 | export function setupPermissionGuard(router) {
6 | router.beforeEach((to, from, next) => {
7 | console.log('beforeEach', to, from)
8 | next()
9 | })
10 |
11 | router.afterEach((to, from) => {
12 | console.log('afterEach', to, from)
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/src/styles/global/utils.less:
--------------------------------------------------------------------------------
1 | // 禁止换行, 文本溢出省略号显示 (一行)
2 | .ellipsis() {
3 | overflow: hidden;
4 | white-space: nowrap;
5 | text-overflow: ellipsis;
6 | }
7 |
8 | // 文本溢出省略号显示 (多行)
9 | // 兼容大部分主流浏览器
10 | .multi-ellipsis(@lines) {
11 | display: -webkit-box;
12 | overflow: hidden;
13 | text-overflow: ellipsis;
14 | -webkit-line-clamp: @lines;
15 |
16 | /* autoprefixer: ignore next */
17 | -webkit-box-orient: vertical;
18 | }
19 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 |
4 | import routeModules from './modules'
5 |
6 | import { HOME_PATH } from './constants'
7 |
8 | Vue.use(VueRouter)
9 |
10 | /**
11 | * @type {import('vue-router').RouteConfig[]}
12 | */
13 | const routes = [
14 | {
15 | path: '/',
16 | redirect: HOME_PATH
17 | },
18 | ...routeModules
19 | ]
20 |
21 | export const router = new VueRouter({
22 | mode: 'hash',
23 | routes
24 | })
25 |
--------------------------------------------------------------------------------
/docs/3.更多功能/1. Axios 封装及接口管理.md:
--------------------------------------------------------------------------------
1 | # Axios 封装及接口管理
2 |
3 | 这里接口请求使用的是基于 Promise 封装的请求库 [Axios](https://axios-http.com/zh/),也是现在使用很广泛的一个请求库。
4 |
5 | ## 目录说明
6 |
7 | 有关请求库的功能都在以下的目录中。
8 |
9 | ```sh
10 | .
11 | ├── src # 主目录
12 | │ ├── api # api 方法配置
13 | │ │ └── demo.js # 演示方法
14 | │ └── utils # 公共方法
15 | │ └── request.js # axios 请求库二次封装
16 | ```
17 |
18 | 在开发时,我们先在 `utils/requset.js` 中配置好适合自己业务的请求拦截和响应拦截。
19 |
20 | 之后在 `api` 文件夹中定义请求方法,这里我一般会以功能进行拆分,同一个功能的请求方法封装在一个文件,之后在需要的地方进行调用。
--------------------------------------------------------------------------------
/src/views/demo/components/StyleTxt.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
你买的什么书?”
4 |
——“编程。”
5 |
“C++ 还是 Java?”
6 |
——“沈从文。”
7 |
8 |
9 |
10 |
13 |
14 |
34 |
--------------------------------------------------------------------------------
/src/router/modules/demo.modules.js:
--------------------------------------------------------------------------------
1 | import { LAYOUT } from '../constants'
2 |
3 | /**
4 | * @type {import('vue-router').RouteConfig[]}
5 | */
6 | export default [
7 | {
8 | path: '/demo',
9 | component: LAYOUT,
10 | redirect: '/demo/route',
11 | children: [
12 | {
13 | path: 'route',
14 | component: () => import('@/views/demo/route.vue'),
15 | meta: { title: '路由使用' }
16 | },
17 | {
18 | path: 'pinia',
19 | component: () => import('@/views/demo/pinia.vue'),
20 | meta: { title: 'Pinia使用' }
21 | },
22 | {
23 | path: 'style',
24 | component: () => import('@/views/demo/style.vue'),
25 | meta: { title: '公共样式' }
26 | }
27 | ]
28 | }
29 | ]
30 |
--------------------------------------------------------------------------------
/docs/2.全家桶/2. 全局状态管理.md:
--------------------------------------------------------------------------------
1 | # 全局状态管理
2 |
3 | 这里全局状态管理器选择了 [Pinia](https://pinia.vuejs.org/),之所以没有选择 Vuex,对于我来说最主要的原因有两点:
4 |
5 | 1. Vue3 官方文档中,已经将 Pinia 放入官方推荐的核心库位置中了。
6 | 2. 对 Composition-API 具有更好的支持。
7 |
8 | 接下来看一段官方的说明:
9 |
10 | > Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分内容,并决定实现它 取而代之的是新的建议。
11 | >
12 | > 与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
13 |
14 | ## 目录说明
15 |
16 | 有关全局状态管理的功能都在以下的目录中。
17 |
18 | ```sh
19 | .
20 | ├── src # 主目录
21 | │ ├── main.js # 主入口
22 | │ ├── store # store 配置
23 | │ │ ├── modules # store 模块
24 | │ │ └── index.js
25 | │ └── views # 页面
26 | ```
27 |
28 | ## store modules
29 |
30 | 我们在开发中需要将不同功能所对应的状态,拆分到不同的 modules,好处在 route moduels 中已经描述过了。
--------------------------------------------------------------------------------
/src/hooks/useRouter.js:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance, shallowRef } from 'vue'
2 | import { router } from '@/router'
3 |
4 | export function useRouter() {
5 | const vm = getCurrentInstance()
6 |
7 | if (vm) {
8 | return router
9 | }
10 |
11 | console.warn('请在 setup 中调用。')
12 |
13 | return undefined
14 | }
15 |
16 | let currentRoute = shallowRef()
17 |
18 | export function useRoute() {
19 | if (!currentRoute.value) {
20 | const vm = getCurrentInstance()
21 |
22 | if (!vm) {
23 | console.warn('请在 setup 中调用。')
24 | return
25 | }
26 |
27 | currentRoute.value = vm.proxy.$route
28 |
29 | // 每次路由切换时,更新 route 参数
30 | const router = useRouter()
31 | router.afterEach(to => (currentRoute.value = to))
32 | }
33 |
34 | return currentRoute
35 | }
36 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('eslint-define-config')
2 |
3 | module.exports = defineConfig({
4 | root: true,
5 | env: {
6 | browser: true,
7 | node: true,
8 | es6: true
9 | },
10 | parser: 'vue-eslint-parser',
11 | parserOptions: {
12 | ecmaFeatures: {
13 | jsx: true
14 | }
15 | },
16 | extends: [
17 | 'eslint:recommended',
18 | /**
19 | * 继承 eslint-plugin-vue 插件的规则
20 | * @link https://eslint.vuejs.org/user-guide/#installation
21 | */
22 | 'plugin:vue/recommended',
23 | /**
24 | * 继承 eslint-plugin-prettier 插件的规则
25 | * @link https://github.com/prettier/eslint-plugin-prettier
26 | */
27 | 'plugin:prettier/recommended'
28 | ],
29 | rules: {
30 | 'vue/multi-word-component-names': 'off'
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | 'stylelint-config-standard',
5 | 'stylelint-config-rational-order',
6 | 'stylelint-config-prettier',
7 | 'stylelint-config-html/vue' // 需要放在最后一位
8 | ],
9 | defaultSeverity: 'warning',
10 | plugins: ['stylelint-order'],
11 | rules: {
12 | 'no-empty-source': null,
13 | 'selector-class-pattern': null,
14 | 'value-keyword-case': null,
15 | 'font-family-no-missing-generic-family-keyword': null
16 | },
17 | overrides: [
18 | {
19 | files: ['*.vue', '**/*.vue'],
20 | rules: {
21 | 'selector-pseudo-class-no-unknown': [
22 | true,
23 | {
24 | ignorePseudoClasses: ['deep', 'global']
25 | }
26 | ],
27 | 'selector-pseudo-element-no-unknown': [
28 | true,
29 | {
30 | ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
31 | }
32 | ]
33 | }
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
36 |
37 |
38 |
43 |
--------------------------------------------------------------------------------
/docs/2.全家桶/1. 路由.md:
--------------------------------------------------------------------------------
1 | # VueRouter 路由
2 |
3 | Vue2.x 中只能使用 vue-router3 版本。
4 |
5 | ## 目录说明
6 |
7 | 有关路由的功能都在以下的目录中。
8 |
9 | ```sh
10 | .
11 | ├── src # 主目录
12 | │ ├── main.js # 主入口
13 | │ ├── router # 路由配置
14 | │ │ ├── guard # 路由守卫
15 | │ │ ├── modules # 路由模块
16 | │ │ └── index.js
17 | │ └── views # 页面
18 | ```
19 |
20 | ## route modules
21 |
22 | 关于路由表,建议根据功能的不同来拆分到 modules 文件夹中。
23 |
24 | 这样做的好处是:
25 |
26 | 1. 方便后期维护
27 | 2. 减少 Git 合并代码时的冲突的可能
28 |
29 | ## componsition-api 中使用
30 |
31 | 在 hooks/useRouter 中封装了两个方法:
32 |
33 | 1. `useRouter`
34 | 2. `useRoute`
35 |
36 | ```javascript
37 | import { watch } from 'vue'
38 | import { useRoute, useRouter } from '@/hooks/useRouter'
39 |
40 | export default {
41 | setup() {
42 | const route = useRoute()
43 | const router = useRouter()
44 |
45 | watch(route, () => {
46 | console.log('route 变化', route.value)
47 | })
48 |
49 | function routeChange() {
50 | router.push({ path: '/home', query: { key: Date.now() } })
51 | }
52 |
53 | return {
54 | routeChange
55 | }
56 | }
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/src/styles/reset.css:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd, ol, ul, li,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td,
10 | article, aside, canvas, details, embed,
11 | figure, figcaption, footer, header, hgroup,
12 | menu, nav, output, ruby, section, summary,
13 | time, mark, audio, video {
14 | margin: 0;
15 | padding: 0;
16 | border: 0;
17 | font-size: 100%;
18 | font: inherit;
19 | vertical-align: baseline;
20 | }
21 |
22 | /* HTML5 display-role reset for older browsers */
23 | article, aside, details, figcaption, figure,
24 | footer, header, hgroup, menu, nav, section {
25 | display: block;
26 | }
27 |
28 | html {
29 | box-sizing: border-box;
30 | }
31 |
32 | *, *::before, *::after {
33 | box-sizing: inherit;
34 | }
35 |
36 | body {
37 | line-height: 1;
38 | }
39 |
40 | ol, ul {
41 | list-style: none;
42 | }
43 |
44 | img,
45 | video {
46 | height: auto;
47 | max-width: 100%;
48 | }
49 |
50 | table {
51 | border-collapse: collapse;
52 | border-spacing: 0;
53 | }
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | const services = axios.create({
4 | baseURL: '/api',
5 | timeout: 8000
6 | })
7 |
8 | // 请求拦截
9 | services.interceptors.request.use(
10 | config => {
11 | /**
12 | * 在这里一般会携带前台的参数发送给后台,比如下面这段代码:
13 | * const token = getToken()
14 | * if (token) {
15 | * config.headers.token = token
16 | * }
17 | */
18 |
19 | return config
20 | },
21 | error => {
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | // 响应拦截
27 | services.interceptors.response.use(
28 | response => {
29 | const res = response.data
30 |
31 | /**
32 | * 这里使用的是自定义 Code 码来做统一的错误处理
33 | * code 等于 -1 则代表接口响应出错(可根据自己的业务来进行修改)
34 | */
35 | if (res.code === -1) {
36 | const msg = res.message || '未知错误,请联系管理员查看'
37 |
38 | console.error('[api]', msg)
39 |
40 | return Promise.reject(msg)
41 | }
42 |
43 | return res.data
44 | },
45 | error => {
46 | const { response } = error
47 | if (response && response.data) {
48 | return Promise.reject(error)
49 | } else {
50 | const { message } = error
51 | console.error('[api]', message)
52 | return Promise.reject(error)
53 | }
54 | }
55 | )
56 |
57 | export default services
58 |
--------------------------------------------------------------------------------
/src/views/demo/pinia.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
当前数量:{{ count }}
8 |
9 |
10 |
11 |
12 |
13 |
54 |
55 |
60 |
--------------------------------------------------------------------------------
/src/views/demo/route.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
当前路由:{{ routePath }}
4 |
上个页面的路径:{{ originPath }}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
54 |
55 |
66 |
--------------------------------------------------------------------------------
/docs/1.工程化实践/3. husky.md:
--------------------------------------------------------------------------------
1 | # Husky Git Hook 工具
2 |
3 | husky Git Hook 工具,为 git 提供一系列钩子函数,在提交前(pre-commit)、提交消息(commit-msg)等钩子触发时可以为我们执行一些脚本。
4 |
5 | 我们可以使用 husky 工具来进行代码提交前的自动格式化,以及 commit message 的校验。
6 |
7 | ## 提交前代码格式化
8 |
9 | 1. 首先安装 husky
10 |
11 | ```bash
12 | pnpm add husky -D
13 | ```
14 |
15 | 2. 初始化 husky
16 |
17 | ```bash
18 | pnpm husky install
19 | ```
20 |
21 | 并在 package.json 中添加如下内容
22 |
23 | ```json
24 | {
25 | "scripts": {
26 | //...
27 | "prepare": "husky install"
28 | }
29 | }
30 | ```
31 |
32 | 3. 添加 git hook
33 |
34 | ```bash
35 | pnpm husky add .husky/pre-commit
36 | ```
37 |
38 | 到这里之后我们还需要使用另外一个工具: [`lint-staged`](https://www.npmjs.com/package/lint-staged),它是对 git 暂存区文件进行 lint 检查的工具。
39 |
40 | 4. 安装 lint-staged
41 |
42 | ```bash
43 | pnpm add lint-staged -D
44 | ```
45 |
46 | 5. 在 `package.json` 中添加如下配置
47 |
48 | ```json
49 | {
50 | //...
51 | "lint-staged": {
52 | "*.{js,ts,jsx,tsx}": [
53 | "prettier --write",
54 | "eslint --fix"
55 | ],
56 | "*.vue": [
57 | "stylelint --fix",
58 | "prettier --write",
59 | "eslint --fix"
60 | ],
61 | "*.{less,css}": [
62 | "stylelint --fix",
63 | "prettier --write"
64 | ]
65 | }
66 | }
67 | ```
68 |
69 | 6. 在 `.husky/pre-commit` 文件中写入以下内容
70 |
71 | ```shell
72 | #!/bin/sh
73 | . "$(dirname "$0")/_/husky.sh"
74 |
75 | pnpm lint-staged
76 | ```
77 |
78 | 经过以上配置之后,我们就可以在每次提交之前对所有代码进行格式化,保证线上代码的规范性。
79 |
80 |
81 | > 在实际中如果遇见 `Use the --allow-empty option to continue, or check your task configuration` 这个问题。
82 | >
83 | > 我们可以修改 `pnpm lint-staged` 为 `pnpm lint-staged --allow-empty` 来暂时屏蔽这个问题。
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue2.7",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview",
9 | "prepare": "husky install",
10 | "lint:eslint": "eslint \"src/**/*.{vue,js,jsx}\" --fix",
11 | "lint:stylelint": "stylelint \"src/**/*.{vue,css,less,scss}\" --fix"
12 | },
13 | "dependencies": {
14 | "axios": "^0.27.2",
15 | "pinia": "^2.0.14",
16 | "vue": "^2.7.5",
17 | "vue-router": "^3.5.4"
18 | },
19 | "devDependencies": {
20 | "@commitlint/cli": "^17.0.3",
21 | "@commitlint/config-conventional": "^17.0.3",
22 | "@vitejs/plugin-legacy": "^1.8.2",
23 | "@vitejs/plugin-vue2": "^1.1.2",
24 | "autoprefixer": "^10.4.7",
25 | "eslint": "^8.18.0",
26 | "eslint-config-prettier": "^8.5.0",
27 | "eslint-define-config": "^1.5.1",
28 | "eslint-plugin-prettier": "^4.2.1",
29 | "eslint-plugin-vue": "^9.1.1",
30 | "husky": "^8.0.1",
31 | "less": "^4.1.3",
32 | "lint-staged": "^13.0.3",
33 | "postcss-html": "^1.4.1",
34 | "postcss-less": "^6.0.0",
35 | "prettier": "^2.7.1",
36 | "stylelint": "^14.9.1",
37 | "stylelint-config-html": "^1.0.0",
38 | "stylelint-config-prettier": "^9.0.3",
39 | "stylelint-config-rational-order": "^0.1.2",
40 | "stylelint-config-standard": "^26.0.0",
41 | "stylelint-order": "^5.0.0",
42 | "vite": "^2.9.9",
43 | "vue-eslint-parser": "^9.0.3"
44 | },
45 | "browserslist": [
46 | "defaults"
47 | ],
48 | "lint-staged": {
49 | "*.{js,ts,jsx,tsx}": [
50 | "prettier --write",
51 | "eslint --fix"
52 | ],
53 | "*.vue": [
54 | "stylelint --fix",
55 | "prettier --write",
56 | "eslint --fix"
57 | ],
58 | "*.{less,css}": [
59 | "stylelint --fix",
60 | "prettier --write"
61 | ]
62 | }
63 | }
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue2'
2 | import legacy from '@vitejs/plugin-legacy'
3 | import serverProxy from './build/proxy'
4 | import { defineConfig, loadEnv } from 'vite'
5 | import { pathResolve, wrapperEnv } from './build/utils'
6 |
7 | const variablesLessPath = pathResolve('./src/styles/global/variables.less')
8 | const utilsLessPath = pathResolve('./src/styles/global/utils.less')
9 |
10 | // https://vitejs.dev/config/
11 | export default ({ mode, command }) => {
12 | const root = process.cwd()
13 | const isBuild = command === 'build'
14 |
15 | // 在 vite 配置文件中使用环境变量,需要通过 loadEnv 来加载
16 | const env = loadEnv(mode, root)
17 |
18 | const { VITE_PUBLIC_PATH, VITE_OUTPUT_DIR, VITE_PORT, VITE_LEGACY } =
19 | wrapperEnv(env)
20 |
21 | const plugins = [vue()]
22 |
23 | /**
24 | * vite 默认打包文件带有 ES6 语法,在旧版浏览器中是不支持的。
25 | * 为了支持旧版浏览器,可以在 .env.production 中开启 VITE_LEGACY 设置
26 | */
27 | if (isBuild && VITE_LEGACY) {
28 | plugins.push(legacy())
29 | }
30 |
31 | return defineConfig({
32 | root,
33 | base: VITE_PUBLIC_PATH,
34 | plugins,
35 | resolve: {
36 | alias: {
37 | // @/xxxx => src/xxxx
38 | '@': pathResolve('./src')
39 | }
40 | },
41 | server: {
42 | host: true,
43 | port: VITE_PORT,
44 | proxy: serverProxy
45 | },
46 | build: {
47 | /**
48 | * 最终构建的浏览器兼容目标
49 | * https://www.vitejs.net/config/#build-target
50 | */
51 | target: 'es2015',
52 | outDir: VITE_OUTPUT_DIR,
53 | brotliSize: false, // 关闭 brotli 压缩大小报告,可提升构建速度
54 | chunkSizeWarningLimit: 2000 // chunk 大小警告的限制(以 kbs 为单位)
55 | },
56 | css: {
57 | preprocessorOptions: {
58 | less: {
59 | modifyVars: {
60 | hask: `
61 | true;
62 | @import (reference) "${variablesLessPath}";
63 | @import (reference) "${utilsLessPath}";
64 | `
65 | },
66 | javascriptEnabled: true
67 | }
68 | }
69 | }
70 | })
71 | }
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue 2.7 + Vite 脚手架
2 |
3 | Vue2.7 已经发布正式版啦,不出意外的话这应该是 Vue2 的最后一个版本了,但是很多公司目前还没有升级 Vue3 的打算(比如我们)。
4 |
5 | 所以封装了这么一个开箱即用的脚手架模板,脚手架的功能可以看下面的功能列表小节,并且配备完整的技术文档。
6 |
7 | 可以直接使用,也可以当作学习源码。
8 |
9 | ## 教程目录
10 |
11 | 1. 工程化实践
12 | - [Eslint 格式化 JS 代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/1.%20eslint.md)
13 | - [Stylelint 格式化 CSS 代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/2.%20stylelint.md)
14 | - [Husky 提交时自动格式化代码](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/3.%20husky.md)
15 | - [Commitlint 校验 Commit Message](./docs/1.%E5%B7%A5%E7%A8%8B%E5%8C%96%E5%AE%9E%E8%B7%B5/4.%20commitlint.md)
16 | 2. 全家桶
17 | - [路由 vue-router](./docs/2.%E5%85%A8%E5%AE%B6%E6%A1%B6/1.%20%E8%B7%AF%E7%94%B1.md)
18 | - [全局状态管理 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)
19 | - [组件库](./docs/2.%E5%85%A8%E5%AE%B6%E6%A1%B6/3.%20%E7%BB%84%E4%BB%B6%E5%BA%93.md)
20 | 3. 更多功能
21 | - [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)
22 | - [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)
23 | - [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)
24 |
25 | ## 基础搭建
26 |
27 | 在这里我们使用的是 [pnpm](https://pnpm.io/zh/) 来作为本项目的包管理工具。
28 |
29 | ```bash
30 | # 安装依赖
31 | pnpm i
32 |
33 | # 运行
34 | pnpm run dev
35 |
36 | # 打包
37 | pnpm run build
38 |
39 | # 打包文件预览
40 | pnpm run preview
41 | ```
42 |
43 | > 本人开发环境
44 | >
45 | > node v16.13.1(大于 14 版本即可)
46 | >
47 | > pnpm v6.30.0
48 |
49 |
50 | ### 插件安装
51 |
52 | 此脚手架必须安以下依赖才能保证,代码自动格式化的有效运行:
53 |
54 | - Vetur
55 | - EditorConfig for VS Code
56 | - ESLint
57 | - Stylelint
58 |
59 | ## 功能列表
60 |
61 | - [x] Vue2.7 + Vite
62 | - [x] Eslint、Stylelint、Commitlint 统一开发规范
63 | - [x] husky + lint-staged (git commit 时自动格式化代码)
64 | - [x] Vue 全家桶集成
65 | - [x] Axios 封装及接口管理
66 | - [x] Css 样式处理
67 | - [x] vite.config.js 基础配置
68 | - [x] 跨域配置
69 | - [x] 多环境变量配置
70 | - [x] 浏览器构建兼容性
71 |
--------------------------------------------------------------------------------
/src/views/demo/style.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
primary
5 |
success
6 |
warning
7 |
danger
8 |
info
9 |
10 |
11 |
Less 全局变量在 styles/global/variables.less 中定义
12 |
13 |
14 | Less全局方法:单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略单行文本省略
15 |
16 |
17 | Less全局方法:多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略多行文本省略
18 |
19 |
20 |
Less 全局方法在 styles/global/utils.less 中定义
21 |
22 |
23 |
24 |
25 | Vue2.7 样式穿透使用 ::v-deep(selector) :deep(selector) 选择器
26 |
27 |
28 |
29 |
30 |
38 |
39 |
100 |
--------------------------------------------------------------------------------
/docs/3.更多功能/2. Css 样式处理.md:
--------------------------------------------------------------------------------
1 | # Css 样式处理
2 |
3 | ## 统一浏览器默认样式
4 |
5 | 在 Web 开发时,因为浏览器内核的不同,所以会导致某些 Html 元素默认的渲染样式有所差别,虽然很细微,但是会影响我们在不同浏览器中的显示效果。
6 |
7 | 为了解决这个问题,聪明的开发者就将某个元素在各个浏览器中表现不同的样式,通过一段 CSS 进行重置,然后再进行开发,这样就可以保证每个浏览器显示效果的统一性。
8 |
9 | 目前常用的有两种解决方案:
10 |
11 | 1. Normalize - [normalize.css](https://necolas.github.io/normalize.css/) 偏向于修复浏览器的默认 BUG 和一致性,但是保留元素的默认样式。
12 | 2. Reset - [reset.css](https://meyerweb.com/eric/tools/css/reset/) 偏向于完全重置浏览器默认样式,可控性更高。
13 |
14 | 在本项目中,我们结合两种方案进行使用,代码在 `styles/normalize.css` 和 `styles/reset.css` 中。
15 |
16 | ## Less 预处理器
17 |
18 | 在 Vite 中使用 Less,我们只需要运行以下命令即可:
19 |
20 | ```bash
21 | pnpm add less -D
22 | ```
23 |
24 | > 如果需要使用 sass 则把 less 换成 sass 就行。
25 |
26 | 安装完成之后,无需多余配置,vite 即可对 less 样式进行解析。
27 |
28 | 在 Vue 文件中,我们如下,即可使用 less 进行样式的编写:
29 |
30 | ```vue
31 |
39 | ```
40 |
41 | > 这里 CSS 命名规范推荐 BEM 命名规范,具体可以自行搜索了解。
42 |
43 | ### Less 全局变量/方法
44 |
45 | 一般在项目中会有自己的设计规范,比如像 Element、Antd,它就有一套自己的样式规范。
46 |
47 | 其中像颜色、边框、边距等都有统一的规范,当我们改一处,所有地方都会改变,这就用到了 less 的变量和 mixin。
48 |
49 | 然而我们每处都 `@import` 就比较麻烦,这个时候我们可以使用在 vite 中进行 less 的全局样式配置。
50 |
51 | ```js
52 | import { defineConfig } from 'vite'
53 | import { resolve } from 'path'
54 |
55 | function pathResolve(dir) {
56 | return resolve(process.cwd(), '.', dir)
57 | }
58 |
59 | // 全局变量
60 | const variablesLessPath = pathResolve('./src/styles/global/variables.less')
61 | // 全局方法
62 | const utilsLessPath = pathResolve('./src/styles/global/utils.less')
63 |
64 | export default defineConfig({
65 | css: {
66 | preprocessorOptions: {
67 | less: {
68 | modifyVars: {
69 | hask: `
70 | true;
71 | @import (reference) "${variablesLessPath}";
72 | @import (reference) "${utilsLessPath}";
73 | `
74 | },
75 | javascriptEnabled: true
76 | }
77 | }
78 | }
79 | })
80 | ```
81 |
82 | ## Vue 样式穿透
83 |
84 | [官方文档](https://vuejs.org/api/sfc-css-features.html#scoped-css)
85 |
86 | 在 Vue2.7 中,改变了以往样式穿透的语法,如果继续使用 `::v-deep`、`/deep/`、`>>>` 等语法的话,会出现一个警告,下面是新的语法:
87 |
88 | ```css
89 | /* 深度选择器 */
90 | :deep(selector) {
91 | /* ... */
92 | }
93 |
94 | /* 插槽选择器 */
95 | :slotted(selector) {
96 | /* ... */
97 | }
98 |
99 | /* 全局选择器 */
100 | :global(selector) {
101 | /* ... */
102 | }
103 | ```
--------------------------------------------------------------------------------
/src/layouts/BaseLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
57 |
58 |
107 |
--------------------------------------------------------------------------------
/docs/1.工程化实践/4. commitlint.md:
--------------------------------------------------------------------------------
1 | # Commitlint 提交信息校验
2 |
3 | 我们在使用 `git commit` 时,git 会记录每一次的 `commit message`(提交信息)。
4 |
5 | 正确的描述 `commit message` 在多人协同开发一个项目时,显得尤其重要。
6 |
7 | 这里我们可以看一下 `angular` 的 [`commit message`](https://github.com/angular/angular/commits/main),会发现它的描述特别的清晰明了。
8 |
9 | 而 [`commitlint`](https://commitlint.js.org/#/) 就是对 `commit message` 进行的检查的一个工具,当不规范时会终止提交。
10 |
11 | 1. 安装依赖
12 |
13 | - `@commitlint/cli`- Commitlint 本体
14 |
15 | - `@commitlint/config-conventional`- 通用的提交规范
16 |
17 | ```bash
18 | pnpm add @commitlint/cli @commitlint/config-conventional -D
19 | ```
20 |
21 | 1. 创建 commitlint 配置
22 |
23 | 在根目录添加一个 `.commitlintrc.js` 文件,内容如下:
24 |
25 | ```javascript
26 | module.exports = {
27 | extends: ['@commitlint/config-conventional']
28 | }
29 | ```
30 |
31 | 1. 在 git `commit-msg` 时进行检查
32 |
33 | 执行下面这条命令即可:
34 |
35 | ```bash
36 | pnpm husky add .husky/commit-msg "pnpm commitlint --edit $1"
37 | ```
38 |
39 | ## Commit Message 格式
40 |
41 | 配置完成之后就可以在每次 `git commit` 时对 `commit message` 进行校验了,规范有两种格式:单行信息和多行信息。
42 |
43 | 1. 单行信息
44 |
45 | 用于业务代码提交时使用,业务代码一般来说更改比较多而且无法具体说明其信息,具体的还需要看产品文档,所以用单行信息即可。
46 |
47 | ```xml
48 | ():
49 | ```
50 |
51 | 1. 多行信息
52 |
53 | 用于提交一些不经常更改的功能型代码时使用,如:某个功能函数的新增、修改或重构,目录结构的调整(工程化调整),架构的更改等,这些我们需要进行详细说明防止出现遗忘。
54 |
55 | ```xml
56 | ():
57 | // 空行
58 |
59 |
60 |