webpack与vite
一、了解 webpack
本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。
入口(entry)
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js
,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点。
module.exports = {
entry: './path/to/my/entry/file.js',
};
输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
在更高层面,在 webpack 的配置中,loader 有两个属性:
test 属性,识别出哪些文件会被转换。
use 属性,定义出在进行转换时,应该使用哪个 loader。
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
};
插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
在上面的示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。
模式(mode)
通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production
。
module.exports = {
mode: 'production',
};
参考官网webpack
二、项目中使用webpack
vue项目
在vue项目中一般会新建一个vue.config.js 进行扩展配置(当然也可以手动创建 webpack.config.js 并配置)
// vue.config.js
module.exports = {
// 入口文件配置(默认是 src/main.js)
pages: {
index: {
entry: 'src/main.js', // 主入口
template: 'public/index.html',
filename: 'index.html'
}
// 多页面配置:可添加其他页面入口
// subpage: { entry: 'src/subpage/main.js', ... }
},
// 其他常用配置
outputDir: 'dist', // 打包输出目录
publicPath: process.env.NODE_ENV === 'production' ? '/prod-path/' : '/', // 公共路径
devServer: {
port: 8080, // 开发服务器端口
open: true, // 自动打开浏览器
proxy: { // 接口代理(解决跨域)
'/api': {
target: 'http://api.example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
},
// 链式修改 Webpack 配置(通过 webpack-chain)
chainWebpack: config => {
// 添加别名
config.resolve.alias
.set('@', path.resolve(__dirname, 'src'))
.set('components', path.resolve(__dirname, 'src/components'));
// 修改图片加载规则
config.module
.rule('images')
.use('url-loader')
.tap(options => ({
limit: 10240, // 小于10kb的图片转为base64
name: 'img/[name].[hash:8].[ext]'
}));
},
// 直接修改 Webpack 配置(合并方式)
configureWebpack: {
plugins: [
// 添加自定义插件
],
optimization: {
// 配置代码分割等
}
}
}
};
react项目
创建 webpack.config.js 并配置
修改 package.json
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production"
}
三、了解vite
Vite 凭借基于原生 ESM 的开发服务器实现极速启动与热更新
,同时通过 Rollup
优化生产构建,兼具简洁配置与强大扩展性,为前端开发提供高效流畅的体验。
构建选项(build)
通过 build 配置项控制生产环境打包输入与输出
export default {
build: {
outDir: 'dist', // 输出目录
assetsDir: 'assets', // 静态资源存放目录
assetsInlineLimit: 4096, // 小于此值的资源将内联为 base64
//sourcemap: false, // 是否生成 sourcemap
minify: env.VITE_APP_ENV == 'prod' ? 'terser' : 'esbuild', //开发、测试环境使用esbuild,它比 terser 快 20-40 倍,压缩率只差 1%-2%;生产环境使用terser
rollupOptions: { // 传递给 Rollup 的输出配置
output: {
chunkFileNames: 'js/[name]-[hash].js', // 用于命名代码拆分时创建的共享块的输出命名
entryFileNames: 'js/[name]-[hash].js', // 用于从入口点创建的块的打包输出格式[name]表示文件名,[hash]表示该文件内容hash值
assetFileNames: "[ext]/[name].[hash].[ext]", // 用于输出静态资源的命名,[ext]表示文件扩展名
}
},
lib: { //打包为库文件
entry: 'src/index.js',
name: 'MyLibrary',
formats: ['es', 'umd'],
fileName: (format) => `my-lib.${format}.js` // 输出文件的名称
}
}
}
模块解析(Resolve)
配置模块查找规则和路径别名
import { resolve } from "path"; // 主要用于alias文件路径别名
function pathResolve (dir) {
return resolve(__dirname, ".", dir);
}
export default {
resolve: {
alias: { // 路径别名
"@": pathResolve("src"),
"@scss": pathResolve("src/assets/scss"),
"@img": pathResolve("src/assets/img"),
"@com": pathResolve("src/components"),
'@config': pathResolve('src/config')
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], // 导入时想要省略的扩展名列表
}
}
模块处理(Module)
CSS 处理:内置支持 CSS、CSS Modules 及预处理器
export default {
css: {
modules: { // CSS Modules 配置
scopeBehaviour: 'local',
localsConvention: 'camelCaseOnly'
},
preprocessorOptions: { // 预处理器配置
scss: {
additionalData: `@import "./src/styles/vars.scss";`
}
},
devSourcemap: true // 开发环境 CSS sourcemap
}
}
服务器选项(Dev Server)
提供快速的开发环境服务
export default {
server: {
port: 8080, // 端口号
open: true, // 自动打开浏览器
proxy: { // 接口代理配置
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
},
hmr: true, // 开启热更新
cors: true, // 允许跨域请求
host: '0.0.0.0' // 指定服务器应该监听哪个 IP 地址,如果设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
}
}
依赖优化选项(Optimization)
依赖预构建提升开发性能
export default {
optimizeDeps: {
include: ['lodash-es', 'axios'], // 强制预构建的依赖
exclude: ['custom-package'], // 不预构建的依赖
esbuildOptions: { // esbuild 配置
target: ['es2020', 'edge88']
}
}
}
插件 (Plugins)
Vite 可以使用插件进行扩展,这得益于 Rollup 优秀的插件接口设计和一部分 Vite 独有的额外选项。
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'
import AutoImport from "unplugin-auto-import/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
export default {
plugins: [
vue(), // Vue 支持
// react(), // React 支持
AutoImport({ // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ["vue"],
extensions: ["vue", "md"],
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
resolvers: [ElementPlusResolver()],
}),
]
}
具体插件可以看这里
插件
公共路径配置(Base)
开发或生产环境服务的公共基础路径。
export default {
base: process.env.NODE_ENV === 'production' ? '/app/' : '/'
}
参考vite中文网
四、项目中使用vite
import { defineConfig } from 'vite'
// import vue from '@vitejs/plugin-vue'
// import react from '@vitejs/plugin-react'
function pathResolve (dir) {
return resolve(__dirname, ".", dir);
}
export default defineConfig({
// 根据框架类型启用
// plugins: [vue()], // Vue 项目启用
// plugins: [react()], // React 项目启用
// 项目基础路径
base: '/',
server: {
port: 8080, // 开发端口
open: false, // 自动打开浏览器
hot: true, // 启用热更新
host: env.VITE_APP_HOST, // 指定服务器应该监听哪个 IP 地址,如果设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
proxy: { // 接口代理(解决跨域)
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
build: {
outDir: 'dist', // 输出目录
assetsDir: 'assets',// 静态资源目录
sourcemap: false, // 不生成 sourcemap(生产环境)
rollupOptions: {
output: {
// 静态资源分类输出
assetFileNames: '[ext]/[name]-[hash].[ext]',
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js'
}
}
},
resolve: {
alias: {
'@': pathResolve('src'),
'components': pathResolve('src/components'),
'@scss': pathResolve('src/assets/scss')
},
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue', '.json'] // 导入时想要省略的扩展名列表
},
css: {
devSourcemap: true, // 开发环境启用 CSS sourcemap
modules: {
// CSS Modules 基础配置
scopeBehaviour: 'local'
}
},
})
五、关于webpack与vite的面试题
1.webpack 执行原理?
从入口模块出发,递归解析所有依赖模块并构建依赖关系图,然后将这些模块按一定规则打包成一个或多个浏览器可识别的输出文件,整个过程包括解析、编译转换、代码拆分、优化等步骤,最终生成优化后的静态资源。
参考官网Under The Hood
2.为什么 Vite 速度⽐ Webpack 快?
- 开发模式的差异
在开发环境中, Webpack 是先打包再启动开发服务器,⽽ Vite 则是直接启动,然后再按需编译
依赖⽂件。(可以启动项⽬后检查源码 Sources 那⾥看到)
这意味着,当使⽤ Webpack 时,所有的模块都需要在开发前进⾏打包,这会增加启动时间和构建时
间。
⽽ Vite 则采⽤了不同的策略,它会在请求模块时再进⾏实时编译,这种按需动态编译的模式极⼤地缩短了编译时间,特别是在⼤型项⽬中,⽂件数量众多, Vite 的优势更为明显。 - 对ES Modules的⽀持
现代浏览器本⾝就⽀持 ES Modules ,会 主动发起 请求去获取所需⽂件。Vite充分利⽤了这⼀点,
将开发环境下的模块⽂件直接作为浏览器要执⾏的⽂件,⽽不是像 Webpack 那样 先打包 ,再交给浏
览器执⾏。这种⽅式减少了中间环节,提⾼了效率。
什么是ES Modules?
通过使⽤ export 和 import 语句,ES Modules 允许在浏览器端导⼊和导出模块。
当使⽤ ES Modules 进⾏开发时,开发者实际上是在构建⼀个 依赖关系图 ,不同依赖项之间通过导⼊
语句进⾏关联。
主流浏览器(除IE外)均⽀持ES Modules,并且可以通过在 script 标签中设置 type=“module” 来
加载模块。默认情况下,模块会延迟加载,执⾏时机在⽂档解析之后,触发DOMContentLoaded事件前。 - 底层语⾔的差异
Webpack 是基于Node.js
构建的,⽽ Vite 则是基于esbuild
进⾏预构建依赖。esbuild
是采
⽤Go
语⾔编写的,Go 语⾔是纳秒
级别的,⽽ Node.js 是毫秒
级别的。因此,Vite 在打包速度上
相⽐Webpack 有 10-100 倍的提升。
什么是预构建依赖?
预构建依赖通常指的是在项⽬ 启动或构建 之前,对项⽬中所需的依赖项进⾏预先的 处理或构建 。这
样做的好处在于,当项⽬实际运⾏时,可以 直接使⽤ 这些已经预构建好的依赖,⽽⽆需再进⾏实时的编译或构建,从⽽提⾼了应⽤程序的运⾏速度和效率。 - 热更新的处理
在 Webpack 中,当⼀个模块或其依赖的模块内容改变时,热更新是基于⽂件级别的,需要重新构建并刷新整个⻚⾯。
在 Vite 中,当某个模块内容改变时,只需要让浏览器重新请求该模块即可,不需要刷新整个⻚⾯,这⼤⼤减少了热更新的时间。
总结
总的来说,Vite 之所以⽐ Webpack 快,主要是因为它采⽤了 不同的开发模式 、 充分利⽤了现代浏览器的 ES Modules ⽀持 、 使⽤了更⾼效的底层语⾔, 并优化了热更新的处理 。这些特点使得Vite在⼤型项⽬中具有显著的优势,能够快速启动和构建,提⾼开发效率。
3.vite构建项目 开发环境和生成环境有什么区别?
开发环境:基于浏览器原生 esbuild 模块运行,无需预打包,通过开发服务器实时响应请求并处理依赖,实现毫秒级启动和即时热更新(HMR),优先保证开发效率。
生产环境:使用 Rollup 进行预打包,会对代码进行压缩、分割、 tree-shaking 等优化,生成高度优化的静态资源,优先保证输出产物的性能和体积。
esbuild ⽤于开发服务器阶段,通过实时编译和提供模块来实现快速的冷启动和热模块替换。⽽ Rollup ⽤于⽣产构建阶段,将源代码打包为最终可发布的⽂件,以⽤于部署到⽣产环境。
- esbuild: esbuild 是⼀个快速、可扩展的 JavaScript 打包器,它被⽤作 Vite 的默认构建⼯具。
esbuild 的主要任务是将源代码转换为浏览器可以理解的代码,同时还⽀持压缩、代码分割、按需
加载等功能。esbuild 利⽤其⾼性能的构建能⼒,实现了快速的开发服务器和热模块替换。 - Rollup: Rollup 是⼀个 JavaScript 模块打包⼯具,也是 Vite 的另⼀个基础依赖。在 Vite 中,
Rollup 主要⽤于⽣产构建阶段。它通过静态分析模块依赖关系,将多个模块打包为⼀个或多个最终
的输出⽂件。Rollup ⽀持多种输出格式,如 ES 模块、CommonJS、UMD 等,可以根据项⽬的需要进⾏配置。
4.vite 底层原理?
- ES 模块:Vite 使⽤了 ES 模块来管理和加载模块。ES 模块是 JavaScript 的标准模块系统,相⽐于
传统的 CommonJS 或 AMD,ES 模块具有更好的静态分析能⼒和更⾼的性能。Vite 通过使⽤浏览器原⽣的 ES 模块加载器,可以实现按需加载和快速构建。 - HTTP/2:Vite 借助于现代浏览器的 HTTP/2 ⽀持来实现更⾼效的资源加载。HTTP/2 ⽀持多路复⽤,可以同时请求多个资源,避免了传统的 HTTP/1 中的队头阻塞问题,加快了资源加载速度。
- 编译器:Vite 使⽤了⾃定义的编译器来处理开发时的模块解析和转换。它能够识别模块的依赖关
系,并将模块转换为浏览器可直接执⾏的代码。Vite 的编译器⽀持热模块替换(HMR),可以在代码修改时⾃动更新浏览器中的⻚⾯,提⾼开发效率。 - 中间件:Vite 使⽤了基于 Koa 框架的中间件来处理开发服务器。通过中间件,Vite 可以拦截和处理
开发时的 HTTP 请求,并根据请求的路径返回相应的模块⽂件。中间件还可以处理各种开发时的特殊需求,如代理 API 请求、路由转发等。
Things you said