為什麼我們要做三份 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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...