webpack4学习笔记(二)

前言

这是我花了几个星期学习webpack4的学习笔记。内容不够细,由于一些相对比较简单的,就随意带过了。但愿文章能给你们带来帮助。若有错误,但愿及时指出。例子都在learn-webpack仓库上。若是你从中有所收获的话,但愿你能给个人github点个starjavascript

tree shaking

一个模块里会导出不少东西。把一个模块里没有被用到的东西都给去掉。不会把他打包到入口文件里。tree shaking只支持es6的方式引入(import),使用require没法使用tree shakingcss

webpackdevelopment没法使用tree shaking功能。除非在打包的配置里加上html

// 开发环境须要加以下代码
optimization: {
  usedExports: true
}

当你须要import某个模块,又不想tree shaking把他给干掉,就须要在package.json里修改sideEffects参数。好比当你import './console.js' , import './index.css'等没有export(导出)模块的文件。又不想tree shaking把它干掉。vue

// package.json
sideEffects: ['./console.js', './index.css']
// 反之
sideEffects: false

development环境即便你使用tree shaking,它也不会把其余多余的代码给干掉。他只会在打包的文件里注明某段代码是不被使用的。java

developmentproduction 区别

development代码不压缩,production代码会压缩node

省略…☺react

webpack-mergejquery

reactvue都会区分环境进行不一样的webpack配置,可是它们必定会有相同的部分。这个时候须要经过使用webpack-merge进行抽离。webpack

// webpack.base.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'production',
    // mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    optimization: {
        usedExports: true
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ]
}

// webpack.dev.config.js
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')
const devConfig = {
    mode: 'development',
}

module.exports = merge(baseConfig, devConfig)

这里就不重复把production环境在配置出来了,主要介绍下webpack-merge用法。git

  • 安装npm i webpack-merge -D
  • 新建一个公共的文件如:webpack.base.config.js
  • developmentproduction两个webpack配置相同的抽离到webpack.base.config.js文件中
  • 在环境配置文件中(具体代码如上)

    • const merge = require('webpack-merge')
    • const baseConfig = require('./webpack.base.config.js')
    • module.exports = merge(baseConfig, devConfig)

code splittingsplitChunks

当你把全部的代码都打包到一个文件的时候,每次改一个代码都须要从新打包。且用户都要从新加载下这个js文件。可是若是你把一些公共的代码或第三方库抽离并单独打包。经过缓存加载,会加快页面的加载速度。

  1. 异步加载的代码,webpack会单独打包到一个js文件中
  2. 同步加载的代码有两种方式

原始代码

import _ from 'lodash'

console.log(666)

打包后的文件:

main.js 551 KiB main [emitted] main
能够看到,webpack将业务代码跟lodash库打包到一个main.js文件了

方法一:

建立一个新文件

import _ from 'lodash'
window._ = _

将文件挂载到window对象上,这样其余地方就能够直接使用了。

而后在webpack配置文件中的entry增长一个入口为该文件。让该文件单独打包。

Asset      Size  Chunks             Chunk Names
lodash.js   551 KiB  lodash  [emitted]  lodash
  main.js  3.79 KiB    main  [emitted]  main

方法二:

经过添加optimization配置参数

optimization: 会将诸如lodash等库抽离成单独的chunk,还会将多个模块公用的模块抽离成单独的chunk

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

打包后文件:

Asset      Size        Chunks             Chunk Names
        main.js  6.78 KiB          main  [emitted]  main
vendors~main.js   547 KiB  vendors~main  [emitted]  vendors~main

能够看到,webpack将lodash抽成公共的chunk打包出来了。

splitChunks里面还能够在添加个参数cacheGroups

optimization: {
    splitChunks: {
        chunks: 'all',
        cacheGroups: {
              // 下面的意思是:将从node_modules中引入的模块统一打包到一个vendors.js文件中
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                filename: 'vendors.js'
            },
            default: false
        }
    }
}

cacheGroupsvendors配置表示将从node_modules中引入的模块统一打包到一个vendors.js文件中

splitChunksvendorsdefault参数:

根据上下文来解释,如上配置了vendors,打包node_modules文件夹中的模块,

那么default将会打包本身编写的公共方法。

当不使用default配置时。

Asset     Size             Chunks             Chunk Names
   main.js  315 KiB               main  [emitted]  main
   test.js  315 KiB               test  [emitted]  test

添加以下配置以后:

optimization: {
    splitChunks: {
        chunks: 'all',
        cacheGroups: {
              // 下面的意思是:将从node_modules中引入的模块统一打包到一个vendors.js文件中
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                filename: 'vendors.js'
            },
            // 打包除上面vendors之外的公共模块
            default: {
                priority: -20,
                reuseExistingChunks: true, // 若是该chunk已经被打包进其余模块了,这里就复用了,不打包进common.js了
                filename: 'common.js'
            }
        }
    }
}

打包后的文件体积为

Asset      Size             Chunks             Chunk Names
 common.js   308 KiB  default~main~test  [emitted]  default~main~test
   main.js  7.03 KiB               main  [emitted]  main
   test.js  7.02 KiB               test  [emitted]  test

配置说明

