webpack之优化篇(一):优化静态资源

webpack系列说明

已完成:

基础篇

  1. webpack之基础篇(一):webpack的基础认识
  2. webpack之基础篇(二):前端基本开发环境的搭建
  3. webpack之基础篇(三):配置loader和plugin
  4. webpack之基础篇(四):webpack-dev-server浅析
  5. webpack之基础篇(五):配置开发、测试、生产环境

优化篇

  1. webpack之优化篇(一):优化静态资源
  2. webpack之优化篇(二):优化配置提高构建速度
  3. webpack之优化篇(三):http压缩之Gzip
  4. webpack之优化篇(四):webpack DllPlugin配置的代替方案hard-source-webpack-plugin

原理篇

  1. webpack之原理篇(二):hash、chunkhash、contenthash的认

编写中:

  1. webpack之原理篇(一):webpack的工作流程和原理
  2. webpack之原理篇(三):webpack-dev-server原理
  3. webpack之原理篇(四):手写loader
  4. webpack之原理篇(五):手写plugin

图片资源

对于项目中的图片资源,我们主要从两方面进行优化:

  1. 对资源进行压缩,减少体积。
  2. 减少请求的数量,降低服务器压力。

对资源进行压缩,减少体积,我们使用对应的loader进行处理。

使用file-loader 来处理图片文件

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /.*\.(gif|png|jpe?g|svg|webp)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {}
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: { // 压缩 jpeg 的配置
                progressive: true,
                quality: 65
              },
              optipng: { // 使用 imagemin-optipng 压缩 png,enable: false 为关闭
                enabled: false,
              },
              pngquant: { // 使用 imagemin-pngquant 压缩 png
                quality: '65-90',
                speed: 4
              },
              gifsicle: { // 压缩 gif 的配置
                interlaced: false,
              },
              webp: { // 开启 webp,会把 jpg 和 png 图片压缩为 webp 格式
                quality: 75
              },
          },
        ],
      },
    ],
  },
}

当我们要将一些图片转换为base64 uri,减少http请求时,就可以用url-loader进行处理
url-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        exclude: [resolve('src')],
        options: {
          limit: 10000, // 限制文件的最大大小
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

CSS Sprites相信大家都了解了,当我们的项目中,使用了大量的svg-icon时,可以用svg-sprite-loader进行处理:

svg-sprite-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: 'svg-sprite-loader',
        // 只对相应目录下的资源做处理
        include: [resolve('src/icons')],
        options: {}
      }
    ]
  }
}

HTML模板

前面有讲到,我们会用html-webpack-pluginindex.html与打包后的进行关联,其实,html-webpack-plugin中还有提供输出配置minify

modeproduction时,minify配置生效

html-webpack-plugin

new HtmlWebpackPlugin({
  filename: 'index.html',
  template: 'index.html',
  inject: true,
  title: 'admin',
  minify: {
    // 合并空格
    collapseWhitespace: true,
    // 移除注解
    removeComments: true,
    // 移除多余的属性
    removeRedundantAttributes: true,
    // 移除脚本类型属性
    removeScriptTypeAttributes: true,
    // 移除样式类型属性
    removeStyleLinkTypeAttributes: true,
    // 使用简短的文档类型
    useShortDoctype: true
    // more options:
    // https://github.com/kangax/html-minifier#options-quick-reference
  }
}),

CSS剥离与压缩

extract-text-webpack-pluginmini-css-extract-plugin是可以提取CSS到单独的文件中。

  module: {
    rules: [
      {
        test: /\.css/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader // 涉及到内容装转换
          },
          'css-loader'
        ]
      }
    ]
  }
  plugins: [
    // extract css into its own file
    // Error contenthash not implemented with webpack > 4.3.0
    // 1. yarn upgrade extract-text-webpack-plugin@next
    // 2. 采用 mini-css-extract-plugin
    new MiniCssExtractPlugin({
      // 因为webpack4.3包含了contenthash这个关键字,所以ExtractTextPlugin中不能使用contenthash
      // 使用md5:contenthash:hex:8代替contenthash
      // github issue https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/765
      filename: 'css/[name].[contenthash].css',
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    })
  ]

