0


2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!

🌻 前言

本文教程 github地址 、码云。 如果对你有帮助,希望能点个star ⭐️⭐️⭐️ 万分感谢😊😊😊

🧱 背景

不久前我司需要重新部署一个前端项目,由我来负责这个项目的搭建。因为这个项目是需要和app混合开发的h5页面,包括以后可能会做一些运营h5,所以自然不能采用常规的

SPA单页面应用

架构(因为大部分页面耦合度低,全都塞一个项目里的话,即使打开个纯静态页都要跑起来整个项目,严重影响页面的加载速度).

另外,我们其实已经有混合开发h5项目,采用的

gulp工作流

,即每个页面都是单独的一个html文件。这种架构的优势很明显,就是体量小,结构清晰等,但是它的缺点也非常明显,例如有些轮子无法使用,组件化、模块化开发非常不便影响开发效率等。

目前此项目已在平稳运行,最近有空闲时间记录一下我搭建项目的过程,同时也希望能帮助到有这方面需求的掘友😊

💡 思路 **

综合考虑,我决定用

vite + vue3 + ts + esint + prettier + stylelint + husky + lint-stage + commitlint

搭建一套多页面项目。

1. 初步定了几个目标:

  • 🍀 支持打包指定子页面,打包后的文件夹:各页面相互独立(各子页面解耦,避免相互影响)
  • 🍀 支持启动指定子页面(常规的多页面项目,启动后需要手动拼接页面地址,或者在根目录做一个重定向的页面,总之调试非常不便)
  • 🍀 支持指令化新建页面(手动创建页面太麻烦,每次都得复制一份干净的文件夹)
  • 🍀 自由选择创建ts页面 / js页面(对于一些重要的页面可以使用ts提高规范性,一些简单的页面则使用js提高开发效率)

