前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >入门webpack的最佳实践(基于webpack4.X 5.X)-- 打包配置优化

入门webpack的最佳实践(基于webpack4.X 5.X)-- 打包配置优化

作者头像
Jou
发布2022-09-28 14:10:35
5700
发布2022-09-28 14:10:35
举报
文章被收录于专栏:前端技术归纳前端技术归纳

theme: channing-cyan

导语

来到这家公司之后,一直在使用webpack,也写了不少笔记,但是都比较零散,现在决定整理一下webpack相关的知识点,由浅入深,方便自己后续查漏补缺,后续会一直更新。

目录

在基础篇中,我们已经构造好了入口,出口,loader,以及js压缩和css压缩分离等基础配置,在本文中,将从以下几个方法进行配置的优化。

  • 多入口打包
  • 多环境打包
  • 文件指纹(hash值)
  • source-map

基础篇传送:https://cloud.tencent.com/developer/article/2126706

多入口打包

现在我们开发的大部分应用都是单页面应用,在单页面打包时,我们的配置是

代码语言:javascript
复制
{
    entry: path.resolve(__dirname, 'src/index.jsx'),
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: '[name].[chunkhash].js',
      publicPath: '/',
    },
}

而多页面应用,如电商应用,打包之后有多个互不影响的页面,所以我们需要修改入口配置为多个入口,这里可以直接将enrty改成数组,如

代码语言:javascript
复制
{
    entry: path.resolve(__dirname, 'src/home/index.jsx','src/login/index.jsx'),
}

如果这样修改的话,我们每次新加页面都需要修改这里的配置,扩展性非常的差,正确的处理方法是,可以用正则来匹配根文件夹下的目录,每一个目录代表一个页面,比如,我们的工程目录是这样的,

这里有两个页面home,login,那么我们可以这样来匹配入口

代码语言:javascript
复制
const setMpa = () => {

  // 多页面打包的入口集合
  const entry = {};

  // 多页面打包的模板集合
  const htmlWebpackPlugins = [];

  // 借助 glob 获取 src 目录下的所有入口文件
  const entryFiles = glob.sync(path.resolve(__dirname, './src/*/index.jsx'));

  // 遍历文件集合,生成所需要的 entry、htmlWebpackPlugins 集合
  entryFiles.map((item, index) => {
    const match = item.match(/src\/(.*)\/index\.jsx$/);
    const pageName = match?.[1];
    entry[pageName] = item;
    // 多页面所需要的模板集合
    htmlWebpackPlugins.push(
      new HtmlWebpackPlugin({
        title: pageName,
        filename: `${pageName}.html`,
        template: path.join(__dirname, `src/index.html`),
        chunks: [pageName],
      }),
    );
  });
  // 对外输出页面打包需要的 入口集合
  return { entry, htmlWebpackPlugins };
};

和入口文件一样,我们的html也需要打包多份,这里我们用的是同一个模板,如果每个入口文件所用到的html模板不同,只需要在template属性中,修改文件的路径为页面目录即可。

代码语言:javascript
复制
 template: path.join(__dirname, `src/${pageName}/index.html`),

多环境打包

在开发的过程中,我们时常需要针对不同的业务来配置不同的环境来打不同的包,比如

  • dev(开发环境)
  • pro(生产环境)
  • release(发布环境)

以前我们可能会使用一个全局config文件,然后针对不同的环境来修改里面的变量,在wbepack5中,我们只需要在打包的时候传入不同的变量,便可以配置不同环境的打包流程。

在package.json中加入

代码语言:javascript
复制
script:{
    "build:dev": "webpack --mode development  --env mode=development",
    "build:pro": "webpack --mode production  --env mode=production",
}

--env mode=development 代表需要传入到webpack.config.js中的变量

然后在webpack.config.js中,我们通过导出一个方法,来接收传入的参数,方法返回我们的配置信息

代码语言:javascript
复制
module.exports = (webpackEnv) => {
    console.log(webpackEnv) // {mode:xxx}
}

这样就能拿到我们打包的参数,然后我们创建出不同环境的.env文件

