webpack4.0實戰那些事兒

whynotgonow發表於2018-03-29

webpack4.0剛剛釋出,官網自稱4.0最大的特點就是零配置。本文就詳細介紹一下webpack4.0實戰那些事兒。

1 什麼是WebPack

打包機

WebPack可以看做是模組打包機:它做的事情是,分析你的專案結構,找到JavaScript模組以及其它的一些瀏覽器不能直接執行的擴充語言(Scss,TypeScript等),並將其打包為合適的格式以供瀏覽器使用。

構建

構建就是把原始碼轉換成釋出到線上的可執行 JavaScrip、CSS、HTML 程式碼,包括如下內容。

  • 程式碼轉換:TypeScript 編譯成 JavaScript、SCSS 編譯成 CSS 等。
  • 檔案優化:壓縮 JavaScript、CSS、HTML 程式碼,壓縮合並圖片等。
  • 程式碼分割:提取多個頁面的公共程式碼、提取首屏不需要執行部分的程式碼讓其非同步載入。
  • 模組合併:在採用模組化的專案裡會有很多個模組和檔案,需要構建功能把模組分類合併成一個檔案。
  • 自動重新整理:監聽本地原始碼的變化,自動重新構建、重新整理瀏覽器。
  • 程式碼校驗:在程式碼被提交到倉庫前需要校驗程式碼是否符合規範,以及單元測試是否通過。
  • 自動釋出:更新完程式碼後,自動構建出線上釋出程式碼並傳輸給釋出系統。

構建其實是工程化、自動化思想在前端開發中的體現,把一系列流程用程式碼去實現,讓程式碼自動化地執行這一系列複雜的流程。 構建給前端開發注入了更大的活力,解放了我們的生產力。

2 快速配置

1 核心概念

  • Entry:入口,Webpack 執行構建的第一步將從 Entry 開始,可抽象成輸入。
  • Module:模組,在 Webpack 裡一切皆模組,一個模組對應著一個檔案。Webpack 會從配置的 Entry 開始遞迴找出所有依賴的模組。
  • Chunk:程式碼塊,一個 Chunk 由多個模組組合而成,用於程式碼合併與分割。
  • Loader:模組轉換器,用於把模組原內容按照需求轉換成新內容。
  • Plugin:擴充套件外掛,在 Webpack 構建流程中的特定時機注入擴充套件邏輯來改變構建結果或做你想要的事情。
  • Output:輸出結果,在 Webpack 經過一系列處理並得出最終想要的程式碼後輸出結果。

2 webpack的工作流程

  • 1 Webpack 啟動後會從Entry裡配置的Module開始遞迴解析 Entry 依賴的所有 Module。
  • 2 每找到一個 Module, 就會根據配置的Loader去找出對應的轉換規則,對 Module 進行轉換後,再解析出當前 Module 依賴的 Module。
  • 3 這些模組會以 Entry 為單位進行分組,一個 Entry 和其所有依賴的 Module 被分到一個組也就是一個 Chunk。
  • 4 最後 Webpack 會把所有 Chunk 轉換成檔案輸出。 在整個流程中 Webpack 會在恰當的時機執行 Plugin 裡定義的邏輯。

3 配置webpack

1) 初始化npm

npm init -y
複製程式碼

在要進行打包的目錄下初始化npm, 在控制檯執行以上命令後會生成一個package.json的檔案。

2) install

npm install webpack webpack-cli -D
複製程式碼

因為從4.0開始,webpack拆分開兩個包分別是webpackwebpack-cli

3) 配置檔案webpack.config.js

module.exports = {
    entry:配置入口檔案的地址
    output:配置出口檔案的地址
    module:配置模組,主要用來配置不同檔案的載入器
    plugins:配置外掛
    devServer:配置開發伺服器
}
複製程式碼

接下來我們就一一介紹一下它們的配置。

3 配置開發伺服器

1 install

npm install webpack-dev-server -D
複製程式碼

2 配置引數

devServer:{
    contentBase:path.resolve(__dirname,'dist'),// 配置開發服務執行時的檔案根目錄
    host:'localhost',// 開發伺服器監聽的主機地址
    compress:true,   // 開發伺服器是否啟動gzip等壓縮
    port:8080        // 開發伺服器監聽的埠
}
複製程式碼

3 配置啟動引數

"scripts": {
    "dev": "webpack-dev-server --open --mode development "
}
複製程式碼

note

從4.0開始,執行webpack時一定要加引數 --mode development 或者--mode production,分別對應開發環境和生產環境。

4 配置module

1 什麼是loader

module主要用來配置不同檔案的載入器。談到載入就離不開loader,那什麼是loader呢?

loader的概念

通過使用不同的Loader,Webpack可以要把不同的檔案都轉成JS檔案,比如CSS、ES6/7、JSX等。

  • test:匹配處理檔案的副檔名的正規表示式
  • use:loader名稱,就是你要使用模組的名稱
  • include/exclude:手動指定必須處理的資料夾或遮蔽不需要處理的資料夾
  • query:為loaders提供額外的設定選項

loader的三種寫法

  • use
  • loader
  • use+loader

2 支援載入css檔案

install

npm install style-loader css-loader -D
複製程式碼

配置載入器

module: {
    rules:[
       {
            test:/\.css$/,
            use:['style-loader','css-loader'],
            include:path.join(__dirname,'./src'),
            exclude:/node_modules/
       }        
    ]
}
複製程式碼

note

**注意:**載入器的載入順序為從右至左。即先用css-loader解析然後用style-loader將解析後的css檔案新增到Head標籤中。

3 支援圖片

install

npm install file-loader url-loader html-withimg-loader -D
複製程式碼
  • file-loader 解決CSS等檔案中的引入圖片路徑問題
  • url-loader 當圖片較小的時候會把圖片BASE64編碼,大於limit引數的時候還是使用file-loader 進行拷貝

配置載入器

{
    test: /\.(png|jpg|gif|svg|bmp|eot|woff|woff2|ttf)$/,
    loader: {
        loader: 'url-loader',
        options: {
            limit: 5 * 1024,// 圖片大小 > limit 使用file-loader, 反之使用url-loader
            outputPath: 'images/'// 指定打包後的圖片位置
        }
    }
}
複製程式碼

usage - 手動新增圖片

let logo = require('./images/logo.png');
let img = new Image();
img.src = logo;
document.body.appendChild(img);
複製程式碼

usage - 在CSS中引入圖片

.img-bg{
    background: url(./images/logo.png);
    width:173px;
    height:66px;
}
複製程式碼

usage - 在HTML中使用圖片

{
    test:/\.(html|html)$/,
    use:'html-withimg-loader',
    include:path.join(__dirname,'./src'),
    exclude:/node_modules/
}
複製程式碼

4 編譯less 和 sass

1) install

npm install less less-loader node-sass sass-loader -D
複製程式碼

2) 配置載入器

把編譯好的程式碼放到head裡面

{
    test: /\.css$/,
    loader: ['style-loader', 'css-loader']
}, {
    test: /\.less$/,
    loader: ['style-loader', 'css-loader']
}, {
    test: /\.scss$/,
    loader: ['style-loader', 'css-loader']
}
複製程式碼

把編譯好的程式碼放到單獨的檔案裡面

const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
let cssExtract = new ExtractTextWebpackPlugin('css.css');
let lessExtract = new ExtractTextWebpackPlugin('less.css');
let sassExtract = new ExtractTextWebpackPlugin('sass.css');
...
{
    test: /\.css$/,
    loader: cssExtract.extract({
        use: ['css-loader?minimize']
    })
}, {
    test: /\.less$/,
    loader: lessExtract.extract({
        use: ['css-loader?minimize', 'less-loader']
    })
}, {
    test: /\.scss$/,
    loader: sassExtract.extract({
        use: ['css-loader?minimize', 'sass-loader']
    })
}
複製程式碼

5 處理CSS3屬性字首

為了瀏覽器的相容性,有時候我們必須加入-webkit,-ms,-o,-moz這些字首

  • Trident核心:主要代表為IE瀏覽器, 字首為-ms
  • Gecko核心:主要代表為Firefox, 字首為-moz
  • Presto核心:主要代表為Opera, 字首為-o
  • Webkit核心:產要代表為Chrome和Safari, 字首為-webkit

install

npm install postcss-loader autoprefixer -D
複製程式碼

usage

postcss-loader 需要配置 postcss.config.js檔案,postcss.config.js 內容如下:

 module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}
複製程式碼
// 把 post-laoder push 到css的loader陣列中
 {
    test: /\.css$/,
    loader: ['style-loader', 'css-loader', 'postcss-loader']
}, {
    test: /\.less$/,
    loader: ['style-loader', 'css-loader', 'less-loader']
}, {
    test: /\.scss$/,
    loader: ['style-loader', 'css-loader', 'sass-loader']
}
複製程式碼

6 轉義ES6/ES7/JSX

Babel其實是一個編譯JavaScript的平臺,可以把ES6/ES7,React的JSX轉義為ES5。

install

npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-preset-react -D
複製程式碼

配置載入器

{
    test:/\.jsx?$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ["env","stage-0","react"]// env --> es6, stage-0 --> es7, react --> react
        }
    },
    include:path.join(__dirname,'./src'),
    exclude:/node_modules/
}
複製程式碼

5 配置plugins

配置外掛

1 自動產出html

我們希望自動能產出HTML檔案,並在裡面引入產出後的資源。

install

npm install html-webpack-plugin -D
複製程式碼

usage

const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html',   // 指定產出的模板
        filename: 'base.html',          // 產出的檔名
        chunks: ['common', 'base'],     // 在產出的HTML檔案裡引入哪些程式碼塊
        hash: true,                     // 名稱是否雜湊值
        title: 'base',                  // 可以給模板設定變數名,在html模板中呼叫 htmlWebpackPlugin.options.title 可以使用
        minify: {                       // 對html檔案進行壓縮
            removeAttributeQuotes: true // 移除雙引號
        }
    })
]
複製程式碼

2 分離CSS

因為CSS的下載和JS可以並行,當一個HTML檔案很大的時候,我們可以把CSS單獨提取出來載入

install

npm install extract-text-webpack-plugin@next -D
複製程式碼

usage

const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
let cssExtract = new ExtractTextWebpackPlugin('css.css');
let lessExtract = new ExtractTextWebpackPlugin('less.css');
let sassExtract = new ExtractTextWebpackPlugin('sass.css');

...

module: {
    rules: [
         {
            test: /\.css$/,
            loader: cssExtract.extract({
                use: ['css-loader?minimize']
            })
        }, {
            test: /\.less$/,
            loader: lessExtract.extract({
                use: ['css-loader?minimize', 'less-loader']
            })
        }, {
            test: /\.scss$/,
            loader: sassExtract.extract({
                use: ['css-loader?minimize', 'sass-loader']
            })
        }
    ]
}

...

plugins: [
    cssExtract,
    lessExtract,
    sassExtract
]
複製程式碼

處理圖片路徑問題

const PUBLIC_PATH='/';

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath:PUBLIC_PATH
}
複製程式碼

3 拷貝靜態檔案

有時專案中沒有引用的檔案也需要打包到目標目錄

install

npm install copy-webpack-plugin -D
複製程式碼

usage

const CopyWebpackPlugin = require('copy-webpack-plugin');
    plugins: [
        new CopyWebpackPlugin([{
            from: path.join(__dirname, 'public'),       // 從哪裡複製
            to: path.join(__dirname, 'dist', 'public')  // 複製到哪裡
    }])
]
複製程式碼

4 打包前先清空輸出目錄

install

npm install  clean-webpack-plugin -D
複製程式碼

usage

const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
    new CleanWebpackPlugin([path.join(__dirname, 'dist')])
]
複製程式碼

5 壓縮JS

install

npm install uglifyjs-webpack-plugin -D
複製程式碼

usage

onst UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
plugins: [
    new UglifyjsWebpackPlugin()
]
複製程式碼