splitChunks: {
  chunk: 'all', // all(所有), async(异步的模块),initial(同步的模块)
  minSize: 3000, // 表示文件大于3000k的时候才对他进行打包
  maxSize: 0,
  minChunks: 1, // 当某个模块知足minChunks引用次数时,才会被打包。例如,lodash只被一个文件import,那么lodash就不会被code splitting,lodash将会被打包进 被引入的那个文件中。若是知足minChunks引用次数,lodash会被单独抽离出来,打出一个chunk。
  maxAsyncRequests: 5, // 在打包某个模块的时候,最多分红5个chunk,多余的会合到最后一个chunk中。这里分析下这个属性过大太小带来的问题。当设置的过大时,模块被拆的太细,形成并发请求太多。影响性能。当设置太小时,好比1,公共模块没法被抽离成公共的chunk。每一个打包出来的模块都会有公共chunk
  automaticNameDelimiter: '~', // 当vendors或者default中的filename不填时,打包出来的文件名就会带~
  name: true,
  cashGroups: {} // 如上
}

maxAsyncRequests

Lazy Loading

异步import的包会被单独打成一个chunk

async function getComponent() {
    const { default: _ } = await import(/* webpackChunkNanem:'lodash */ 'lodash')
    const element = document.createElement('div')
    element.innerHTML = _.join(['Dell', 'Lee'], '-')
    return element
}
document.addEventListener('click', () => {
    getComponent().then(element => {
        document.body.appendChild(element)
    })
})

lazy loading

chunk

每个js文件都是一个chunk

chunk是使用Webpack过程当中最重要的几个概念之一。在Webpack打包机制中,编译的文件包括entry(入口,能够是一个或者多个资源合并而成,由html经过script标签引入)和chunk(被entry所依赖的额外的代码块,一样能够包含一个或者多个文件)。从页面加速的角度来说,咱们应该尽量将全部的js打包到一个bundle.js之中,可是总会有一些功能是使用过程当中才会用到的。出于性能优化的须要,对于这部分资源咱们能够作成按需加载。

打包分析

打包分析:

安装:npm install --save-dev webpack-bundle-analyzer

// package.json => scripts

"analyz": "NODE_ENV=production npm_config_report=true npm run build"
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins: [
  new BundleAnalyzerPlugin()
]

执行命令npm run analyz

浏览器就会自动打开localhost:8888,分析图就会展示在你眼前

很是清晰直观的看出

image-20190421142354243

CSS文件的代码分割

咱们以前写的css文件都会被打包进js文件中,要想把css单独打包成一个css文件该怎么作呢?

这个时候就须要用到MiniCssExtractPlugin

开发环境用不到这个功能,通常都是用在生产环境中。

安装:npm install --save-dev mini-css-extract-plugin

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module: {
  rules: [
    {
      test: /\.css$/,
      use: [{
        loader: MiniCssExtractPlugin.loader,
        options: {
          // 能够在此处指定publicPath
          // 默认状况下,它在webpackoptions.output中使用publicPath
          publicPath: '../',
          // hmr: process.env.NODE_ENV === 'development',
        },
      }, 'css-loader']
    }
  ]
},
plugins: [
  new MiniCssExtractPlugin({
    // 与webpackoptions.output中相同选项相似的选项
    // 两个选项都是可选的
    filename: '[name].css',
    chunkFilename: '[id].css',
  }),
]

// index.js
import './index.css';
console.log('haha')

// index.css
body {
    background: green;
}

这样打包以后,css会被单独打包成一个css文件。

缓存

目前为止,咱们每次修改内容,打包出去后的文件名都不变。线上环境的文件是有缓存的。因此当你文件名不变的话,更新内容打包上线。有缓存的电脑就没法获取到最新的代码。

这个时候咱们就会用到contenthash

咱们先记录配置contenthash以前打包的文件名。

Asset       Size  Chunks             Chunk Names
index.html  180 bytes          [emitted]  
   main.js   3.81 KiB    main  [emitted]  main

接下来咱们来配置下contenthash (就是根据你文件内容生成的hash值)

// webpack.config.js
output: {
  path: path.resolve(__dirname, '../dist'),
  filename: '[name][contenthash].js'
},

打包完以后会在main后面接上hash值。

Asset       Size  Chunks             Chunk Names
                 index.html  200 bytes          [emitted]  
mainf5faa2d3d1e119256290.js   3.81 KiB    main  [emitted]  main

当你不更新内容从新打包后,contenthash还会维持不变。因此线上用户访问的时候就不会去服务器从新拿取代码,而是从缓存中取文件。

shimming (预置依赖)

jquery为例,代码以下

// index.js
import $ from 'jquery'
$('body').html('HHAHAH')
import func from './test.js'
func()


// test.js
export default function func() {
    $('body').append('<h1>2</h1>')
}

当你不在test.js中引入import $ from 'jquery'

那么浏览器访问的时候,会报

test.js:5 Uncaught ReferenceError: $ is not defined

这个时候就须要使用垫片功能

const webpack = require('webpack')

plugins: [
  new webpack.ProvidePlugin({
    $: 'jquery'
  })
]

当你加上这段代码后,模块在打包的时候,发现你使用了$就会在你模块顶部自动加入import $ from 'jquery'

其余关于shimming的内容参考webpack官网 shimming

相关文章
相关标签/搜索