通过拿到的参数,我们可以很轻松的获取对应文件,然后读取相应的配置

代码语言:javascript
复制
  const { mode } = webpackEnv;
  //通过mode环境变量,获取对应配置文件
  const dotenvFile = `process.${mode}.env`;
  let env = {};
  //判断文件是否存在
  if (fs.existsSync(dotenvFile)) {
    env = dotenv.parse(fs.readFileSync(dotenvFile));
  } else {
    throw new Error('错误的环境变量:' + mode);
  }

  // 将配置文件转化为对象
  const raw = Object.keys(env).reduce((_env, key) => {
    _env[key] = JSON.stringify(env[key]);
    return _env;
  }, {});

最后通过webpack.definePlugin插件,将所有的配置项注入到全局中

代码语言:javascript
复制
...
plugin:[
      // 全局注入环境变量
      new webpack.DefinePlugin({
        'process.env': raw
      }),
]

然后我们就可以在项目的文件中,通过 process.env.XXX 来访问配置信息

注意一下,在webpack5之前,要定义全局的环境变量,使用--env.key=value的语法,现在使用--env key=value

文件指纹(hash值)

文件指纹指的是 webpack 中的 hash、chunkhash、contenthash 几种hash值

用途:hash 一般是结合 CDN 缓存来使用,通过 webpack 构建之后,生成对应文件名自动带上对应的 hash 值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的 HTML 引用的 URL 地址也会改变,触发 CDN 服务器从源服务器上拉取对应数据,进而更新本地缓存。

但是在实际使用的时候,这几种 hash 计算还是有一定区别。

hash

默认由md5生成,默认32位,一般取前8位,hash 是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,并且全部文件都共用相同的 hash 值。

chunkhash

采用 hash 计算的话,每一次构建后生成的哈希值都不一样,即使文件内容压根没有改变。这样子是没办法实现缓存效果,我们需要换另一种哈希值计算方式,即 chunkhash。

chunkhash 和 hash 不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的 chunk,生成对应的哈希值,当我们有代码发生变动时,只会重新生成对应chunk的hash值。

contenthash

在使用 chunkhash 的例子中,如果 index.css 被 index.js 引用了,那么就会共用相同的 chunkhash 值。但是这样子有个问题,如果 index.js 更改了代码,css 文件就算内容没有任何改变,由于是该模块发生了改变,导致 css 文件会重复构建。

这个时候,我们可以使用 extra-text-webpack-plugin 里的 contenthash 值,保证即使 css 文件所处的模块里就算其他文件内容改变,只要 css 文件内容不变,那么不会重复构建。

使用方法

在配置项中,使用对应字符串占位,这里的数字代表编码长度

"nameext"

source-map

SourceMap 是一种映射关系,当项目运行后,如果出现错误,我们可以利用 SourceMap 反向定位到源码位置

五种关键配置

有五种关键的配置,然后实际运用中取其组合

关键字

含义

source-map

产生.map的文件

eval

使用eval包裹模块代码

cheap

不包含列信息,也不包含loader的sourcemap

module

包含loader的sourcemap(比如jsx),否则无法定义源文件

inline

将.map作为DataURL嵌入,不单独生成.map文件

常见配置项

source-map

定位信息最全,可以精确的定位到代码出错的位置,但生成的.map 文件也最大,效率最低。

eval

eval 包裹源代码进行执行,信息和js文件在一起,利用字符串可缓存从而提效,无法定位到错误位置,只能定位到某个文件,不生成map文件

Inline-source-map

将 map 作为 DataURI 嵌入,信息和js文件在一起,不单独生成.map 文件,减少文件数,但是生成文件会很大

cheap-source-map

错误信息只会定义到行,而不会定义到列,精准度降低换取文件内容的缩小,对于经由 babel 之类工具转义的代码,只能定位到转换后的代码

cheap-module-source-map

会保留 loader 处理前后的文件信息映射,解决对于使用cheap 配置项导致无法定位到 loader 处理前的源代码问题

cheap-module-eval-source-map

生成代码通过 eval 执行,包含 dataUrl 形式的 SourceMap 文件,可以在编译后的代码中定位到错误所在信息,不需要定位列信息,打包速度较快,在源代码中定位到错误所在信息