6 如何除錯打包後的程式碼

webapck通過配置可以自動給我們source maps檔案,map檔案是一種對應編譯檔案和原始檔的方法

usage

devtool:'eval-source-map'
複製程式碼

devtool的引數詳解

  • source-map 把對映檔案生成到單獨的檔案,最完整最慢
  • cheap-module-source-map 在一個單獨的檔案中產生一個不帶列對映的Map
  • eval-source-map 使用eval打包原始檔模組,在同一個檔案中生成完整sourcemap
  • cheap-module-eval-source-map sourcemap和打包後的JS同行顯示,沒有對映列

7 打包第三方類庫

1 直接引入

import _ from 'lodash';
alert(_.join(['a','b','c'],'@'));
複製程式碼

2 外掛引入

new webpack.ProvidePlugin({
    _:'lodash'
})
複製程式碼

8 watch

當程式碼發生修改後可以自動重新編譯

 watch: true,
    watchOptions: {
        ignored: /node_modules/, //忽略不用監聽變更的目錄
        aggregateTimeout: 500,  // 檔案發生改變後多長時間後再重新編譯(Add a delay before rebuilding once the first file changed )
        poll:1000               //每秒詢問的檔案變更的次數
    }
複製程式碼

9 伺服器代理

如果你有單獨的後端開發伺服器 API,並且希望在同域名下傳送 API 請求 ,那麼代理某些 URL 會很有用。

//請求到 /api/users 現在會被代理到請求 http://localhost:9000/api/users。
proxy: {
    "/api": "http://localhost:9000",
}
複製程式碼

10 resolve解析

1 extensions

指定extension之後可以不用在require或是import的時候加副檔名,會依次嘗試新增副檔名進行匹配

resolve: {
    //自動補全字尾,注意第一個必須是空字串,字尾一定以點開頭
   extensions: ["",".js",".css",".json"],
},
複製程式碼

2 alias

配置別名可以加快webpack查詢模組的速度

  • 每當引入jquery模組的時候,它會直接引入jqueryPath,而不需要從node_modules資料夾中按模組的查詢規則查詢
  • 不需要webpack去解析jquery.js檔案
const bootstrap = path.join(__dirname,'node_modules/bootstrap/dist/css/bootstrap.css');

resolve: {
    alias: {
        'bootstrap': bootstrap
    }
}
複製程式碼

11 暴露全域性物件

install

npm install expose-loader -D
複製程式碼

action

把模組的匯出暴露給全域性變數,

usage-1

require("expose-loader?libraryName!./file.js");
複製程式碼

usage-2

rules: [{
    test: require.resolve('jquery'),// 注意 這裡是require的resolve 方法
    use: {
        loader: "expose-loader",
        options: "$"
    }
}]
複製程式碼

13 多入口

有時候我們的頁面可以不止一個HTML頁面,會有多個頁面,所以就需要多入口

usage

// 多個入口,可以給每個入口新增html模板
entry: {
    index: './src/index.js',
    main:'./src/main.js'
},
output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js',
    publicPath:PUBLIC_PATH
},

plugins: [
    new HtmlWebpackPlugin({
        minify: {
            removeAttributeQuotes:true
        },
        hash: true,
        template: './src/index.html',
        chunks:['index'],
        filename:'index.html'
    }),
    new HtmlWebpackPlugin({
        minify: {
            removeAttributeQuotes:true
        },
        hash: true,
        chunks:['login'],
        template: './src/login.html',
        filename:'login.html'
    })]
]
   
複製程式碼

14 externals

如果我們想引用一個庫,但是又不想讓webpack打包,並且又不影響我們在程式中以CMD、AMD或者window/global全域性等方式進行使用,那就可以通過配置externals。

webpack.config.js

externals: {
    jquery: "jQuery"
    //如果要在瀏覽器中執行,那麼不用新增什麼字首,預設設定就是global
},
複製程式碼

index.js

const $ = require("jquery");
const $ = window.jQuery;
複製程式碼

15 參考文章

相關文章