Webpack4.0入門

1. 介紹

  1. webpack是一個模塊打包(module bundler)工具。

A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows to load parts for the application on demand. Through "loaders", modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and you custom stuff.

  1. 大版本變化
    (1) Webpack V1.0.0 - 2014.2.20
    編譯打包、HMR(模塊熱更新)、代碼分割、文件處理。
    (2) Webpack V2.2.0 - 2017.1.18
    Tree Shaking、ES6 modules(無需babel)、動態(tài)Import、新的文檔。
    (3) Webpack V3.0.0 - 2017.6.19
    Scope Hoisting(作用域提升)、Magic Comments(魔法注釋,配合動態(tài)import使用)
    (4) Webpack V4.0.0 - 2018.2.25
    支持零配置、開發(fā)與生產(chǎn)模式、SplitChunksPlugin替換CommonChunkPlugin。

2. 初始化

  1. 創(chuàng)建項目文件夾
    webpack-operate
  2. 創(chuàng)建package.json文件
    ? webpack-operate npm init
  3. 設置當前npm項目為私有項目
    package.json中添加"private": true
  4. 安裝webpack以及webpack-cli
    ? webpack-operate npm i webpack webpack-cli --save-dev

業(yè)務邏輯中需要使用的包通過--save方式安裝;只有打包過程需要使用的包通過--save-dev方式安裝。

  1. 命令介紹
  • 獲取Webpack信息
    npm info webpack
  • 安裝指定版本Webpack
    npm i webpack@4.16.5
  • 獲取全局環(huán)境中webpack版本
    webpack -v
  • 獲取當前項目中webpack版本
    npx webpack -v

3. 模塊打包

  1. 創(chuàng)建打包配置文件
    webpack.dev.config.js
  2. 創(chuàng)建入口文件
    ./src/index.js
  3. 編輯打包配置文件
    webpack.dev.config.js
const path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, 'dist')
  }
}

'./src/index.js'{"main": './src/index.js'}的簡寫。其中,mainchunk name。
如果希望將腳本打包到dist/js/文件夾中,可以修改filename值為js/main.js。即filename不僅可以設置文件名,還可以設置路徑。

  1. 創(chuàng)建打包命令
    package.json文件
"scripts": {
    "bundle": "webpack --config ./webpack.dev.config.js --mode development"
}
  • mode的含義
    mode的值為developmentproduction。當取值為production時,輸出文件會被壓縮。
  • 打包配置
    如果沒有配置文件,則使用默認配置,入口文件為./src/index.js,輸出文件為dist/main.js。
    如果有./webpack.config.js文件,則默認使用該文件作為配置文件。
    如果使用其它配置文件,則需要通過--config指定配置文件。
  • webpack版本
    如果直接通過webpack命令打包,則是使用全局安裝的webpack打包。
    如果通過npx webpack/npm run xxx(scripts內(nèi)部定義命令),則使用當前項目安裝的webpack打包。
?  webpack-demo webpack -v
3.8.1
?  webpack-demo npx webpack -v
4.29.6
?  webpack-demo npm run version
> webpack-demo@1.0.0 ver /Users/nimengwei/Code/mycode/webpack/webpack-demo
> webpack -v
4.29.6

webpack3.X版本不支持零配置(沒有配置文件),除非在命令行中指定入口文件和出口文件(webpack entry output)。
webpack3.X版本不支持通過mode定義development(開發(fā)模式)或production(生產(chǎn)模式)。

  1. 執(zhí)行打包命令
    npm run bundle
    此時,生成dist文件夾,內(nèi)部有打包后文件main.js。
?  webpack-operate npm run bundle                      

> webpack-operate@1.0.0 bundle /Users/nimengwei/Code/mycode/webpack/webpack-operate
> webpack --config ./webpack.dev.config.js --mode development

Hash: 5b90c629ada6fed12c59 //當前打包的唯一標識
Version: webpack 4.29.6 //webpack版本
Time: 75ms  //打包用時
Built at: 04/02/2019 7:29:52 PM
  //輸出文件   大小  輸入chunks         輸入chunks名稱
  Asset      Size  Chunks             Chunk Names
main.js  3.77 KiB    main  [emitted]  main
Entrypoint main = main.js
[./src/index.js] 0 bytes {main} [built]

這里的chunk名稱可以在entry入口中設置。

4. 清理打包文件

  1. 安裝依賴
    ? webpack-operate npm i clean-webpack-plugin -D
  2. 編輯打包配置文件
    webpack.dev.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
  //...
  plugins: [
    new CleanWebpackPlugin(),
  ]
  //...
}
  1. plugin的作用是當打包進行到某一個時刻時,執(zhí)行某些操作。例如,clean-webpack-plugin就是在打包前清除上次打包生成文件。

5. html文件

5.1 html模板

  1. html-webpack-plugin文檔
  2. 安裝依賴
    ? webpack-operate npm i html-webpack-plugin --save-dev
  3. 創(chuàng)建html模板文件
    ./index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Webpack操作指南</title>
</head>
<body>
  <h1>Webpack操作指南</h1>
</body>
</html>
  1. 編輯打包配置文件
    webpack.dev.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html'
    })
  ]
  //...
}
  1. 執(zhí)行打包
    npm run bundle
    此時,打包結(jié)束后,打包文件夾中會以./index.html為模板,自動生成一個html文件,并將打包生成的 js文件自動引入到該html文件中。
  2. html-webpack-plugin就是在打包結(jié)束時根據(jù)指定模板生成html文件并插入打包生成的js文件。
  3. 其它配置項
    new HtmlWebpackPlugin({
      template: './index.html', //模板文件
      chunks: ['app'], //生成html文件添加哪些chunks,默認全部添加
      minify: {
        collapseWhitespace: true //壓縮html
      }
    })

5.2 html中引入圖片

  1. 準備工作
    配置5.1 html模板;
    配置9. 加載圖片
  2. 修改./index.html
...
<img src="./assets/controls.png" data-src="./assets/controls.png">
<img src="${require('./assets/controls.png')}" data-src="${require('./assets/controls.png')}">
...
  1. 執(zhí)行打包
    npm run bundle
    發(fā)現(xiàn)第一種圖片引入方式無法顯示,第二種圖片引入方式可以顯示。

第二種圖片引入方式可以顯示的前提是需要在配置中為圖片指定 loader(推薦 file-loaderurl-loader)。

  1. 安裝依賴
    npm i -D html-loader
  2. 編輯打包配置文件
    webpack.dev.config.js
{
  test: /\.html$/,
  use: [
    {
      loader: "html-loader",
      options: {
        attrs: [':src', ':data-src']
      }
    }
  ]
}
  1. 執(zhí)行打包
    npm run bundle
    此時第一種圖片引入方式可以顯示,第二種圖片引入方式無法顯示。

6. 模塊熱更新

6.1 webpack --watch

  1. 編輯打包命令
    package.json文件
  "scripts": {
    //...
    "watch": "webpack --watch --config ./webpack.dev.config.js --mode development",
    //...
  }
  1. 執(zhí)行打包
    npm run watch
    webpack會監(jiān)聽打包文件,當文件內(nèi)容發(fā)生變化時自動重新執(zhí)行打包操作。
    例如,修改src/index.js文件并保存(webpack會自動重新打包),刷新dist/index.html文件,頁面執(zhí)行修改后的js文件。

webpack --watch命令僅僅實現(xiàn)了自動打包操作,無法自動打開瀏覽器,無法開啟一個本地服務,無法自動刷新。

  1. webpack命令配置參數(shù)
    語法:webpack [--config webpack.config.js]
    webpack --json: 以 JSON 格式輸出 webpack 的運行結(jié)果
    --config: 配置文件的路徑
    --progress:打印出編譯進度的百分比值
    --watch, -w:觀察文件系統(tǒng)的變化
    --color, --colors:開啟/關閉控制臺的顏色
    --display-reasons:顯示模塊包含在輸出中的原因

6.2 webpack-dev-server

6.2.1 快速開發(fā)應用程序

  1. 安裝依賴
    ? webpack-operate npm i webpack-dev-server --save-dev
  2. 編輯打包配置文件
    webpack.dev.config.js
module.exports = {
  //...
  devServer: {
    open: true, //瀏覽器自動打開
    port: 9000, //指定端口號
    contentBase: './dist' //啟動服務的目錄
  }
  //...
}

默認瀏覽器不自動打開,默認端口為8080

  1. 編輯打包命令
    package.json文件
"scripts": {
    "bundle": "webpack --config ./webpack.dev.config.js --mode development",
    "dev": "webpack-dev-server --config ./webpack.dev.config.js --mode development"
  },

webpack3.X版本不支持--mode development/production設置打包模式。

  1. 執(zhí)行打包命令
    npm run dev
    此時瀏覽器自動打開http://localhost:9000,可以看到html-webpack-plugin在打包結(jié)束后生成的index.html文件(由于html-webpack-plugin的功能,該文件以./index.html為模板,以./release/bundle.jsJavaScript腳本)。且修改 ./src/index.js文件時,瀏覽器自動刷新。
  2. webpack-dev-server依賴于webpack-cli。安裝webpack-cli會同時安裝webpack-dev-server。對于webpack3.X版本來說,使用最新版本的webpack-dev-server會報錯,可通過降低版本解決,例如2.9.7版本。

