webpack笔记总结(二)

时间:2021-1-18 作者:admin

三、webpack的高级概念

1.Tree Shaking概念

前端中的tree-shaking可以理解为通过工具”摇”我们的JS文件,将其中用不到的代码”摇”掉,是一个性能优化的范畴。具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

Tree Shaking 只支持ES Module

如何配置?

在开发环境

// webpack.config.js
optimization: {
  usedExports: true
}
// package.json
"sideEffect": false,
// "sideEffect": ["*.css"] 不希望tree-shaking的配置在这里

在生产环境,自动配置好tree-shaking

2.Dev和Prod模式的区分打包

在开发环境和生成环境,有source-map,DevServer、HMR、压缩等的区别。

区分两个环境运行

package.json

{
  "scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
}

新建两个文件webpack.dev.js和webpack.prod.js

但是有很多相同的代码!

解决公共部分提取

提取公共部分到webpack.common.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    publicPath: '/',
    filename: '[name].js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  useBuiltIns: 'usage',
                  targets: {
                    edge: '17',
                    firefox: '60',
                    chrome: '67',
                    safari: '11.1'
                  }
                }
              ]
            ]
          }
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
};

安装 webpack-merge

npm install webpack-merge -D

改写webpack.prod.js

const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const prodConfig = {
  mode: 'production'
};

module.exports = merge(commonConfig, prodConfig);

改写webpack.dev.js

const webpack = require('webpack');
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const devConfig = {
  mode: 'development',
  devtool: 'source-map',
  devServer: {
    contentBase: './dist'
    // hot: true,
    // hotOnly: true
  },
  plugins: [new webpack.HotModuleReplacementPlugin()],
  optimization: {
    usedExports: true
  }
};

module.exports = merge(commonConfig, devConfig);

3.Webpack和Code Splitting

代码分割

在以前,为了减少 HTTP 请求,通常地,我们都会把所有的代码都打包成一个单独的 JS 文件。但是,如果这个 JS 文件体积很大的话,那就得不偿失了。

这时,我们不妨把所有代码分成一块一块,需要某块代码的时候再去加载它;还可以利用浏览器的缓存,下次用到它的话,直接从缓存中读取。很显然,这种做法可以加快我们网页的加载速度,美滋滋!

所以说,Code Splitting 其实就是把代码分成很多很多块( chunk)咯。

配置

optimization: {
  splitChunks: {
    chunks: 'all'
  }
}

然后会生成main.js和vendors~main.js

异步代码

function getComponent() {
  return import('lodash').then(({ default: _ }) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['Dell', 'Lee'], '-');
    return element;
  });
}

getComponent().then(element => {
  document.body.appendChild(element);
});

代码分割和webpack无关,webpack中实现代码分割,两种方式

1.同步代码:只需要在webpack.common.js中做optimization的配置即可

2.异步代码(import):异步代码,无需做任何配置,会自动进行代码分割

4.代码分割SplitChunkPlugin参数配置详解

SplitChunkPlugin配置

魔法注释

return import(/* webpackChunkName: "lodash" */'lodash')

这样会生成vendors~lodash.js这个文件

打包生成文件名和指定名称相同

optimization: {
  splitChunks: {
    cacheGroups: {
      vendors: false,
      default: false
    }
  }
}

默认配置

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // chunks:async只对异步代码进行代码分割,all会同步异步都分割
      // 我们打包一个同步代码块,会寻找vendors配置,如果在node_modules中,会进行分割
      chunks: 'async',
      // 大于30kb,进行代码分割
      minSize: 30000,
      maxSize: 0,
      // 至少用几次才会被分割
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      // =>缓存组
      cacheGroups: {
         // 不同的组
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          // 重用现有模块
          reuseExistingChunk: true
        }
      }
    }
  }
};

5.Lazy Loading 懒加载,chunk是什么

懒加载:通过import()来异步的加载一个模块

import()返回一个Promise类型

function getComponent() {
  return import(/* webpackChunkName: "lodash-dj" */ 'lodash').then(
    ({ default: _ }) => {
      var element = document.createElement('div');
      element.innerHTML = _.join(['Dell', 'Lee'], '-');
      return element;
    }
  );
}

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

async await改写

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

document.addEventListener('click', async () => {
  const element = await getComponent();
  document.body.appendChild(element);
})

chunk

打包只生成的每一个js文件都称为一个chunk

// 简易配置代码分割,交给webpack自动处理
optimization: {
  splitChunks: {
    chunks: 'all',
  }
}

6.打包分析,Preloading,Prefetching

打包分析

github webpack/analyse

# 输出打包信息到 stats.json
webpack --profile --json > stats.json --config webpack.dev.js

webpack-bundle-analyzer工具使用

Install

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

Usage (as a plugin)

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
plugins: [
  new BundleAnalyzerPlugin()
]

打包完成之后自动打开127.0.0.1:8888查看分析

Preloading和Preloading

参考原文:聊一聊webpack中的preloading和prefetching

官方文档:预取/预加载模块(prefetch/preload module)

在浏览器中 按 Ctrl + Shift + P ,然后在弹出的对话框中输入 coverage

从红色的方框中可以看出当前加载的文件中在当前页面中的利用率

通过异步加载,提升代码利用率,在加载当前页的时候,异步加载不必要的代码

现在我们就看出来 webopack 为什么要使用 chunks: 'async' 这样的默认配置了。

webpack 优化的侧重点是代码的使用率而不是缓存,只是使用缓存的方式来优化意义是不大的,通过异步的方式提高代码的利用率才能比较大程度地提高网站的性能。

有些小伙伴可能会想,能不能在加载完页面网络空闲的时候先把这些文件加载进来呀,真聪明,这就是接下来要讲的 Preloading 和 Prefetching。

  • Prefetching

    使用方法也比较简单,就是在要异步加载的文件前面加上 /* webpackPrefetch: true */ 这个 magic comment 即可。

    我们把需要交互才能用到的代码,完全可以写在异步组件里。

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

    生成的 0.js 是 click.js 打包之后,可以看出在页面加载完之后的空闲时间还没有点击页面时已经加载了 0.js ,当点击页面时,0.js 直接从缓存中读取,因此耗时非常短。

  • Preloading 和 Prefetching 有什么区别?

    两者的最大区别在于,Prefetching 是在核心代码加载完成之后带宽空闲的时候再去加载,而 Preloading 是和核心代码文件一起去加载的。

因此,使用 Prefetching 的方式去加载异步文件更合适一些。

7.CSS文件的代码分割

MiniCssExtractPlugin

8.Webpack与浏览器缓存Cache

production环境

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

只要我们不该源代码,文件的值就不会变,用户第二次访问,就会用本地的缓存

源代码改变,hash值改变

有利于重新带包上线的优化

9.Shimming

简书:webpack打包兼容之shimming(垫片)

10.环境变量的使用

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。