為什麼我們要做三份 Webpack 配置文件

来源:https://www.cnblogs.com/souleigh-hong/archive/2018/05/16/9047290.html
-Advertisement-
Play Games

時至今日,Webpack 已經成為前端工程必備的基礎工具之一,不僅被廣泛用於前端工程發佈前的打包,還在開發中擔當本地前端資源伺服器(assets server)、模塊熱更新(hot module replacement)、API Proxy 等角色,結合 ESLint 等代碼檢查工具,還可以實現在對 ...


時至今日,Webpack 已經成為前端工程必備的基礎工具之一,不僅被廣泛用於前端工程發佈前的打包,還在開發中擔當本地前端資源伺服器(assets server)、模塊熱更新(hot module replacement)、API Proxy 等角色,結合 ESLint 等代碼檢查工具,還可以實現在對源代碼的嚴格校驗檢查。

正如上文中提到的,前端從開發到部署前都離不開 Webpack 的參與,而 Webpack 的預設配置文件只有一個,即 webpack.config.js,那麼問題來了,開發期和部署前應該使用同一份 Webpack 配置嗎?答案肯定是否定的,既然 webpack.config.js 是一個 JS 文件,我們當然可以在文件里寫 JavaScript 業務邏輯,通過讀取環境變數 NODE_ENV 來判斷當前是在開發(dev)時還是最終的生產環境(production),然而很多同學習慣把這兩者的配置都混寫在根目錄下的 webpack.config.js,通過很多零散的 if…else 來“臨時”決定某一個 plugin 或者某一個 loader 的配置項,隨著 loaders 和 plugins 的不斷增加,久而久之 webpack.config.js 變得原來越隆長,代碼的可讀性和可維護性也大大下降。

我想通過本文來介紹一種用 3 個 JS 文件來配置 Webpack 的方法,這裡借鑒了很多開源項目的配置,同時也結合了我們自己在開發中碰到的種種問題解決方案。

本文中提及的配置基於 Webpack 2 或以上,建議使用 3.0 及以上版本

開發環境與生產環境的區別

  • 開發環境

    NODE_ENV 為 development
    啟用模塊熱更新(hot module replacement)
    額外的 webpack-dev-server 配置項,API Proxy 配置項
    輸出 Sourcemap

  • 生產環境

    NODE_ENV 為 production
    將 React、jQuery 等常用庫設置為 external,直接採用 CDN 線上的版本
    樣式源文件(如 css、less、scss 等)需要通過 ExtractTextPlugin 獨立抽取成 css 文件
    啟用 post-css
    啟用 optimize-minimize(如 uglify 等)
    中大型的商業網站生產環境下,是絕對不能有 console.log() 的,所以要為 babel 配置 Remove console transform

    這裡需要說明的是因為開發環境下啟用了 hot module replacement,為了讓樣式源文件的修改也同樣能被熱替換,不能使用 ExtractTextPlugin,而轉為隨 JS Bundle 一起輸出。

你需要三份配置文件

  1. webpack.base.config.js
    在 base 文件里,你需要將開發環境和生產環境中通用的配置集中放在這裡:

    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const path = require('path');
    const webpack = require('webpack');

    // 配置常量
    // 源代碼的根目錄(本地物理文件路徑)
    const SRC_PATH = path.resolve('./src');
    // 打包後的資源根目錄(本地物理文件路徑)
    const ASSETS_BUILD_PATH = path.resolve('./build');
    // 資源根目錄(可以是 CDN 上的絕對路徑,或相對路徑)
    const ASSETS_PUBLIC_PATH = '/assets/';

    module.exports = {
    context: SRC_PATH, // 設置源代碼的預設根路徑
    resolve: {
        extensions: ['.js', '.jsx']  // 同時支持 js 和 jsx
    },
    entry: {
        // 註意 entry 中的路徑都是相對於 SRC_PATH 的路徑
        vendor: './vendor',
        a: ['./entry-a'],
        b: ['./entry-b'],
        c: ['./entry-c']
    },
    output: {
        path: ASSETS_BUILD_PATH,
        publicPath: ASSETS_PUBLIC_PATH,
        filename: './[name].js'
    },
    module: {
        rules: [
        {
            enforce: 'pre',  // ESLint 優先順序高於其他 JS 相關的 loader
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'eslint-loader'
        },
        {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            // 建議把 babel 的運行時配置放在 .babelrc 里,從而與 eslint-loader 等共用配置
            loader: 'babel-loader'
        },
        {
            test: /\.(png|jpg|gif)$/,
            use:
            [
            {
                loader: 'url-loader',
                options:
                {
                limit: 8192,
                name: 'images/[name].[ext]'
                }
            }
            ]
        },
        {
            test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use:
            [
            {
                loader: 'url-loader',
                options:
                {
                limit: 8192,
                mimetype: 'application/font-woff',
                name: 'fonts/[name].[ext]'
                }
            }
            ]
        },
        {
            test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use:
            [
            {
                loader: 'file-loader',
                options:
                {
                limit: 8192,
                mimetype: 'application/font-woff',
                name: 'fonts/[name].[ext]'
                }
            }
            ]
        }
        ]
    },
    plugins: [
        // 每次打包前,先清空原來目錄中的內容
        new CleanWebpackPlugin([ASSETS_BUILD_PATH], { verbose: false }),
        // 啟用 CommonChunkPlugin
        new webpack.optimize.CommonsChunkPlugin({
        names: 'vendor',
        minChunks: Infinity
        })
    ]
    };
  1. webpack.dev.config.js

    這是用於開發環境的 Webpack 配置,繼承自 base:

    const webpack = require('webpack');
    
    // 讀取同一目錄下的 base config
    const config = require('./webpack.base.config');
    
    // 添加 webpack-dev-server 相關的配置項
    config.devServer = {
    contentBase: './',
    hot: true,
    publicPath: '/assets/'
    };
    // 有關 Webpack 的 API 本地代理,另請參考 https://webpack.github.io/docs/webpack-dev-server.html#proxy
    
    config.module.rules.push(
    {
        test: /\.less$/,
        use: [
        'style-loader',
        'css-loader',
        'less-loader'
        ],
        exclude: /node_modules/
    }
    );
    
    // 真實場景中,React、jQuery 等優先走全站的 CDN,所以要放在 externals 中
    config.externals = {
    react: 'React',
    'react-dom': 'ReactDOM'
    };
    
    // 添加 Sourcemap 支持
    config.plugins.push(
    new webpack.SourceMapDevToolPlugin({
        filename: '[file].map',
        exclude: ['vendor.js'] // vendor 通常不需要 sourcemap
    })
    );
    
    // Hot module replacement
    Object.keys(config.entry).forEach((key) => {
    // 這裡有一個私有的約定,如果 entry 是一個數組,則證明它需要被 hot module replace
    if (Array.isArray(config.entry[key])) {
        config.entry[key].unshift(
        'webpack-dev-server/client?http://0.0.0.0:8080',
        'webpack/hot/only-dev-server'
        );
    }
    });
    config.plugins.push(
    new webpack.HotModuleReplacementPlugin()
    );
    
    module.exports = config;
  1. webpack.config.js

    這是用於生產環境的 webpack 配置,同樣繼承自 base:

    const webpack = require('webpack');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    // 讀取同一目錄下的 base config
    const config = require('./webpack.base.config');
    
    config.module.rules.push(
    {
        test: /\.less$/,
        use: ExtractTextPlugin.extract(
        {
            use: [
            'css-loader',
            'less-loader'
            ],
            fallback: 'style-loader'
        }
        ),
        exclude: /node_modules/
    }
    );
    
    config.plugins.push(
    // 官方文檔推薦使用下麵的插件確保 NODE_ENV
    new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
    }),
    // 啟動 minify
    new webpack.LoaderOptionsPlugin({ minimize: true }),
    // 抽取 CSS 文件
    new ExtractTextPlugin({
        filename: '[name].css',
        allChunks: true,
        ignoreOrder: true
    })
    );
    
    module.exports = config;