使用webpack-dev-server打包會在內(nèi)存中生成打包文件,提升打包效率。 并不會實際生成dist文件夾以及打包輸出文件。如果需要打包生成文件,仍然需要使用webpack命令。
當前配置雖然實現(xiàn)了代碼修改自動打包瀏覽器自動刷新。但瀏覽器刷新之前的頁面操作無法保存,尚未實現(xiàn)不刷新瀏覽器在運行時更新所修改模塊。

6.2.2 devServer.historyApiFallback

  1. 當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html。通過傳入以下啟用:
historyApiFallback: true
  1. 通過傳入一個對象,比如使用 rewrites 這個選項,此行為可進一步地控制:
historyApiFallback: {
  rewrites: [
    { from: /^\/$/, to: '/views/landing.html' },
    { from: /^\/subpage/, to: '/views/subpage.html' },
    { from: /./, to: '/views/404.html' }
  ]
}
  1. 可以通過正則表達式匹配通用參數(shù)
historyApiFallback: {
  rewrites: [
    {
      from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/,
      to: function(context) {
        console.log(context.match[1], context.match[2])
        return '/' + context.match[1] + context.match[2] + '.html'
      }
    },
  ]
}

6.2.3 代理

  1. 文檔
    devserver-proxy
    http-proxy-middleware
  2. 配置代理
    編輯打包配置文件
    webpack.dev.config.js
module.exports = {
  //...
  devServer: {
    open: true, //瀏覽器自動打開
    port: 9000,
    proxy: {
      '/api': {
        changeOrigin: true, //changes the origin of the host header to the target URL
        target: 'http://localhost:8880'
        //訪問hocalhost:9000/api/...時,會被代理到hocalhost:8880/api/...
      }
    }
  }
}

此時,訪問hocalhost:9000/api/...時,會被代理到hocalhost:8880/api/...。

proxy代理只在開發(fā)環(huán)境中使用。

  1. 支持https
    默認情況下,不接受運行在 HTTPS 上,且使用了無效證書的后端服務器。如果你想要接受,修改配置如下:
proxy: {
  '/api': {
    target: 'http://localhost:8880',
      secure: false
    }
}

6.3 模塊熱更新(HMR)

  1. 模塊熱替換(hot module replacementHMR)是 webpack 提供的最有用的功能之一。它允許在運行時更新所有類型的模塊,而無需完全刷新。

模塊熱更新為無刷新瀏覽器更新。

  1. 編輯打包配置文件
    webpack.dev.config.js
  //...
  devServer: {
    open: true, //瀏覽器自動打開
    port: 9000,
    contentBase: './dist',
    hot: true,
    hotOnly: true,
  }
  //...
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    //...
  ]
  //...

hotOnlytrue,表示當html文件失效時,不自動刷新瀏覽器。

  1. 執(zhí)行打包命令
    npm run dev
    此時實現(xiàn)了CSS文件的模塊熱更新。
  2. 監(jiān)聽js文件變化回調(diào)
if (module.hot) {
   module.hot.accept('./print.js', function() {
     console.log('Accepting the updated printMe module!');
    printMe();
   })
 }

./print.js為當前打包中引入的模塊。
js模塊熱更新需要module.hot.accept監(jiān)聽文件變化的方式實現(xiàn)。
css-loader已經(jīng)幫我們做了module.hot.accept監(jiān)聽CSS文件變化的代碼。
reactvue項目開發(fā)時,第三方loader也已經(jīng)實現(xiàn)了module.hot.accept監(jiān)聽。

6.4 自定義打包腳本

  1. 安裝依賴
    ? webpack-operate npm i express webpack-dev-middleware -D
  2. 編輯打包命令
    package.json文件
  "scripts": {
    //...
    "server": "node server.js"
    //...
  }
  1. 編輯打包配置文件
    webpack.dev.config.js
  output: {
    //...
    publicPath: './',
    //...
  }
  1. 創(chuàng)建并編輯打包腳本
    ./server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const config = require('./webpack.dev.config')

//根據(jù)config配置,返回一個編譯器
//在node中直接使用Webpack
const complier = webpack(config) 

//創(chuàng)建一個express服務
const app = express();

//使用webpackDevMiddleware中間件
app.use(webpackDevMiddleware(complier, {
  publicPath: config.output.publicPath
}))

//開啟服務,監(jiān)聽8080端口
app.listen(8080, () => {
  console.log("server is running!")
})