2. 本文将从以下几个方面逐步讲解:

  • 项目目录结构
  • 新建项目
  • 安装依赖及一些基础插件
  • vite配置项修改
  • ts配置
  • 多页面入口配置
  • 多页面打包配置
  • 指令化新建子页面(*重点
  • 多页面架构改造(*重点
  • 完善项目架构

🌈 教程

一、 项目目录结构

├── README.md 
├── .husky   //git hook钩子
│   ├── commit-msg //规范 commit message 信息
│   └── verify-commit-msg.mjs  //脚本:commitlint 替代方案
├── dist //打包输出目录
├── scripts //存放一些脚本
│   ├── template         //创建子页面的js模版
│   ├── template-ts      //创建子页面的ts模版
│   ├── index.mjs        //创建子页面的脚本
│   └── multiPages.json  //子页面描述说明集合文件
├── src 
│   ├── arrets       //公共静态资源
│   ├── components   //公共组件
│   ├── store        //pinia 共享状态存储库
│   ├── utils        //公共方法
│   └── Projects     //多页面文件夹
├── types  //ts 声明文件
├── .env.development   //开发环境-环境变量
├── .env.production    //生产环境-环境变量
├── .eslintrc.cjs      //eslint 配置
├── .gitignore         //git 提交忽略文件
├── .prettierignore    //prettier 忽略文件
├── .prettierrc.js     //prettier 配置
├── .stylelintignore   //stylelint 忽略文件
├── .stylelintrc.js    //stylelint 配置
├── .pnpm-lock.yaml    //锁定项目于一份各个依赖稳定的版本信息
├── .stats.html        //chunck size 分析页面
├── tsconfig.json      //ts 配置
├── tsconfig.node.json //vite在node环境中的 ts 规则
├── vite.config.ts     //vite 配置
├── package.json

二、 新建项目

首先我们用命令行新建一个vite项目,不要使用模板创建,就创建一个基础模板就行,注意创建过程选择 vue3+typescript。创建命令如下:

# npm 6.x
npm create vite\@latest vue3-mpa

# npm 7+, extra double-dash is needed:
npm create vite\@latest  vue3-mpa

# yarn
yarn create vite  vue3-mpa

# pnpm
pnpm create vite  vue3-mpa

三、 安装依赖及一些基础插件

新建项目后记得

npm i

安装依赖。然后我们先装一些基础的插件,例如vue-router等,方便后面调试,这里可能没装全,毕竟是返工,有些东西忘记了,大家根据报错提示自行安装即可。

//安装vue-router4
npm install vue-router\@next -S//安装 sass
npm install sass -D//安装 chalk(chalk是一个颜色的插件。可以通过chalk.blue(‘hello world’)来改变console打印的颜色)
npm install chalk@^4.1.2//处理使用 node 模块代码飘红,例如 ‘找不到模块 “path“ 或其相对应的类型声明’
npm install @types/node --save-dev

四、 vite配置项修改

vite.config.ts

进行调整,先做一些基础的配置,后面我们调通项目之后再丰富项目插件。

🍀 配置下启动端口,热更新

server:{host:'localhost',// 指定服务器主机名port:8880,// 指定服务器端口hmr:true,// 开启热更新open:true,// 在服务器启动时自动在浏览器中打开应用程序https:false// 是否开启 https}

🍀 配置文件路径的别名,方便书写文件引入路径。

resolve:{alias:{'@': path.join(__dirname,'./src'),'@Project': path.join(__dirname,'./src/Project')}}

五、 ts配置

项目根目录下找到

tsconfig.json

文件,它是是用来配置 TS 编译选项的。
这一步可以晚一点配置,但是避免后面操作过程中的一些报错,可以提前配置好,以下是我使用的配置项,基本对每一项都做了解释。

{"compilerOptions":{"target":"esnext",//用于指定 TS 最后编译出来的 ES 版本"types":["vite/client"],//要包含的类型声明文件名列表"useDefineForClassFields":true,//将 class 声明中的字段语义从 [[Set]] 变更到 [[Define]]"module":"esnext",// 设置编译后代码使用的模块化系统:commonjs | UMD | AMD | ES2020 | ESNext | System"moduleResolution":"node",// 模块解析策略,ts默认用node的解析策略,即相对的方式导入"strict":true,//开启所有的严格检查"jsx":"preserve",//在 `.tsx`文件里支持JSX: `"React"`或 `"Preserve"`"sourceMap":false,// 生成目标文件的sourceMap文件"resolveJsonModule":true,//允许导入扩展名为“.json”的模块"isolatedModules":true,//确保每个文件都可以在不依赖其他导入的情况下安全地进行传输"esModuleInterop":true,//支持导入 CommonJs 模块"lib":["esnext","dom","ES2015"],//TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",// "noLib": false, //不包含默认的库文件( lib.d.ts)"skipLibCheck":true,//忽略所有的声明文件( *.d.ts)的类型检查"allowJs":true,// 允许编译器编译JS,JSX文件"noEmit":true,// 不输出文件,即编译后不会生成任何js文件"allowSyntheticDefaultImports":true,//允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。默认值:module === "system" 或设置了 --esModuleInterop 且 module 不为 es2015 / esnext"baseUrl":"./", 解析非相对模块的基地址,默认是当前目录"paths":{"@/*":["src/*"],//解决引入报错  找不到模块“@/xxxx” 或其相应的类型声明"@Project":["src/Project/*"]}},"include":["scripts/**/*.ts","src/**/*.ts","src/**/*.js","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue","scripts/index.mts","scripts/template-ts/router/routes.ts","scripts/template-ts/router/index.ts","scripts/template-ts/main.ts","src/env.d.ts","src/global.d.ts"],"exclude":["vite.config.ts"],"references":[{"path":"./tsconfig.node.json"}]//每个引用的`path`属性都可以指向到包含`tsconfig.json`文件的目录,或者直接指向到配置文件本身(名字是任意的)}

六、 多页面入口配置

vite

webpack

配置多页面的方式不同。vite 使用的是

rollup

的打包方式。

1. 基本配置

想要将项目改造成多页面项目,我们可以自定义底层的 Rollup 打包配置,只需要指定多个

.html

文件作为入口点即可,此设置在

build.rollupOptions.input

配置项下。

首先我们现在 Project 文件夹下新建两个子页面

pageone、pagetwo 

,目录结构如下:

image.png

然后在

vite.config.ts

文件中指定这两个子页面的入口。

build:{rollupOptions:{//自定义底层的 Rollup 打包配置input:{project1:resolve(__dirname,'src/Project/pageone/index.html'),project2:resolve(__dirname,'src/Project/pagetwo/index.html')}}}

这里需要注意的是:
**

__dirname

** 占位符指的是

vite.config.js

文件所在的目录,即使修改了项目的根目录,它的值也不会变(后面我们将会修改项目的根目录)。

2. 动态生成多页面入口

因为我们要不断新建子页面,不可能每个子页面都手动去配置入口,所以可以获取到

/Progect

文件夹下文件名后,动态配置多页面入口。

fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求,这里我们用到了

fs.readdirSync

方法。

fs.readdirSync

方法同步返回一个包含“指定目录下所有文件的名称”的数组对象。

import fs from"fs";functiongetEntryPath(){const map ={}//最后生成的多页面配置项constPAGE_PATH= path.resolve(\__dirname,'./src/Project')//指定要查询的目录const entryFiles = fs.readdirSync(PAGE_PATH)//获取到指定目录下的所有文件名
    entryFiles.forEach(filePath=>{//遍历处理每个子页面的入口
        map[filePath]= path.resolve(__dirname,`src/Project/${filePath}/index.html`)})return map
}// 自定义底层的 Rollup 打包配置rollupOptions:{input:getEntryPath()}

3. 配置重定向页面

配置完多页面入口后,我们可以启动项目看看效果。

npm run dev

启动项目:

image.png

你会发现什么都没有,因为此时项目跟路径还是

‘/’

,找不到可以作为入口的index.html文件,这时我们只能手动拼接上地址

/src/Project/pageone/

,进入子项目的

index.html

文件

image.png

每次启动项目的时候都手动拼接地址未免也太麻烦了,太沙壁了😅。ok,那我们在项目根目录下写一个重定向的页面,专门用来启动项目后跳转到指定的子项目,代码如下:

注意: 到目前为止我们没有修改过项目根目录,仍然在

vite.config.ts

文件同级文件夹下。

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>重定向</title></head><body><p><a href="./src/Project/pageone/index.html">子页面1</a></p><p><a href="./src/Project/pagetwo/index.html">子页面2</a></p><script></script></body></html>

image.png

七、 多页面打包配置

build 选项指定多个入口之后,就可以进行多页面打包了。我们执行

npm run build

看看打包生成的

dist

文件夹结构。

image.png

你会发现所有静态资源全都打包到了assets文件夹下,我们可以修改

rollupOptions

选项中的

output

选项修改输出文件夹的格式,如下:

build:{rollupOptions:{input: 指定多页面入口,output:{assetFileNames:'[ext]/[name]-[hash].[ext]',//静态文件输出的文件夹名称chunkFileNames:'js/[name]-[hash].js',//chunk包输出的文件夹名称entryFileNames:'js/[name]-[hash].js',//入口文件输出的文件夹名称}}},

占位符说明 >>>

  • [extname] :文件扩展名,包括前面的 . ,例如 .css
  • [ext] :文件扩展名,不包括前面的 . ,例如 css
  • [name] :文件名;
  • [hash] :基于文件内容生成的哈希值,可以通过[hash:10]设置特定的哈希长度;

八、 指令化新建子页面(

重点来了~

到这里其实已经改造出来了一个多页面项目脚手架。但是离我们都目标还相差甚远:

  • ☘️ 不能指令化创建页面
  • ☘️ 不能单独启动指定的子项目
  • ☘️ 打包后所有子项目的静态文件都混淆在一起

那么怎么解决这些问题呢? 我们逐个剖析:

先来解决指令化新建页面的问题,既然要创建页面,就是要和文件打交道,所以我们还是要使用到

fs

文件系统模块。先来了解它的一下几个方法,我们之后会用到(这里只是大概对方法简单说明,具体使用自行查询):

🔥 **

fs.mkdirSync( path, options )

** 方法用于同步创建目录,创建子页面主要就是使用这个方法。

🔥 **

fs.readFile( filename, encoding, callback_function )

** 方法用于异步读取指定文件中的内容。

🔥 **

fs.writeFile( file, data, options, callback )

** 方法用于异步读取指定文件中的内容。

🔥 **

fs.existsSync( path )

** 方法用于同步检测目录是否存在;

🔥 **

fs.readdirSync( path, options )

** 方法用于同步读取给定目录的内容。该方法返回一个数组,其中包含目录中的所有文件名或对象。

🔥 **

fs.copyFileSync( src, dest, mode )

** 方法用于将文件从源路径同步复制到目标路径。

了解这些以后,我们捋一下创建文件的步骤:

  1. package.json 文件中添加以下指令,以执行创建子页面的脚本;
"scripts":{..."new:page":"node ./scripts/index.mjs"}
  1. 然后开发写创建子页面的脚本,第一步要先提示用户输入要创建的页面名称和描述,并验证输入的格式;
//./scripts/index.mjsconstlog=(message)=> console.log(chalk.green(`${message}`))constsuccessLog=(message)=> console.log(chalk.blue(`${message}`))consterrorLog=(error)=> console.log(chalk.red(`${error}`))log('请输入要生成的"页面名称:页面描述"、会生成在 /src/Project 目录下')
  1. 使用 fs.existsSync 方法验证是否已存在同名页面;
//process.stdin属性是流程模块的内置应用程序编程接口,用于侦听用户输入,它使用on()函数来监听事件。
process.stdin.on('data',async(chunk)=>{// 获取输入的信息const content =String(chunk).trim().toString()const inputSearch = content.search(':')if(inputSearch ==-1){errorLog('格式错误,请重新输入')return}// 拆分用户输入的名称和描述const inputName = content.split(':')[0]const inputDesc = content.split(':')[1]|| inputName
const isTs = process.env.npm_config_ts
successLog(`将在 /src/Project 目录下创建 ${inputName} 文件夹`)const targetPath =resolve('./src/Project', inputName)// 判断同名文件夹是否存在const pageExists = fs.existsSync(targetPath)if(pageExists){errorLog('页面已经存在,请重新输入')return}})
  1. 在同级 script 文件夹下,新建 multiPages.json 用于记录目前已有的页面名称,每次新建页面都会写入进去;
// 获取multiPages.json文件内容,获取当前已有的页面集合await fs.readFile(
path.resolve('./scripts','multiPages.json'),'utf-8',(err, data)=>{//获取老数据let datas =JSON.parse(data)//和老数据去重let index = datas.findIndex((ele)=>{return ele.chunk == inputName
  })if(index ==-1){//写入新页面的let obj ={chunk: inputName,chunkName: inputDesc
    }
    datas.push(obj)setFile(datas)}})// 写入multiPages.jsonfunctionsetFile(datas){// 通过writeFile改变数据内容
fs.writeFile(
  path.resolve('./scripts','multiPages.json'),JSON.stringify(datas),'utf-8',(err)=>{if(err)throw err
    // 在project中建立新的目录
    fs.mkdirSync(targetPath)const sourcePath =resolve(
      isTs ?'./scripts/template-ts':'./scripts/template')copyFile(sourcePath, targetPath)
    process.stdin.emit('end')})}...

process.stdin.on('end',()=>{
console.log('exit')
process.exit()})
  1. 将新页面的信息写入 multiPages.json 文件后,在 Project 文件夹下复制我们提前创建好的模板页面。我这里创建了两个模版页面,分别是js和ts的模版,如果需要创建支持 TS 的子页面,创建命令为 npm run new:page --ts
// 判断文件夹是否存在,不存在创建一个constisExist=(path)=>{if(!fs.existsSync(path)){
        fs.mkdirSync(path)}}//递归复制模版文件夹内的文件constcopyFile=(sourcePath, targetPath)=>{const sourceFile = fs.readdirSync(sourcePath,{withFileTypes:true})

sourceFile.forEach((file)=>{const newSourcePath = path.resolve(sourcePath, file.name)const newTargetPath = path.resolve(targetPath, file.name)//isDirectory() 判断这个文件是否是文件夹,是就继续递归复制其内容if(file.isDirectory()){isExist(newTargetPath)copyFile(newSourcePath, newTargetPath)}else{
          fs.copyFileSync(newSourcePath, newTargetPath)}})}

九、 多页面架构改造

到这里可以做如下思考:

🤔 如何支持单独启动指定的子页面?
指定子页面可以在执行

npm run dev

命令时配置自定义环境变量。格式为

npm run dev --变量名=值

,例如我要单独启动 pageone 子页面:

npm run dev --page=pageone

。关于npm环境变量的使用可以查看传送门
如何启动后直接进入这个子页面呢?这时候我们就需要修改项目的根路径了:

注意:将根路径修改到指定子页面目录下后,就只能单独启动/打包 指定的子页面了,无法打包全部的子页面。这么做有以下优缺点:
优点:打包后的子页面相互独立,可以直接启动指定的子页面;
缺点:无法一次性打包全部页面,需要配置重定向页面方便调试

所以你可以根据需要决定是否要修改项目根目录,因为我考虑到页面是逐步新增的,需要一次性打包全部页面的情况很少,而且我认为打包后的子页面相互独立非常重要,避免出现一些意料之外的问题影响多个页面,所以我果断选择了每次都单独打包页面。

这里我把两种情况的处理都列出来:

情况1:支持单页面打包和打包全部页面

处理方式:

  1. 多页面入口配置\

使用说明:

npm run build

打包全部页面;

npm run build --page=页面名称

打包单页面;

//vite.config.ts// 引入多页面配置文件const project =require('./scripts/multiPages.json')// 获取npm run dev后缀 配置的环境变量constnpm_config_page:string =  process.env.npm_config_page ||''let filterProjects =[]if(npm_config_page){//如果指定了单页面打包,过滤出这个页面的配置项
filterProjects = project.filter((ele)=>{return ele.chunk.toLowerCase()=== npm_config_page.toLowerCase()})
console.log(`--------单独构建:${filterProjects[0]['chunkName']}--------`)}else{
filterProjects = project
}//多页面入口constgetEnterPages=(p)=>{const pages ={}
p.forEach((ele)=>{const htmlUrl = path.resolve(
  __dirname,`src/Project/${ele.chunk}/index.html`)
pages[ele.chunk]= htmlUrl
})return pages
}//入口配置build:{rollupOptions:{input:getEnterPages(),...
  1. 项目根路径修改为 root: './src/Project/' ,不修改其实也能实现,但是不改的话打包后dist文件夹层级太深;
  2. 修改打包的输出路径为:
build:{outDir: path.resolve(__dirname,`dist`),// 指定输出路径...

打包后的dist目录结构:

image.png
4. 修改 envDir 配置项,修改到项目根的路径,用于加载.env 环境变量文件

envDir: path.resolve(__dirname)
  1. 在自定义的 root 根路径下(这里是 src/Project 文件夹下)创建重定向页面,方便启动后调试。

情况2:只支持单页面打包

  1. 多页面入口配置\

使用说明:

npm run build --page=页面名称

单独打包指定页面。

//vite.config.tsimport chalk from'chalk'// 引入多页面配置文件const project =require('./scripts/multiPages.json')// 获取npm run dev后缀 配置的环境变量constnpm_config_page:string =  process.env.npm_config_page ||''// 命令行报错提示consterrorLog=(error)=> console.log(chalk.red(`${error}`))//获取指定的单页面入口constgetEnterPages=()=>{if(!npm_config_page)errorLog('-------------------请在命令行后以 `--page=页面名称` 格式指定页面名称!-------------------')const filterArr = project.filter(item=> item.chunk.toLowerCase()== npm_config_page.toLowerCase())if(!filterArr.length)errorLog('-------------------不存在此页面,请检查页面名称!-------------------')return{[npm_config_page]: path.resolve(
          __dirname,`src/Project/${npm_config_page}/index.html`)}}//入口配置build:{rollupOptions:{input:getEnterPages(),...
  1. 项目根路径修改到用户输入的单页面文件夹下
