什么是构建

构建工具,最终的目标都是转化开发环境的代码为生产环境中可用的代码。在不同的前端项目中使用的技术栈是不一样的。不同的框架、不同的样式处理方案等,为了生产出生产环境可用的 JS、CSS,构建工具实现了诸如:代码转换、代码压缩、tree shaking、code spliting 等。

Webpack

五个核心概念

Entry

指定 webpack 打包的入口

Output

指定 webpack 打包资源的存放位置

Loader

让 webpack 能够处理非 JavaScript 文件, 用于对模块的代码转换,如将 Typescript 转为 Javascript、将 less、sass 转为 css、将.vue、.tsx 等文件转为 js、css 等。

Plugins

可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等, 解决 loader 无法实现的其他事

mode

指定 webpack 当前的构建环境类型。设置mode可以自动触发webpack内置的函数,达到优化的效果

Loader和Plugin的区别

Loader 模块导出一个运行在Node上的JavaScript函数。返回转换后的结果。因为 webpack 只认识 JS,所以 loader 相当于它的翻译官,对资源做转译的预处理工作。
Plugin 是一个基于事件流的插件,它是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在整个编译生命周期都可以访问 compiler 对象。

配置
根据根目录里webpack.config.js
如以下配置示例

const webpack = require('webpack');
const path = require('path');
const { HotModuleReplacementPlugin } = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /.vue$/,
use: {
loader: 'vue-loader',
},
},
{
test: /.css$/,
use: ['vue-style-loader', 'css-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
plugins: [
new HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
],
devServer:{
port:3000,// 设置端口号
host:"",
open:true,// 自动在浏览器中打开页面
compress:true,// false关闭gzip压
hot: true
}
};

编写loader和plugin 参考 webpack文档

Rollup

核心概念

  • 入口文件
    入口文件是你的应用程序或库的主文件,是 Rollup 开始构建的起点。入口文件指定了依赖关系图的根节点。
  • 依赖关系
    依赖关系图是 Rollup 根据入口文件和所有相关依赖自动构建的一张图表。它了各个模块之间的依赖关系,包括每个模块依赖的其他模块和导出的内容。
  • 模块
    模是代码的单独单元,可以是一个文件或一个文件中的一个部分。每个模块都有自己的作域,并且可以导入和导出变量、函数和类等。
  • 导入和导出
    通过导入和导出语法,模块可以与其他模块进行交互。导入语法允许模块使用其他模块中导出的内容,而导出语法允许模块将自己的内容暴露给其他模块使用。
  • Bundle
    Bundle 是 Rollup 构建输出的最终文件。它包含所有被捆绑在一起的模块和它们的依赖关系。Bundle 可以是单个 JavaScript 文件,也可以是多个文件组成的目录。
  • 模块格式
    模块格式定义了将模块打包到 Bundle 中的方式。常见的模块格式包括 ES 模块(ESM),CommonJS 模块(CJS),AMD 模块以及全局变量 IIFE)等。
  • 插件
    插件是 Rollup 的扩展,用于在构建过程中自定义和增强功能。它们可以在各个阶段进行代码转换、优化、添加附加功能和处理其他任务。通过使用插件,你可以根据自己的特定需求来扩展和定制 Rollup 的行为。
  • Tree Shaking
    Tree Shaking 是 Rollup 的一个重要特性,它能够通过静态分析准确识别未使用的代码,并将其从 Bundle 中删除。这样可以减小 Bundle 的大小,优化应用的性能。

配置

import babel from 'rollup-plugin-babel';
import serve from 'rollup-plugin-serve';

export default {
/* 设置打包的入口文件 */
input: "./src/index.js",
/* 输出配置项 */
output: {
/* 配置出口路径 */
file: "dist/umd/xxx.js",
/* 统一模块规范 */
format: "umd",
/* es6->es5 开启源码调试(显示报错位置) */
sourcemap: true
},
/* 使用插件 */
plugins: [
babel({
exclude: "node_modules/**"
}),
process.env.ENV === 'development' ? serve({
open: true,
/* 默认打开的 HTML 文件路径 */
openPage: "/public/index.html",
port: 3000,
contentBase: ""
}) : null
]
}

vite

配置 vite.config.js

