webpack v3 学习笔记(二) 分离js代码

195 阅读3分钟

提取公共代码

提取公共代码要借助 webpack 的 CommonsChunkPlugin 插件(v4 已移除),另外这个插件是不支持单入口的公共代码块提取的。

npm init
npm install webpack@^3.0.0 lodash
touch webpack.config.js
mkdir src
touch src/pageA.js
touch src/pageB.js
touch src/subPageA.js
touch src/subPageB.js
touch src/subPageModule.js

pageA.js:

import './subPageA'
import './subPageB'
import * as _ from 'lodash'

export default 'pageA'

pageB.js:

import './subPageA'
import './subPageB'
import * as _ from 'lodash'

export default 'pageB'

subPageA.js

import './subPageModule'

export default 'subPageA'

subPageB.js

import './subPageModule'

export default 'subPageB'

subPageModule.js

export default 'subPageModule'

代码引入关系如下:

pageA(pageB)-> lodash,subPageA,subPageB -> subPageModule

如果我们使用平常的多入口打包,即:

webpack.config.js:

var webpack = require('webpack')
var path = require('path')

module.exports = {
  entry: {
    'pageA': './src/pageA',
    'pageB': './src/pageB'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, './dist')
  }
}

打包出来的两个文件是十分大的,并且这两个文件中存在大量的重复的引入代码:

改而使用插件:

webpack.config.js:

var webpack = require('webpack')
var path = require('path')

module.exports = {
  entry: {
    'pageA': './src/pageA',
    'pageB': './src/pageB',
    // 需要提取出来的第三方库需要在下面设置
    'vendor': ['lodash']
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, './dist')
  },
  plugins: [
    /*
    *  下面三个插件的顺序在配置中发现只能按照这样的顺序打包才正确打包
    */
    // 提取pageA,pageB的公共代码
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      minChunks: 2,
      chunks: ['pageA', 'pageB'] // 解决打包报错
    }),
    // 提取第三方库的代码,与上面的vendor对应
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: Infinity
    }),
    // 提取webpack的生成代码
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    })
  ]
}

在打包结果中,lodash 被提取到了 vendor.bundle.js,subPageA、subPageB、subPageModule 被提取到了 common.bundle.js 中。总体代码的体积也缩减了许多(lodash 包太大)

代码分割

上面说到 CommonsChunkPlugin 提取公共代码是不支持单入口文件的,使用代码分割就可以实现单入口的代码块的分离。

webpack的代码分割功能起初是要使用 require.ensure 和 require.include,后面被动态 import() 取代。

require.ensure & require.include

require.ensure 会把给定 dependencies 参数对应的文件拆分到一个单独的 bundle 中,此 bundle 会被异步加载。

require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)

require.ensure 依赖于 promise,使用时最好搭配 polyfill 使用。

require.include 则是用来加载 require.ensure 加载的 chunk 中的子模块。加载的位置要位于父 chunk 之前。

例:

npm init
npm install webpack@^3.0.0 lodash
touch webpack.config.js
mkdir src
touch src/pageA.js
touch src/pageB.js
touch src/subPageA.js
touch src/subPageB.js
touch src/subPageModule.js

pageA.js:

require.include('./subPageModule')

require.ensure([], function () {
  var subPageA = require('./subPageA')
}, 'subPageA')
require.ensure([], function () {
  var subPageB = require('./subPageB')
}, 'subPageB')
require.ensure([], function () {
  var _ = require('lodash')
}, 'vendor')

export default 'pageA'

subPageA.js

import './subPageModule'

console.log('subPageA')

export default 'subPageA'

subPageB.js

import './subPageModule'

console.log('subPageB')

export default 'subPageB'

subPageModule.js

export default 'subPageModule'

webpack.config.js

var webpack = require('webpack')
var path = require('path')

module.exports = {
  entry: {
    'pageA': './src/pageA'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, './dist'),
    publicPath: './dist/',
    chunkFilename: '[name].bundle.js' // name 为 ensure 参数中的 chunkname
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    })
  ]
}

打包结果如下:

动态 import()

import() 引入模块后返回一个 promise,并且使用 magic comments 来配置选项。

npm init
npm install webpack@^3.0.0 lodash
touch webpack.config.js
mkdir src
touch src/pageA.js
touch src/pageB.js
touch src/subPageA.js
touch src/subPageB.js
touch src/subPageModule.js

pageA.js:

import * as _  from 'lodash'

import(/* webpackChunkName: 'subPageA' */'./subPageA').then(function (subPageA) {
  console.log(subPageA)
})

// 使用 magic comments 配置 chunkName
import(/* webpackChunkName: 'subPageB' */'./subPageB').then(function (subPageB) {
  console.log(subPageB)
})

export default 'pageA'

pageB.js:

import * as _  from 'lodash'

import(/* webpackChunkName: 'subPageA' */'./subPageA').then(function (subPageA) {
  console.log(subPageA)
})

import(/* webpackChunkName: 'subPageB' */'./subPageB').then(function (subPageB) {
  console.log(subPageB)
})

export default 'pageB'

subPageA.js

import './subPageModule'

console.log('subPageA')

export default 'subPageA'

subPageB.js

import './subPageModule'

console.log('subPageB')

export default 'subPageB'

subPageModule.js

export default 'subPageModule'

webpack.config.js

var webpack = require('webpack')
var path = require('path')

module.exports = {
  entry: {
    'pageA': './src/pageA',
    'pageB': './src/pageB',
    'vendor': ['lodash']
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, './dist'),
    publicPath: './dist/',
    chunkFilename: '[name].bundle.js'
  },
  plugins: [
    // 提取分割出来的代码块中的公共代码,这里是提取到 subPageModule
    new webpack.optimize.CommonsChunkPlugin({
      names: ['pageA', 'pageB'], // 提取的 entry
      async: 'common', // 异步设为 true 并且设置提取出的前缀
      minChunks: 2,
      children: true // 允许查找子模块
    }),
    // 提取第三方包和生成的代码
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
      minChunks: Infinity
    })
  ]
}

打包结果如下: