简介
从零搭建 vue3.x 项目工程环境,集成 ESLint、Prettier、Stylelint、husky、lint-staged 等
仓库地址 - github,欢迎使用和点亮小星星。
所用技术栈
- 构建工具: Vite
- 前端框架: Vue3.x
- 编程语言: TypeScript
- 代码规范:
- 提交规范:
- css预处理器: less
- 路由工具: Vue Router 4
- 状态管理: Vuex 4
vite 初始化项目
npm init vite@latest <project-name> vue-ts
alias 别名配置
修改 vite.config.js
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
// __dirname找不到,需要安装 npm install @types/node --save-dev
'@': resolve(__dirname, 'src'),
},
},
});
修改 tsconfig.json
"compilerOptions": {
...
"paths": {
"@/*": [ "./src/*" ],
}
},
配置 eslint 、prettier
安装所需依赖
yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-vue prettier eslint-config-prettier eslint-plugin-prettier -D
添加 .eslintrc.js 及其配置
module.exports = {
parser: 'vue-eslint-parser',
extends: ['plugin:vue/recommended', 'plugin:prettier/recommended'],
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
},
rules: {
'generator-star-spacing': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
semi: [2, 'always'],
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
'no-undef': 'off',
'eol-last': [1, 'always'],
'no-mixed-spaces-and-tabs': 2,
'prettier/prettier': [
'error',
{ singleQuote: true, vueIndentScriptAndStyle: false, semi: true },
],
'vue/no-multiple-template-root': 0, // template 唯一跟节点校验
'vue/multi-word-component-names': 0,
},
};
添加 prettier.config.js 及其配置
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: 'all', // 未尾逗号
vueIndentScriptAndStyle: false,
singleQuote: true, // 单引号
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'all', // 未尾分号
jsxBracketSameLine: false,
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf',
};
添加 .editorconfig 及其配置
EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格
# Editor configuration, see http://editorconfig.org
# 表示是最顶层的 EditorConfig 配置文件
root = true
# 匹配全部文件
[*]
# 缩进风格,可选"space"、"tab"
indent_style = space
# 缩进的空格树 2
indent_size = 2
# 结尾换行符,可选"if"、"or"、"orif"
end_of_line = lf
# 设置字符集
charset = utf-8
# 删除一行中的前后空格
trim_trailing_whitespace = true
# 在文件结尾插入新行
insert_final_newline = true
配置 stylelint
stylelint 是一个 css 检测器,可以让开发者编写样式是遵循一致的约定,避免代码混乱和不必要的错误。
安装所需依赖
yarn add stylelint stylelint-config-recommended-vue stylelint-config-standard postcss-html postcss-less -D
添加 .stylelintrc 及相关依赖
{
"extends": ["stylelint-config-standard", "stylelint-config-recommended-vue"],
"rules": {
"no-empty-source": null,
"property-no-vendor-prefix": null,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"length-zero-no-unit": true,
"value-list-comma-space-after": "always",
"declaration-colon-space-after": "always",
"value-list-max-empty-lines": 0,
"shorthand-property-no-redundant-values": true,
"declaration-block-no-duplicate-properties": true,
"declaration-block-no-redundant-longhand-properties": true,
"declaration-block-semicolon-newline-after": "always",
"block-closing-brace-newline-after": "always",
"media-feature-colon-space-after": "always",
"media-feature-range-operator-space-after": "always",
"at-rule-name-space-after": "always",
"indentation": 2,
"no-eol-whitespace": true,
"string-no-newline": null,
"string-quotes": "double",
"at-rule-no-unknown": [ true, {
"ignoreAtRules": [
"/-/"
]
}]
}
}
配置 package.json 脚本
{
"scripts": {
...,
"lint:style": "stylelint \"src/**/*.(vue|less|css)\" --customSyntax postcss-less"
}
}
配置代码检查和提交规范钩子
配置 husky
安装 husky
yarn add husky --dev
启用 git hooks
yarn husky install
增加 pre-commit
npx husky add .husky/pre-commit "npm run lint"
此时会在 .husky/ 目录下创建 pre-commit 文件
配置 package.json 脚本
{
"scripts": {
...,
"lint": "eslint . --ext .js,.ts,.vue --ignore-path .gitignore",
"prepare": "husky install"
}
}
添加 commit 提交内容规范 git hooks
npx husky add .husky/commit-msg 'yarn commitlint --edit "$1"'
此时会在 .husky/ 目录下创建 commit-msg 文件
添加 commitlint.config.js 及相关依赖
Git 提交的正确姿势:Commit message 编写指南
yarn add @commitlint/cli @commitlint/config-conventional -D
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
},
};
配置 lint-staged
lint-staged 只会对提交的文件进行检查、修复处理,以保证代码是符合本项目 eslint 等规范的。
安装
yarn add lint-staged -D
添加 .lintstagedrc 配置文件及依赖
{
"*.{js,ts,vue}": ["npm run lint"],
"*.{vue,css,scss,sass,less}": ["npm run lint:style"]
}
修改 pre-commit 文件
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
- npm run lint
+ yarn lint-staged --allow-empty "$1"
验证代码检查和提交规范钩子功能
代码检查
# 执行
git add .
git commit -m "test..."
# 输出
yarn run v1.13.0
warning package.json: No license field
$ /vue-base-project/node_modules/.bin/lint-staged --allow-empty ''
✔ Preparing...
⚠ Running tasks...
❯ Running tasks for *.{js,ts,vue}
✖ npm run lint [FAILED]
❯ Running tasks for *.{html,vue,css,scss,sass,less}
✖ stylelint --fix [FAILED]
↓ Skipped because of errors from tasks. [SKIPPED]
✔ Reverting to original state because of errors...
✔ Cleaning up...
✖ stylelint --fix:
Error: No configuration provided for /vue-base-project/src/components/UserPane/UserInfo.vue
at module.exports (/vue-base-project/node_modules/stylelint/lib/utils/configurationError.js:10:14)
at getConfigForFile (/vue-base-project/node_modules/stylelint/lib/getConfigForFile.js:60:4)
at async isPathIgnored (/vue-base-project/node_modules/stylelint/lib/isPathIgnored.js:25:17)
at async lintSource (/vue-base-project/node_modules/stylelint/lib/lintSource.js:37:20)
at async /vue-base-project/node_modules/stylelint/lib/standalone.js:218:27
at async Promise.all (index 0)
at async standalone (/vue-base-project/node_modules/stylelint/lib/standalone.js:257:22)
✖ npm run lint:
jsxBracketSameLine is deprecated.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! vue-base-project@0.0.1 lint: `eslint . --ext .js,.ts,.vue --ignore-path .gitignore "/vue-base-project/commitlint.config.js" "/vue-base-project/src/components/UserPane/UserInfo.vue"`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the vue-base-project@0.0.1 lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /.npm/_logs/2021-11-04T07_56_03_833Z-debug.log
> vue-base-project@0.0.1 lint /vue-base-project
> eslint . --ext .js,.ts,.vue --ignore-path .gitignore "/vue-base-project/commitlint.config.js" "/vue-base-project/src/components/UserPane/UserInfo.vue"
/vue-base-project/src/components/UserPane/UserInfo.vue
9:51 error Insert `;` prettier/prettier
9:51 error Missing semicolon semi
✖ 2 problems (2 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
husky - pre-commit hook exited with code 1 (error)
从错误信息可以看出,检测到本次提交的代码中,src/components/UserPane/UserInfo.vue 文件存在不符合 eslint 规范的内容
提交规范
# 执行
git add .
git commit -m "test..."
# 输出
yarn run v1.13.0
warning package.json: No license field
$ /vue-base-project/node_modules/.bin/lint-staged --allow-empty ''
✔ Preparing...
✔ Running tasks...
✔ Applying modifications...
✔ Cleaning up...
✨ Done in 2.50s.
yarn run v1.13.0
warning package.json: No license field
$ /vue-base-project/node_modules/.bin/commitlint --edit .git/COMMIT_EDITMSG
⧗ input: test...
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
husky - commit-msg hook exited with code 1 (error)
从错误信息可以看出,git代码提交的描述:"test...",不符合规范
配置 less 预处理器
安装所需依赖
yarn add less less-loader -D
less 全局变量
添加 custom.less 文件
@primary-color : #3e94e1;
修改 vite.config.ts 配置
export default defineConfig({
css: {
preprocessorOptions: {
less: {
additionalData: `@import "${resolve(__dirname, 'src/assets/css/custom.less')}";`,
},
},
},
});
引入 normalize.css
配置 vue-router
安装
yarn add vue-router@4
添加 router 目录及其配置
创建 router/index.ts 文件
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'home',
component: () => import('@/views/Home/index.vue'),
},
{
path: '/user',
name: 'user',
component: () => import('@/views/User/index.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
注册 router 实例
import { createApp } from 'vue';
import App from './App.vue';
import router from '@/router/index';
const app = createApp(App);
app.use(router);
app.mount('#app');
使用示例
<template>
<button @click="toPage('home')">to home page</button>
<button @click="toPage('user')">to user page</button>
<br />
<br />
<br />
<router-view></router-view>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
export default defineComponent({
setup() {
const router = useRouter();
const toPage = (name: string) => {
router.push({
name,
});
};
return {
toPage,
};
},
});
</script>
<style></style>
配置 vuex
安装
yarn add vuex@next --save
添加 store 目录及其配置
目录结构
├── store
│ ├── getters.ts
│ ├── index.ts
│ └── modules
│ └── user.ts
创建 userStore Module 实例
import { UserInfoType } from '@/types/user';
import { fetchUsetInfo } from '@/api/user';
const userStore = {
namespaced: true,
state() {
return {
info: {
name: '',
desc: '',
},
};
},
mutations: {
SET_USER_INFO(state: any, info: UserInfoType) {
state.info = info;
},
},
actions: {
getUserInfo({ commit }: any, name: string) {
return new Promise((resolve, reject) => {
fetchUsetInfo(name)
.then((response) => {
commit('SET_USER_INFO', response);
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
},
};
export default userStore;
创建 store
import { createStore } from 'vuex';
import user from './modules/user';
import getters from './getters';
const store = createStore({
modules: {
user,
},
getters,
});
export default store;
注册 store 实例
import { createApp } from 'vue';
import App from './App.vue';
import store from '@/store/index';
const app = createApp(App);
app.use(store);
app.mount('#app');
使用示例
<template>
<div class="user-info">
<div>name: {{ userInfo.name }}</div>
<div>desc: {{ userInfo.desc }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
export default defineComponent({
props: {
count: {
type: String,
default: null,
},
},
setup(props) {
const store = useStore();
const getUserInfo = async (name: string) => {
store.dispatch('user/getUserInfo', name);
};
onMounted(() => {
getUserInfo('yoo');
});
return {
userInfo: computed(() => store.getters.userInfo),
};
},
});
</script>
FAQ
vscode提示:The template root requires exactly one element
原因:
vue3 版本中 template 下支持多个节点,而这在 vue2 版本是不允许的
解决方法:
- Vetur插件修改配置,设置=>首选项=>搜索“eslint”=>取消勾选Vetur下的
Vetur template validation - eslint rules 增加
rules: {
...
'vue/no-multiple-template-root': 0, // template 唯一跟节点校验
},
VSCode中ESLint、Prettier 配置冲突问题
想要修改prettier的默认配置,在.eslintrc.js中覆盖prettier插件的设置。需要配置rules
rules: {
...
'prettier/prettier': ['error', { singleQuote: true, parser: 'flow', semi: true }],
},
VSCode中ESLint、Prettier 配置冲突问题原因及解决方案
Vite alias 引入ts文件报错
tsconfig.json 中增加配置:
"compilerOptions": {
...
"paths": {
"@/*": [ "./src/*" ],
}
},
stylelint 检测 .vue 文件提示 Unknown word CssSyntaxError
原因:
styleliint 不能直接解析 vue 文件中的 stylus ,需要增加额外插件支持
postcss-html#linting-with-stylelint
解决方法:
安装插件:
yarn add stylelint-config-recommended-vue postcss-html postcss-less -D
修改 .stylelintrc 配置:
{
- "extends": ["stylelint-config-standard"],
+ "extends": ["stylelint-config-standard", "stylelint-config-recommended-vue"],
}
修改 package.json 中 lint:style 脚本:
"scripts": {
"lint:style": "stylelint \"src/**/*.(vue|less|css)\" --customSyntax postcss-less"
}
总结
本文主要讲解了如何一步步搭建 vue3.x 项目工程环境,以及如何对代码规范、提交进行约束,基本涵盖了一个项目前期搭建的整个流程。
篇幅有点长,有错误的地方欢迎大家指正哈,谢谢大家的支持!
❤️ ❤️ ❤️ ❤ ❤️