为统一团队前端项目技术栈(针对后续的常规web项目),规范开发环境配置,特定制此前端脚手架项目。
根据具体 工具/包/插件 的使用要求,分为必选,推荐,参考三个级别:
必选:如无特殊情况,要求必须使用该项工具/包/插件推荐:推荐优先选择,不做强制要求参考:针对一些常用工具,脚手架中给出使用范例,仅供参考
当前脚手架的技术选型,具体见下表:
| 类别 | 包/工具 | 使用要求 | 可选的替代 | 备注 |
|---|---|---|---|---|
| IDE | VS Code | 推荐 |
Webstorm / Atom / sublime 等 | 结合vue3,推荐使用vscode + Volar插件的组合 |
| 包管理器 | pnpm | 推荐 |
yarn / npm | pnpm vs npm vs yarn |
| 构建 | vite | 必选 |
-- | -- |
| 框架 | vue 3 | 必选 |
React / Svelte 等 | 允许某些特殊项目需要React或其他框架 |
| 语言 | typescript | 必选 |
-- | -- |
| css预处理器 | sass(scss) | 必选 |
-- | -- |
| 语法检查 | eslint | 必选 |
-- | 具体规则可根据项目情况和个人习惯进行自主配置。推荐vscode的eslint插件(dbaeumer.vscode-eslint) |
| 格式化 | prettier | 必选 |
-- | 具体规则可根据项目情况和个人习惯进行自主配置。推荐vscode的prettier插件(esbenp.prettier-vscode) |
| 样式语法检查 | stylelint | 必选 |
-- | 具体规则可根据项目情况和个人习惯进行自主配置。推荐vscode的stylelint插件(stylelint.vscode-stylelint) |
| git提交预检 | simple-git-hooks + lint-staged | 推荐 |
husky + lingt-staged | simple-git-hooks相比husky的配置更直观简单,性能更稳定 |
| UI库 | element-plus | 必选 |
-- | 根据项目具体情况,如不需要UI库可以不用 |
| 状态管理 | pinia | 必选 |
-- | -- |
| 路由 | vue-router | 必选 |
-- | -- |
| 网络 | axios | 必选 |
-- | -- |
| css框架 | unocss | 推荐 |
windicss / tailwind / twind | unocss兼容windicss和tailwind,同时附带iconify等比较方便的功能; 推荐vscode的unocss插件(antfu.unocss) |
| 图标 | @unocss/preset-icons | 推荐 |
-- | unocss整合的iconify方案,可方便使用众多图标库,以及引入自定义图标 |
| 工具 | lodash-es vueuse | 参考 |
-- | -- |
| 测试 | vitest | 参考 |
-- | -- |
# 项目首次运行前,执行prepare脚本
$ pnpm prepare
# 安装依赖
$pnpm i
# 开发
$pnpm dev
# 打包
$pnpm build- 基本框架,约定技术选型
- cli工具
- 表单通用验证规则范例
- 表单通用交互规则范例
- 统一的UI规范(色系/字体/间距等)
- 更多..
$ pnpm create vite根据命令行提示,选择vue和typescript,然后cd到新建的项目目录下
2.1 Node.js v16.13后引入了corepack作为"包管理器的管理器", 推荐项目使用pnpm作为包管理器。执行以下步骤启用pnpm:
-
启用corepack
# 当前应用激活 $ corepack enable
-
修改package.json,新增指定包管理器
... "packageManager": "pnpm@7.18.1" ...
2.2 更新包到最新版本
$ pnpm install
# 更新包到最新版本
$ pnpm up --latestinstall prettier
$ pnpm add -D prettier新增prettier.config.cjs 文件:
module.exports = {
semi: true,
tabWidth: 2,
bracketSpacing: true,
bracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
arrowParens: 'always',
printWidth: 100,
htmlWhitespaceSensitivity: 'ignore',
endOfLine: 'auto',
vueIndentScriptAndStyle: true
}新增.prettierignore文件
node_modules/
public/
dist/
dist-ssr/
__snapshots__/
*.local
*.css
*.md
pnpm-lock.yaml
.DS_Store
在package.json文件中添加format和format:check脚本:
{
// ...
"scripts": {
// ...
"format": "prettier . --write",
"format:check": "prettier . --check",
// ...格式化当前项目代码:
$ pnpm format安装 @types/node,给 __dirname 和 import * as path from 'path'等加持ts类型支持 :
$ pnpm add -D @types/node
vite中配置环境变量的类型支持, 在src/vite-env.d.ts中添加interface ImportMetaEnv(如果没有vite-env.d.ts或者env.d.ts,在src目录下创建一个)
interface ImportMetaEnv {
// .env中设置的各环境变量,例如VITE_BASE_URL
VITE_BASE_URL: string
...
}配置src的目录别名,方便路径引入,例如 import HelloWorld from '@/components/HelloWorld.vue'.
编辑 vite.config.ts, 添加新的 resolve key, 如下:
import vue from '@vitejs/plugin-vue'
import * as path from 'path'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})通过配置tsconfig,使typescript也可以解析该别名,并支持自动补全路径。在compilerOptions下添加baseUrl和paths配置:
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
// ...安装eslint及相关插件:
$ pnpm add -D eslint eslint-plugin-vue eslint-config-prettier
$ pnpm add -D vue-eslint-parser @typescript-eslint/parser
$ pnpm add -D @typescript-eslint/eslint-plugin新增 .eslintrc.cjs 文件:
module.exports = {
env: { node: true },
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'prettier',
],
parser: 'vue-eslint-parser',
plugins: ['@typescript-eslint'],
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
rules: {
'@typescript-eslint/explicit-function-return-type': 'warn',
},
globals: {
defineEmits: 'readonly',
defineProps: 'readonly',
},
};添加以下内容到 package.json 文件:
{
// ...
"scripts": {
// ...
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src/",
"lint:check": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src/",
// ...给src/vite-env.d.ts 添加eslint-disable:
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
const component: DefineComponent<{}, {}, any>
export default component
}执行pnpm lint进行代码格式化,并修复可能出现的语法错误
$ pnpm lintinstall packages
$ pnpm add -D sass postcss postcss-scss postcss-html stylelint stylelint-scss stylelint-config-standard-scss stylelint-config-recommended-vue stylelint-config-recess-order stylelint-config-css-modules
# 各package的功能说明:
# postcss // peer dependency of postcss-scss
# postcss-scss // peer dependency of stylelint-config-recommended-scss
# postcss-html // peer dependency of stylelint-config-recommended-vue
# stylelint-scss // lint-rules for scss
# stylelint-config-standard-scss // standard lint-rules configure for SCSS
# stylelint-config-recommended-vue // add overrides for .Vue files
# stylelint-config-recess-order // use the recess order for properties
# stylelint-config-css-modules // configure for CSS Modules methodology
# stylelint-config-prettier // turn off any rules that conflict with Prettier新增stylelint.config.cjs文件:
module.exports = {
plugins: ['stylelint-scss'],
extends: [
'stylelint-config-standard-scss', // configure for SCSS
// 'stylelint-config-recommended-vue', // add overrides for .Vue files
'stylelint-config-recommended-vue/scss', // add overrides for .Vue files
'stylelint-config-recess-order', // use the recess order for properties
'stylelint-config-css-modules', // configure for CSS Modules methodology
// stylelint v15 nologner need stylelint-config-prettier
// 'stylelint-config-prettier', // turn off any rules that conflict with Prettier
],
rules: {
'rule-empty-line-before': null,
'comment-empty-line-before': null,
'color-hex-length': null,
'custom-property-empty-line-before': null,
'selector-class-pattern': null,
'value-keyword-case': null,
// 'at-rule-no-unknown': [true, { ignoreAtRules: ['apply'] }],
'at-rule-no-unknown': null,
'scss/at-rule-no-unknown': true,
// 'function-no-unknown': [true, { ignoreFunctions: ['theme'] }],
'function-no-unknown': null,
'scss/function-no-unknown': true,
'scss/dollar-variable-pattern': null,
'scss/dollar-variable-empty-line-before': null,
'scss/selector-no-redundant-nesting-selector': true,
},
ignoreFiles: ['dist/**/*'],
};编辑package.json,添加stylelint脚本
{
// ...
"scripts": {
// ...
"lint:style": "stylelint ./src/**/*.{vue,css,scss} --fix",
"lint:style:check": "stylelint ./src/**/*.{vue,css,scss}",
// ...修改vscode配置.vscode/settings.json,禁用自带的样式检查器,使用stylelint进行css和scss的样式检查。
editor.codeActionsOnSave配置保存文件时进行stylelint和eslint自动格式化和错误修复。
{
// ...
"css.validate": false,
"scss.validate": false,
"less.validate": false,
"stylelint.snippet": ["css", "less", "postcss", "scss", "vue"],
"stylelint.validate": ["css", "less", "postcss", "scss", "vue"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
}
//...
}注意保持vscode-stylelint插件默认设置,避免无法解析vue文件而报Unknown word (CssSyntaxError)错误。
执行stylelint,修复可能的样式语法错误
$ pnpm lint:style编辑package.json,添加pre-commit和lint-staged脚本
{
...
"simple-git-hooks": {
"pre-commit": "npx lint-staged"
},
"lint-staged": {
"*.{vue,js,ts,jsx,tsx,md,json}": "eslint --ignore-path .gitignore --fix",
"*.{vue,js,ts,jsx,tsx,md,json,scss,css}": "prettier . --write",
"*.{scss,css,vue}": "stylelint --fix"
}
...
}pnpm add -D simple-git-hooks lint-staged
npm pkg set scripts.prepare="simple-git-hooks"
pnpm prepareinstall
$pnpm add vue-router新增router文件src/router/index.ts:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/modules/home/views/Home.vue';
export const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
];
export const router = createRouter({
history: createWebHistory(),
routes,
});编辑 src/main.ts 文件:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { router } from './router'
createApp(App).use(router).mount('#app')编辑App.vue文件,添加RouterView组件
$ pnpm add pinia安装&配置详见pinia官方文档
$ pnpm add element-plus根据官方文档,按需选择完整引入或者按需引入,完整引入时build后element相关文件大小约0.8M。当前脚手架项目选择完整引入。
// tsconfig.json
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}安装unocss和@iconify/json。@iconify/json包含了iconify所有的图标库,你也可以只安装需要用的图标库:@iconify-json/[the-collection-you-want]
$ pnpm add -D unocss @iconify/json添加unocss插件到vite.config.ts
...
import Unocss from 'unocss/vite';
import {
presetAttributify,
presetIcons,
presetUno,
transformerDirectives,
transformerVariantGroup,
} from 'unocss';
export default defineConfig({
plugins: [
vue(),
Unocss({
presets: [
presetUno(),
presetAttributify(),
presetIcons({
scale: 1.2,
warn: true,
}),
],
transformers: [transformerDirectives(), transformerVariantGroup()],
}),
]
...
});在vite入口文件main.ts中引入unocss
import { createApp } from 'vue';
...
import 'uno.css'; // add this line
...
import App from '@/App.vue';
createApp(App).mount('#app');$ pnpm add axios安装lodash-es
$ pnpm add lodash-es
$ pnpm add -D @types/lodash-es安装@vueuse/core 和 @vueuse/head
$ pnpm add @vueuse/core @vueuse/head添加如下内容到.gitignore:
# Vitest related files
__snapshots__/
coverage/安装vitest:
$ pnpm add -D vitest @vitest/coverage-c8 @vue/test-utils jsdom添加如下脚本到package.json:
{
// ...
"scripts": {
// ...
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:update": "vitest --update",
"test:watch": "vitest",
// ...编辑tsconfig.json,添加类型"vitest/globals":
{
"compilerOptions": {
// ...
"types": ["element-plus/global", "vitest/globals"]
}
}添加如下内容到vite.config.ts,注意第一行添加reference标签:
/// <reference types="vitest" />
// ...
test: {
environment: 'jsdom',
globals: true,
}
})创建文件test/setup.ts:
import { vi } from 'vitest'
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
})
vi.mock('axios', () => {
return {
default: {
create() {
return {
get() {
return Promise.resolve({})
},
post() {
return Promise.resolve({})
}
}
}
}
}
})编辑vite.config.ts,配置刚创建的setup文件:
{
// ...
test: {
// ...
setupFiles: [resolve(__dirname, 'test/setup.ts')]
}
}