vite和webpack的区别
一、Webpack
webpack是一个静态模块的打包工具。他会在内部从一个或多个入口点构建一个依赖图,然后将项目中所需的每一个模块合成一个或多个bundles进行输出,他们均为静态资源。输出的文件已经编译好了可以在浏览器运行。
webpack五大核心概念
- 入口
- 输出
- 解析器
- 插件
- 模式
1.webpack的作用
- 模块打包
可以将不同模块的文件打包整合在一起,并保证他们之间的引用正确,执行有序
- 编译兼容
通过webpack的loader机制,可以编译转换诸如 .less .vue .jsx 这类在浏览器中无法识别的文件
- 能力扩展
通过webpack的plugin机制,可以进一步实现诸如按需加载,代码压缩等功能
2. Loader是什么?
能够让webpack去处理那些非js的文件,webpack自身只能理解js,json
可以是同步的也可以是异步的,支持链式调用,链中每个loader会处理之前已处理过的资源
-
在 webpack 的配置中,loader 有两个属性:
test:识别出哪些文件会被转换
use:定义在进行转换时,应该使用哪个 loader
链式调用
配置多个loader时,从右到左/从上到下执行
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
}
}
或
module.exports = {
module: {
rules: [
{
test: /\.scss$/, // 注意这里的 test 改成了 .scss
use: [
'style-loader',
{
loader: 'css-loader',
options: { modules: true }
},
'sass-loader'
]
}
]
}
};
由最外层的loader把css注入到DOM中
3.有哪些常见的loader?
babel-loader:使用Babel加载ES2015+ 代码并将其转换为ES5
ts-loader: 将TypeScript转换成JavaScript
sass-loader:将SCSS/SASS代码转换成CSS
style-loader:将模块导出的内容作为样式添加到DOM中
css-loader:加载CSS文件并解析import的CSS文件
less-loader:将Less编译为CSS
node-loader:处理Node.js插件
source-map-loader:加载额外的Source Map文件,以方便断点调试
4. Pluhin是什么?
打包优化,资源管理,注入环境变量。plugin 会运行在 webpack 的不同阶段,贯穿整个编译周期,目的在于解决 loader 无法实现的其他事。
- 常见的Plugin
clean-webpack-plugin: 用于在打包前清理上一次项目生成的bundle文件
mini-css-extract-plugin: 分离样式文件,CSS提取为独立文件
webpack-bundle-analyzer: 可视化Webpack输出文件的体积
speed-measure-webpack-plugin: 可以看到每个Loader和Plugin执行耗时
optimize-css-assets-webpack-plugin:压缩css文件
css-minimizer-webpack-plugin:压缩css文件(用于 webpack 5)
uglifyjs-webpack-plugin:压缩js文件
compression-webpack-plugin:启用gzip压缩
html-webpack-plugin:自动生成一个html文件,并且引用bundle.js文件
terser-webpack-plugin: 可以压缩和去重 js 代码(Webpack4)
5. Loader和Plugin的区别?
-
loader 运行在打包文件之前
plugins 在整个编译周期都起作用 -
loader 在 module.rules 中配置,类型为数组。每一项都是 Object,包含了 test、use 等属性
Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 实例,参数都通过构造函数传入 -
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过Webpack 提供的 API 改变输出结果
对于 loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将 A.scss 转变为 B.css ,单纯的文件转换过程
6. Webpack 的热更新原理
Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
// 开启 HMR 特性
hot: true
// hotOnly: true
}
}
通过上述这种配置,如果我们修改并保存css文件,确实能够以不刷新的形式更新到页面中。但是,当我们修改并保存js文件之后,页面依旧自动刷新了,这里并没有触发热模块。所以,HMR并不像 Webpack 的其他特性一样可以开箱即用,需要有一些额外的操作,我们需要去指定哪些模块发生更新时进行HRM。
1.使用 module.hot.accept()
通过在代码中显式调用 module.hot.accept()
,可以指定哪些模块发生变化时进行热更新。
if (module.hot) {
module.hot.accept('./myModule.js', () => {
const newModule = require('./myModule.js');
console.log('myModule updated:', newModule);
});
}
2.配置 HotModuleReplacementPlugin
和 devServer.hot
在 webpack.config.js
中启用 HMR 插件和开发服务器配置。
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: {
hot: true
}
};
3.React 项目中使用 react-refresh
在 React 项目中可使用 @pmmmwh/react-refresh-webpack-plugin
实现组件级别的 HMR。
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
plugins: [
new ReactRefreshWebpackPlugin()
]
};
7. webpack proxy工作原理?为什么能解决跨域?
webpack 中提供服务器的工具为 webpack-dev-server,是 webpack 官方推出的一款开发工具,将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在了一起,目的是为了提高开发者日常的开发效率,只适用在开发阶段
- 配置 通过devServer属性提供
module.exports = {
// ...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
proxy: {
'/api': {
target: 'https://api.github.com'
}
}
// ...
}
}
利用 http-proxy-middleware 这个 http 代理中间件,实现请求转发给其他服务器。
代理服务器是从开发服务器本身的地址(例如 localhost:3000)内部向目标地址发请求的,但这个请求并不经过浏览器,是 Webpack 的开发服务器直接去访问你的后端服务器地址,服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制,所以不会有跨域问题。
二、Vite
1. 如何指定 vite 插件 的执行顺序?
可以使用 enforce 修饰符来强制插件的位置:
pre:在 Vite 核心插件之前调用该插件
post:在 Vite 构建插件之后调用该插件
2. Vite是否支持 commonjs 写法?
纯业务代码,一般建议采用 ESM 写法。如果引入的三方组件或者三方库采用了 CJS 写法,vite 在预构建的时候就会将 CJS 模块转化为 ESM 模块。
3. 为什么说 vite 比 webpack 要快
-
vite 不需要做全量的打包
vite 在解析模块依赖关系时,利用了 esbuild,更快(esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍) -
按需加载:在HMR(热更新)方面,当改动了一个模块后,vite 仅需让浏览器重新请求该模块即可,不像 webpack 那样需要把该模块的相关依赖模块全部编译一次,效率更高。
-
由于现代浏览器本身就支持 ES Module,会自动向依赖的Module发出请求。vite充分利用这一点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像 webpack 那样进行打包合并。
-
按需编译:当浏览器请求某个模块时,再根据需要对模块内容进行编译,这种按需动态编译的方式,极大的缩减了编译时间。
-
webpack 是先打包再启动开发服务器,vite 是直接启动开发服务器,然后按需编译依赖文件。由于 vite在启动的时候不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。
4. vite 对比 webpack ,优缺点在哪?
(1)优点:
-
更快的冷启动: Vite 借助了浏览器对 ESM 规范的支持,采取了与 Webpack 完全不同的 unbundle 机制
-
更快的热更新: Vite 采用 unbundle 机制,所以 dev server 在监听到文件发生变化以后,只需要通过 ws 连接通知浏览器去重新加载变化的文件,剩下的工作就交给浏览器去做了。
(2)缺点:
-
开发环境下首屏加载变慢:由于 unbundle 机制, Vite 首屏期间需要额外做其它工作。不过首屏性能差只发生在 dev server 启动以后第一次加载页面时发生。之后再 reload 页面时,首屏性能会好很多。原因是 dev server 会将之前已经完成转换的内容缓存起来
-
开发环境下懒加载变慢:由于 unbundle 机制,动态加载的文件,需要做 resolve 、 load 、 transform 、 parse 操作,并且还有大量的 http 请求,导致懒加载性能也受到影响。
-
Webpack 有大量的插件和加载器可以使用,可以实现各种复杂的构建场景,例如代码分割、按需加载、CSS 预处理器等;Vite 的插件和加载器相对较少
当需要打包到生产环境时,Vite使用传统的rollup进行打包,所以,vite的优势是体现在开发阶段,缺点也只是在开发阶段存在。
5. Vite常见配置
- css.preprocessorOptions: 传递给 CSS 预处理器的配置选项,常用来设置:自动引入全局样式、定义全局变量、启用某些功能开关(如 JS 支持)
- 引入全局变量/全局变量文件
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `@import '/src/assets/styles/variables.scss';` // 引入全局变量文件
}
less: {
modifyVars: {
'primary-color': '#1DA57A'
}
}
}
}
})
- 启用js支持
Less 有一个配置项叫 javascriptEnabled,表示是否允许在 Less 文件中使用 JavaScript 表达式。
默认情况Less 是不允许你在样式中写 JS 表达式的,因为这可能带来安全隐患或增加复杂度。
你可以这样启用:
js
// Vite 中的配置
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true
}
}
}
使用 JS 表达式的场景示例:
@var: `Math.floor(Math.random() * 10)`; // JS 表达式
.width {
width: @var * 10px;
}
- css.postcss: PostCSS 也是用来处理 CSS 的,只不过它更像是一个工具箱,可以添加各种插件来处理 CSS(解决浏览器样式兼容问题、浏览器适配等问题)。例如:移动端使用 postcss-px-to-viewport 对不同设备进行布局适配:
// vite.config.js
import { defineConfig } from 'vite'
import postcssPxToViewport from 'postcss-px-to-viewport'
export default defineConfig({
css: {
postcss: {
plugins: [
// viewport 布局适配
postcssPxToViewport({
viewportWidth: 375
})
]
}
}
})
- server.proxy
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
}
})
- bulid.outdir
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
build: {
outDir: 'build' // 打包文件的输出目录
}
})
- plugins