Webpack 之常用配置(三)

作者:余韵之

LazyLoading 懒加载 Chunk是什么?

我们可以使用懒加载的方式引入模块,比如说当触发了某个条件,在通过import的方式引入模块。这样可以使得项目的性能会更加的好。

举个例子

当我们点击页面的时候,才会去引入lodash模块,这里 import()返回的是promise

async function getComponent() {
  const {default: _} = await import(/* webpackChunkName:"lodash" */'lodash')
  const element = document.createElement('div')
  element.innerHTML = _.join(['y', 'kk'], '-');
  return element;
}

document.addEventListener('click', () => {
  getComponent().then(element => {
    document.body.appendChild(element)
  })
})

同样的路由懒加载的意思,表示当我们监听到路由变化了,才会去引入对应的页面模块。

因此可以知道,Chunk是什么?打包生成几个JS文件,就是几个Chunk

Shimming

Shimming:在打包过程中,有时候需要对代码兼容。这种兼容不局限于浏览器高低版本。

举个例子

每个文件都是一个模块,每个模块都应该引入自己的依赖才能使用该依赖。

import $ from 'jquery'
export function setBackground(){
  $('body').css('background','red')
}

但是这样的话,每个文件都要写一遍

import $ from 'jquery'

因此可以使用垫片的方式来自动配置

plugins:[
    new webpack.ProvidePlugin({
      $:'jquery',
      _join:['lodash','join']
    })
]

当我们配置了上述内容,那么意味着当运行代码的时候看到

  • $这个符号就会自动去node_modules里引入jquery。他的原理就是自动帮我们添加了import的步骤。
  • 看到_join就会自动找到 lodash里join的方法

于是我们可以直接这些一个文件模块使用

export function setBackground(){
  $('body').css('background', _join(['green'], ''))
}

webpack与浏览器缓存(caching)

当我们打包生成了

index.html
main.js
vendors.js

客户端从服务端拿到了两个js文件,会保存在浏览器里。客户端刷新后浏览器会先从缓存里获取。
当开发修改了代码,重新打包,生成了上述三个同名文件时,用户刷新后仍然是原来的代码并没有更新。
这样如何解决呢?

1、打包文件添加contentHash

开发环境因为是热更新的,所以本地调试可以不添加,
生产环境在output里改为

output: {
    filename: '[name].[contentHash].js',
    chunkFilename: '[name].[contentHash].js'
}

contentHash 表示只要不改变源代码的内容,那么contentHash所产生的hash值是不会变的。如果代码是分割成不同的chunk,某个chunk没有修改,该chunk的文件名contentHash也不会修改

同时做了代码分割的参数最好也配置一下

optimization.splitChunks.cacheGroup.vendors.filename = 'vendors.[contentHash].js'

2、对老版本的兼容

可能老的版本即使不改内容,contentHash值也会改变,这个时候可以配置

optimization: {
    runtimeChunk:{
      name:'runtime'
    }
}

新版本添加这个runtimeChunk也是没有问题的,此时打包会多出一个runtime.xxxxxxx.js文件

这是怎么回事呢?

因为在旧的webpack版本里main.js(业务逻辑) 和 vendors.js(第三方库) 之间是存在关联的,这部分的处理代码放在mainfest,虽然没有改变代码,但在旧版本的webpack里 mainfest 包和包之间的关系每次打包可能会变化,当我们配置了runtimeChunk的时候就把mainfest这块关系相关的代码抽离出来了放在runtime.js里

因此这样就解决了这个兼容老版本的问题。

对CSS代码分割

正常打包的话,会把CSS文件一起打包的main.js文件,如何进行CSS代码分割呢?

1、使用一个插件:mini-css-extract-plugin

但这个插件有一个缺点,目前不支持热更新,所以适合线上环境做打包

配置方式安装依赖

yarn add mini-css-extract-plugin

在生产环境的webpack.config.js配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

  module:{
    rules:[
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      }, {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css", //index.html直接引入的文件
      chunkFilename: "id.css" // 间接引用的文件
    })
  ]