这俩个插件的作用是相同的,至于为什么选择mini-css-extract-plugin?

不用extract-text-webpack-plugin的原因:extract-text-webpack-plugin默认安装的版本是3.x.x,还不支持webpack的4.x版本

关于webpackhash相关的详细内容,请移步重学webpack之原理篇(二):hash+chunkhash+contenthash

JavaScript

webpack4.x中已经可以将压缩的插件放置在minimizer中了。
UglifyJsPlugin进行JS代码压缩

module.exports = {
  mode: 'production',
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          mangle: {
            safari10: true
          },
          // 清除生产环境的控制台输出
          compress: {
            warnings: false,
            drop_debugger: true,
            drop_console: true
          }  
        },
        sourceMap: config.build.productionSourceMap,
        cache: true,
        parallel: true
      }),
      // Compress extracted CSS. We are using this plugin so that possible
      // duplicated CSS from different components can be deduped.
      new OptimizeCSSAssetsPlugin()
    ]
  }
})

关于代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。
代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,果使用合理,会极大影响加载时间。

常用的代码分离方法有三种:

  1. 入口起点:使用 entry 配置手动地分离代码。
  2. 防止重复:使用 SplitChunksPlugin 去重和分离 chunk。
  3. 动态导入:通过模块中的内联函数调用来分离代码。

入口起点

我们可以通过配置多个入口文件,从而进行代码分离

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: ['./src/main.js', './src/index.js']
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js'
  }
}

但是,这样做有两个缺点:

  1. 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
  2. 这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码。

关于模块重复的问题,可以用``解决

防止重复

optimization中的splitChunks即可做到

注意:splitChunks是webpack4.x的解决方案,webpack3.x是用CommonsChunkPlugin插件

webpack3.x

 new webpack.optimize.CommonsChunkPlugin(options)

webpack4.x

 const path = require('path');

  module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      another: './src/another-module.js',
    },
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
      splitChunks: {
        chunks: 'all', // chunks 代码公共的部分分离出来成为一个单独的文件
      }
    }
  };

更详细的配置内容:

optimization: {
    minimize: env === 'production' ? true : false, // 开发环境不压缩
    splitChunks: {
        chunks: "async", // 共有三个值可选:initial(初始模块)、async(按需加载模块)和all(全部模块)
        minSize: 30000, // 模块超过30k自动被抽离成公共模块
        minChunks: 1, // 模块被引用>=1次,便分割
        maxAsyncRequests: 5,  // 异步加载chunk的并发请求数量<=5
        maxInitialRequests: 3, // 一个入口并发加载的chunk数量<=3
        name: true, // 默认由模块名+hash命名,名称相同时多个模块将合并为1个,可以设置为function
        automaticNameDelimiter: '~', // 命名分隔符
        cacheGroups: { // 缓存组,会继承和覆盖splitChunks的配置
            default: { // 模块缓存规则,设置为false,默认缓存组将禁用
                minChunks: 2, // 模块被引用>=2次,拆分至vendors公共模块
                priority: -20, // 优先级
                reuseExistingChunk: true, // 默认使用已有的模块
            },
            vendors: {
                test: /[\\/]node_modules[\\/]/, // 表示默认拆分node_modules中的模块
                priority: -10
            }
        }
    }
}

plitChunks是拆包优化的重点,如果你的项目中包含 element-ui 等第三方组件(组件较大),建议单独拆包,如下所示:

splitChunks: {
    // ...
    cacheGroups: {    
        elementUI: {
            name: "chunk-elementUI", // 单独将 elementUI 拆包
            priority: 10, // 权重需大于其它缓存组
            test: /[\/]node_modules[\/]element-ui[\/]/
        }
    }
}

动态导入

使用ECMAScript的 import(), webpack 会自动处理使用该语法编写的模块。

// import 作为一个方法使用,传入模块名即可,返回一个 promise 来获取模块暴露的对象
// 注释 webpackChunkName: "lodash" 指定 chunk 的名称,输出文件时有用
import(/* webpackChunkName: "lodash" */ 'lodash').then((_) => { 
  console.log(_.lash([1, 2, 3])) // 打印 3
})

vue-router中也有类似的应用:
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值