基於webpack4.x專案實戰

鹹魚老弟發表於2019-03-03

webpack在前端開發者的世界再熟悉不過了,網上也很多關於webpack的文章,自己也寫一下,加深印象

webpack 是js模組打包器,一直在更新,本文是基於webpack4.29.5版本,將來的某一天,發覺本文章的一些配置用不了,那可能是webpack已經更新到更高的版本了

webpack4.0的零配置

安裝webpack4webpack-cli,由於webpack4中和webpack-cli抽離了,所以需要分別安裝,我們全域性安裝一個:

npm install webpack webpack-cli -g

搞一個demo試試

mkdir webpack-demo && cd webpack-demo
npm init -y
複製程式碼

新建一個index.js檔案,./src/index.js

--package.json
--src
  -- index.js
複製程式碼

webpack4支援0配置,預設./src/index.js為入口檔案,webpack執行時,會根據mode的值採取不同的預設配置,mode兩個可選值:productiondevelopment。沒有傳mode,會有一個警告

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
複製程式碼

解決這個警告,修改package.json部分,傳入mode即可

   "scripts": {
      "dev": "webpack --mode development",
      "build": "webpack --mode production"
   }
複製程式碼

developmentproduction的區別在於一個程式碼沒壓縮,一個有壓縮和優化,執行 npm run dev,就會生產一個./dist/main.js檔案。

如果我們只是想要一個簡單的打包功能,使用預設配置就夠用了。都不需要建立webpack.config.js

自定義webpack配置

在我們的專案中,使用webpack的預設配置明顯是不夠用的,還是需要自定義我們的webpack配置

在我們的專案根目錄,建立webpack.config.js,webpack配置的概況如下

module.exports = {
    entry: '',               // 入口檔案
    output: {},              // 出口檔案
    module: {},              // 模組相關配置
    plugins: [],             // 外掛相關配置
    resolve: { }             // 解析模組的可選項
    devServer: {},           // 開發伺服器相關配置
    devtool: 'inline-source-map',  //開發工具,比如啟動source-map
    mode: 'development'      // 模式配置 development/production
}
複製程式碼

接下來,我們一點點往裡面新增內容,以此更好的理解webpack

單入口配置

一些專案中,只有一個入口檔案,那麼,入口檔案和出口檔案可以這樣配置

const path = require('path');

module.exports = {
    entry: './src/index.js',    
    output: {
        filename: 'main.js',    // 打包後的檔名稱
        path: path.resolve(__dirname, 'dist')  // 打包後的目錄
    }
}
複製程式碼

多入口配置

如果有多個入口,可以將entry配置成一個array或者object,如果是array,則是將多個入口檔案最終生成一個出口檔案,如果是object,則對應生成多個檔案,如下: entry為array

const path = require('path');
module.exports = {
    entry: ['./src/a.js', './src/b.js'], 多個入口檔案打包為一個js檔案
    output: {
        filename: 'main.js', 
        path: path.resolve(__dirname, 'dist')
    }
}
複製程式碼

entry為object

const path = require('path');
module.exports = {
    entry: {
        a: './src/a.js',
        b: './src/b.js'
    },
    output: {
        filename: '[name].js',      // 打包後為a.js和b.js檔案
        path: path.resolve(__dirname, 'dist')
    }
}
複製程式碼

新增loader

由於webpack只能處理js,當我們需要處理其他非js檔案時,我們需要引入對應的loader

1.載入css

我們想從javaScript 模組中 import 一個 CSS 檔案,需要在 module 配置中 安裝並新增 style-loadercss-loader

npm install style-loader css-loader -D

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
};
複製程式碼

src/css/style.css

body {
    background: red;
}
複製程式碼

src/index.js

 import './css/style.css';
複製程式碼

開啟index.html,可以看到我們頁面的背景色此時為紅色。執行編譯,css檔案將會打包到js檔案當中

2.處理圖片

載入圖片,也需要安裝對應的loader npm install file-loader url-loader -D 在css引入背景圖片時,需要指定一下相對路徑

module: {
    rules: [
        {
            test: /\.(png|svg|jpg|gif)$/, // 載入圖片
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 8192,  // 小於8k的圖片自動轉成base64格式
                    name: 'images/[name].[ext]?[hash]', // 圖片打包後存放的目錄
                    publicPath: '../'  // css圖片引用地址,可修正打包後,css圖片引用出錯的問題
                }
            }]
        },
    ]
}
複製程式碼