注意 如果此时使用了tree shaking 看看 package.json 里 sideEffects 配置。如果为false那么css代码是没有分割的。需要把css tree shaking排除在外。修改如下:

 "sideEffects": [
    "*.css"
  ],

2、接下来对合并的CSS代码压缩

安装依赖

yarn add optimize-css-assets-webpack-plugin -D

生产环境

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')

optimization:{
    minimizer:[new OptimizeCSSAssetsPlugin({})]
}

打包分析

生成webpack打包分析json

webpack --profile --json > stats.json --config ./build/webpack.dev.js

然后把生成的stats.json放入相关的分析网站就可以看到可视化的数据。当然也可以配置analyzer

安装依赖

npm install webpack-bundle-analyzer --save-dev

在webpack.config.js配置

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 
module.exports = {
  ...
  configureWebpack: {
    plugins: [
        new BundleAnalyzerPlugin({
                    //  可以是`server`,`static`或`disabled`。
                    //  在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
                    //  在“静态”模式下,会生成带有报告的单个HTML文件。
                    //  在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
                    analyzerMode: 'server',
                    //  将在“服务器”模式下使用的主机启动HTTP服务器。
                    analyzerHost: '127.0.0.1',
                    //  将在“服务器”模式下使用的端口启动HTTP服务器。
                    analyzerPort: 8888, 
                    //  路径捆绑,将在`static`模式下生成的报告文件。
                    //  相对于捆绑输出目录。
                    reportFilename: 'report.html',
                    //  模块大小默认显示在报告中。
                    //  应该是`stat`,`parsed`或者`gzip`中的一个。
                    //  有关更多信息,请参见“定义”一节。
                    defaultSizes: 'parsed',
                    //  在默认浏览器中自动打开报告
                    openAnalyzer: true,
                    //  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
                    generateStatsFile: false, 
                    //  如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
                    //  相对于捆绑输出目录。
                    statsFilename: 'stats.json',
                    //  stats.toJson()方法的选项。
                    //  例如,您可以使用`source:false`选项排除统计文件中模块的来源。
                    //  在这里查看更多选项:https:  //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
                    statsOptions: null,
                    logLevel: 'info' // 日志级别。可以是'信息','警告','错误'或'沉默'。
        })
    ]
  }, 
  ...
};

使用方式 在打包或者启动的时候加 --report

npm run serve --report
npm run build --report

prefetching && preloading

关注代码使用率:一开始不会执行的代码不要加载出来,而是当交互了再去加载。

webpack希望尽可能使用异步加载模快在第一次加载提高性能,而同步是第二次加载了增加缓存对性能的提升是有限的。

document.addEventListener('click', () => {
  import('./click.js').then(({default:func}) => {
    func()
  })
})

注意 但这样会存在一个问题,就是当用户点击交互了才下载代码可能会有一点小小的延迟,如何解决这个问题?

解决方案就是 : prefetching/preloading

  • prefetching 会等到核心先展示的代码加载完毕了,等宽带空闲再继续下载。

只需要在代码添加如下内容即可:

document.addEventListener('click', () => {
  import(/* webpackPrefetch:true */'./click.js').then(({default:func}) => {
    func()
  })
})

当用户点击了,仍然会下载click.js文件,但是使用的时间会非常短,因为之前已经下载过了有了缓存了。

  • preloading 会和主的代码同时下载。
document.addEventListener('click', () => {
  import(/* webpackPreload:true */'./click.js').then(({default:func}) => {
    func()
  })
})

因此性能优化最好去考虑代码的使用率更高。


对 Electron 感兴趣?请关注我们的开源项目 Electron Playground,带你极速上手 Electron。

我们每周五会精选一些有意思的文章和消息和大家分享,来掘金关注我们的 晓前端周刊


我们是好未来 · 晓黑板前端技术团队。
我们会经常与大家分享最新最酷的行业技术知识。
欢迎来 知乎掘金SegmentfaultCSDN简书开源中国博客园 关注我们。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容