最佳实践

开发环境

我们在开发环境对 sourceMap 的要求是:快(eval),信息全(module), 且由于此时代码未压缩,我们并不那么在意代码列信息(cheap),

所以开发环境比较推荐配置:devtool: cheap-module-eval-source-map

生产环境
  • 一般情况下,我们并不希望任何人都可以在浏览器直接看到我们未编译的源码,
  • 所以我们不应该直接提供 sourceMap 给浏览器。但我们又需要 sourceMap 来定位我们的错误信息,
  • 一方面 webpack 会生成 sourcemap 文件以提供给错误收集工具比如 sentry,另一方面又不会为 bundle 添加引用注释,以避免浏览器使用。

这时我们可以设置devtool: hidden-source-map

如果强调安全性,并完全不需要定位错误可以直接设置为:none

通过map文件进行回溯

假设源文件为script.js

代码语言:javascript
复制
let a=1;
let b=2;
let c=3;

其打包后的文件为script-min.js

代码语言:javascript
复制
var a=1,b=2,c=3;

其.map文件为script-min.js.map,格式化之后如下

代码语言:javascript
复制
{
"version":3,
"file":"script-min.js",
"lineCount":1,
"mappings":"AAAA,IAAIA,EAAE,CAAN,CACIC,EAAE,CADN,CAEIC,EAAE;","sources":["script.js"],
"names":["a","b","c"]
}

字段含义分析

字段

含义

version

Source map 的版本,目前为 3

file

转换后的文件名

sourceRoot

转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空

sources

转换前的文件,该项是一个数组,表示可能存在多个文件合并

names

转换前的所有变量名和属性名

mappings

记录位置信息的字符串

位置记录信息Mapping

  • 行对应 :以分号(;)表示,每个分号对应转换后源码的一行。所以,第一个分号前的内容,就对应源码的第一行,以此类推。
  • 位置对应:以逗号(,)表示,每个逗号对应转换后源码的一个位置。所以,第一个逗号前的内容,就对应该行源码的第一个位置,以此类推。
  • 分词信息:以 VLQ 编码表示,代表记录该位置对应的转换前的源码位置、原来属于那个文件等信息。如AAAA代表该位置转换前的源码位置,以VLQ编码表示;
分词信息每一位的含义

其中【分词信息】每组最多五位(如果不是变量,只会有四位),分别是:

  • 第一位,表示这个位置在【转换后代码】的第几列。
  • 第二位,表示这个位置属于【sources 属性】中的哪一个文件。
  • 第三位,表示这个位置属于【转换前代码】的第几行。
  • 第四位,表示这个位置属于【转换前代码】的第几列。
  • 第五位,表示这个位置属于【names 属性】的哪一个变量。
为何不用坐标存储位置

因为体积,如果直接坐标记录信息,至少存在两点空间损耗:编译后文件的纵坐标大的惊人;因为坐标信息是数字,如果采用数组存储,将有大量存储空间浪费。

将上面的mappings对应的字符串输入,将会得到对应的数字信息,如AAAA对应的是0000,这两者之间的映射规则就是base64vlq编码。

在线转换网站 https://www.murzwin.com/base64vlq.html

最后

感谢你能看到这里,本文总结了webpack中的四个常用的配置,希望这篇文章对你有所帮助,后续会陆续更新其他webpack相关的文章

本系列的其他文章:

入门webpack基础篇:https://cloud.tencent.com/developer/article/2126706

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
  • 目录
  • 多入口打包
  • 多环境打包
  • 文件指纹(hash值)
    • hash
      • chunkhash
        • contenthash
          • 使用方法
          • source-map
            • 五种关键配置
              • 常见配置项
                • source-map
                • eval
                • Inline-source-map
                • cheap-source-map
                • cheap-module-source-map
                • cheap-module-eval-source-map
              • 最佳实践
                • 开发环境
                • 生产环境
              • 通过map文件进行回溯
                • 位置记录信息Mapping
                • 最后
                相关产品与服务
                内容分发网络 CDN
                内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档