export default defineConfig({
root:默认process.cwd(), index.html所在的位置,绝对位置或相对位置
base:开发或生产环境服务的公共基础路径
绝对 URL 路径名,例如 /foo/
完整的 URL,例如 https://foo.com/
空字符串或 ./(用于开发环境)
通过命令指定:vite build --base=/my/public/path/
代码中获取base:import.meta.env.BASE_URL全局变量在代码中使用,原样出现(例如import.meta.env['BASE_URL']是无效的)
mode:将会把serve和build时的模式都覆盖掉。也可以通过命令行 --mode 选项来重写
'development'(serve)
'production'(build)
define:定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换
{
__DEV__: 'dev',
}
plugins:[] 插件数组
publicDir:作为静态资源服务的文件夹。该目录中的文件在开发期间在 / 处提供,并在构建期间复制到 outDir 的根目录,并且始终按原样提供或复制而无需进行转换。
该值可以是文件系统的绝对路径,也可以是相对于项目的根目录的相对路径。
默认'public'
cacheDir:存储缓存文件的目录。此目录下会存储预打包的依赖项或 vite 生成的某些缓存文件,使用缓存可以提高性能。
如需重新生成缓存文件,你可以使用 --force 命令行选项或手动删除目录。此选项的值可以是文件的绝对路径,也可以是以项目根目录为基准的相对路径。当没有检测到 package.json 时,则默认为 .vite
默认"node_modules/.vite"
assetsInclude:解析额外的定义内置type以外的静态导入资源
['**/*.gltf']
logLevel:调整控制台输出的级别,默认为 'info'
'info' | 'warn' | 'error' | 'silent'
clearScreen:设为 false 可以避免 Vite 清屏而错过在终端中打印某些关键信息。命令行模式下可以通过 --clearScreen false 设置。
默认:true
envDir:用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径
默认:根路径
envPrefix:自定义环境变量前缀,以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中
默认:VITE_


// 解析相关
resolve:{
alias:路径别名
{
"@": path.resolve(__dirname, "src"),
}

[{
find: '@', 字符串|正则
replacement: path.resolve(__dirname, 'src')
}]
dedupe:使用此选项强制 Vite 始终将列出的依赖项解析为同一副本
比如当安装了两个不同版本的依赖,如vue2和vue3,通过这个声明最终引入的版本
[]
condition:情景导出
[{
"exports": {
".": {
"import": "./index.esm.js",
"require": "./index.cjs.js"
}
}
}]
mainFields:根据package.json中的字段,在不同环境中导入库的入口文件位置
import引入的文件对应module中的路径
require引入的文件对应main中的路径
默认:['module', 'jsnext:main', 'jsnext','main']
extensions导入时想要省略的扩展名列表。不建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持
默认:['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']
preserveSymlinks:启用此选项会使 Vite 通过原始文件路径(即不跟随符号链接的路径)而不是真正的文件路径(即跟随符号链接后的路径)确定文件身份
默认:false

}

// css相关
css:{
modules:配置 CSS modules 的行为。选项将被传递给 postcss-modules。
postcss:内联的 PostCSS 配置(格式同 postcss.config.js),或者一个(默认基于项目根目录的)自定义的 PostCSS 配置路径
preprocessorOptions:指定传递给 CSS 预处理器的选项。
{
scss: {
additionalData: `$injectedColor: orange;`
}
}
}

// JSON相关
json:{
namedExports:是否支持从 .json 文件中进行按名导入。
默认:true
stringify:若设置为 true,导入的 JSON 会被转换为 export default JSON.parse("...")
默认:false

}

// esbuild相关
esbuild:{
jsxFactory: 'h',
jsxFragment: 'Fragment' 以上为自定义JSX

ESbuild会被应用在 ts、jsx、tsx 文件,以下选项对要处理的文件类型进行配置
include:string | RegExp | (string | RegExp)[]
exclude:string | RegExp | (string | RegExp)[]

jsxInject:自动为每一个被 ESbuild 转换的文件注入内容
`import React from 'react'`
}

// server相关
server:{
host:指定服务器应该监听哪个 IP 地址, 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址
默认:'127.0.0.1'
命令设置:--host 0.0.0.0 或 --host
port:指定开发服务器端口。注意:如果端口已经被使用,Vite 会自动尝试下一个可用的端口,所以这可能不是开发服务器最终监听的实际端口
默认:3000
strictPort:设为true时若端口已被占用则会直接退出,而不是尝试下一个可用端口
默认:false
https:启用 TLS + HTTP/2。注意:当 server.proxy 选项 也被使用时,将会仅使用 TLS
当为true:启用 TLS + HTTP/2。注意:当 server.proxy 选项 也被使用时,将会仅使用 TLS
这个值也可以是一个传递给 https.createServer() 的 选项对象
https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener
open:在开发服务器启动时自动在浏览器中打开应用程序。
'/docs/index.html'
设置打开的浏览器:设置环境变量 process.env.BROWSER='firefox'
open其他配置:https://github.com/sindresorhus/open#app
proxy:服务器代理
{
'/foo': 'http://localhost:4567', 字符串简写写法
'/api': { 以 ^ 开头,将会被解释为正则,如:'^/fallback/.*'
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
configure: (proxy, options) => {
// proxy 是 'http-proxy' 的实例
}
},
'/socket.io': { 代理 websockets or socket.io
target: 'ws://localhost:3000',
ws: true
}
}
cors:为开发服务器配置 CORS。默认启用并允许任何源
传递一个 选项对象 来调整行为,https://github.com/expressjs/cors#configuration-options
设置false表示禁用
force:依赖预构建
设置为 true 强制使依赖预构建。
hmr:热更新相关
false禁用
{
protocol?: string, 协议
host?: string,
port?: number,
path?: string,
timeout?: number,
overlay?: boolean, 为 false 可以禁用开发服务器错误的屏蔽
clientPort?: number, 只在客户端的情况下覆盖端口,这允许你为 websocket 提供不同的端口,而并非在客户端代码中查找。
如果需要在 dev-server 情况下使用 SSL 代理,这非常有用。
server?: Server, 当使用 server.middlewareMode 或 server.https 时,你需将 server.hmr.server 指定为你 HTTP(S) 的服务器,
这将通过你的服务器来处理 HMR 的安全连接请求。
这在使用自签证书或想通过网络在某端口暴露 Vite 的情况下,非常有用。
}
middlewareMode:以中间件模式创建 Vite 服务器
'ssr' | 'html'SSR中使用
fs.strict:限制为工作区 root 路径以外的文件的访问
默认:true
fs.allow:限制哪些文件可以通过 /@fs/ 路径提供服务,Vite 将会搜索此根目录下潜在工作空间并作默认使用
fs.deny:用于限制 Vite 开发服务器提供敏感文件的黑名单。
默认为 ['.env', '.env.*', '*.{pem,crt}']
watch:监听文件改变
通过命令:vite build --watch
{
ignored: ['!**/node_modules/your-package-name/**'] 默认会忽略对 .git/ 和 node_modules/ 目录的监听,
如果需要对 node_modules/ 内的包进行监听,可以为 server.watch.ignored 赋值一个取反的 glob 模式
其他选项:使用的是rollup的选项配置:https://rollupjs.org/guide/en/#watch-options
}
}

// optimizeDeps依赖优化相关
optimizeDeps:{
entries:依赖入口点
默认情况下,Vite 会抓取你的 index.html 来检测需要预构建的依赖项
如果指定了 build.rollupOptions.inputVite 将转而去抓取这些入口点
string | string[]:指定自定义条目,该值需要遵循 fast-glob 模式
exclude:['your-package-name'] 被watch中手动设置监听的包必须被排除在优化之外,以便它能出现在依赖关系图中并触发热更新
include:默认情况下,不在 node_modules 中的,链接的包不会被预构建。使用此选项可强制预构建链接的包。
[]
应用场景:一些第三方插件中的依赖不是node_modules中的并且也不是es module的格式
esbuildOptions:在部署扫描和优化过程中传递给 esbuild 的选项。



}

// build构建相关
build:{
target:设置最终构建的浏览器兼容目标
默认:'modules':指支持原生 ES 模块的浏览器。
"esnext" :即假设有原生动态导入支持,并且将会转译得尽可能小:
如果 build.minify 选项为 'terser''esnext' 将会强制降级为 'es2019'
其他情况下将完全不会执行转译。
'es2015':自定义目标也可以是一个 ES 版本
["chrome58",...]:一个浏览器版本或是多个目标组成的一个数组
polyfillModulePreload:用于决定是否自动注入 module preload 的 polyfill.
true:此 polyfill 会被自动注入到每个 index.html 入口的 proxy 模块中
outDir:指定输出路径(相对于 项目根目录).
默认:dist
assetsDir:指定生成静态资源的存放路径(相对于 build.outDir
默认:assets
assetsInlineLimit:小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求。
默认:4096 (4kb)
设置为 0 可以完全禁用此项。
cssCodeSplit:css代码分割
默认:true,在异步 chunk 中导入的 CSS 将内联到异步 chunk 本身,并在其被加载时插入。
false:整个项目中的所有 CSS 将被提取到一个 CSS 文件中
cssTarget:允许用户为 CSS 的压缩设置一个不同的浏览器 target
默认值和配置与target一致
sourcemap:构建后是否生成 source map 文件
默认:false
true:创建一个独立的 source map 文件
'inline':source map 将作为一个 data URI 附加在输出文件中
'hidden':与 'true' 相似,只是 bundle 文件中相应的注释将不被保留
rollupOptions:自定义的 Rollup 打包配置,并将与 Vite 的内部 Rollup 选项合并
https://rollupjs.org/guide/en/#big-list-of-options
commonjsOptions:传递给 @rollup/plugin-commonjs 插件的选项。
dynamicImportVarsOptions:传递给 @rollup/plugin-dynamic-import-vars 的选项。
lib:打包库
{
entry: string,
name?: string, 暴露的全局变量
formats?: ('es' | 'cjs' | 'umd' | 'iife')[], 包含 'umd''iife' 时是必须的。默认 formats 是 ['es', 'umd']
fileName?: string | ((format: ModuleFormat) => string) 输出的包文件名
默认 fileName 是 package.json 的 name 选项
}
manifest:包含了没有被 hash 过的资源文件名和 hash 后版本的映射。
true:包含了没有被 hash 过的资源文件名和 hash 后版本的映射。
字符串:作为 manifest 文件的名字
ssrManifest:生成 SSR 的 manifest 文件,以确定生产中的样式链接与资产预加载指令
配置和manifest一致
ssr:生成面向 SSR 的构建
默认:undefined
字符串:用于直接定义 SSR 的入口
true:需要通过设置 rollupOptions.input 来指定 SSR 的入口
minify:压缩相关
默认:'esbuild'
false:禁用
'terser'
在lib模式下使用 'es' 时,build.minify 选项将失效
terserOptions:传递给 Terser 的更多 minify 选项。
write:磁盘写入相关
默认:true
false:禁用将构建后的文件写入磁盘
emptyOutDir:关闭警告
默认:若 outDir 在 root 目录下,则为 trueVite 会在构建时清空该目录
outDir在根目录之外:会抛出一个警告避免意外删除掉重要的文件。可以设置该选项来关闭这个警告
通过命令: --emptyOutDir 来使用
reportCompressedSize:启用/禁用 gzip 压缩大小报告。压缩大型输出文件可能会很慢,因此禁用该功能可能会提高大型项目的构建性能。
默认:true
chunkSizeWarningLimit:chunk 大小警告的限制(以 kbs 为单位)
默认:500
watch:构建监听器
默认:null
{}:启用 rollup 的监听器。在涉及只用在构建时的插件时和集成开发流程中很常用。

}

// 构建预览preview相关
preview:{
host:为开发服务器指定 ip 地址,默认取server.host
0.0.0.0true:监听所有地址,包括局域网和公共地址。
通过命令:--host 0.0.0.0 或 --host
port:指定开发服务器端口
默认:4173
strictPort:默认取server.strictPort
https:默认取server.https
open:默认取server.openy
proxy:默认取server.proxy
cors:默认取server.cors

}

// SSR相关
ssr:{
external:为 SSR 强制外部化的依赖。
[]
noExternal:防止被 SSR 外部化依赖项
string | RegExp | (string | RegExp)[]
true:将没有依赖被外部化
target:SSR 服务器的构建目标。
默认:'node'
'webworker'

}

// Worker相关
worker:{
format:worker bundle 的输出类型。
默认:'iife'
'es'
plugins:适用于 worker bundle 的 Vite 插件。
[]
rollupOptions:用于构建 worker bundle 的 Rollup 配置项
}


})

其实配置详解大家都差不多, 目的也是相同的

Vite 相比于 Webpack 而言,没有打包的过程,而是直接启动了一个开发服务器 devServer。Vite 劫持浏览器的 HTTP 请求,在后端进行相应的处理将项目中使用的文件通过简单的分解与整合,然后再返回给浏览器(整个过程没有对文件进行打包编译)。所以编译速度很快。

Vite 底层使用 Esbuild 实现对.ts、jsx、.js 代码文件的转化,所以先看下什么是 es-build。 Esbuild 是一个 JavaScript`` Bundler 打包和压缩工具,它提供了与 Webpack、Rollup 等工具相似的资源打包能力。可以将 JavaScript 和 TypeScript 代码打包分发在网页上运行。但其打包速度却是其他工具的 10 ~ 100 倍。

生产阶段使用Rollup打包。

Vite 其核心原理是利用浏览器现在已经支持 ES6 的 import,碰见 import 就会发送一个 HTTP 请求去加载文件,Vite 启动一个 koa 服务器拦截这些请求,并在后端进行相应的处理将项目中使用的文件通过简单的分解与整合,然后再以 ESM 格式返回返回给浏览器。Vite 整个过程中没有对文件进行打包编译,做到了真正的按需加载,所以其运行速度比原始的 webpack 开发编译速度快出许多!