Vite4.3+Typescript+Vue3+Pinia 最新搭建企业级前端项目
先附上源码地址:github.com/cwjbjy/vite…
1. 初始化项目
1. node 版本要求
使用 node16 以下版本在运行 vue-tsc 时会报错。node 版本不足 16 的,可先升至 nodev16.20.0
2. vscode 插件安装
Volar(确保开发 vue2 时的 Vetur 插件禁用),TypeScript Vue Plugin,Prettier - Code formatter,ESlint
3. 创建项目
npm create vite@latest vite-vue-ts-seed -- --template vue-ts
提示:vscode 在文件中通过鼠标右击,选择使用…格式化文档,将 prettier 插件设为默认值
4. 安装项目依赖
yarn
2. 配置 tsconfig
1. 修改 tsconfig.json
{
"compilerOptions": {
"target": "ESNext", // 将代码编译为最新版本的 JS
"module": "ESNext", // 使用 ES Module 格式打包编译后的文件
"moduleResolution": "node", // 使用 Node 的模块解析策略
"lib": ["ESNext", "DOM", "DOM.Iterable"], // 引入 ES 最新特性和 DOM 接口的类型定义
"skipLibCheck": true, // 跳过对 .d.ts 文件的类型检查
"resolveJsonModule": true, // 允许引入 JSON 文件
"isolatedModules": true, // 要求所有文件都是 ES Module 模块。
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"jsx": "preserve", // 保留原始的 JSX 代码,不进行编译
"strict": true, // 开启所有严格的类型检查
"noUnusedLocals": true, //报告未使用的局部变量的错误
"noUnusedParameters": true, //报告函数中未使用参数的错误
"noFallthroughCasesInSwitch": true, //确保switch语句中的任何非空情况都包含
"esModuleInterop": true, // 允许使用 import 引入使用 export = 导出的内容
"allowJs": true, //允许使用js
"baseUrl": ".", //查询的基础路径
"paths": { "@/*": ["src/*"], "#/*": ["types/*"] } //路径映射,配合别名使用
},
//需要检测的文件
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }] //为文件进行不同配置
}
2. 修改 tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
3. 新建 src/typings.d.ts(在 src 文件夹下新建)
//声明window上自定义属性,如事件总线
declare interface Window {
eventBus: any;
}
//声明.vue文件
declare module "*.vue" {
import { DefineComponent } from "vue";
const component: DefineComponent<object, object, any>;
export default component;
}
提示:遇到 ts 报错,有些时候是配置未生效,可以重启 vscode 或 ts 服务(vscode 快捷键 ctrl+shift+p 调出命令行,输入 Restart TS Server)
4. 修改 package.json
"scripts": {
"ts": "vue-tsc --noEmit",
},
运行 yarn run ts 即可查看文件是否有 ts 类型错误
3. 配置路径别名
1. 安装
yarn add @types/node -D
2. 修改 vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path"; //这个path用到了上面安装的@types/node
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
//这里进行配置别名
resolve: {
alias: {
"@": path.resolve("./src"), // @代替src
"#": path.resolve("./types"), // #代替types
},
},
});
3. ts 路径映射
路径映射在上述修改 tsconfig.json 时通过 baseUrl 与 paths 属性已完成
4. 配置 ESLint 和 prettier
1. 安装
//eslint 安装
yarn add eslint -D
//eslint vue插件安装
yarn add eslint-plugin-vue -D
//eslint 识别ts语法
yarn add @typescript-eslint/parser -D
//eslint ts默认规则补充
yarn add @typescript-eslint/eslint-plugin -D
//eslint prettier插件安装
yarn add eslint-plugin-prettier -D
//用来解决与eslint的冲突
yarn add eslint-config-prettier -D
//安装prettier
yarn add prettier -D
2. 新建 .eslintrc
{
"env": {
"browser": true,
"node": true,
"es2021": true
},
"parser": "vue-eslint-parser",
"extends": [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"eslint-config-prettier"
],
"parserOptions": {
"ecmaVersion": "latest",
"parser": "@typescript-eslint/parser",
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"plugins": ["vue", "@typescript-eslint", "prettier"],
"rules": {
"vue/multi-word-component-names": "off", // 禁用vue文件强制多个单词命名
"@typescript-eslint/no-explicit-any": ["off"], //允许使用any
"@typescript-eslint/no-this-alias": [
"error",
{
"allowedNames": ["that"] // this可用的局部变量名称
}
],
"@typescript-eslint/ban-ts-comment": "off", //允许使用@ts-ignore
"@typescript-eslint/no-non-null-assertion": "off", //允许使用非空断言
"no-console": [
//提交时不允许有console.log
"warn",
{
"allow": ["warn", "error"]
}
],
"no-debugger": "warn" //提交时不允许有debugger
}
}
rules 更多配置:eslint.org/docs/latest…
3. 新建 .eslintignore
# eslint 忽略检查 (根据项目需要自行添加)
node_modules
dist
4. 新建 .prettierrc
{
"endOfLine": "auto",
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"bracketSpacing": true
}
5. 新建 .prettierignore
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
6. 重启 vscode 使配置生效
7. 配置 package.json
可以看到 App.vue 文件在 import 处飘红,因为结尾没有使用分号
修改 package.json
"scripts": {
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx --max-warnings 0"
},
运行 yarn run lint,可以看到上述 eslint(prettier/prettier)问题都将被修复
5. 配置 husky、lint-staged、@commitlint/cli
husky:一个为 git 客户端增加 hook 的工具
lint-staged:仅对 Git 代码暂存区文件进行处理,配合 husky 使用
@commitlint/cli:让 commit 信息规范化
1. 创建 git 仓库
git init
2. 安装
//目前husky为8.0.3版本
yarn add husky -D
yarn add lint-staged -D
yarn add @commitlint/cli @commitlint/config-conventional -D
3. 运行
//生成 .husky 的文件夹
npx husky install
// 添加 hooks,会在 .husky 目录下生成一个 pre-commit 脚本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"
// 添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
4. 修改 package.json
"lint-staged": {
"src/**/*.{vue,js,jsx,ts,tsx,json}": [
"yarn run lint",
"prettier --write"
]
}
5. 新建 commitlint.config.cjs
提示:由于 package.json 的"type": “module”,需将 commonjs 文件显示声明为.cjs
module.exports = {
extends: ["@commitlint/config-conventional"],
};
提交格式:
git commit -m <type>[optional scope]: <description> //注意冒号后面有空格
- type:提交的类型(如新增、修改、更新等)
- optional scope:涉及的模块,可选
- description:任务描述
type 类型:
类别 | 含义 |
---|---|
feat | 新功能 |
fix | 修复 bug |
style | 样式修改(UI 校验) |
docs | 文档更新 |
refactor | 重构代码(既没有新增功能,也没有修复 bug) |
perf | 优化相关,比如提升性能、体验 |
test | 增加测试,包括单元测试、集成测试等 |
build | 构建系统或外部依赖项的更改 |
ci | 自动化流程配置或脚本修改 |
revert | 回退某个 commit 提交 |
6. 示范(非规范提交,将提交失败)
git commit -m 'feat: 增加 xxx 功能'
git commit -m 'bug: 修复 xxx 功能'
6. vscode 保存自动格式化
在.vscode 下新建 settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
之后每次文件有修改,保存时,都会自动格式化
7. 配置路由
8. 配置 pinia
受限于篇幅原因,我把路由和 pinia 单独发了两篇文章
9. 配置 scss
1. 安装
yarn add sass -D
2. 配置全局 scss 样式文件
新建 src/assets/styles/index.scss
$test-color: red;
配置 vite.config.ts
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/assets/styles/index.scss";',
},
},
},
组件中使用
<style lang="scss">
body {
color: $test-color;
}
</style>
重启项目,查看效果
10. 配置 element-plus
1. 安装
yarn add element-plus @element-plus/icons-vue
2. 按需引入插件
yarn add unplugin-vue-components unplugin-auto-import -D
3. 配置 vite.config.ts
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
});
4. 在 main.ts 引入
注意:按需引入时 element-plus 不需要在 main.ts 中引入,插件会自动挂载处理,可以在全局直接使用
这里在 main.ts 中引入 element-plus 样式与图标
import * as ElementPlusIconsVue from "@element-plus/icons-vue"; //引入图标
import "element-plus/dist/index.css"; //引入样式
//...
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
11. 配置环境变量
新建 .env(所有环境生效).env.development(开发环境配置) .env.production(生产环境配置)
1. 定义变量
以 VITE_ 为前缀定义变量
VITE_BASE_URL = "//127.0.0.1:9000/api";
2. 定义变量 ts 类型
修改 vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
3. 使用变量
import.meta.env.VITE_BASE_URL;
12. 配置 axios
1. 安装
yarn add axios
2. 新建 utils/axios.ts
import axios from "axios";
/*
* 创建实例
* 与后端服务通信
*/
const HttpClient = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
});
/**
* 请求拦截器
* 功能:配置请求头
*/
HttpClient.interceptors.request.use(
(config) => {
const token = "222";
config.headers.authorization = "Bearer " + token;
return config;
},
(error) => {
console.error("网络错误,请稍后重试");
return Promise.reject(error);
}
);
/**
* 响应拦截器
* 功能:处理异常
*/
HttpClient.interceptors.response.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
export default HttpClient;
3. 新建 apis/model/userModel.ts
定义请求参数类型与返回数据类型
//定义请求参数
export interface ListParams {
id: number; //用户id
}
export interface RowItem {
id: number; //文件id
fileName: string; //文件名
}
//定义接口返回数据
export interface ListModel {
code: number;
data: RowItem[];
}
4. 新建 apis/user.ts
import HttpClient from "../utils/axios";
import type { ListParams, ListModel } from "./model/userModel";
export const getList = (params: ListParams) => {
return HttpClient.get < ListModel > ("/list", { params });
};
5. 使用
<script setup lang="ts">
import { getList } from "@/apis/user";
getList({ id: 2 });
</script>
在 chrome 的 network 中可以看到请求了http://127.0.0.1:9000/api/list?id=2
13. 配置端口与代理
修改 vite.config.ts
export default defineConfig({
...
server: {
host: '0.0.0.0',
port: 8080,
open: true,
https: false,
proxy: {
'/api': {
target: '要代理的地址',
changeOrigin: true,
ws: true,
rewrite: (path: string) => path.replace(/^\/api/, ''),
},
},
},
});
14. 打包配置
修改 vite.config.ts
1. 分包
通过() => import()形式加载的组件会自动分包,第三方插件需手动分包
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'pinia', 'vue-router'],
elementIcons: ['@element-plus/icons-vue'],
},
},
},
},
2. 生成.gz 文件
- 安装
yarn add vite-plugin-compression -D
- 修改 vite.config.ts
默认情况下插件在开发 (serve) 和生产 (build) 模式中都会调用,使用 apply 属性指明它们仅在 ‘build’ 或 ‘serve’ 模式时调用
这里打包生成 .gz 插件仅需在打包时使用
import viteCompression from 'vite-plugin-compression'
plugins: [
//...
{
...viteCompression(),
apply: 'build',
},
],
3. js 和 css 文件夹分离
export default defineConfig({
build: {
rollupOptions: {
output: {
chunkFileNames: "static/js/[name]-[hash].js",
entryFileNames: "static/js/[name]-[hash].js",
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
},
},
},
});
15. vite 与 webpack 使用区别
1. 静态资源处理
webpack:使用 require 处理
vite:使用 new URL(url, import.meta.url).href 处理
import.meta.url 包含了对于目前 ES 模块的绝对路径
new URL(url [, base]) 构造函数返回一个新创建的 URL 对象,如果 url 是相对 URL,则会将 base 用作基准 URL。如果 url 是绝对 URL,则无论参数 base 是否存在,都将被忽略
new URL("../assets/images/home.png", import.meta.url).href;
//在src/constants/menus.ts下引入图片
//import.meta.url返回 http://localhost:8080/src/constants/menus.ts
//new URL(...).href返回
//http://localhost:8080/src/assets/images/home.png
2. 组件自动化注册
webpack
<script>
const path = require("path");
//读取@/components/BaseEchartsModel下所有.vue文件
const files = require.context("@/components/BaseEchartsModel", false, /\.vue$/);
const modules = {};
files.keys().forEach((key) => {
const name = path.basename(key, ".vue");
modules[name] = files(key).default || files(key);
});
export default {
name: "BaseEcharts",
components: modules,
};
</script>
vite
<script setup lang="ts">
//读取@/components/BaseEchartsModel下所有.vue文件
import.meta.glob("@/components/BaseEchartsModel/*.vue");
</script>