現在在你的工程文件夾里應該已經有三個 Webpack 配置文件,它們分別是:

webpack.base.config.js
webpack.dev.config.js
webpack.config.js

最後,你還需要在 package.json 里添加相應的配置:


    {
    ...
    "scripts": {
        "build": "webpack --optimize-minimize",
        "dev": "webpack-dev-server --config webpack.dev.config.js",
        "start": "npm run dev" // 或添加你自己的 start 邏輯
    },
    ...
    }

和很多項目一樣,在開發環境下的時候,你需要使用 npm run dev 來啟動,而在生產環境中,則用 npm run build 來發佈。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、什麼是一個事件委托? 說到事件委托先說javascript的事件機制。 在js中分為DOM 0級事件和 DOM 2級事件。 下麵來看下什麼是一個dom0級事件: DOM 0級事件的缺點,當你給一個對象綁定多個事件的時候後寫的事件會覆蓋先寫的事件 下麵是DOM2級事件: DOM 2級事件: add ...
  • 上一篇 總結了模版驅動表單的基本用法,示例中的校驗使用的是原生HTML5的校驗方式,本文補上自定義校驗的部分。 HTML5原生的表單校驗屬性(必填,長度限制,取值間隔,正則表達式等等)可以滿足普通的校驗需求,但是有些場景必須用到自定義校驗,比如註冊時的密碼確認,有比對關係的時間/數值選擇, 需要到請 ...
  • 分析文檔描述 CSS 支持動畫的屬性中的 height 屬性如下: height :yes, as a length, percentage or calc() 即:當 height 的值是 length,百分比或 calc() 時支持 CSS3 過渡。 所以當元素 height : auto 時, ...
  • 引文 vue文檔列表渲染中有條註意事項: 這裡提到的兩種情況實際改變了數據但是 沒有觸發視圖更新 。 由此引出Vue.set(),先上文檔API: this.$set()和Vue.set()本質方法一樣,前者可以用在methods中使用。 set方法調用時,可以觸發頁面全部重新渲染。 比如在vue中 ...
  • 低版本的IE不支持JSON,JSON對象解析不是隨著javascript產生的,找到一段相容常用的JSON.parse和JSON.stringify的代碼 1 if (!window.JSON) { 2 window.JSON = { 3 parse: function(jsonStr) { 4 r... ...
  • 1:更新現有包 2:安裝依賴 3:安裝nvm,管理伺服器上的node版本 4:安裝指定node版本,並且在當前應用下指定node版本 5:新建node啟動文件進行測試 打開139.199.184.174:8081即可正常訪問 ...
  • 前傳 中間件的由來 redux的操作的過程,用戶操作的時候,我們通過dispatch分發一個action,純函數reducer檢測到該操作,並根據action的type屬性,進行相應的運算,返回state,然後更新view。 但是一個很重要的問題,reducer對於action會立即進行運算,並返回 ...
  • 一個基於原生JavaScript開發的、輕量的驗證碼生成插件 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...