Vite 从 0 到 1 搭建脚手架
注意点
搭建一个基础脚手架时,需要考虑的因素有很多,以下是一些可能需要注意的点:
项目类型:
需要确定项目是什么类型的项目,例如是 Web 应用、移动应用,还是桌面应用。不同类型的应用可能需要不同的工具和框架来支持。
构建工具:
需要选择一个合适的构建工具,例如 webpack、Rollup 等。构建工具能够将源代码转换成可执行的 JavaScript 代码,并且可以支持模块化、代码压缩、代码拆分等功能。
代码风格和规范:
需要定义一套代码风格和规范,以保证整个项目代码风格统一、易于维护。可以使用一些工具来自动化检测和修复代码风格,例如 ESLint、Prettier 等。
多环境配置:
需要根据不同的环境(例如开发环境、测试环境、生产环境等)来配置不同的参数,例如 API 地址、日志级别等。
代码分块:
需要使用代码分块来提高应用性能,将代码按照功能或页面进行分块,可以利用 webpack 的代码分割功能来实现。
异步请求:
需要使用一个合适的异步请求库来发送网络请求,例如 axios、fetch 等。需要对请求进行拦截和响应处理,以统一处理异常情况和成功返回数据。
目录结构:
需要定义一个合理的目录结构,以便组织代码、资源和配置文件。可以按照功能或者文件类型进行分类,例如按照页面、组件、样式、图片等进行分类。
路由定义:
需要定义一个合适的路由结构,以支持应用的页面导航和跳转。可以使用一个路由库(例如 vue-router、react-router 等)来实现路由功能。
状态管理:
如果应用的状态比较复杂,可以使用一个全局状态管理库来管理状态,例如 Vuex、Redux 等。需要将状态的定义、修改、订阅等功能进行封装,以便于应用其他地方使用。
测试:
需要编写测试用例来验证应用的功能和逻辑是否正确。可以使用一些测试工具来进行自动化测试,例如 Jest、Mocha 等。
以上是一些需要注意的点,当然在实际开发中还有很多细节需要关注。在搭建一个基础脚手架时,需要结合具体的需求和场景来进行选择和实现。
该基础架构
- 项目类型:Web Vue3 管理后台
- 构建工具: Vite
- 代码风格和规范:Prettier
- 多环境配置:开发、测试、生产
- 代码分块:代码分割
- 异步请求:axios
- 目录结构:
apis 所有的api存放目录
components 可以复用的组件
layouts 目录结构页面
stores 状态管理
layouts 目录结构页面
utils 工具函数目录
- 路由定义:VueRouter4.0
- 状态管理:Pinia
Vite 官方脚手架
安装环境-初始化 vite-启动项目
需要安装环境
vscode
https://code.visualstudio.com/download
git
nodejs
https://nodejs.org/dist/v18.14.1/node-v18.14.1.pkg
https://nodejs.org/dist/v18.14.1/node-v18.14.1-x64.msi
初始化 vite
npm create vite@latest
custom
启动项目
npm install --registry=https://registry.npmmirror.com
npm run dev
初始化 git-使用 prettier 自动格式化代码
初始化 git
git init
提交代码使用 prettier 自动格式化
https://prettier.io/docs/en/install.html
npm install --save-dev --save-exact prettier
创建 .prettierrc.json 文件
给.prettierrc.json 填写配置
{
"arrowParens": "always", // 要求箭头函数参数用括号括起来。例如,`(x) => x + 1`
"bracketSameLine": true, // 将对象的左括号放在同一行,而不是另起一行。
"bracketSpacing": false, // 对象字面量的大括号之间不加空格
"embeddedLanguageFormatting": "auto", // 是否启用嵌入语言的格式化规则,如HTML、Markdown等
"htmlWhitespaceSensitivity": "css", // HTML文件空格敏感度,可以是"css"或"strict"
"jsxSingleQuote": false, // 在JSX中使用单引号而不是双引号
"printWidth": 120, // 每行最大字符数
"proseWrap": "preserve", // 换行格式,可以是"preserve"或"always"
"quoteProps": "as-needed", // 只在必要时为对象属性添加引号
"insertPragma": false, // 插入Prettier的特殊注释来表明文件已被格式化过
"requirePragma": false, // 要求在文件开头加上Prettier特殊的注释才会格式化文件
"semi": true, // 是否在语句末尾添加分号
"singleQuote": true, // 是否使用单引号而不是双引号
"tabWidth": 2, // 缩进宽度
"trailingComma": "none", // 数组、对象、函数等结尾是否添加逗号,可选值:"none"、"es5"、"all"
"useTabs": false, // 是否使用制表符进行缩进
"vueIndentScriptAndStyle": false // 是否为Vue文件中的<script>和<style>标签缩进
}
使用 npx prettier --write . 自动修复代码使其符合规范
使用 npx prettier --check . 检查有哪些文件需要修复的
安装 husky 和 lint-staged:
npm install --save-dev husky lint-staged
npx husky install
npm pkg set scripts.prepare="husky install"
npx husky add .husky/pre-commit "npx lint-staged"
将以下内容添加到您的 package.json:
{
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
}
或者
{
"lint-staged": {
"**/*": "prettier --check --ignore-unknown"
}
}
目录结构调整-scss-CSSresetORnormalize
目录结构调整
apis 所有的api存放目录
components 可以复用的组件
stores 状态管理
layouts 目录结构页面
utils 工具函数目录
css 预处理器
https://cn.vitejs.dev/guide/features.html#css-pre-processors
npm add -D sass
CSS reset OR normalize
npm install normalize.css
https://ffffee.com/#/frontend/230216-reset.css-vs-normalize.css.md
多环境打包
"build": "run-p type-check build-only",
这个是什么意思
这段代码是一个 NPM script,它使用 npm-run-all 包中的 run-p 命令同时运行两个脚本命令:type-check 和 build-only。这里的 type-check 命令使用了 vue-tsc 工具来进行 TypeScript 类型检查,而 build-only 命令使用 vite build 命令来构建项目。
因此,运行 npm run build 命令将同时运行这两个命令。首先,type-check 命令将执行 TypeScript 类型检查,如果存在类型错误,则会抛出一个错误并停止构建进程。如果类型检查通过,则会继续运行 build-only 命令,这将构建你的项目并生成静态文件,以便在生产环境中使用。
使用这种方式可以在构建项目之前先进行类型检查,以确保项目代码的类型正确性,从而减少运行时错误的出现。同时,将两个命令放在一个 run-p 命令中并行运行,可以加快构建时间,提高开发效率。
多环境配置根据命令打包出来不同的目录包
https://cn.vitejs.dev/guide/env-and-mode.html
.env.development
NODE_ENV=development
.env.production
NODE_ENV=production
.env.qa
NODE_ENV=production
package.json
"build": "run-p type-check build-only",
"qa": "run-p type-check build-qa",
"build-only": "MODE=prod vite build",
"build-qa": "MODE=qa vite build --mode qa",
vite.config.ts
build: {
outDir: process.env.MODE || 'dist'
},
运行打包命令
# 测试1
npm run testing
# 测试2
npm run qa
# 生产|线上环境
npm run build
utils request axios 请求拦截响应拦截
https://axios-http.com/zh/docs/interceptors
npm install axios --save
@/utils/request.ts
/**
* @file request
* @description 封装 Axios 请求库
* @module utils/request
*/
import axios from 'axios';
// Axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_BASE_URL, // url = base url + request url
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // 默认的
// withCredentials: true, // send cookies when cross-domain requests
timeout: 1000 * 60 // request timeout
});
// 请求拦截 设置统一 header
service.interceptors.request.use(
(config) => {
// const userToken = localStorage.getItem('userToken', userToken);
// if (userToken) {
// config.headers['Authorization'] = userToken;
// }
return config;
},
(error: any) => {
return Promise.reject(error);
}
);
// 响应拦截 401 token过期处理
service.interceptors.response.use(
(response) => {
return response;
},
(error: any) => {
return Promise.reject(error);
}
);
export default service;
@/apis/roles.ts
/**
* @file 系统管理 => 角色管理 api
* @description 程序员李钟意
*/
import request from '@/utils/request';
// 角色管理 => 获取用户列表
export function getRoles(params: Record<string, unknown> = {}): Promise<any> {
return request({method: 'get', url: '/api/roles/', params});
}
// 角色管理 => 根据角色id获取用户权限
export function getRolesByIdPermission(roleId: number): Promise<any> {
return request({method: 'get', url: `/api/roles/${roleId}/`});
}
// 角色管理 => 添加用户
export function postRoles(data: any): Promise<any> {
return request({method: 'post', url: '/api/roles/', data});
}
// 角色管理 => 修改用户
export function putRoles(id: number, data: any): Promise<any> {
return request({method: 'put', url: `/api/roles/${id}/`, data});
}
// 角色管理 => 删除用户
export function deleteRoles(id: number): Promise<any> {
return request({method: 'delete', url: `/api/roles/${id}/`});
}
https://jsonplaceholder.typicode.com/todos/
配置反向代理
https://cn.vitejs.dev/config/server-options.html#server-proxy
server: {
proxy: {
// 带选项写法:http://localhost:5173/api/todos -> http://jsonplaceholder.typicode.com/todos
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
// logLevel: 'debug', // 日志级别,设置为debug
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
添加 element-plus 组件库、按需引入配置
https://element-plus.gitee.io/zh-CN/guide/installation
npm install element-plus --save
如果您使用 Volar,请在 tsconfig.json 中通过 compilerOptions.type 指定全局组件类型。
// tsconfig.json
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
按需引入
npm install -D unplugin-vue-components unplugin-auto-import
Vite 打包优化
路由异步组件
defineAsyncComponent
解决路径问题
https://cn.vuejs.org/api/general.html#defineasynccomponent
vite 打出 .gzip 包怎么做
https://github.com/vbenjs/vite-plugin-compression/tree/main#readme
npm i vite-plugin-compression -D
webpack 的 webpack-bundle-analyzer 这个插件,在 vite 里面有差不多的插件吗
https://github.com/btd/rollup-plugin-visualizer
npm install --save-dev rollup-plugin-visualizer
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig, type PluginOption } from 'vite'
export default defineConfig({
plugins: [visualizer() as PluginOption],
})
代码分割的配置
vite.config.ts
rollupOptions: {
output: {
manualChunks(id) {
// @ts-ignore
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
},
chunkFileNames: (chunkInfo) => {
const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/') : [];
const fileName = facadeModuleId[facadeModuleId.length - 2] || '[name]';
return `js/${fileName}/[name].[hash].js`;
}
}
}
这段代码是用来配置 Vite 使用 Rollup 打包时的一些选项。其中,output 选项用来配置输出文件的相关选项,这里包括了两个选项:
manualChunks: 该选项指定了如何对代码进行分块,返回的是一个函数,用于根据代码的 ID 判断应该将代码分块到哪个文件中。这里的代码分块是指将代码分割为多个文件,以便在浏览器中并行下载,提高网页的加载速度。该函数的实现逻辑是,如果代码的 ID 中包含了
node_modules
字符串,那么就将该代码分块到该模块的名称(即node_modules
目录下的第一级目录名)对应的文件中。chunkFileNames: 该选项指定了代码块文件的文件名格式。在该选项的函数中,根据 chunkInfo 对象中的信息,可以生成每个代码块文件的文件名。这里的文件名格式是
js/${fileName}/[name].[hash].js
,其中${fileName}
是从 chunkInfo 中解析出来的文件名,[name]
代表代码块的名称,[hash]
代表该文件的哈希值。
docker 部署前端项目
https://www.docker.com/products/docker-desktop/
Dockerfile
# 使用官方的 node 镜像作为基础镜像
# 从 node:14.19.3 镜像创建一个名为 development 的镜像
FROM node:14.19.3 AS development
# 设置维护者信息
LABEL maintainer="1454598684@qq.com"
# 设置工作目录为 /app
WORKDIR /app
# 将 package.json 和 package-lock.json 复制到容器中
COPY package*.json ./
# 安装所需的包
RUN npm install --registry=https://registry.npmmirror.com
# 将项目中的所有其他文件复制到容器中
COPY . .
# 打包构建
RUN npm run prod
# 从 development 镜像创建一个名为 build 的镜像
FROM development AS build
# 从 nginx:1.21.6 镜像创建一个镜像
FROM nginx:1.21.6
# 从 build 镜像中复制 nginx 配置文件到容器中的 /etc/nginx/conf.d/default.conf
COPY --from=build /app/.nginx/nginx.conf /etc/nginx/conf.d/default.conf
# 设置工作目录为 /usr/share/nginx/html
WORKDIR /usr/share/nginx/html
# 删除该目录中的所有内容
RUN rm -rf ./*
# 从 build 镜像中复制项目文件到容器中的 /usr/share/nginx/html 目录
COPY --from=build /app/prod .
# 设置容器的入口点,运行 nginx 命令
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# 此 Dockerfile 使用了多阶段构建,先在开发环境中构建项目,再在生产环境中运行项目。
# 这样可以保证镜像的小尺寸,同时避免了在生产环境中安装多余的工具。
docker-compose.yml
services:
vite_vue3_demo:
build: .
container_name: vite_vue3_demo
volumes:
- ./prod:/app/prod # 挂载卷 把当前项目根目录的 ./frontend/rd 映射到 容器/app/rd
ports:
- 8000:80
docker-compse build
docker-compse up -d