目录
- 1. pnpm下载
- 1.1 安装
- 1.2 差异
- 1.3 镜像源设置
- 2. 项目
- 2.1 vite创建项目
- 2.2 项目配置
- 2.3 pinia+vue-router
- main.js
- store/index.js
- router/index.js
- vue文件里使用示例
- 2.4 eslint + prettier
- .eslintrc.js
- .eslintignore
- .prettierrc
- 2.5 样式
- 2.6 commitizen
- package.json
- .cz-config.js
- commitlint.config.js
- 2.7 husky+lint-staged
- 项目优化
- vite.config.js
- 命令工具:pnpm
- 前端框架:vue3+vite+js
- vue库:pinia+vue-router
- 样式库:sass
- 格式化:eslint+prettier
- git提交规范化:commitizen
- 预检查工具:husky+lint-staged
- 组件库:ant-design-vue
- 优化库:viteCompression+viteImagemin+terser
1. pnpm下载
选择pnpm主要原因:编译速度pnpm
>> yarn
>> npm
。
1.1 安装
npm install pnpm -g
win11系统,pnpm build 的时候遇到了问题
set-ExecutionPolicy RemoteSigned
解决。如果早一些的版本需要管理员身份运行。
1.2 差异
## 增加包
npm install
pnpm add## 卸载包
npm uninstall
pnpm remove## 运行项目
npm run dev
pnpm dev## 打包项目
npm run build
pnpm build
1.3 镜像源设置
#查看
pnpm get registry
#临时
pnpm --registry https://registry.npmmirror.com install axios
#永久
pnpm config set registry https://registry.npmmirror.com
2. 项目
2.1 vite创建项目
## 创建项目,根据提示步骤进行
pnpm create vite## 运行项目
pnpm dev
## 生产打包
pnpm build
2.2 项目配置
复制package.json
保留想要用的安装包,pnpm install
即可全部安装。
scripts
部分根据需要保留对应指令。commitizen
和lint-staged
不用可以删除。- 格式化等去查看对应部分,增加相应的配置。
{"name": "vue3-collection","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vite build","preview": "vite preview","prettier:comment": "自动格式化当前目录下的所有文件","prettier": "prettier --write","eslint:comment": "使用 ESLint 检查并自动修复 src 目录下所有扩展名为 .js 和 .vue 的文件","eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src","commit:comment": "引导设置规范化的提交信息","commit": "git-cz","prepare": "husky install"},"config": {"commitizen": {"path": "./node_modules/cz-customizable"}},"lint-staged": {"*.{js,ts}": ["pnpm eslint","pnpm prettier"]},"dependencies": {"ant-design-vue": "^4.0.7","pinia": "^2.1.7","pinia-plugin-persistedstate": "^3.2.0","vite-plugin-compression": "^0.5.1","vue": "^3.3.8","vue-router": "^4.2.5"},"devDependencies": {"@commitlint/cli": "^18.4.3","@commitlint/config-conventional": "^18.4.3","@vitejs/plugin-vue": "^4.5.0","commitizen": "^4.3.0","commitlint-config-cz": "^0.13.3","cz-conventional-changelog": "^3.3.0","cz-customizable": "^7.0.0","eslint": "^8.55.0","eslint-config-prettier": "^9.1.0","eslint-plugin-prettier": "^5.0.1","eslint-plugin-vue": "^9.19.2","husky": "^8.0.3","lint-staged": "^15.2.0","mockjs": "^1.1.0","prettier": "^3.1.0","sass": "^1.69.5","terser": "^5.26.0","vite": "^5.0.7","vite-plugin-imagemin": "^0.6.1","vite-plugin-mock": "^2.9.6"}
}
2.3 pinia+vue-router
pinia官网:pinia.web3doc.top/
pnpm i pinia --save
pnpm i vue-router --save
#函数包
pnpm i @vueuse/core
#持久化
pnpm i pinia-plugin-persistedstate --save
main.js
import { createPinia } from 'pinia'
import router from './router/index'
app.use(createPinia()).use(router)
store/index.js
import { defineStore } from "pinia";
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore("main", {state: () => {return {count: 0,};},getters: {},actions: {increment() {this.count++;},},
//数据默认是存在localStoragepersist: {enabled: true, //开启storage:sessionStorage, //修改存储位置key:'userInfo', //设置存储的keypaths: ['id'],//指定要长久化的字段},
});
router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const routes = [{path: '/login',name: 'Login',meta: {title: '登录',keepAlive: true,requireAuth: false},component: () => import('@/pages/login.vue')},{path: '/',redirect: '/login'}
]const router = createRouter({history: createWebHistory(),routes
});
export default router;
vue文件里使用示例
这时候store
不同于vuex
,他是一个ref
变量,在标签里直接使用{{store.xx}}
<template><div>{{store.count}}</div><button @click="go('/login')">go link</button>
</template>
<script setup>
import { useStore } from '@/store/index'
import { storeToRefs } from 'pinia';
import {useRouter} from 'vue-router'
const store = useStore();
const router = useRouter();
//不能解构而是用storeToRefs保持响应性
const {count} = storeToRefs(store)
//更新
store.increment();function go(path){router.push({path:path})
}
</script>
2.4 eslint + prettier
## 安装
pnpm i eslint eslint-plugin-vue --save-dev
pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev
.eslintrc.js
semi
设置0,关闭了对逗号检查comma-dangle
设置0,关闭了分号检查space-before-function-paren
设置0,关闭了方法名括号前空格检查vue/html-self-closing
,html
-void
改为always
规避img
等单标签报错- 删除了
parser: 'babel-eslint'
解决了缺包提示 - 最外层增加
parser: 'vue-eslint-parser'
,编译es6等不出错
// 'off' or 0 - 关闭这个规则校验
// 'warn' or 1 - 开启这个规则校验,但只是提醒,不会退出
// 'error' or 2 - 开启这个规则校验,并退出module.exports = {root: true,parserOptions: {ecmaVersion: 7,ecmaFeatures: {jsx: true,modules: true,},},parser: 'vue-eslint-parser',env: {es6: true,node: true,},extends: ['eslint:recommended','plugin:vue/vue3-recommended','prettier','plugin:prettier/recommended',],plugins: ['vue'],globals: {document: false,navigator: false,window: false,},rules: {'accessor-pairs': 2,'arrow-spacing': [2,{before: true,after: true,},],'block-spacing': [2, 'always'],'comma-dangle': [0, 'never'],'comma-spacing': [2,{before: false,after: true,},],'comma-style': [2, 'last'],'constructor-super': 2,curly: [2, 'multi-line'],'dot-location': [2, 'property'],'eol-last': 2,eqeqeq: [2, 'allow-null'],'generator-star-spacing': [2,{before: true,after: true,},],'handle-callback-err': [2, '^(err|error)$'],indent: [2,2,{SwitchCase: 1,},],'jsx-quotes': [2, 'prefer-single'],'key-spacing': [2,{beforeColon: false,afterColon: true,},],'keyword-spacing': [2,{before: true,after: true,},],'new-cap': [2,{newIsCap: true,capIsNew: false,},],'new-parens': 2,'no-array-constructor': 2,'no-console': 0,'no-caller': 2,'no-class-assign': 2,'no-cond-assign': 2,'no-const-assign': 2,'no-control-regex': 2,'no-delete-var': 2,'no-dupe-args': 2,'no-dupe-class-members': 2,'no-dupe-keys': 2,'no-duplicate-case': 2,'no-empty-character-class': 2,'no-empty-pattern': 2,'no-eval': 2,'no-ex-assign': 2,'no-extend-native': 2,'no-extra-bind': 2,'no-extra-boolean-cast': 2,'no-extra-parens': [2, 'functions'],'no-fallthrough': 2,'no-floating-decimal': 2,'no-func-assign': 2,'no-implied-eval': 2,'no-inner-declarations': [2, 'functions'],'no-invalid-regexp': 2,'no-irregular-whitespace': 2,'no-iterator': 2,'no-label-var': 2,'no-labels': [2,{allowLoop: false,allowSwitch: false,},],'no-lone-blocks': 2,'no-mixed-spaces-and-tabs': 2,'no-multi-spaces': 2,'no-multi-str': 2,'no-multiple-empty-lines': [2,{max: 1,},],'no-native-reassign': 2,'no-negated-in-lhs': 2,'no-new-object': 2,'no-new-require': 2,'no-new-symbol': 2,'no-new-wrappers': 2,'no-obj-calls': 2,'no-octal': 2,'no-octal-escape': 2,'no-path-concat': 2,'no-proto': 2,'no-redeclare': 2,'no-regex-spaces': 2,'no-return-assign': [2, 'except-parens'],'no-self-assign': 2,'no-self-compare': 2,'no-sequences': 2,'no-shadow-restricted-names': 2,'no-spaced-func': 2,'no-sparse-arrays': 2,'no-this-before-super': 2,'no-throw-literal': 2,'no-trailing-spaces': 2,'no-undef': 0,'no-undef-init': 2,'no-unexpected-multiline': 2,'no-unmodified-loop-condition': 2,'no-unneeded-ternary': [2,{defaultAssignment: false,},],'no-unreachable': 2,'no-unsafe-finally': 2,'no-unused-vars': [2,{vars: 'all',args: 'none',},],'no-useless-call': 2,'no-useless-computed-key': 2,'no-useless-constructor': 2,'no-useless-escape': 0,'no-whitespace-before-property': 2,'no-with': 2,'one-var': [2,{initialized: 'never',},],'operator-linebreak': [2,'after',{overrides: {'?': 'before',':': 'before',},},],'padded-blocks': [2, 'never'],quotes: [2,'single',{avoidEscape: true,allowTemplateLiterals: true,},],semi: [0, 'never'],'semi-spacing': [2,{before: false,after: true,},],'space-before-blocks': [2, 'always'],'space-before-function-paren': [0, 'always'],'space-in-parens': [2, 'never'],'space-infix-ops': 2,'space-unary-ops': [2,{words: true,nonwords: false,},],'spaced-comment': [2,'always',{markers: ['global','globals','eslint','eslint-disable','*package','!',',',],},],'template-curly-spacing': [2, 'never'],'use-isnan': 2,'valid-typeof': 2,'wrap-iife': [2, 'any'],'yield-star-spacing': [2, 'both'],yoda: [2, 'never'],'prefer-const': 2,'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,'object-curly-spacing': [2,'always',{objectsInObjects: false,},],'array-bracket-spacing': [2, 'never'],'vue/jsx-uses-vars': 2,'vue/max-attributes-per-line': ['error',{singleline: 3,multiline: {max: 13,},},],'vue/html-self-closing': ['error',{html: {void: 'always',normal: 'any',component: 'always',},svg: 'always',math: 'always',},],},
};
.eslintignore
如果遇到报错可对应增加!xx
解决配置类文件被检测报错的问题。
node_modules/
dist/
index.html
!.eslintrc.js
!.prettierrc.js
!.gitignore
.prettierrc
{"printWidth": 80,"tabWidth": 2,"useTabs": false,"semi": true,"singleQuote": true,"quoteProps": "as-needed","jsxSingleQuote": false,"trailingComma": "all","bracketSpacing": true,"jsxBracketSameLine": false,"arrowParens": "always","rangeStart": 0,"rangeEnd": 9999999,"requirePragma": false,"insertPragma": false,"proseWrap": "preserve","htmlWhitespaceSensitivity": "css","endOfLine": "auto"
}
2.5 样式
#scss
pnpm add -D sass
全局变量
css: {preprocessorOptions: {scss: {/** 如果引入多个文件,可以使用* '@import "@/assets/scss/globalVariable1.scss";* @import"@/assets/scss/globalVariable2.scss";'**/additionalData: '@import "@/style/var.scss";',},},},
2.6 commitizen
#安装 commitizen (交互式提交 + 自定义提示文案 + Commit规范)
pnpm install -D commitizen cz-conventional-changelog @commitlint/config-conventional @commitlint/cli commitlint-config-cz cz-customizable
package.json
package.json
scripts
添加,使用pnpm commit
命令替代git commit
scripts:{"commit:comment": "引导设置规范化的提交信息","commit": "git-cz",
}
"config": {"commitizen": {"path": "./node_modules/cz-customizable"}},
.cz-config.js
types
为自定义内容,value
跟commitlint.config.js
对应
module.exports = {types: [{value: 'feature',name: 'feature: 增加新功能'},{value: 'bug',name: 'bug: 测试反馈bug列表中的bug号'},{value: 'fix',name: 'fix: 修复bug'},{value: 'ui',name: 'ui: 更新UI'},{value: 'docs',name: 'docs: 文档变更'},{value: 'style',name: 'style: 代码格式(不影响代码运行的变动)'},{value: 'perf',name: 'perf: 性能优化'},{value: 'refactor',name: 'refactor: 重构(既不是增加feature,也不是修复bug)'},{value: 'release',name: 'release: 发布'},{value: 'deploy',name: 'deploy: 部署'},{value: 'test',name: 'test: 增加测试'},{value: 'chore',name: 'chore: 构建过程或辅助工具的变动(更改配置文件)'},{value: 'revert',name: 'revert: 回退'},{value: 'build',name: 'build: 打包'}],// override the messages, defaults are as followsmessages: {type: '请选择提交类型:',customScope: '请输入您修改的范围(可选):',subject: '请简要描述提交 message (必填):',body: '请输入详细描述(可选,待优化去除,跳过即可):',footer: '请输入要关闭的issue(待优化去除,跳过即可):',confirmCommit: '确认使用以上信息提交?(y/n/e/h)'},allowCustomScopes: true,skipQuestions: ['body', 'footer'],subjectLimit: 72};
commitlint.config.js
module.exports = {extends: ['@commitlint/config-conventional', 'cz'],rules: {'type-enum': [2,'always',['feature', // 新功能(feature)'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况'fix', // 修补bug'ui', // 更新 ui'docs', // 文档(documentation)'style', // 格式(不影响代码运行的变动)'perf', // 性能优化'release', // 发布'deploy', // 部署'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)'test', // 增加测试'chore', // 构建过程或辅助工具的变动'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址'build', // 打包],],// <type> 格式 小写'type-case': [2, 'always', 'lower-case'],// <type> 不能为空'type-empty': [2, 'never'],// <scope> 范围不能为空'scope-empty': [2, 'never'],// <scope> 范围格式'scope-case': [0],// <subject> 主要 message 不能为空'subject-empty': [2, 'never'],// <subject> 以什么为结束标志,禁用'subject-full-stop': [0, 'never'],// <subject> 格式,禁用'subject-case': [0, 'never'],// <body> 以空行开头'body-leading-blank': [1, 'always'],'header-max-length': [0, 'always', 72],},
};
2.7 husky+lint-staged
根据步骤完成前 5 步的设置,git提交代码commit时就会先执行husky进行检查。检查通过方可提交。
# 1.安装
pnpm i husky lint-staged -D
# 2.生成 .husky 的文件夹
npx husky install
# 3.添加 hooks,会在 .husky 目录下生成一个 pre-commit 脚本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"
# 4.添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
# 5.package.json中添加以下配置"lint-staged": {"*.{js,ts}": ["pnpm eslint","pnpm prettier"]}
# 6.提交代码 `git commit -m "message"` 就会看到 hook 生效了。
项目优化
vite官网中mock相关配置:vite-plugin-mock
vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// import { visualizer } from 'rollup-plugin-visualizer';
import viteCompression from 'vite-plugin-compression';
import viteImagemin from 'vite-plugin-imagemin';
import { viteMockServe } from 'vite-plugin-mock';
import path from 'path';// https://vitejs.dev/config/
export default defineConfig({base: './',//解决刷新404问题resolve: {alias: { '@': path.resolve('src') },//根路径extensions: ['.mjs', '.mts', '.js', '.tsx', '.jsx', '.json'],//可省略后缀的文件},plugins: [vue(),//mock数据:injectCode在生产环境启用的话需要。viteMockServe({mockPath:'./mock',localEnabled: true,prodEnabled: false,// injectCode: `import {mockServer} from 'mockServer';mockServer()`,}),//代码压缩viteCompression({ threshold: 10240, algorithm: 'gzip' }),//图片压缩viteImagemin({gifsicle: {optimizationLevel: 7,interlaced: false,},optipng: {optimizationLevel: 7,},mozjpeg: {quality: 20,},pngquant: {quality: [0.8, 0.9],speed: 4,},svgo: {plugins: [{name: 'removeViewBox',},{name: 'removeEmptyAttrs',active: false,},],},}),],// 全局变量设置css: {preprocessorOptions: {scss: {/** 如果引入多个文件,可以使用* '@import "@/assets/scss/globalVariable1.scss";* @import"@/assets/scss/globalVariable2.scss";'**/additionalData: '@import "@/style/var.scss";',},},},build: {minify: 'terser',terserOptions: {compress: {drop_console: true,drop_debugger: true,},},rollupOptions: {output: {manualChunks(id) {if (id.includes('node_modules')) {// 让每个插件都打包成独立的文件return id.toString().split('node_modules/')[1].split('/')[0].toString();}},},},},server: {host: '0.0.0.0', // ipport: 5001, // 端口号open: false, // 是否自动在浏览器打开https: false, // 是否开启 https// 跨域代理配置proxy: {'/api': {target: 'http:192.168.1.100:9094',changeOrigin: false,rewrite: (path) => path.replace(/^\/api/, ''),},},},
});
mockServer.js
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';//mock在最外层根目录下
import routes from '../mock/index';export function mockServer() {createProdMockServer([...routes]);
}