Vite 基本配置及原理
介绍
如果你还不知道 Vite,或者不知道为什么有了 Webpack 还要出现 Vite,可以先移步看 从零入门 Vite 与 Webpack 对比 。
vite.config.js
前文说过,Vite 使用
Koa
或者
express
这种后端服务框架搭建了一个开发服务器,当我们执行
npm run dev
命令去启动这个开发服务器时,会提示我们访问 http://127.0.0.1:5173/ 这个服务地址打开项目,这就是我们的本地开发服务器。
在服务器启动后 node 会去读取项目配置文件
vite.config.js
,通过配置的内容来启动我们的服务器,比如配置中有很多的插件
plugins
,服务器启动过程中,在不同的插件的不同生命周期会执行插件不同的配置内容,这些配置会直接返回并影响这个本地服务器最终给出的结果,也就是呈现在浏览器的内容。
先从零开始一个 Vite 构建实例:
<!-- index.html --><!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Vite</title></head><body><scriptsrc="./main.js"type="module"></script></body></html>
// main.jsimport _ from'lodash-es'
console.log('lodash', _)
// package.json"scripts":{"dev":"vite","build":"vite build"}
npm init -ynpm i lodash-es
scripts
是 npm 提供的脚本命令功能,也可以使用工程内部执行指令
npx
,即:
npm run dev
或
npx vite
都可以
执行
npm run dev
命令启动项目后,Vite 会自动解析根目录下名为
vite.config.js
文件:
// vite.config.jsexportdefault{// 配置内容...}
思考:
vite.config.js
是 node 来执行的,那为什么它 可以书写成 ESModule 的形式?
因为:Vite 服务器启动后会读取vite.config.js
文件,根据配置的内容是启动和配置服务器,这时候 node 会率先去解析文件语法,如果发现是 ESModule 规范会直接将其替换成 CommonJS 规范。
optimizeDeps.exclude
在预构建中强制排除的依赖项,在依赖预构建时,Vite 将有许多内部模块的 ESModule 依赖关系转换为单个模块:
// vite.config.jsexportdefault{optimizeDeps:{exclude:['lodash-es']// 不依赖预构建的包}}
或者
// vite.config.js// 编辑器会根据 defineConfig 给出配置的 key 提示import{ defineConfig }from'vite'exportdefaultdefineConfig({optimizeDeps:{exclude:['lodash-es']// 不依赖预构建的包}})
是否依赖预构建打包结果如下:
- 依赖预构建(默认):
- 不依赖预构建:
不同环境的 vite 配置
// vite.config.jsimport{ defineConfig }from'vite'exportdefaultdefineConfig(({command})=>{if(command ==='build'){return{// 生产环境配置}}else{return{// 开发环境配置}}})
其中
command
有两个值,根据命令行执行的命令会赋予它不同的值,当
npm run dev
时候,
command
值为
serve
表示开发环境,当
npm run build
时,
command
值为
build
表示生产环境,Vite 会根据执行命令的不同通过
command
值的判断来选择不同的构建配置,形成打包成果物。
为了更好地扩展性写配置,进行一下变体:
// vite.config.jsimport{ defineConfig }from'vite'import viteBaseConfig from'./vite.base.config'// 通用基础配置内容import viteDevConfig from'./vite.dev.config'// 开发环境配置内容import viteProdConfig from'./vite.prod.config'// 生产环境配置内容const envResolver ={'build':()=>{
console.log('生产环境')return{...viteBaseConfig,...viteProdConfig}},'serve':()=>{
console.log('开发环境')return{...viteBaseConfig,...viteDevConfig}}}exportdefaultdefineConfig(({command})=>{// command: build(生产环境), serve(开发环境)return envResolver[command]()})
css配置
Vite 对 css 的处理
如果你用过 Webpack 应该知道,Webpack 默认是不处理
.css
文件的,如果需要处理样式文件需要使用插件配置,其配置原理和具体内容可以参考 webpack入门到实战(上) 。但是,Vite 天生就支持对
.css
文件的直接处理,不需要进行额外的配置,还是上面的例子,这里我们加入引入 CSS 文件的内容:
<!-- index.html --><!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Vite</title></head><body><divclass="app">Vite Demo</div><scriptsrc="./main.js"type="module"></script></body></html>
// main.jsimport'./index.css'
/* index.css */.app{background-color: purple;color: green;}
看下页面元素内容:
发现网络请求中的
index.css
文件内容发生了变化,并且样式内容被插入到了
index.html
的
head
中,Vite 处理
.css
的过程如下:
- 读取到
main.js
中导入了index.css
- 使用
fs
模块读取index.css
文件内容 - 创建
<style>
标签并将读取到的内容copy进这个标签中 - 将这个
<style>
标签及其内容插入到index.html
的head
中 - 将该
index.css
文件中的内容直接替换为 js 脚本(方便热更新和CSS模块化),同时设置content-type
为 js,让浏览器以 js 脚本的形式来执行这个.css
后缀文件。
Vite 对 cssmodule 的处理和配置
当不同组件中使用了相同的类名,打包结果中会出现类名覆盖的问题,所以需要 cssmodule,Vite 对 css 模块化的处理是修改
.css
后缀名为
.module.css
,内容不用变:
// main.jsimport IndexCss from'./index.module.css'
document.querySelector('.app').className = IndexCss.app
通过
vite.config.js
配置可以修改 Vite 对 cssmodule 的默认处理方式,不过一般都没什么需要配置的,这里只是作为一个了解:
// vite.config.jsimport{ defineConfig }from'vite'exportdefaultdefineConfig({// 对css的行为进行配置css:{// 对css模块化的默认行为进行覆盖modules:{// localsConvention: 'camelCaseOnly', // 模块化后的css类名命名规则,驼峰还是中划线形式// scopeBehaviour: 'local', // 配置当前的模块化行为是模块化还是全局化(结果中有hash就是开启了模块化,可以保证样式不被覆盖)// generateScopedName: '[name]_[local]_[hash:5]', // 生成的类名的规则(也可以接收一个函数返回最终的名字)// hashPrefix: '', // hash是根据文件名类名等生成的,这个前缀可以使生成的hash更加复杂更加独特// globalModulePaths: [] // 代表不想要参加到css模块化的路径}}})
Vite 对预处理器的配置
相信你一定用过 less,这里引入一个
.less
样式文件:
// main.jsimport IndexCss from'./index.module.css'import IndexLess from'./index.module.less'const appEle = document.querySelector('.app')
appEle.className = IndexCss.app
appEle.className +=' '+ IndexLess.app
/* index.module.less */.app{font-size: 50px / 2;}
npm i less-D
在没有构建工具的情况下,如果想要单独编译
.less
文件为
.css
文件,在安装了
less
以后就可以直接在命令行执行命令
npx lessc index.less test.css
,less 编译器 lessc 会将
index.less
编译为
test.css
,当我们引入了
.module.less
文件并安装
npm i less -D
,Vite 就可以编译
.less
文件了。
跟 Webpack 类似,在执行命令行时也可以加入一些 Less.js Options 即编译参数,来告诉 less 编译器应该以什么方式来编译,最终结果应该呈现什么样,那么如何在构建工具 Vite 中配置 Less.js Options 呢?
// vite.config.jsimport{ defineConfig }from'vite'exportdefaultdefineConfig({css:{// 对css的行为进行配置modules:{...},preprocessorOptions:{less:{math:'always',// 始终编译数学表达式// 定义全局变量globalVars:{fontSize:'24px'// .less中直接使用 @fontSize 变量}}}}})
devSourcemap
sourceMap 代表文件之间的索引,上线后的代码是被编译压缩后的,当程序出错时,很难给出正确的错误提示位置信息。如果设置了 sourceMap 会索引源代码的错误位置。
// vite.config.jsimport{ defineConfig }from'vite'exportdefaultdefineConfig({css:{modules:{...},preprocessorOptions:{...},devSourcemap:true// 是否开启 css 的 sourcemap}})
devSourcemap
为false
的结果:devSourcemap
为true
的结果:
Vite 对 postcss 的支持
postcss 到底是干什么的呢?这里做个简单的介绍,更多详细说明和配置可以去 postcss 官方文档 查阅。
- 对未来 css 新属性的降级兼容问题(类似
babel
对 js 语法的兼容处理) - 不同浏览器的 css 属性前缀补全功能(
--webkit--
、--ms--
等)
所以,postcss 并不是和 less、sass 同级的东西,它主要用来处理 less 和 sass 编译后的结果(css 内容),给编译结果增加语法降级和前缀补全功能,所以业内也称其为“后处理器”。
Vite 天生对 postcss 有良好的支持:
npm i postcss-preset-env -D# postcss的预设plugin, 会帮我们安装postcss一些基本插件
// vite.config.jsimport{ defineConfig }from'vite'const PostcssPresetEnv =require('postcss-preset-env')exportdefaultdefineConfig({css:{...devSourcemap:true,postcss:{plugins:[PostcssPresetEnv()],// 例如:width: clamp(100px, 30%, 300px); 会语法降级转化为 width: max(100px, min(30%, 300px));}}})
ps:除了在
vite.config.js
中添加
postcss
字面量配置以外,也可以单独创建一个
postcss.config.js
配置文件,导出配置内容也一样生效。字面量的优先级高于配置文件的内容。
Vite 静态资源别名设置
对于前端来说,静态资源可能可能指的是字体、图片这些资源;但是,对于服务端而言,除了 API 对数据库的操作,其他内容几乎都可以算是静态资源,例如
.html
、
.js
文件等等,浏览器加载这些
img
文件、
.js
文件都是有网络请求的。
例如:当浏览器访问
index.html
,后端在接收到这个请求时,对请求 url 进行分析判断(也就是后端路由),会去找到指定的静态 HTML 文件资源返回给浏览器,浏览器接收到返回的 HTML 文件发现是
text/html
类型,就会以页面的形式展示给用户。
在
vite.config.js
中对
alias
进行了别名配置,就允许开发者以下面的路径访问文件:
// main.js// 其中 assets 文件中在 src 文件夹下,用来存放前端静态资源import IndexCss from'./index.module.css'import IndexLess from'./index.module.less'import Img from'@/assets/quan.jpg'const appEle = document.querySelector('.app')
appEle.className = IndexCss.app
appEle.className +=' '+ IndexLess.app
const imgEle = document.createElement('img')
imgEle.src = Img
document.body.appendChild(imgEle)
import{ defineConfig }from'vite'import path from'path'exportdefaultdefineConfig({resolve:{alias:{"@": path.resolve(__dirname,'./src')}}}
alias
别名配置的原理是什么呢?
答:在导入
quan.jpg
这个图片资源时,对于后端来说就是要去请求和加载这个静态资源文件,所以 Vite 服务器在后端路由中,先将请求的路径结合
vite.config.js
配置,将请求路径进行字符串替换,也就是将代码中请求路径中的
@
替换成
path.resolve
方法生成后的绝对路径,通过这个绝对路径找到对应的资源再将结果返回到浏览器。
Vite 生产环境配置
尽管原生 ES Module 现在(2022年11月7日)得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ES Module 仍然效率低下,另外为了在生产环境中获得最佳的加载性能,例如 tree-shaking、懒加载、chunk 代码分割、CSS处理、不同浏览器语法兼容等,Vite 附带了一套 构建优化 的 构建命令,同样的,开箱即用,通过
build.rollupOptions
直接调整底层的 Rollup 选项,如果不额外配置打包参数,就是默认参数。
npm run build
默认执行打包结果:
因为浏览器在下一次访问资源时,如果发现文件名称有跟上一次一样的,就会使用上一次缓存的文件,所以当我们更改了项目内容打包后如果文件名不变,上线后很可能是看不到内容更新的,因为文件名跟上次浏览器使用了缓存的文件。也是因为这个原因,所以当打包结果文件名会加上 hash 值,项目内容有任何变化重新打包后的文件名的 hash 值都会不一样(例如:图片文件没改变,改动了 css 或者 js 内容,那么重新打包后图片资源不变,浏览器可以从缓存中取,节省了网络请求,而css 或 js 内容因为打包后文件名变了,浏览器会重新拉去最新的文件),利用好 hash 算法,可以更好的去控制浏览器缓存机制。
实际项目中,打包的结果最终是要放在服务器上运行的,所以这里使用 VS Code 的插件
Live Server
(为什么以及如何使用这个插件)运行打包后的
index.html
文件,就是打包后的效果了。
注:
Live Server
是基于当前工程目录开启服务的,所以在模拟线上打包结果运行效果时,需要重新开启一个 VS Code 用来打开打包后的结果,也就是
dist
目录(线上环境最终也是将成果物
dist
直接放在服务器),然后再通过插件运行
index.html
文件,呈现线上效果。
更多 Vite 配置
版权归原作者 土豆Coder 所有, 如有侵权,请联系我们删除。