3.載入字型檔案

module: {
    rules: [
        {
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
            'file-loader'
            ]
        }
    ]
}
    
複製程式碼

webpack外掛

webpack提供了各種各樣的外掛,下面介紹幾種常用的外掛,只試過在webpack4.0以上的版本,其他版本沒試過,所以如果有報錯,那可能是版本不支援

使用webpack外掛,我們要寫在配置中的plugins裡面,在使用外掛之前,我們都需要先安裝該外掛。下面介紹幾個常用的外掛

1. html-webpack-plugin: 建立 HTML 檔案

該外掛的作用為:生成html頁面,自動引入js檔案,自動消除src引入的快取問題,上線之前壓縮。

使用前安裝該外掛npm install html-webpack-plugin --save -dev;

let HtmlWebpackPlugin = require('html-webpack-plugin');
module: {
    ...
    plugins:[
        new HtmlWebpakPlugin({
            minify:{
                collapseWhitespace: true,   // 摺疊空白區域 也就是壓縮程式碼
                removeAttributeQuotes: true // 移除雙引號,
            },
            hash:true, //向html引入的src連結後面增加一段hash值,消除快取
            template:'./src/index.html', // 模板地址
            title: 'webpack' // 標題
        })
    ]
}
複製程式碼

關於該外掛的更多使用方法,可以看這裡

2. extract-text-webpack-plugin:拆分單獨的css

前面安裝的css-loader在js中引入css檔案時,打包後的css是和js混合在一起的,如果我們想打包後的css檔案時單獨的存在,需要引入這個外掛。

安裝npm i extract-text-webpack-plugin@next -D,其中@next表示可以在webpack4中使用

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: "css-loader"
                })
            }
        ]
    },
  plugins: [
      new ExtractTextPlugin("css/styles.css"), // 打包後的css檔案
  ]
}
複製程式碼

關於該外掛的更多使用方法,可以看這裡

3.新增css3字首

利用postcss中的autoprefixer來為css3自動新增字首。

安裝npm i postcss-loader autoprefixer -D

在根目錄下新建postcss.config.js裡面寫入

module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}
複製程式碼

webpack.config.js裡面,配置postcss-loader

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}
複製程式碼

在package.json上,新增需要支援的版本

"browerslist": [
    "last 2 versions",
    "IE 8",
    "UCAndroid"
  ],
複製程式碼

webpack提供的外掛非常多,更多的外掛,可以看這裡

轉義es6

安裝npm i -D @babel/core @babel/plugin-transform-runtime @babel/preset-env babel-loader 在webpack.config.js中新增

..略
module:{
    rulse:[
        ...略
        {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/, // 忽略掉該檔案下的js
        }
    ]
}
複製程式碼

在根目錄下,也就是和webpack.config.js同一個目錄下,新增.babelrc檔案

{
    "presets": ["@babel/preset-env"], 
    "plugins": ["@babel/plugin-transform-runtime"] 
}
複製程式碼

webpack-dev-server:為webpack新增靜態伺服器

webpack-dev-server為自動重新整理和模組熱替換機制,裝上它,可以我們的改動可以自動重新整理

安裝npm install webpack-dev-server -D

修改一下我們的webpack.config.js

var path = require('path');

module.exports = {
  //...
  devServer: {
    contentBase: path.join(__dirname, 'dist'), // 伺服器資源的根目錄,不寫的話,預設為bundle.js
    compress: true, // 伺服器資源採用gzip壓縮
    port: 9000,  // 執行的埠
    overlay: true  // 出錯程式碼是否顯示在html頁面上
    hot: true //熱載入
  }
};
複製程式碼

再修改我們的package.json

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

執行npm run dev,就會自動開啟瀏覽器,監聽你的修改了

注意點:webpack-dev-server輸出的檔案只存在於記憶體中,不輸出真實的檔案,也就是說,你啟動它,你的dist檔案其實是沒有生產新的檔案的

resolve: 設定模組如何被解析

resolve是webpack自帶的,主要作用是設定模組如何被解析 主要介紹幾個:

1. resolve.alias 配置別名

resolve.alias配置項通過別名來把原來匯入路徑對映成一個新的匯入路徑,例如:

resolve: {
    alias: {
        components: './src/components/'
    }
}
複製程式碼

這樣,我們原來import Dialog from './src/components/dialog'可以縮減為import Dialog from 'components/dialog';

2. resolve.extensions 自動解析擴充套件,意味著我們匯入模組可以省略不寫字尾名

resolve: {
    extensions: ['.js', '.json']
}
複製程式碼

我們用import data from './data'時,webpack就會依次尋找data.js是否存在,不存在繼續尋找data.json是否存在,最後尋找data/index.js是否存在

3. resolve.modules 配置 Webpack 去哪些目錄下尋找第三方模組

Webpack找第三方模組,預設是隻會去node_modules目錄下尋找。如果你的專案中,很多模組要引用這個目錄下的, 那目錄可能很長。打個比方,src/components/test.js要去node_modules下面找dialog元件,那麼可能這樣寫:import '../../../components/dialog',利用resolve.modules優化後:

resolve.modules:['./src/components','node_modules']
複製程式碼

你可以簡單通過 import 'dialog' 匯入。

webpack實踐

通過以上的介紹,我們大概熟悉了webpack的一些基礎配置,下面我們來進入實戰

我們配置具有如下功能的webpack配置:

  • 支援es6
  • 支援css3自動字首
  • less、css單獨存為css檔案,而不是打包到js中
  • 支援引入圖片
  • 支援熱跟新專案
  • 支援引用別名以及省略一些檔案字尾

我們目錄結構如下:

——src
    ├─components
    ├─css
    └─images
    ├─index.js   
——index.html
——.babelrc
——package.json
——postcss.config.js
——webpack.config.js
複製程式碼

webpack.config.js的配置如下:

const path = require('path');
const htmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
    entry: './src/index.js', // 入口檔案
    output: {
        filename: 'main.js',    // 打包後的檔名稱
        path: path.resolve(__dirname, 'dist')  // 打包後的目錄
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({ // 拆分單獨的css檔案
                    fallback: "style-loader",
                    use: ['css-loader', 'postcss-loader'] // 載入css
                })
            },
            // 載入less
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|svg|jpg|gif)$/, // 載入圖片
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 8192,  // 小於8k的圖片自動轉成base64格式
                        name: 'images/[name].[ext]?[hash]', // 圖片打包後的目錄
                        publicPath: '../'  // css圖片引用地址
                    },
                }]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/, // 載入字型檔案
                use: [
                    'file-loader'
                ]
            },
            // 轉義es6
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: /src/,          // 只轉化src目錄下的js
                exclude: /node_modules/, // 忽略掉node_modules下的js
            }
        ]
    },
    resolve: {
        alias: {
            components: path.resolve(__dirname, 'src/components/') // 別名
        },
        extensions: ['.js', '.json'], // 忽略檔案字尾
        modules: ['node_modules']
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "./index.html",
            filename: "index.html",
            inject: true,
            hash: true,
            chunksSortMode: 'none' //如使用webpack4將該配置項設定為'none'
        }),
        new ExtractTextPlugin("css/styles.css"), 
        new OptimizeCssAssetsPlugin({ // 優化css
            cssProcessor: require('cssnano'), //引入cssnano配置壓縮選項
            cssProcessorOptions: {
                discardComments: { removeAll: true }
            },
            canPrint: true //是否將外掛資訊列印到控制檯
        })
    ],
    devServer: {
        hot: true,
        contentBase: path.join(__dirname, 'dist'),
        port: 3002,
    },
};
複製程式碼

程式碼地址: 這裡

後記

目前vue、react都有自己的webpack配置,都已經配好了,直接拿來用即可。不過作為一個優秀的前端,我們也需要知道如何從零開始配置屬於自己的webpack.config.js。 後續寫一個只需配置一次,可以多個專案公共的webpack配置,敬請期待...




參考文章:

https://blog.csdn.net/qq_16339527/article/details/80641245 https://juejin.im/post/5aa3d2056fb9a028c36868aa https://juejin.im/post/5adea0106fb9a07a9d6ff6de https://juejin.im/post/5b304f1f51882574c72f19b0

相關文章