root: path.resolve(__dirname,`./src/Project/${npm_config_page}`)
  1. 修改打包的输出路径为:
build:{outDir: path.resolve(__dirname,`dist/${ npm_config_page }`)// 指定输出路径...

打包后的dist目录结构:

image.png

  1. 修改 envDir 配置项,修改到项目根的路径,用于加载.env 环境变量文件
envDir: path.resolve(__dirname)

到这里,多页面架构已经基本改造完成了🎉🎉🎉

🤔 再做一些打包的优化:

build:{sourcemap:false,// 这个生产环境一定要关闭,不然打包的产物会很大assetsInlineLimit:4096,//小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求emptyOutDir:true,//Vite 会在构建时清空该目录rollupOptions:{input:...output:{...compact:true,//压缩代码,删除换行符等manualChunks:(id: string)=>{//配置分包if(id.includes("node_modules")){return id.toString().split('node_modules/')[1].split('/')[0].toString();// 拆分多个vendors}}}}...

十、 完善项目架构

1.

pnpm

替代

npm

管理依赖包

pnpm 是一款磁盘空间高效的软件包管理器。 如果你还在用 npm 管理依赖包,我建议你不妨试试pnpm,因为他是真的香,速度杠杠的😆
详细介绍这里就不做展开了,官网传送门https://pnpm.io/zh/motivation

//全局安装 pnpm
npm install -g pnpm
//切换淘宝源
pnpm config set registry https://registry.npmmirror.com///删除node_modules文件夹,执行 pnpm i

2. 引入

eslint

+

stylelint

+

prettier

规范代码风格和质量

eslint

是代码检查工具(lint工具),我们用它来避免低级错误和统一代码的风格,以保证代码质量;

stylelint

为css的lint工具。可格式化css代码,检查css语法错误与不合理的写法,指定css书写顺序等;

prettier

是代码格式化工具,我们用它来统一代码格式,风格。

**🍀 首先先来安装

eslint

** ,依次执行以下命令:

// 安装 eslint
pnpm add eslint -D// eslint初始化
pnpm eslint --init

初始化配置项如下:

image.png

eslint初始化之后,会生成

.eslintrc.cjs

配置文件。刚初始化完的时候你会发现有个

module

报错了,很简单,只要在

env

配置项下新增选项

"node": true

即可。

截屏2023-04-10 下午6.38.39.png

// --ext 为指定lint哪些后缀的文件    --fix 开启自动修复"lint":"eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"

此时打开

.eslintrc.cjs

配置文件会出现一个报错,需要再

env

字段中增加

node: true

配置以解决eslint找不到module的报错。

注意:

.eslintrc.cjs

配置文件名需以

.cjs

结尾,如果以

.js

结尾,需要删除

package.json

文件中的字段

"type": "module"

prettier

同理,因为我们要用到 chalk 插件,所以统一采用

.cjs

文件。

具体

eslint

规则根据自己的习惯进行配置,这里也贴下我的粗糙配置:

module.exports ={"env":{"browser":true,"es2021":true,"node":true},"extends":["eslint:recommended","plugin:vue/vue3-essential","plugin:@typescript-eslint/recommended"],"overrides":[],"parser":"vue-eslint-parser","parserOptions":{"ecmaVersion":"latest","sourceType":"module","parser":"@typescript-eslint/parser"},"plugins":["vue","@typescript-eslint","prettier"],"rules":{'@typescript-eslint/no-var-requires':0,//解决报错:Require statement not part of import statement.'vue/multi-word-component-names':'off'//关闭组件命名规则娇艳},"root":true}

**🍀 再来安装

stylelint

**

pnpm add stylelint stylelint-order stylelint-config-standard  stylelint-config-prettier -D

依赖说明:

  • stylelint:检验工具
  • stylelint-order:css样式书写顺序插件
  • stylelint-config-standard:stylelint的推荐配置
  • stylelint-config-prettier:关闭所有不必要的或可能与 Prettier 冲突的规则

创建

.stylelintrc.js

配置文件:

module.exports ={// 注册 stylelint 的 prettier 插件plugins:['stylelint-prettier'],// 继承一系列规则集合extends:['stylelint-config-standard-scss','stylelint-config-html/vue','stylelint-config-recommended-vue/scss','stylelint-config-recess-order',// 接入 Prettier 规则'stylelint-config-prettier','stylelint-prettier/recommended'],// 配置 rulesrules:{// 开启 Prettier 自动格式化功能'prettier/prettier':true}}

及忽略文件

.stylelintignore
node_modules/*
/dist/

最后配置命令

{"script":{"lint:stylelint":"stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"}}

**🍀 最后安装

prettier

**

pnpm add prettier -D

安装好之后在项目根目录创建

.prettierrc.cjs

配置文件,这里也贴下我的配置,大家自己根据需要调整规则。

//.prettierrc.cjs
module.exports ={printWidth:80,//一行的字符数,如果超过会进行换行,默认为80singleAttributePerLine:false,//每行强制换行,只能使用单个属性tabWidth:2,// 一个 tab 代表几个空格数,默认为 2 个useTabs:false,//是否使用 tab 进行缩进,默认为false,表示用空格进行缩减singleQuote:true,// 字符串是否使用单引号,默认为 false,使用双引号semi:false,// 行尾是否使用分号,默认为truetrailingComma:'none',// 是否使用尾逗号bracketSpacing:true,// 对象大括号直接是否有空格,默认为 true,效果:{ a: 1 }endOfLine:'auto',jsxBracketSameLine:false// 在jsx中把'>' 是否单独放一行}

同时创建

.prettierignore

为Prettier忽略指定文件

.DS_Store
node_modules
dist
dist-ssr

**/*.svg
**/*.sh
eslint

prettier

的配置难免会有冲突,目前比较成熟的方案是使用以下两个插件:

  • eslint-plugin-prettier: 基于 prettier 代码风格的 eslint 规则,即eslint使用pretter规则来格式化代码。
  • eslint-config-prettier: 禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突,确保将其放在 extends 队列最后,这样它将覆盖其他配置
pnpm add eslint-config-prettier eslint-plugin-prettier -D

安装完成后在

.eslintrc.cjs

文件中

extends

的最后添加一个配置

{
extends: [
‘eslint:recommended’,
‘plugin:vue/vue3-essential’,
‘plugin:@typescript-eslint/recommended’,
// 新增,必须放在最后面
‘plugin:prettier/recommended’
]
}

3. 引入

husky

+

lint-staged

+

Commitlint

规范 git 工作流程

在团队协作开发时,为了避免开发人员代码格式和风格不同导致的冲突,我们最好遵循

husky
  • Commitlint
    
  • lint-staged
    
    git工作流规范,这样在本地提交代码前,能够强制让开发人员进行统一的代码规范化处理,避免造成冲突和一些潜在问题。
husky

是常见的git hook工具,使用husky可以挂载Git钩子,当我们本地进行git commit或git push等操作前,能够执行其它一些操作,比如进行ESLint检查,如果不通过,就不允许commit或push。

pnpm add husky -D
# 执行以下命令会生成 .husky 文件
npx husky install
// 配合lint-staged使用
npx husky add .husky/pre-commit "npx lint-staged"
Lint-staged

可以在git staged阶段(即git add 后的暂存阶段)的文件上执行Linters,简单说就是当我们运行ESlint或Stylelint命令时,可以通过设置指定只检查我们通过git add添加到暂存区的文件,可以避免我们每次检查都把整个项目的代码都检查一遍,从而提高效率。

pnpm add husky lint-staged -D

package.json

文件中配置lint-staged

"lint-staged":{"src/**/*.{js,ts,vue}":["pnpm run lint:script","pnpm run lint:prettierrc","git add ."]}
Commitlint

是用来规范git提交代码时

Commit Message

格式的工具。结合husky一起使用,可以在开发者进行commit前就对Commit Message进行检查,只有符合规范,才能够进行commit。

安装

Commitlint
pnpm add @commitlint/cli @commitlint/config-conventional
Commitlint

配合

husky

使用:执行下方命令,会在

.husky

目录下生成

commit-msg

文件。

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'

然后在根目录创建

commitlint.config.js

文件进行配置。

module.exports ={extends:['@commitlint/config-conventional'],rules:{'type-case':[2,'always',['lower-case','upper-case']],'type-enum':[2,'always',['feat',//新的特性'fix',//修复Bug'docs',//添加或更新文档'style',//代码格式的更改'refactor',//代码进行重构'perf',//提升性能'test',//添加或更新测试用例'chore',//更改配置文件'revert',//版本回退'merge',//分支合并'build',//打包工具的更改'release',//发布/版本标签'ci'//对CI配置和脚本的更改]]}}

**🌈 脚本替代

Commitlint

方案**

我这里为了在输入

commit msg

时有比较清晰的报错提示,所以决定采用脚本代替

Commitlint

,当然,你也可以采用

Commitlint

prompt

(命令行提示工具) ,并在

package.json

文件中添加 commit 命令来规范 git 提交操作(但是我认为这么整太麻烦了,降低开发效率)。

脚本的思路无非就是定义一个

commit type

的字典集,然后比对输入的类型,不符合要求则用node插件

boxen

(控制台输出盒子)进行规范化提示。

注意要将

husky

commit-msg

钩子修改为我们的脚本地址

//修改husky文件夹下的 commit-msg 文件
#!/usr/bin/env sh
."$(dirname -- "$0")/_/husky.sh"

node .husky/verify-commit-msg.mjs 

在mac上如果报错

[hint](https://so.csdn.net/so/search?q=hint&spm=1001.2101.3001.7020): The '.husky/pre-commit' hook was ignored because it's not set as executable.

,因为指定的文件是不可执行的,只需要执行以下命令即可解决:

chmod 777./husky/*
//安装 boxen插件
pnpm add boxen -D
import fs from'fs'import chalk from'chalk'//设置chalk等级 解决颜色无效问题
chalk.level =1// 引入node控制台输出盒子插件import boxen from'boxen'// 挂载boxen
console.boxen=(text)=>{const options ={margin:{top:1,bottom:1},padding:{left:1,right:1},borderColor:'yellow',borderStyle:'classic',title:'Vite-demo'}
console.log(`\n${boxen(text, options)}`)}constTYPE_MAP=newMap([['feat',{emoji:'✨',title:'feat',description:'新的特性'}],['fix',{emoji:'🐛',title:'fix',description:'修复Bug'}],['merge',{emoji:'🔀',title:'merge',description:'分支合并'}],['style',{emoji:'🎨',title:'style',description:'代码格式的更改'}],['perf',{emoji:'🚀',title:'perf',description:'提升性能'}],['test',{emoji:'✅',title:'test',description:'添加或更新测试用例'}],['revert',{emoji:'⏪️',title:'revert',description:'版本回退'}],['build',{emoji:'📦',title:'build',description:'打包工具的更改'}],['chore',{emoji:'🔧',title:'chore',description:'更改配置文件'}],['ci',{emoji:'👷',title:'ci',description:'对CI配置和脚本的更改'}],['refactor',{emoji:'💻',title:'refactor',description:'代码进行重构'}],['docs',{emoji:'📝',title:'docs',description:'添加或更新文档'}],['release',{emoji:'🔖',title:'release',description:'发布/版本标签'}]])// commit regexpconst commitRE =newRegExp(`^(${[...TYPE_MAP.values()].map(({ title, emoji })=>`${title}|${emoji}${title}`).join('|')})(\\(.+\\))?: .{1,100}`)try{const msgPath = process.argv.slice(2,3)[0]const msg = fs.readFileSync(msgPath,'utf-8').replace(/\n#.*/g,'').trim()if(/Merge.+branch \'.+\'/.test(msg)){
fs.writeFileSync(msgPath,`🔀 ${msg.replace('Merge','merge:')}`)

process.exit(0)}if(msg.length >100){throw`commit信息内容不得超出100个字符串长度`}if(commitRE.test(msg)){// 添加emojifor(const[key,{ emoji }]ofTYPE_MAP){if(msg.startsWith(key)){
    fs.writeFileSync(msgPath,`${emoji}${msg}`)break}}}else{// show error feedback
console.log(`${chalk.hex('#fbb957')('✨feat: 新的特性')}\n${chalk.hex('#41ae3c')('🐛fix: 修复Bug')}\n${chalk.hex('#51c4d3')('🔀merge: 分支合并')}\n${chalk.hex('#813c85')('🎨style: 代码格式的更改')}\n${chalk.hex('#ef475d')('🚀perf: 提升性能')}\n${chalk.hex('#40a070')('✅test: 提升性能添加或更新测试用例')}\n${chalk.hex('#63bbd0')('⏪️revert: 版本回退')}\n${chalk.hex('#f8df70')('📦build: 打包工具的更改')}\n${chalk.hex('#158bb8')('🔧chore: 更改配置文件')}\n${chalk.hex('#f9d367')('👷ci: 对CI配置和脚本的更改')}\n${chalk.hex('#f86b1d')('💻refactor: 代码进行重构')}\n${chalk.hex('#d2568c')('📝docs: 添加或更新文档')}\n${chalk.hex('#f9d367')('🔖release: 发布/版本标签')}`)
console.boxen(`commit信息不符合规范\n正确示例: "feat: 新增xxx功能..."\n正确示例: "feat(moduleName): 新增xxx功能..." `)// process quit
process.exit(1)}}catch(err){
console.error(err)
console.boxen('commit 提交异常')
process.exit(1)}

4. 配置

.env

环境变量

因为我们修改了项目的根目录,所以在配置环境变量前,一定要记得在

vite.config.ts

中修改

envDIr

到相对于项目根的路径:

envDir: path.resolve(__dirname)

然后就可以在根目录创建

.env.development

.env.production

文件来分别定义开发环境和生产环境的环境变量了。

需要注意的是: 只有以

VITE_

为前缀的变量才会生效。例如我们设置开发环境的接口基础路径:

VITE_BASE_URL= https://api-test.xxx.com/

5. 安装一些插件

  • unplugin-vue-components 【组件自动按需导入】

这里以

vant

为例,如下配置后,就不需要引入vant插件了,直接可以使用:

pnpm add vant
pnpm add unplugin-vue-components -D//vite.config.tsimport Components from'unplugin-vue-components/vite'//组件自动按需引入import{ VantResolver }from'unplugin-vue-components/resolvers';plugins:[Components({resolvers:[VantResolver()]})]
  • unplugin-auto-import/vite 【自动导入 Composition API】

配置这个插件后,就不需要引入例如

ref、reactive、useRouter

等 api 了。
为了避免这些 api

undefined

报错,需要生成

auto-import.d.ts

说明文件,注意在项目根路路径生成。

pnpm add unplugin-auto-import/vite -Dimport autoImport from'unplugin-auto-import/vite'//自动导入 Composition API//vite.config.tsplugins:[autoImport({imports:['vue','vue-router','pinia'],dts: path.resolve(__dirname,"types/auto-import.d.ts"),eslintrc:{// 已存在文件设置默认 false,需要更新时再打开,防止每次更新都重新生成enabled:false,// 生成文件地址和名称filepath: path.resolve(__dirname,"./.eslintrc-auto-import.json"),globalsPropValue:true,}})]
  • rollup-plugin-visualizer 【打包size分析工具】

打包后会在根目录下生成一个

stats.html

文件,用浏览器打开即可查看 chunk size 的结构。

pnpm add rollup-plugin-visualizer -Dimport{ visualizer }from'rollup-plugin-visualizer'//打包size分析工具//vite.config.tsplugins:[visualizer()]
  • vite-plugin-compression 【打包压缩工具 gzip / br 】

打包时使用

gzip

brotli

进行压缩资源,以减小打包体积:

pnpm add vite-plugin-compression -Dimport compression from'vite-plugin-compression'//gzip/br 压缩//vite.config.tsplugins:[// gzip格式compression({threshold:1024*500,// 体积大于 threshold 才会被压缩,单位 bext:'.gz',// 压缩文件格式deleteOriginFile:false// 是否删除源文件}),// br格式// compression({//   threshold: 1024 * 500,    // 体积大于 threshold 才会被压缩,单位 b//   ext: '.br',//   algorithm: 'brotliCompress',//   deleteOriginFile: false// })]

⏰ 写在最后

在搭建的过程中,一定要注意因为修改了项目根目录(root)所带来的问题,利用

path.resolve(__dirname, ...)

将路径重置到相对于项目根的位置!

关于如何使用这个多页面脚手架,可以看 README.md 文件。

github地址 、码云。 如果对你有帮助,希望能点个star ⭐️⭐️⭐️ 万分感谢😊😊😊

标签: 前端 vite mpa

本文转载自: https://blog.csdn.net/qq_38974163/article/details/130226217
版权归原作者 前端阿彬 所有, 如有侵权,请联系我们删除。

“2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!”的评论:

还没有评论