命令行中使用webpack。node.js中使用webpack。

  1. 執(zhí)行打包命令
    npm run server
    此時,開啟了一個本地服務,手動打開http://localhost:8080/地址,可以看到html-webpack-plugin在打包結(jié)束后生成的index.html文件。與webpack --watch類似,修改代碼后會重新打包,但沒有實現(xiàn)自動打開瀏覽器,沒有實現(xiàn)自動刷新。可以通過修改server.js的方式添加這些功能。

7. 開發(fā)調(diào)試

7.1 devtool

  1. SourceMap
    保存打包前后代碼位置信息,方便調(diào)試。
  2. 編輯打包配置文件
    webpack.dev.config.js
module.exports = {
  //...
  devtool: 'inline-source-map',
  //...
}
  1. devtool字段值含義
    image.png

source-map:完整映射到原始源代碼。
inline: sourceMappingURL指向base64字符串,否則打包生成map文件。
cheap:代碼只映射到行信息,只映射業(yè)務代碼,不映射第三方模塊代碼。
module: 不僅映射業(yè)務代碼,而且映射第三方模塊代碼。
eval:以eval(string)的方式運行代碼,打包速度最快。

eval打包速度最快。

  1. devtool舉例介紹
    source-map表示完整映射到原始源代碼;sourceMappingURL指向打包生成的map文件。
    inline-source-map表示完整映射到原始源代碼;sourceMappingURL指向base64字符串。
    inline-cheap-source-map表示代碼只映射到行信息;sourceMappingURL指向base64字符串。
    cheap-module-source-map表示代碼只映射到行信息;不僅映射業(yè)務代碼,而且映射第三方模塊代碼;sourceMappingURL指向打包生成的map文件。
  2. devtool適用環(huán)境
    開發(fā)環(huán)境:eval、eval-source-mapcheap-eval-source-map、cheap-module-eval-source-map。
    生產(chǎn)環(huán)境:source-map、hidden-source-mapnosources-source-map。
  3. devtool最佳實踐
    開發(fā)(development)環(huán)境:cheap-module-eval-source-map/cheap-module-source-map
    生產(chǎn)(production)環(huán)境:source-map

7.2 插件

  1. 插件實現(xiàn)了對 source map 生成,進行更細粒度的控制。它可以替代 devtool 選項。
  2. 文檔
    eval-source-map-dev-tool-plugin
    source-map-dev-tool-plugin

7.3 CSS SourceMap

  1. 準備工作
    10. 加載樣式
  2. 編輯打包配置文件
    webpack.dev.config.js
{
  test: /\.css$/,
  use: [
    {
      loader: "style-loader",
      options: {
        //singleton: true,
        sourceMap: true
      }
    },
    {
      loader: 'css-loader',
      options: {
        sourceMap: true
      }
    },
    {
      loader: 'postcss-loader',
      options: {
        ident: 'postcss',
        plugins: [ require('autoprefixer')()],
        sourceMap: true
      }
    }
  ]
}
  1. CSS開啟SourceMap配置總結(jié)
    (1) 關閉style-loader中的singleton選項。
    (2) 處理樣式的所有loader(style-loadercss-loader、postcss-loader)都開啟sourceMap選項。

8. 加載腳本

8.1 ES6語法

  1. 安裝依賴
    npm i -D babel-loader @babel/core @babel/preset-env

打開babel文檔,可以看到在不同工具中使用babel的方法。

  1. 編輯打包配置文件
   //...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
          plugins: []
        }
      }
    ]
  }
  //...
  1. 執(zhí)行打包命令
    npm run bundle
    此時生成的app.bundle.js大小為28.8 KB

@babel/preset-env只支持stage-4范圍內(nèi)的JavaScript語法。
stage的包含順序是:左邊包含右邊全部特性。
stage-0 > stage-1 > stage-2 > stage-3 > stage-4。

8.2 添加polyfill

  1. babel-polyfill文檔
  2. babel-polyfill介紹
    Babel默認只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如IteratorGenerator、Set、Maps、ProxyReflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉(zhuǎn)碼。
    舉例來說,ES6Array對象上新增了Array.from方法。Babel就不會轉(zhuǎn)碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,為當前環(huán)境提供一個墊片。
  3. 安裝依賴
    npm install --save @babel/polyfill
    @babel/polyfill依賴安裝到dependencies,而不是devDependencies。
  4. 編輯入口文件
    src/index.js
