更多内容可参考我的博客
pnpm i -g create-vite-app
create-vite-app vite-demo
pnpm init @vitejs/app
pnpm i typescript -D
在 项目根目录下创建 typescript 的配置文件 tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictFunctionTypes": false,
"jsx": "react",
"baseUrl": ".",
"allowJs": true, // 允许编译器编译JS,JSX文件
"checkJs": false, // 允许在JS文件中报错,通常与allowJS一起使用
"sourceMap": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"types": ["vite/client", "jest", "pinia-plugin-persist"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": ["node_modules", "dist", "**/SwiperScroll.vue"]
}
把 main.js 修改成 main.ts
在根目录,打开Index.html
<script type="module" src="/src/main.js"></script>
// 修改为:
<script type="module" src="/src/main.ts"></script>
pnpm i -D @vitejs/plugin-vue-jsx
//vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [vue(), vueJsx()],
});
pnpm i vue-router pinia pinia-plugin-persist -S
在根目录下创建 store/index.ts
/*
* @Description: index
* @Autor: huoyou
*/
import type { App } from "vue";
import { createPinia } from "pinia";
import piniaPersist from "pinia-plugin-persist";
export function setupStore(app: App<Element>) {
const pinia = createPinia();
pinia.use(piniaPersist);
app.use(pinia);
}
在根目录下创建 router/index.ts
/*
* @Description: router
* @Autor: huoyou
*/
import type { App } from "vue";
import { useUserStore } from "@/store/useUser";
import { useKeepAliveStore } from "@/store/useKeepAlive";
import {
createRouter,
createWebHashHistory,
RouteRecordRaw,
RouteLocationNormalized,
} from "vue-router";
interface IRoute extends RouteLocationNormalized {
name: string;
}
// 自动注入modules下的路由
const modulesFiles = import.meta.globEager("./modules/**/*.(ts|js)");
let routerList: Array<RouteRecordRaw> = [];
for (const path in modulesFiles) {
routerList = [
...routerList,
...(modulesFiles[path].default || modulesFiles[path]),
];
}
const router = createRouter({
history: createWebHashHistory(), // hash模式: createWebHashHistory, history模式: createWebHistory
routes: routerList,
// @ts-ignore
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
// 后退才有savedPosition
return savedPosition;
} else {
if (to.meta.isKeepAlive) {
to.meta.scrollTop = document.body.scrollTop;
}
return { left: 0, top: to.meta.scrollTop || 0 };
}
},
});
// @ts-ignore
router.beforeEach((to: IRoute, from, next) => {
const userStore = useUserStore();
const keepAliveStore = useKeepAliveStore();
to.meta.isKeepAlive && keepAliveStore.setKeepAlive(to.name);
if (to.matched.some((res) => res.meta.requireAuth)) {
// 判断是否需要登录权限
if (userStore.token) {
next();
} else {
next({
path: `/login`, // 未登录则跳转至login页面
query: { redirect: to.fullPath }, // 登陆成功后回到当前页面,这里传值给login页面,to.fullPath为当前点击的页面
});
}
} else {
next();
}
});
router.afterEach((to, from) => {
// 添加转场效果
const toDepth = to.path.split("/").length;
const fromDepth = from.path.split("/").length;
to.meta.transitionName = toDepth < fromDepth ? "slide-right" : "slide-left";
});
export function setupRouter(app: App<Element>) {
app.use(router);
}
export default router;
main.ts 修改
import { createApp } from "vue";
import App from "./App.vue";
import router, { setupRouter } from "@/router";
import { setupStore } from "@/store";
import requirePlugins from "./plugins";
import "lib-flexible/flexible"; // 用于设置rem基准值
import "vite-plugin-svg-icons/register"; // 注入svg
const app = createApp(App);
// router
setupRouter(app);
// store
setupStore(app);
// ui & global components & directives & other plugins
requirePlugins(app);
router.isReady().then(() => {
app.mount("#app");
});
components/HelloWord.vue 修改
<template>
<h1>{{ msg }}</h1>
<button @click="inCrement"> count is: </button>
<p>{{ count }}</p>
</template>
<script>
import { defineComponent, computed } from 'vue'
import { useStore } from 'vuex'
import { key } from '../store'
export default defineComponent({
name: 'HelloWorld',
props: {
msg: {
type: String,
default: ''
}
},
setup() {
const store = useStore(key)
const count = computed(() => store.state.count)
return {
count,
inCrement: () => store.commit('increment')
}
}
})
</script>
pnpm i vant@next -S
pnpm i vite-plugin-style -D
vite.config.js 中加入
plugins: [
...// 新增
styleImport({
libs: [
{
libraryName: "vant",
esModule: true,
resolveStyle: (name) => `vant/es/${name}/style`,
},
],
}),
];
pnpm i amfe-flexible -S
pnpm i postcss postcss-pxtorem [email protected] -D
根目录下 postcss.config.js
module.exports = {
plugins: {
autoprefixer: {},
// 将单位转换为rem
"postcss-pxtorem": {
rootValue: 37.5, // 值:设计图宽度/20 (目标是将屏幕转化为20rem)
propList: ["*"],
// 该项仅在使用 Circle 组件时需要
// van-circle__layer 原因参见 https://github.com/youzan/vant/issues/1948
selectorBlackList: ["van-circle__layer", ".norem"], // 过滤掉.norem-开头的class,不进行rem转换
},
},
};
main.js 中加入
import "amfe-flexible"; // 用于设置rem基准值
新增.browserslistrc
# @Description: 浏览器兼容
# @Autor: huoyou
# @Date: 2021-07-23 11:08:28
# @LastEditTime: 2021-07-23 11:21:49
> 1%
last 2 versions
not dead
iOS >= 7
Android >= 4.1
IE >= 10
参考链接
pnpm i unplugin-vue-components -D
vite.config.js 配置
import Components from 'unplugin-vue-components/vite';
import {
// AntDesignVueResolver,
// ElementPlusResolver,
VantResolver
} from 'unplugin-vue-components/resolvers';
plugins: [
vue(),
// @ts-ignore
// 新增
Components({
resolvers: [VantResolver()],
extensions: ['vue'],
dts: 'src/types/components.d.ts',
directoryAsNamespace: true
}),
viteSvgIcons({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), './src/assets/icons')],
// Specify symbolId format
symbolId: 'icon-[dir]-[name]'
})
],
pnpm i unplugin-auto-import -D
vite.config.js 配置
// vite.config.js
import AutoImport from 'unplugin-auto-import/vite'
module.exports =
plugins: [
AutoImport({
imports: ['vue', 'vue-router', 'vuex'],
dts: 'src/types/auto-imports.d.ts'
})
]
}
eslint 插件:
pnpm i vue-global-api -D
.eslintrc.js
globals: {
Message: true,
env: true,
// 新增
useRoute: true,
useRouter: true,
useStore: true
},
extends: [
...
'vue-global-api',
]
使用前:
<script setup>
import {(ref, computed, watch)} from 'vue' const counter = ref(0) const
doubled = computed(() => counter.value * 2) watch(doubled, (v) =>{" "}
{console.log("New value: " + v)})
</script>
使用后:
<script setup>
const counter = ref(0) const doubled = computed(() => counter.value * 2)
watch(doubled, (v) => {console.log("New value: " + v)})
</script>
pnpm i eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
eslint: ESLint 的核心代码
prettier:prettier 插件的核心代码
eslint-config-prettier:解决 ESLint 中的样式规范和 prettier 中样式规范的冲突,以 prettier 的样式规范为准,使 ESLint 中的样式规范自动失效
eslint-plugin-prettier:将 prettier 作为 ESLint 规范来使用
eslint-plugin-vue:包含常用的 vue 规范
@typescript-eslint/parser:ESLint 的解析器,用于解析 typescript,从而检查和规范 Typescript 代码
@typescript-eslint/eslint-plugin:包含了各类定义好的检测 Typescript 代码的规范
react
pnpm i eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
注意: 如果不生效时, 考虑是否版本问题, [email protected] [email protected]
vue 中,在根目录下建立 eslint 配置文件: .eslintrc.js
/**
* @module .eslintrc
* @author: huoyou
* @description: eslint配置
*
* 所需插件
* prettier // 规则见 https://prettier.io/docs/en/options.html
* eslint // 规则见 https://cn.eslint.org/docs/rules/
* eslint-plugin-vue 规则见 https://github.com/vuejs/eslint-plugin-vue
* eslint-plugin-prettier // 将prettier作为ESLint规范来使用
* eslint-config-prettier
* @typescript-eslint/eslint-plugin
* @typescript-eslint/parser // ESLint的解析器,用于解析typescript,从而检查和规范Typescript代码
*
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
globals: {
Message: true,
env: true,
useRoute: true,
useRouter: true,
useStore: true,
},
/* 指定如何解析语法。可以为空,但若不为空,只能配该值,原因见下文。*/
parser: "vue-eslint-parser",
/* 优先级低于parse的语法解析配置 */
parserOptions: {
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: "module", // Allows for the use of imports
ecmaFeatures: {
// tsx: true, // Allows for the parsing of JSX
jsx: true,
},
},
extends: [
"vue-global-api",
"eslint:recommended",
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended",
"plugin:jest/recommended",
],
plugins: ["vue"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? 1 : 0,
"no-debugger": process.env.NODE_ENV === "production" ? 1 : 0,
eqeqeq: 2, // 要求使用 === 和 !==
"vue/eqeqeq": 2, // 要求使用 === 和 !==
"no-undef": 2, // 禁用未声明的变量
"vue/require-v-for-key": 1, // 当v-for写在自定义组件上时,它需要同时使用v-bind:key。在其他元素上,v-bind:key也最好写。
"no-unused-vars": 0, // 禁止出现未使用过的变量
"vars-on-top": 0, // 要求所有的 var 声明出现在它们所在的作用域顶部
"prefer-destructuring": 0, // 优先使用数组和对象解构
"no-useless-concat": 1, // 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-escape": 0, // 禁止不必要的转义字符
"consistent-return": 0, // 要求 return 语句要么总是指定返回的值,要么不指定
camelcase: 0, // 强制使用骆驼拼写法命名约定
"no-redeclare": 1, // 禁止多次声明同一变量
"array-callback-return": 1, // 强制数组方法的回调函数中有 return 语句,Array有几种过滤,映射和折叠的方法。如果我们忘记return在这些回调中写入语句,那可能是一个错误。
"default-case": 1, // 要求 switch 语句中有 default 分支
"no-fallthrough": 1, // 禁止 case 语句落空
"no-lonely-if": 1, // 禁止 if 作为唯一的语句出现在 else 语句中.如果一个if陈述是该else块中唯一的陈述,那么使用一个else if表格通常会更清晰。
"no-irregular-whitespace": 1, // 禁止在字符串和注释之外不规则的空白
"prefer-const": 0, // 要求使用 const 声明那些声明后不再被修改的变量.如果一个变量从不重新分配,使用const声明更好。const 声明告诉读者,“这个变量永远不会被重新分配,”减少认知负荷并提高可维护性。
"no-use-before-define": 1, // 禁止在变量定义之前使用它们
"vue/attributes-order": 2, // vue api使用顺序
"vue/order-in-components": [
2,
{
order: [
"el",
"name",
"key",
"parent",
"functional",
["delimiters", "comments"],
["components", "directives", "filters"],
"extends",
"mixins",
["provide", "inject"],
"validate",
"scrollToTop",
"transition",
"loading",
"inheritAttrs",
"model",
["props", "propsData"],
"emits",
"setup",
"asyncData",
"data",
"computed",
"watch",
"created",
"mounted",
"methods",
["template", "render"],
"renderError",
],
},
],
"vue/no-multiple-template-root": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-empty-function": 0,
},
overrides: [
{
files: ["**/__tests__/*.{j,t}s?(x)"],
env: {
mocha: true,
},
},
],
};
react 中,在根目录下建立 eslint 配置文件: .eslintrc.js
/**
* @module .eslintrc
* @author: huoyou
* @description: eslint配置
*
* 所需插件
* prettier // 规则见 https://prettier.io/docs/en/options.html
* eslint // 规则见 https://cn.eslint.org/docs/rules/
* eslint-plugin-react 规则见 https://github.com/yannickcr/eslint-plugin-react
* eslint-plugin-prettier // 将prettier作为ESLint规范来使用
* eslint-config-prettier
* @typescript-eslint/eslint-plugin
* @typescript-eslint/parser // ESLint的解析器,用于解析typescript,从而检查和规范Typescript代码
*
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
/* 优先级低于parse的语法解析配置 */
parserOptions: {
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: "module", // Allows for the use of imports
ecmaFeatures: {
// tsx: true, // Allows for the parsing of JSX
jsx: true,
},
},
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended",
],
plugins: ["react"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? 1 : 0,
"no-debugger": process.env.NODE_ENV === "production" ? 1 : 0,
eqeqeq: 2, // 要求使用 === 和 !==
"vue/eqeqeq": 2, // 要求使用 === 和 !==
"no-undef": 2, // 禁用未声明的变量
"vue/require-v-for-key": 1, // 当v-for写在自定义组件上时,它需要同时使用v-bind:key。在其他元素上,v-bind:key也最好写。
"no-unused-vars": 1, // 禁止出现未使用过的变量
"vars-on-top": 1, // 要求所有的 var 声明出现在它们所在的作用域顶部
"prefer-destructuring": 0, // 优先使用数组和对象解构
"no-useless-concat": 1, // 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-escape": 0, // 禁止不必要的转义字符
"consistent-return": 1, // 要求 return 语句要么总是指定返回的值,要么不指定
camelcase: 0, // 强制使用骆驼拼写法命名约定
"no-redeclare": 1, // 禁止多次声明同一变量
"array-callback-return": 1, // 强制数组方法的回调函数中有 return 语句,Array有几种过滤,映射和折叠的方法。如果我们忘记return在这些回调中写入语句,那可能是一个错误。
"default-case": 1, // 要求 switch 语句中有 default 分支
"no-fallthrough": 1, // 禁止 case 语句落空
"no-lonely-if": 1, // 禁止 if 作为唯一的语句出现在 else 语句中.如果一个if陈述是该else块中唯一的陈述,那么使用一个else if表格通常会更清晰。
"no-irregular-whitespace": 1, // 禁止在字符串和注释之外不规则的空白
"prefer-const": 0, // 要求使用 const 声明那些声明后不再被修改的变量.如果一个变量从不重新分配,使用const声明更好。const 声明告诉读者,“这个变量永远不会被重新分配,”减少认知负荷并提高可维护性。
"no-use-before-define": 1, // 禁止在变量定义之前使用它们
"@typescript-eslint/explicit-module-boundary-types": 0,
"react/react-in-jsx-scope": 0,
"@typescript-eslint/no-var-requires": 0,
},
overrides: [
{
files: ["**/__tests__/*.{j,t}s?(x)"],
env: {
mocha: true,
},
},
],
};
在根目录下建立 prettier 配置文件: .prettierrc.js
/*
* @module .prettierrc
* @author: huoyou
* @description: eslint配置
*/
module.exports = {
printWidth: 100, // 单行输出(不折行)的(最大)长度
tabWidth: 2, // 每个缩进级别的空格数
tabs: false, // 使用制表符 (tab) 缩进行而不是空格 (space)。
semi: true, // 是否在语句末尾打印分号
singleQuote: true, // 是否使用单引号
quoteProps: "as-needed", // 仅在需要时在对象属性周围添加引号
jsxSingleQuote: false, // jsx 不使用单引号,而使用双引号
trailingComma: "none", // 去除对象最末尾元素跟随的逗号
bracketSpacing: true, // 是否在对象属性添加空格
jsxBracketSameLine: true, // 将 > 多行 JSX 元素放在最后一行的末尾,而不是单独放在下一行(不适用于自闭元素),默认false,这里选择>不另起一行
arrowParens: "always", // 箭头函数,只有一个参数的时候,也需要括号
proseWrap: "always", // 当超出print width(上面有这个参数)时就折行
htmlWhitespaceSensitivity: "ignore", // 指定 HTML 文件的全局空白区域敏感度, "ignore" - 空格被认为是不敏感的
vueIndentScriptAndStyle: false, // 在VUE文件中不要缩进脚本和样式标记
stylelintIntegration: true,
endOfLine: "auto",
};
pnpm i stylelint stylelint-scss stylelint-config-standard-scss stylelint-config-prettier -D
根目录下新增 **.stylelintrc.js**
文件
/**
* @module .stylelintrc
* @author: huoyou
* @description: css校验配置
*/
module.exports = {
extends: ["stylelint-config-standard-scss", "stylelint-config-prettier"],
rules: {
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"declaration-block-trailing-semicolon": null,
"declaration-block-semicolon-space-before": "never",
"media-feature-name-no-unknown": null,
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["deep"],
},
],
"rule-empty-line-before": [
"always",
{
ignore: ["after-comment", "first-nested"],
},
],
// style calc中使用v-bind
"function-calc-no-unspaced-operator": null,
},
};
pnpm i @vue/test-utils@next jest vue-jest@next ts-jest @types/jest eslint-plugin-jest -D
创建 jest.config.js
module.exports = {
moduleFileExtensions: ["vue", "js", "ts"],
preset: "ts-jest",
testEnvironment: "jsdom",
transform: {
"^.+\\.vue$": "vue-jest", // vue 文件用 vue-jest 转换
"^.+\\.ts$": "ts-jest", // ts 文件用 ts-jest 转换
},
// 匹配 __tests__ 目录下的 .js/.ts 文件 或其他目录下的 xx.test.js/ts xx.spec.js/ts
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(ts)$",
};
在上面的 jest.config.js
文件中,我们配置只匹配 __tests__
目录下的任意 .ts
文件或其他目录下的 xx.test.ts
/xx.spec.ts
文件进行单元测试。
这里,我们在项目根目录下创建 tests
目录来存储单元测试文件
├── src/
└── tests/ // 单元测试目录
├── Test.spec.ts // Test 组件测试
<template>
<div class="test-container page-container">
<div class="page-title">Unit Test Page</div>
<p>count is: {{ count }}</p>
<button @click="increment">increment</button>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'Vuex',
setup() {
const count = ref<number>(0)
const increment = () => {
count.value += 1
}
return { count, increment }
}
})
</script>
import { mount } from "@vue/test-utils";
import Test from "../src/views/Test.vue";
test("Test.vue", async () => {
const wrapper = mount(Test);
expect(wrapper.html()).toContain("Unit Test Page");
expect(wrapper.html()).toContain("count is: 0");
await wrapper.find("button").trigger("click");
expect(wrapper.html()).toContain("count is: 1");
});
TypeScript 的编译器也会提示 jest 的方法和类型找不到,我们还需把 @types/jest 添加根目录下的 ts.config.json
(TypeScript 配置文件)中:
{
"compilerOptions": {
...
"types": ["vite/client", "jest"]
},
}
添加 eslint-plugin-jest 到 ESLint 配置文件 .eslintrc.js
中
module.exports = {
...
extends: [
...
'plugin:jest/recommended'
],
...
}
在 package.json 命令(scripts)中加入 “test”: “jest” 。 如下:
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "jest"
},
运行 yarn test 查看测试结果
使用 husky 命令在 .husky
目录下自动创建 pre-push
hook 文件,并在此执行单元测试命令 npm run test
npx husky add .husky/pre-push "npm run test $1"
{
pnpm i husky lint-staged @commitlint/cli @commitlint/config-conventional -D
@commitlint/cli
: commitlint 的命令行工具@commitlint/config-conventional
: commitlint 的规则集husky
: 阻止不符合提交规则的 git 记录npx husky-init
修改 ./husky/pre-commit 钩子
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
修改 ./husky/commit-msg
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --config .commitlintrc.js --edit $1
package.json 配置:
"scripts": {
"fix": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue,.html src",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.vue,.html src",
"stylelint": "stylelint --fix .css,.less,.scss src",
},
"lint-staged": {
"src/**/*.{js,jsx,ts,.tsx,vue,html,md}": "eslint --config .eslintrc.js",
"src/**/*.{css,less,scss}": "stylelint --config .stylelintrc.js",
"*.{ts,tsx,js,json,html,yml,css,less,md}": "prettier --write"
}
根目录创建** .commitlintrc.js**
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2,
"always",
[
"feat", // 新功能(feature)
"fix", // 修补bug
"docs", // 文档(documentation)
"style", // 格式(不影响代码运行的变动)
"refactor", // 重构(即不是新增功能,也不是修改bug的代码变动)
"test", // 增加测试
"revert", // 回滚
"config", // 构建过程或辅助工具的变动
"chore", // 其他改动
],
],
"type-empty": [2, "never"], // 提交不符合规范时,也可以提交,但是会有警告
"subject-empty": [2, "never"], // 提交不符合规范时,也可以提交,但是会有警告
"subject-full-stop": [0, "never"],
"subject-case": [0, "never"],
},
};
安装插件 ESlint Prettier-Code formatter Stylelint Vetur Vue VSCode Snippets
注意: vue3 安装 volar、Vue 3 Snippets 插件, 同时 disabled Vetur Vue VSCode Snippets 这两个插件
settings.json配置:
{
// 基础设置
"editor.detectIndentation": false, // 重置 editor.tabSize属性
"editor.tabSize": 2, // 缩进2单元格
"files.autoSave": "afterDelay", // 打开自动保存
"editor.fontSize": 14, // 默认字体大小
"editor.lineHeight": 20, // 行高
"editor.mouseWheelZoom": false, // 通过使用鼠标滚轮同时按住 Ctrl 可缩放编辑器的字体
"editor.wordWrap": "on", // 行太长自动换行
"editor.formatOnPaste": false, // 编辑粘贴自动格式化
"html.format.wrapAttributes": "auto", // 格式化html的标签属性
"terminal.integrated.rendererType": "dom",
"search.followSymlinks": false, // 关闭rg.exe进程
"workbench.startupEditor": "welcomePage", // 图标
"workbench.iconTheme": "vscode-icons",
"workbench.colorTheme": "escook dark soft",
"files.saveConflictResolution": "overwriteFileOnDisk",
"explorer.confirmDelete": false,
// 格式化校验
"javascript.validate.enable": false,
"editor.defaultFormatter": "esbenp.prettier-vscode", // 设置默认格式化工具为prettier
"editor.formatOnSave": false, // 保存自动格式化
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"vetur.format.enable": false,
"vetur.validation.template": false,
"vetur.validation.interpolation": false,
"vetur.validation.style": false,
"vetur.languageFeatures.codeActions": false,
"vetur.validation.script": false,
// "vetur.format.defaultFormatter.html": "js-beautify-html",
// "vetur.format.defaultFormatterOptions": {
// "js-beautify-html": {
// "wrap_attributes": "force-aligned" //属性强制折行对齐
// }
// },
"eslint.format.enable": true, //是否开启vscode的eslint
"eslint.options": {
//指定vscode的eslint所处理的文件的后缀
"extensions": [".js", ".jsx", ".vue", ".react", ".ts", ".tsx"]
},
// eslint 校验准则
"eslint.validate": [
"vue",
"html",
"jsx",
"tsx",
"javascript",
"typescript",
"javascriptreact",
"typescriptreact"
],
"stylelint.autoFix": true,
"stylelint.enable": true,
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"css.lint.unknownAtRules": "ignore",
"scss.lint.unknownAtRules": "ignore"
}
pnpm i conventional-changelog-cli conventional-changelog-custom-config -D
package.json 下加入
"scripts": {
"log": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0 -n ./changelog-option.js"
},
"changelog": {
"bugsUrl": "https://github.com/huoyou/vue_cli3/issues/",
"emojis": true,
"authorName": true,
"authorEmail": true
}
根目录下添加 changelog-option.js
const compareFunc = require("compare-func");
module.exports = {
writerOpts: {
transform: (commit, context) => {
let discard = true;
const issues = [];
commit.notes.forEach((note) => {
note.title = "BREAKING CHANGES";
discard = false;
});
if (commit.type === "feat") {
commit.type = "✨ Features | 新功能";
} else if (commit.type === "fix") {
commit.type = " Bug Fixes | Bug 修复";
} else if (commit.type === "perf") {
commit.type = "⚡ Performance Improvements | 性能优化";
} else if (commit.type === "revert" || commit.revert) {
commit.type = "⏪ Reverts | 回退";
// } else if (discard) {
// return;
} else if (commit.type === "docs") {
commit.type = " Documentation | 文档";
} else if (commit.type === "style") {
commit.type = " Styles | 风格";
} else if (commit.type === "refactor") {
commit.type = "♻ Code Refactoring | 代码重构";
} else if (commit.type === "test") {
commit.type = "✅ Tests | 测试";
} else if (commit.type === "build") {
commit.type = " Build System | 构建";
} else if (commit.type === "ci") {
commit.type = " Continuous Integration | CI 配置";
} else if (commit.type === "chore") {
commit.type = " Chores | 其他更新";
} else {
commit.type = " others | 未命名";
}
if (commit.scope === "*") {
commit.scope = "";
}
if (typeof commit.hash === "string") {
commit.hash = commit.hash.substring(0, 7);
}
if (typeof commit.subject === "string") {
let url = context.repository
? `${context.host}/${context.owner}/${context.repository}`
: context.repoUrl;
if (url) {
url = `${url}/issues/`;
// Issue URLs.
commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => {
issues.push(issue);
return `[#${issue}](${url}${issue})`;
});
}
if (context.host) {
// User URLs.
commit.subject = commit.subject.replace(
/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g,
(_, username) => {
if (username.includes("/")) {
return `@${username}`;
}
return `[@${username}](${context.host}/${username})`;
}
);
}
}
// remove references that already appear in the subject
commit.references = commit.references.filter((reference) => {
if (issues.indexOf(reference.issue) === -1) {
return true;
}
return false;
});
return commit;
},
groupBy: "type",
commitGroupsSort: "title",
commitsSort: ["scope", "subject"],
noteGroupsSort: "title",
notesSort: compareFunc,
},
};