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

相關文章
相關標籤/搜索