import "@babel/polyfill";
//...
  1. 執(zhí)行打包命令
    npm run bundle
    由于將所有的polyfill都打包輸出,此時生成的app.bundle.js大小為534 KB
  2. polyfill按需打包
    (1) 安裝依賴
    npm i core-js@2 --save
    (2) 編輯打包配置文件
    webpack.dev.config.js
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            [ "@babel/preset-env", {"useBuiltIns": "usage", "corejs": 2}]
          ],
          plugins: []
        }
      }

useBuiltIns可選值為一個布爾值,默認為false。當設置為true時,表示使用瀏覽器內(nèi)置的語法,而不是對任意插件進行全量polyfill。

(3) 編輯入口文件
src/index.js

//import "@babel/polyfill";
//...

When setting "useBuiltIns": "usage", polyfills are automatically imported when needed. Please remove the import '@babel/polyfill' call or use "useBuiltIns": 'entry' instead.

(4) 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為84.6 KB。

  1. 指定項目支持的瀏覽器版本
    (1) 編輯打包配置文件
    webpack.dev.config.js
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: "babel-loader",
  options: {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: {
            //browsers: [ ">1%"],
            chrome: "67",
            safari: "11.1"
          },
          "useBuiltIns": "usage",
          "corejs": 2
        }
      ]
    ],
    plugins: []
  }
}

當指定targets瀏覽器版本時,@babel/preset-env@babel/polyfill只對指定瀏覽器版本不支持的語法進行轉(zhuǎn)碼和添加墊片。

(2) 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為28.7 KB。

8.3 transform-runtime

  1. transform-runtime文檔
  2. transform-runtimebabel-polyfill對比
    運行環(huán)境中并沒有實現(xiàn)的API,babel-polyfill會給其做兼容。 這樣做有一個缺點,就是會污染全局變量。不推薦在一些方法類庫中去使用。為了不污染全局對象和內(nèi)置的對象原型,又想使用新的API,就可以使用transform-runtime

transform-runtime可以以閉包的形式實現(xiàn)babel-polyfill的功能,不污染全局變量。src/index.js文件中無需引入import "@babel/polyfill";。

  1. 安裝依賴
    npm install --save-dev @babel/plugin-transform-runtime
    npm install --save @babel/runtime
    npm install --save @babel/runtime-corejs2
  2. 編輯打包配置文件
    webpack.dev.config.js
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: "babel-loader",
  options: {
    plugins: [
      [
        "@babel/plugin-transform-runtime",
        {
          "corejs": 2,
          "helpers": true,
          "regenerator": true,
          "useESModules": false
        }
      ]
    ]
  }
}

corejs如果設置為false,則不需要安裝@babel/runtime-corejs2

  1. 執(zhí)行打包命令
    npm run bundle
    此時生成的app.bundle.js大小為116 KiB 。
  2. 簡化打包配置文件
    創(chuàng)建.babelrc文件,并將options值剪切到該文件中。
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
  ]
}

如果你寫的是業(yè)務代碼,則使用babel-polyfill。如果你寫的是第三方類庫,則需要使用plugin-transform-runtime。

8.4 Stage-0語法支持

  1. 安裝依賴
?  webpack-operate npm i @babel/plugin-proposal-function-bind @babel/plugin-proposal-export-default-from @babel/plugin-proposal-logical-assignment-operaors @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-pipeline-operator @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-do-expressions @babel/plugin-proposal-decorators @babel/plugin-proposal-function-sent @babel/plugin-proposal-export-namespace-from @babel/plugin-proposal-numeric-separator @babel/plugin-proposal-throw-expressions @babel/plugin-syntax-dynamic-import @babel/plugin-syntax-import-meta @babel/plugin-proposal-class-properties @babel/plugin-proposal-json-strings -D
  1. 編輯babel配置文件
 "plugins": [
    // Stage 0
    "@babel/plugin-proposal-function-bind",

    // Stage 1
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    ["@babel/plugin-proposal-optional-chaining", { "loose": false }],
    ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }],
    ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }],
    "@babel/plugin-proposal-do-expressions",

    // Stage 2
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", { "loose": false }],
    "@babel/plugin-proposal-json-strings"
  ]

Babel 7,Removing Babel's Stage Presets

