Webpack实战(九):实现资源按需加载-资源异步加载

2,491 阅读4分钟

第八篇[《教你搞懂webpack如果实现代码分片(code splitting)》] (blog.csdn.net/lfcss/artic…) 主要分享了webpack实现代码分片 的几种方式:合理地规划入口,使用Commons-ChunkPlugin或SplitChunks配置。这几种方法实现了资源按需加载。今天我分享另外一种实现方法--资源异步加载。

资源异步加载主要解决的问题是,当模块数量过多、资源体积过大时,可以把一些暂时使用不到的模块延迟加载。这样使页面初次渲染的时候用户下载的资源尽可能小,后续的模块等到恰当的时机再去触发加载。

webpack资源异步加载的方法

在webpack中主要提供两种方法:import函数和require.ensure。在webpack4之前,webpack 在编译时,会静态地解析代码中的 require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 通过 jsonp 来按需加载。今天主要webpack4以后的import加载。

Webpack中的import

与正常ES6中的import语法不同,通过import函数加载的模块及其依赖会被异步地进行加载,并返回一个Promise对象。 一般正常的模块加载代码如下:

// index.js
import { add } from  './index2.js';
console.log(add(2,3));

// index2.js
export function add(a, b) {
  return a + b
}

打包的效果及运行效果:

在这里插入图片描述
在这里插入图片描述
假设index2.js的资源体积很大,并且我们在页面初次渲染的时候并不需要使用它,就可以对它进行异步加载。 index.js和index2.js代码如下:

// index.js
import('./index2.js').then(({add}) => {
  console.log(add(2,3))
})

// index2.js
export function add(a, b) {
  return a + b
}

webpack.config.js配置代码如下:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  context: path.join(__dirname, './src'),
  entry: {
    index: './index.js'
  },
  output: {
    //path: path.join(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/dist/'
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              [
                'env', {
                  modules: false
                }
              ]
            ]
          }
        }
      }
    ],
  }
}

打包的效果及运行效果:

在这里插入图片描述
在这里插入图片描述
首屏加载的JS资源地址是通过页面中的script标签来指定的,而间接资源(通过首屏JS再进一步加载的JS)的位置则要通过output.publicPath来指定。上面我们的import函数相当于使index2.js成为了一个间接资源,我们需要配置publicPath来告诉Webpack去哪里获取它。

由上图可以看出,Chrome的nextwork面板看到一个0.js的请求,它就是index2及其依赖产生的资源。观察面板中的Initinator字段,可以看出它是有index.js产生的请求。查看index.js可以看出,是由这段代码动态的在head头部添加script标签引入的方式,引入0.js,查看源代码并不能找到这段引入的代码,所以是js动态添加进去的。

在这里插入图片描述

在element 面板能查到script标签动态插入的0.js代码。

在这里插入图片描述

注意:import函数还有一个比较重要的特性。ES6 Module中要求import必须出现在代码的顶层作用域,而Webpack的import函数则可以在任何我们希望的时候调用

异步chunk的配置

由上面的方法我们已经实现了异步资源,但是此产生的资源名称都是数字如0.js,比较随机,不具有可读可维护性。此时就需要我们去配置Webpack的一些参数来为其添加有意义的名字,便于我们维护它们。

我们在Webpack的配置中添加了output.chunkFilename,用来指定异步chunk的文件名。其命名规则与output.filename基本一致,不过由于异步chunk默认没有名字,其默认值是[id].js。 配置代码为:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  context: path.join(__dirname, './src'),
  entry: {
    index: './index.js'
  },
  output: {
    //path: path.join(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/dist/',
    chunkFilename:'[name].js'
  },
  mode: 'development',
  // optimization: {
  //   splitChunks: {
  //     chunks: 'all'
  //   }
  // },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              [
                'env', {
                  modules: false
                }
              ]
            ]
          }
        }
      }
    ],
  }
}

index.js修改为:

import(/* webpackChunkName:'index2'*/'./index2.js').then(({add}) => {
  console.log(add(2,3))
})

在这里插入图片描述
在index.js中,我们通过特有的注释来让Webpack获取到异步chunk的名字,并配置output.chunkFilename为[name].js,由上图可以看出,0变成了index2.js

chrome network上的element面板效果如下图:

在这里插入图片描述

总结

资源异步加载方法import()和require.ensure(),也可以有效地缩小资源体积,同时更好地利用缓存,给用户更友好的体验。如果想了解更多,请扫描二维码:

在这里插入图片描述