环境搭建
本文档将从0到1记录该项目搭建过程,不定时更新内容
注意: 由于本项目是我的个人学习项目,可能内容不一定完全正确,有错误或者更好的设计方案欢迎评论
本项目环境
PS C:\Users\周文锋\Desktop\frame_front-master\frame_front-master> node -v
v20.13.1
PS C:\Users\周文锋\Desktop\frame_front-master\frame_front-master> pnpm -v
9.1.2
初始化项目
- 按官方教程搭建一个vite项目
- 删除掉多于文件,我们后续会重新搭建(
有报错文件都删除就行后续我们会一一介绍)
- package.json
{
"name": "frame_front",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.31"
},
"devDependencies": {
"@types/node": "^20.14.11",
"@vitejs/plugin-vue": "^5.0.5",
"typescript": "^5.2.2",
"vite": "^5.3.4",
"vue-tsc": "^2.0.24"
}
}
配置tsconfig
当我们启动vite项目的时候,ts编译器默认会读取当前目录 (输入命令行的目录) 下的tsconfig.json文件中的配置。
{
// 编译器的选项
"compilerOptions": {
"target": "ESNext", // 设置编译后的目标版本
// 启用此选项后,TypeScript 编译器将允许你使用 # 语法来声明类字段,这些字段将被视为私有的,只能在类的内部访问。
"useDefineForClassFields": true,
// 指定要使用的模块化规范
"module": "ESNext",
"moduleResolution": "Node", // 决定如何处理模块
"types": ["vite/client"], //指定全局组件类型
/* 严格的类型检查选项*/
"strict": true /* 启用所有严格类型检查选项。 */,
"noImplicitAny": false /* 对隐含的` any `类型的表达式和声明抛出错误。*/,
// "strictNullChecks": true, /* 启用严格的null检查 */
// "strictFunctionTypes": true, /* 启用函数类型的严格检查。*/
// "strictBindCallApply": true, /* 在函数上启用严格的` bind `、` call `和` apply `方法。 */
// "strictPropertyInitialization": true, /* 在类中启用属性初始化的严格检查。 */
// "noImplicitThis": true, /* 在“这个”表达式上提高错误,并带有隐含的“任何”类型。*/
// "alwaysStrict": true, /* 以严格模式解析,并对每个源文件发出“use strict”。 */
"removeComments": false, // 编译是否移除注释
"jsx": "preserve", // 指定jsx代码的生成
"resolveJsonModule": true, // 将json文件视为模块,支持ES6风格的import语法导入
"isolatedModules": true, // 将每个文件作为单独的模块(与“ts.transpileModule”类似)。
"esModuleInterop": true, // 提供对CommonJs和ESM模块的导入方式的兼容性
//指定要使用的包
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
// 不生成输出文件 开启这个选项后 tsc不会将ts文件编译出来输出js文件
"noEmit": true,
"noEmitOnError": false, //当有错误时,并生成编译后的文件
"baseUrl": "./",
"paths": {
"@": ["src"],
"@/*": ["src/*"]
}
},
"files": ["./vite.config.ts"],
// 用来指定需要编译的ts文件
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"build/**/*.ts",
"build/**/*.d.ts",
],
//排除文件
"exclude": ["node_modules", "dist", "**/*.js"]
}
步骤:
(1)读取配置文件中的include和file 中指定文件进行编译,并排除exclude中指定的文件不进行编译
"files": ["./vite.config.ts"],
// 用来指定需要编译的ts文件
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"build/**/*.ts",
"build/**/*.d.ts",
],
"exclude": ["node_modules", "dist", "**/*.js"] //排除文件
(2) 读取compilerOptions中配置的编译器选项规则
{
// 编译器的选项
"compilerOptions": {
"target": "ESNext", // 设置编译后的目标版本
// 启用此选项后,TypeScript 编译器将允许你使用 # 语法来声明类字段,这些字段将被视为私有的,只能在类的内部访问。
"useDefineForClassFields": true,
// 指定要使用的模块化规范
"module": "ESNext",
"moduleResolution": "Node", // 决定如何处理模块
"types": ["vite/client"], //指定全局组件类型
/* 严格的类型检查选项*/
"strict": true /* 启用所有严格类型检查选项。 */,
"noImplicitAny": false /* 对隐含的` any `类型的表达式和声明抛出错误。*/,
// "strictNullChecks": true, /* 启用严格的null检查 */
// "strictFunctionTypes": true, /* 启用函数类型的严格检查。*/
// "strictBindCallApply": true, /* 在函数上启用严格的` bind `、` call `和` apply `方法。 */
// "strictPropertyInitialization": true, /* 在类中启用属性初始化的严格检查。 */
// "noImplicitThis": true, /* 在“这个”表达式上提高错误,并带有隐含的“任何”类型。*/
// "alwaysStrict": true, /* 以严格模式解析,并对每个源文件发出“use strict”。 */
"removeComments": false, // 编译是否移除注释
"jsx": "preserve", // 指定jsx代码的生成
"resolveJsonModule": true, // 将json文件视为模块,支持ES6风格的import语法导入
"isolatedModules": true, // 将每个文件作为单独的模块(与“ts.transpileModule”类似)。
"esModuleInterop": true, // 提供对CommonJs和ESM模块的导入方式的兼容性
//指定要使用的包
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
// 不生成输出文件 开启这个选项后 tsc不会将ts文件编译出来输出js文件
"noEmit": true,
"noEmitOnError": false, //当有错误时,并生成编译后的文件
"baseUrl": "./",
"paths": { //别名
"@": ["src"],
"@/*": ["src/*"]
}
},
}
配置vite.config.js文件
当以命令行方式运行 vite 时,Vite 会自动解析项目根目录下名为 vite.config.js的文件
注意:即使项目没有在 package.json 中开启 type: "module",Vite 也支持在配置文件中使用 ESM 语法。这种情况下,配置文件会在被加载前自动进行预处理。
你可以显式地通过 --config 命令行选项指定一个配置文件(相对于 cwd 路径进行解析)
vite --config my-config.js
vite.config.js
import { ConfigEnv, defineConfig, loadEnv, UserConfig } from 'vite'
import {resolve } from 'path'
import { createVitePlugins } from './build/plugins';
import { createProxy } from './build/proxy';
import { wrapperEnv } from './build/getEnv';
// https://vitejs.dev/config/
export default defineConfig( ({ mode } : ConfigEnv) : UserConfig => {
// 当前node进程的工作目录
const root = process.cwd();
// 根据当前工作目录中的 `mode` 加载 .env 文件
// 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
const env = loadEnv(mode, root,'');
// 对环境变量进行格式处理优化
const viteEnv = wrapperEnv(env);
return {
// 根目录
base: viteEnv.VITE_PUBLIC_PATH,
root,
// 配置别名
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
// 反向代理与跨域配置
server: {
host: '0.0.0.0',
port: viteEnv.VITE_PORT,
open: viteEnv.VITE_OPEN,
cors: true, //为开发服务器配置CORS,默认启动并允许任何源
// 反向代理配置
proxy: createProxy(viteEnv.VITE_PROXY),
},
plugins: createVitePlugins(viteEnv),
// 打包构建规则
build: {
// 指定输出路径
outDir: 'dist',
// minify: 'esbuild',
// esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log
minify: "terser",
terserOptions: {
compress: {
drop_console: viteEnv.VITE_DROP_CONSOLE,
drop_debugger: true
}
},
sourcemap: false,
// 在构建新输出文件时 清除原输出目录中的所有文件
emptyOutDir: true,
// 禁用 gzip 压缩大小报告,可略微减少打包时间
reportCompressedSize: false,
// 规定触发警告的 chunk 大小
chunkSizeWarningLimit: 2000,
rollupOptions: {
output: {
// Static resource classification and packaging
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
},
},
},
};
})
在根目录新建环境变量配置文件.env
====================.env===========================
# title 标题
VITE_GLOB_APP_TITLE = 李太白
# 本地运行端口号
VITE_PORT = 8848
# 启动时自动打开浏览器
VITE_OPEN = false
# 打包后是否生成包分析文件
VITE_REPORT = false
====================.env.development===========================
# 本地环境
VITE_USER_NODE_ENV = development
# 公共基础路径
VITE_PUBLIC_PATH = /
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE = history
# 打包时是否删除 console
VITE_DROP_CONSOLE = true
# 是否开启 VitePWA
VITE_PWA = false
# 开发环境接口地址
VITE_API_URL = /api
# 开发环境跨域代理,支持配置多个
VITE_PROXY = [["/api","http://127.0.0.1:8888"]]
关于vite中环境变量相关知识可以看官网中的介绍环境变量
执行步骤
- 当我们以pnpm run dev命令运行
vite时,Vite 会自动解析项目根目录(index.html所在的目录下名为 vite.config.js的文件,及上述配置文件 - 首先通过第12行
loadEnv方法可以获取配置文件信息
- 通过自定义方法对获取的配置文件进行格式化处理
// 对环境变量进行格式转换
export function wrapperEnv(envConf: Recordable): ViteEnv {
const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') realName = Number(realName);
if (envName === 'VITE_PROXY') {
try {
realName = JSON.parse(realName);
} catch (error) {}
}
ret[envName] = realName;
}
return ret;
}
这里我们使用了ts的范型,需要在tsconfig中include指定的编译文件中进行类型声明
// 配置对象的类型声明
declare interface ViteEnv {
// 路由模式
VITE_ROUTER_MODE: 'hash' | 'history';
// 公共路径
VITE_PUBLIC_PATH: string;
// 端口
VITE_PORT: number;
// 启动开发服务时是否自动打开浏览器
VITE_OPEN: boolean;
// 跨域声明
VITE_PROXY: [string, string][];
// 标题
VITE_GLOB_APP_TITLE: string;
}
/* Vite */
declare type Recordable<T = any> = Record<string, T>;
- 第32行 createProxy()方法配置反向代理
import type { ProxyOptions } from 'vite';
type ProxyItem = [string, string];
type ProxyList = ProxyItem[];
type ProxyTargetList = Record<string, ProxyOptions>;
/**
* 创建代理,用于解析 .env.development 代理配置
* @param list
*/
export function createProxy(list: ProxyList = []) {
const ret: ProxyTargetList = {};
for (const [prefix, target] of list) {
const httpsRE = /^https:\/\//;
const isHttps = httpsRE.test(target);
// https://github.com/http-party/node-http-proxy#options
ret[prefix] = {
target: target, // 要使用 url 模块解析的 url 字符串
changeOrigin: true, // 当设置为 true 时,它指示代理服务器在转发请求时修改 Origin 请求头,以匹配目标服务器的主机名。
ws: true,
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
// https is require secure=false
...(isHttps ? { secure: false } : {}),
};
}
return ret;
/**
{ 22:04:23
'/api': { // 接口地址代理
target: 'http://127.0.0.1:8888', // 接口的域名
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
ws: true,
rewrite: [Function: rewrite]
}
}
*/
}
- 第35行 通过createVitePlugins()方法配置vite插件
import { PluginOption } from 'vite';
import vue from '@vitejs/plugin-vue';
/**
* 创建 vite 插件
* @param viteEnv
*/
export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => {
const { VITE_GLOB_APP_TITLE } = viteEnv;
return [
vue(),
];
};
vite-plugin-html 插件
vite-plugin-html 是一个 Vite 插件,用于简化 HTML 模板的处理。它允许你在 Vite 项目中更轻松地使用 HTML 文件,支持以下特性:
- 基础模板支持:允许你指定一个基础 HTML 模板,其他页面的 HTML 模板将继承这个基础模板。
- 多页面支持:可以配置多个入口 HTML 文件,每个文件对应一个页面。
- 模板变量替换:支持在 HTML 中使用模板变量,这些变量可以从 Vite 的构建配置中获取。
- 环境变量注入:可以将环境变量注入到 HTML 中,例如
VITE_APP_开头的环境变量。 - CSS 和 JS 资源引用:自动处理 CSS 和 JS 资源的引用路径,确保资源正确加载。
- 自定义模板函数:允许你使用自定义函数来修改 HTML 模板。
安装
vite-plugin-html
首先,你需要安装 vite-plugin-html 插件:
npm install vite-plugin-html --save-dev
配置
vite-plugin-html
然后,在createVitePlugins()方法中添加该配置
import { PluginOption } from 'vite';
import vue from '@vitejs/plugin-vue';
import { createHtmlPlugin } from 'vite-plugin-html'
/**
* 创建 vite 插件
* @param viteEnv
*/
export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => {
const { VITE_GLOB_APP_TITLE } = viteEnv;
return [
vue(),
// 配置html模板插件
createHtmlPlugin({
minify: true, // 是否压缩 html
inject: { // 注入 HTML 的数据
data: { title: VITE_GLOB_APP_TITLE }, // 注入的数据
},
})
];
};
使用
vite-plugin-html
在项目中使用 vite-plugin-html,你可以这样做:
- 创建 HTML 模板:在指定的基础模板路径创建 HTML 文件,使用模板语法来定义页面结构。
- 配置入口文件:为每个页面指定一个入口 JavaScript 文件。
- 构建项目:运行 Vite 的构建命令,插件将处理 HTML 文件,生成最终的 HTML 页面。
- 自定义模板:如果需要,可以编写自定义模板函数来进一步自定义 HTML 的生成过程。
通过使用 vite-plugin-html,你可以简化 Vite 项目中 HTML 模板的管理,提高开发效率,尤其是在创建多页面应用时非常有用。
- 在
index.html中增加 EJS 标签,
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%- title %></title> // 模板语法
</head>
</html>
- 在 vite.config.ts 中配置,该方式可以按需引入需要的功能即可
import { defineConfig, Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createHtmlPlugin } from 'vite-plugin-html'
export default defineConfig({
plugins: [
vue(),
createHtmlPlugin({
minify: true,
/**
* 在这里写entry后,你将不需要在`index.html`内添加 script 标签,原有标签需要删除
* @default src/main.ts
*/
entry: 'src/main.ts',
/**
* 如果你想将 `index.html`存放在指定文件夹,可以修改它,否则不需要配置
* @default index.html
*/
template: 'public/index.html',
/**
* 需要注入 index.html ejs 模版的数据
*/
inject: {
data: {
title: 'index',
injectScript: `<script src="./inject.js"></script>`,
},
},
}),
],
})
- 多页应用配置
import { defineConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
export default defineConfig({
plugins: [
createHtmlPlugin({
minify: true,
pages: [
{
entry: 'src/main.ts',
filename: 'index.html',
template: 'public/index.html',
injectOptions: {
data: {
title: 'index',
injectScript: `<script src="./inject.js"></script>`,
},
},
},
{
entry: 'src/other-main.ts',
filename: 'other.html',
template: 'public/other.html',
injectOptions: {
data: {
title: 'other page',
injectScript: `<script src="./inject.js"></script>`,
},
},
},
],
}),
],
})
- 参数说明
vite-plugin-html是一个用于处理 HTML 文件的 Vite 插件,提供了压缩、加载、CDN 功能等。以下是vite-plugin-html的一些关键配置参数说明:
- minify (
boolean | MinifyOptions): 是否压缩 HTML 代码。默认值为true。如果需要自定义压缩选项,可以提供html-minifier-terser的Options配置。 - loading (
boolean | HtmlLoadingOptions): 是否在应用根节点增加 loading 代码,避免网络问题造成的白屏。提供HtmlLoadingOptions配置,包括 selector、style、before、after 等属性。 - cdn (
false | HtmlCdnOptions): 配置vite build将模块改为 CDN 的方式引用,提高打包速度和减小包体积。HtmlCdnOptions包括 modules、type、url、local 等属性 。 - entry (
string): 入口文件,默认值为src/main.ts。指定你的应用程序入口文件 。 - template (
string): 模板的相对路径,默认值为index.html。这是 HTML 模板文件的路径 。 - inject (
InjectOptions): 注入 HTML 的数据。InjectOptions包括 data、ejsOptions、tags 等属性 。 - pages (
PageOption): 多页配置。PageOption包括 filename、template、entry、injectOptions 等属性 88。 - env 注入: 默认会向
index.html注入.env文件的内容,类似 Vite 的loadEnv函数 。
使用 vite-plugin-html 时,可以在 vite.config.ts 或 vite.config.js 中配置这些参数,以满足项目需求。例如:
import { defineConfig } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
export default defineConfig({
plugins: [
createHtmlPlugin({
minify: true,
entry: 'src/main.ts',
template: 'public/index.html',
inject: {
data: {
title: 'My App',
},
},
// 其他配置...
}),
],
});
以上配置提供了 vite-plugin-html 的基本使用方式,你可以根据项目的具体需求调整这些参数。
集成ESlint和Prettier
- prettier官网: www.prettier.cn
- Eslint官网: eslint.cn/docs/rules
安装Pinia
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。如果你熟悉组合式 API 的话,你可能会认为可以通过一行简单的 export const state = reactive({}) 来共享一个全局状态。对于单页应用来说确实可以,但如果应用在服务器端渲染,这可能会使你的应用暴露出一些安全漏洞。 而如果使用 Pinia,即使在小型单页应用中,你也可以获得如下功能:
-
测试工具集
-
插件:可通过插件扩展 Pinia 功能
-
为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
-
支持服务端渲染
-
Devtools 支持
- 追踪 actions、mutations 的时间线
- 在组件中展示它们所用到的 Store
- 让调试更容易的 Time travel
-
热更新
- 不必重载页面即可修改 Store
- 开发时可保持当前的 State
开始
- 安装
npm install pinia - 创建实例
import { createPinia } from 'pinia'
// 创建pinia实例
const pinia = createPinia()
export default pinia;
- 在vue中引用中间件
import { createApp } from "vue"
import App from "@/App.vue"
// pinia
import pinia from "@/stores"
createApp(App).use(pinia).mount("#app")
- 安装pinia持久化插件
pnpm install pinia-plugin-persistedstate
pinia-plugin-persistedstate用于将 Pinia store 中的状态持久化存储,通常存储在浏览器的localStorage或sessionStorage中。这样,即使在页面刷新或关闭后,状态也能被保留并在下次加载时恢复。
- 在pinia中注册插件
import { createPinia } from 'pinia'
// pinia 持久化插件
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// 创建pinia实例
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default pinia;
- 配置插件
import { PersistedStateOptions } from "pinia-plugin-persistedstate";
/**
* @description pinia 持久化参数配置
* @param {String} key 存储到持久化的 name
* @param {Array} paths 需要持久化的 state name
* @return persist
* */
const piniaPersistConfig = (key: string, paths?: string[]) => {
const persist: PersistedStateOptions = {
key,
storage: localStorage,
// storage: sessionStorage,
paths
};
return persist;
};
/**
* persist 配置项可以是一个对象,包含如下选项:
* key: 用于引用 storage 中数据的键名,默认为 store 的 $id。
* storage: 将数据持久化到的 storage,默认为 localStorage。
* paths: 指定 state 中哪些数据需要被持久化。如果设置为空数组,则不持久化任何状态。
* serializer 和 deserializer: 自定义序列化和反序列化方法。
* beforeRestore 和 afterRestore: 恢复数据前后的钩子函数。
* debug: 是否开启调试模式,开启后会在控制台输出错误信息
*/
export default piniaPersistConfig;
安装路由
pnpm add vue-router@4- 配置路由
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
const mode = import.meta.env.VITE_ROUTER_MODE;
// 路由模式
const routerMode = {
hash: () => createWebHashHistory(),
history: () => createWebHistory()
};
//创建路由实例
const router = createRouter({
history: routerMode[mode],
routes: []
});
export default router;
``
# 异常处理
## ts无法识别.vue文件的问题

> 解决方案
在tsconfig.json的`files`或者`include`配置中指定的.d.ts文件下添加vue文件声明
```ts
// 扩展模块 (不懂去看ts关于声明文件的内容)
declare module '*.vue' {
import { ComponentOptions } from 'vue'
const componentOptions: ComponentOptions
export default componentOptions
}
安装element-plus
pnpm install element-plus
按需引入
- 安装插件
npm install -D unplugin-vue-components unplugin-auto-import - 配置vite按需引入
import { PluginOption } from "vite"
import { createHtmlPlugin } from "vite-plugin-html"
import vue from "@vitejs/plugin-vue"
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
/**
* 创建 vite 插件
* @param viteEnv
*/
export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => {
// 标题
const { VITE_GLOB_APP_TITLE } = viteEnv
return [
vue(),
// 注入变量到 html 文件
createHtmlPlugin({
minify: true,
inject: {
data: { title: VITE_GLOB_APP_TITLE }
}
}),
// ElementPlus按需引入配置
AutoImport({
resolvers:[ElementPlusResolver()],
}),
Components({
resolvers:[ElementPlusResolver()]
})
]
}
安装element-plus/icons-vue
pnpm install @element-plus/icons-vue --save- 在main.ts中引入并注册图标
import { createApp } from "vue"
import App from "@/App.vue"
// element plus
import ElementPlus from "element-plus";
// element icons
import * as Icons from "@element-plus/icons-vue";
// pinia
import pinia from "@/stores"
// router
import router from "@/routers"
// 全局样式
import '@/styles/general.css'
const app = createApp(App)
// 注册element的Icons组件
Object.keys(Icons).forEach(key => {
app.component(key, Icons[key as keyof typeof Icons]);
});
app.use(ElementPlus).use(router).use(pinia).mount("#app")
- 声明图标类型文件
/// <reference types="vite/client" />
// 扩展模块 (不懂去看ts关于声明文件的内容)
declare module "*.vue" {
import { ComponentOptions } from "vue"
const componentOptions: ComponentOptions
export default componentOptions
}
// element plus 图标声明
declare module '@element-plus/icons-vue';
安装scss
pnpm install sass --save-dev
安装axios
pnpm install axios