8.5 React語法

  1. babel-preset-react文檔
  2. 安裝依賴
    ? webpack-operate npm i react react-dom --save
    ? webpack-operate npm install --save-dev @babel/preset-react
  3. 編輯babel配置文件
{
  "presets": [
    [
      "@babel/preset-env", {
        "targets": {
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": 2
      }
    ],
    "@babel/preset-react"
  ]
}

presets的執(zhí)行順序為數(shù)組從后向前。@babel/preset-env@babel/preset-react的順序不能變。

  1. 編輯入口文件
    ./src/index.js
import React, { Component } from 'react'
import ReactDom from 'react-dom'

class App extends Component {
  render() {
    return (
      <div>
        Hello world!!!
      </div>
    )
  }
}

ReactDom.render(<App/>, document.getElementById('root'))

9. 加載圖片

9.1 file-loader

  1. 安裝依賴
    npm install --save-dev file-loader
  2. 編輯打包配置文件
    webpack.dev.config.js
  module: {
    rules: [{
      test: /\.(png|svg|jpg|gif)$/,
      use: [
        'file-loader'
      ]
    }]
  }
  1. 添加圖片
    src/assets/weixin.jpg
  2. 修改src/index.js文件
var img = require('./assets/weixin.jpg')

var myLogo = new Image()
myLogo.src = img
document.body.appendChild(myLogo)

圖片資源作為img元素。

  1. 修改src/style.css文件
body {
  background: url('./logo.png')
}

圖片資源作為元素背景。

  1. 修改圖片打包輸出路徑及文件名
    webpack.dev.config.js
  module: {
    rules: [{
      test: /\.(png|svg|jpg|gif)$/,
      use: {
        loader:'file-loader',
        options: {
          name: '[name]-[hash:5].[ext]',
          outputPath: 'images/'
        }
      }
    }]
  }

file-loader中的placeholders文檔。

9.2 url-loader

  1. 安裝url-loader
    ? webpack-operate npm i url-loader --save-dev
  2. 修改打包配置文件
    webpack.dev.config.js
module: {
    rules: [{
      test: /\.(png|svg|jpg|gif)$/,
      use: {
        loader:'url-loader',
        options: {
          name: '[name]-[hash:5].[ext]',
          outputPath: 'images/'
        }
      }
    }]
  }

url-loader擁有file-loader的全部功能。
url-loader會將圖片轉(zhuǎn)化為base64字符串。

  1. 將大于4KB的圖片資源打包輸出
module: {
    rules: [{
      test: /\.(png|svg|jpg|gif)$/,
      use: {
        loader:'url-loader',
        options: {
          name: '[name]-[hash:5].[ext]',
          outputPath: 'images/',
          limit: 4096
        }
      }
    }]
  }

9.3 其它loader

  1. 圖片壓縮
    img-loader
  2. 自動合成雪碧圖
    postcss-sprites

10. 加載樣式

10.1 使用CSS樣式

  1. 安裝依賴
    npm install --save-dev style-loader css-loader
  2. 編輯打包配置文件
    webpack.dev.config.js
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
              "style-loader",  // 將 JS 字符串生成為 style 節(jié)點
              "css-loader",    // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
        ]
      }
    ]
  }

webpackloader數(shù)組的執(zhí)行順序為從后向前。

  1. 創(chuàng)建src/style.css文件
.img {
  width: 300px;
  height: 300px;
  transform: translate(100px, 100px);
}
  1. 修改src/index.js文件
import './style.css'
var logo = require('./assets/weixin.jpg')

var myLogo = new Image()
myLogo.src = logo
myLogo.classList.add('img')
document.body.appendChild(myLogo)

使用file-loaderurl-loader后才能支持weixin.jpg文件引入。
這種方式引入的CSS文件會被style標簽包裹并插入到頁面head內(nèi)部。

  1. 查詢參數(shù)importLoaders
    用于配置css-loader作用于 @import 的資源之前有多少個loader

10.2 style-loader配置項

  1. style-loader配置項文檔
  2. 編輯打包配置文件
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
            options: {
              insertInto: () => document.querySelector("#root"),//插入到指定DOM
              singleton: true, //是否只使用一個style標簽
              transform: './src/css-transform.js'
            }
          },
          'css-loader'
        ]
      }
  1. 創(chuàng)建并編輯變形文件
module.exports = function(css) {
  // Here we can change the original css
  console.log(css);
  if(window.innerWidth > 768) {
    return css.replace('yellow', 'red');
  } else {
    return css.replace('yellow', 'pink');
  }
};

10.3 style-loader/useable

  1. 編輯打包配置文件
    webpack.dev.config.js
  {
    test: /\.css$/,
    use: [
      'style-loader/useable',
      'css-loader'
    ]
  }
  1. 修改src/index.js文件
import base from './style.css'

let flag = true;
setInterval(() => {
  if(flag) {
    base.unuse()
  } else {
    base.use()
  }
  flag = !flag
}, 500);

./style.css樣式每500ms在生效和不生效兩種狀態(tài)之間切換一次。

使用style-loader/useable后,樣式默認不生效。必須調(diào)用use()方法才會生效。

10.4 CSS模塊化

  1. 修改webpack.dev.config.js
{
  test: /\.css$/,
  use: [
    {
      loader: 'css-loader',
      options: {
        modules: true,
        localIdentName: '[path][name]__[local]--[hash:base64:5]'
      }
    }
  ]
}
  1. 修改src/index.js文件
import style from './style.css'
var src = require('./assets/weixin.jpg')

var logo1 = new Image()
logo1.src = src
logo1.classList.add(style.img)
document.body.appendChild(logo1)

var logo2 = new Image()
logo2.src = src
logo2.classList.add('img')
document.body.appendChild(logo2)

./style.css并不會成為全局樣式。
style.img類會創(chuàng)建一個由[path][name]__[local]--[hash:base64:5]組成的類名,并添加./style.css中的.img樣式。
'img'類并不會添加./style.css中的.img樣式。

  1. 作用域
    :local(.className) 可以被用來在局部作用域中聲明 className。局部的作用域標識符會以模塊形式暴露出去。
    :global(.className) 可以用來聲明一個明確的全局選擇器。

以上配置都是css-loader@2.X版本的配置,css-loader@1.X版本配置@2.X版本配置不同。

10.5 使用SCSS樣式

  1. 安裝依賴
    npm install sass-loader node-sass --save-dev
  2. 編輯打包配置文件
    webpack.dev.config.js
{
  test: /\.scss$/,
  use: [
    "style-loader", // 將 JS 字符串生成為 style 節(jié)點
    "css-loader", // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
    "sass-loader" // 將 Sass 編譯成 CSS,默認使用 Node Sass
  ]
}
  1. 創(chuàng)建src/style.scss文件
body {
  img {
    width: 300px;
    height: 300px;
  }
}
  1. 引入src/style.scss文件
import './style.scss'

10.6 添加樣式兼容前綴

  1. 安裝依賴
    ? webpack-operate npm i postcss-loader autoprefixer -D

-D等價與--save-dev

  1. 編輯打包配置文件
    webpack.dev.config.js
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: [ require('autoprefixer')()]
            }
          }
        ]
      }
    ]
  }

postcss-loader如果與sass-loader同時使用,則postcss-loaderloader數(shù)組中位于sass-loader之后。

10.7 使用字體圖標

  1. 進入阿里云圖標網(wǎng)站
  2. 創(chuàng)建項目并添加圖標
  3. 下載Unicode至本地并解壓
  4. 拷貝文件
    iconfont.eot、iconfont.svg、iconfont.ttf、iconfont.woff文件拷貝到項目中。項目路徑如下:
    ./src/font/iconfont.XXX
  5. iconfont.css文件內(nèi)容拷貝到src/style.css并修改url路徑
@font-face {font-family: "iconfont";
  src: url('./font/iconfont.eot?t=1554215262535'); /* IE9 */
  src: url('./font/iconfont.eot?t=1554215262535#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALoAAsAAAAABpQAAAKbAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAqBHIEcATYCJAMICwYABCAFhG0HMxvFBRHVk0tkXx9wcomVY8Ck6KrLNGtjkwhFT/9iuvM1Ch7cCAfnfTw87fft3Dfz/86KGyLaRCuNRKKSNVESGRqRrB2P4om6rwEKuABNzW1ibUkMt+iNRsSTWPbMSH8ks/pynaO0JXqChifQ1nSPpAg5c5zvkrXsITvJd+x8WEbQKUyFsAKgSxAl3/k81+QroAPJD3SOt6qgosQBjvc8QLvsaCAD3hg3jF3gEh4TaDNlQ5xZNJpUkVmrAnE7nQ+pkkvLMqsVoVlzMI/FkUr1qHJ5HP4+fu0KiUojsdou3apPqBNfsZekXb3Sni8IdoyEDWTiWm35giQYm7SVszn21Rx8Xaxr3yv26hLsr7MaO8AEVO5J42pvtVbBbU21iUOjPpfYvNt9hgdX+HwprZvevb4Q0sYkMrwR8lH7b63z+LyxOsJm/44P0PtN9uLlapBnxrYD/u7smF2/dxj+WHCwmMGTRa1+YXbxX60ql7Pck+SViyP1t7rUCdRb9T+Hk/PvXnP2eSl3U+5/6WXw7mVuqO8XRTlDJPhTXdl6i7zAdVa5ymiZHKhs3kFHmzbUULzPl7H2wduqh2JwRLTFZJpLirWMGTIbaGi3B03FcbRZd/54u353JJFbseYeQOjxFJUuL5D0eEtk5jMahvxBU0+waHMjuLDdUoCpNiUMGJ8Q/iCN6ko6Ptai5gvGryKgrC7kHkgqzYGlm8XaEiukObaod2wzS5BUl7AAz2FR1NBQnWHEesLcOIYh696kR3UpNDElDBifEP4gjepK+qNZK33/gvGrCKhloCZ/IKl0drB0swexNFe9Bu5FjXrHNrMESXUJCzAPi6KGpn5ehhHryYhi4xj2MNnXrK9vLb/uGLQJy1F17B1JV3FAXli8UAgA') format('woff2'),
  url('./font/iconfont.woff?t=1554215262535') format('woff'),
  url('./font/iconfont.ttf?t=1554215262535') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
  url('./font/iconfont.svg?t=1554215262535#iconfont') format('svg'); /* iOS 4.1- */
}

:global(.iconfont) {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

:global(.iconstar_blue:before) {
  content: "\e625";
}

由于css-loader使用了css模塊化(modules), 所以這里需要使用:global(.className)聲明全局選擇器。

  1. 修改src/index.js
import './style.css'

document.body.innerHTML = `<div>
    <span class='iconfont iconstar_blue'></span>
    <span class='iconfont'>&#xe625;</span>
</div>`
  1. 編輯打包配置文件
 {
    test: /\.(eot|ttf|svg|woff)$/,
    use: {
      loader: "file-loader",
      options: {
         name: '[name]-[hash:5].[ext]',
         outputPath: 'font/'
      }
    }
  }

11. Entry與Output配置

11.1 多個輸出文件

  1. 創(chuàng)建src/print.js文件
console.log('print')
  1. 修改打包配置文件
    webpack.dev.config.js
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
  1. 執(zhí)行打包命令
    npm run dev
    webpack打包輸出幾個bundle文件并不是由模塊依賴樹決定,而是根據(jù)打包配置文件中的output決定。即使src/index.js文件引入了src/print.js文件,仍會將后者打包輸出為單獨的bundle。

11.2 publicPath配置

  1. 修改打包配置文件
    webpack.dev.config.js
  //...
  output: {
    publicPath: 'http://cdn.com.cn',
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
  //...
  1. 執(zhí)行打包命令
    npm run dev
    此時,通過html-webpack-plugin插件在dist目錄中生成的html文件中引入打包生成的js文件的src會添加http://cdn.com.cn公共路徑。
<script type="text/javascript" src="http://cdn.com.cn/app.bundle.js"></script>
<script type="text/javascript" src="http://cdn.com.cn/print.bundle.js"></script>

11.3 瀏覽器長緩存(Caching)

  1. 提取第三方庫的代碼、使用contentHash、提取webpack runtime。
  2. 在生產(chǎn)環(huán)境中。為了利用瀏覽器緩存并及時更新。我們希望當代碼變化時,打包生成的文件名變化。當代碼沒有變化時,打包生成的文件名不變。
  3. 修改打包配置文件
    webpack.dev.config.js
//...
output: {
    filename: '[name].[contenthash].bundle.js',
    chunkFilename: '[name].[contenthash].chunk.js',
  },
//...

打生產(chǎn)環(huán)境包才需要這樣配置。
在使用 ExtractTextWebpackPlugin 時,可以用 [contenthash] 來獲取提取文件的 hash既不是[hash]也不是[chunkhash])。

  1. webpack 4早期版本中,可能出現(xiàn)代碼內(nèi)容沒有變化,但是重新打包contentHash仍然變化的情況。原因是打包輸出的js文件之間的關系代碼manifest發(fā)生變化??梢酝ㄟ^將manifest代碼分割的方式解決。
  //...
  optimization: {
    runtimeChunk: {
        name: 'runtime'
    }
  }
  //...

最新版本webpack不需要此項配置。

參考資料

Javascript 設計模式系統(tǒng)講解與應用
Webpack中文文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 前言 本文主要從webpack4.x入手,會對平時常用的Webpack配置一一講解,各個功能點都有對應的詳細例子,...
    BetterChen閱讀 2,045評論 0 3
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 webpack介紹和使用 一、webpack介紹 1、由來 ...
    it筱竹閱讀 11,485評論 0 21
  • webpack 介紹 webpack 是什么 為什么引入新的打包工具 webpack 核心思想 webpack 安...
    yxsGert閱讀 6,681評論 2 71
  • 寫在開頭 先說說為什么要寫這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,452評論 4 31
  • 保持每場比賽都記錄吧,雖然說我很菜,寫得不好,但會慢慢變強,也會把博客寫好的。 比賽鏈接 A A題鏈接 中文題意:...
    不知名小號閱讀 371評論 0 0

友情鏈接更多精彩內(nèi)容