webpack4.0打包優化策略(二)

布羅利發表於2018-04-06

打包優化策略

區分開發和生產環境

通常我們在開發網頁時需要區分構建環境

  • 開發環境(development) 開發過程中方便開發除錯的環境
  • 生產環境(production) 釋出到線上使用的執行環境

通過npm命令區分

通過cross-env模組設定環境變數

cross-env 跨平臺地設定及使用環境變數,而不必擔心為平臺正確設定或使用環境變數。

npm i cross-env -D
複製程式碼

Usage

npm scripts中:

{
    "scripts": {
        "build": "cross-env NODE_ENV=production webpack --mode production",
        "dev": "cross-env NODE_ENV=development webpack-dev-server --mode development",
    }
}
複製程式碼

執行npm命令切換環境

npm run build // 生產環境 process.env.NODE_ENV === 'production'
npm run dev // 開發環境 process.env.NODE_ENV === 'development'
複製程式碼

接下來我們就可以在webpack.config.js 通過process.env.NODE_ENV來得知當前環境標識

程式碼中區分環境

定義環境常量

webpack4以前都是通過DefinePlugin來定義NODE_ENV環境變數,以決定library中應該引用哪些內容。

NODE_ENV是一個由Node.js暴露給執行指令碼的環境變數。通常用於決定在開發環境與生產環境下,服務工具、構建指令碼和客戶端library的行為。

在webpack.config.js 中新增DefinePlugin外掛

const webpack = require('webpack');

plugins: [
    new webpack.DefinePlugn({
       'process.env': {
           'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
       }
    })
]
複製程式碼

在main.js中通過判斷process.env.NODE_ENV常量 來執行相應邏輯

mode模式配置

在webpack4 增加了mode模式配置 在瀏覽器環境下指定了 process.env.NODE_ENV 的值,預設是development,但node環境中還是需要cross-env來設定。 mode 是 webpack 4 中新增加的引數選項,其有兩個可選值:production 和 development。mode 不可預設,需要二選一:

production 模式:
  • 1.生產環境預設開啟了很多程式碼優化(minify,splite等)
  • 2.開發時開啟注視和驗證,並且自動加上了eval devtool
  • 3.生產環境不支援watching,開發環境優化了重新打包的速度
  • 4.預設開啟了Scope hoisting和Tree-shaking(原ModuleConcatenationPlugin)
  • 5.自動設定process.env.NODE_ENV到不同環境,也就是不需要DefinePlugin來做這個了
  • 6.如果你給mode設定為none,所有預設配置都去掉了
  • 7.如果不加這個配置webpack會出現提醒,所以還是加上吧

development 模式:

  • 1.主要優化了增量構建速度和開發體驗

  • 2.process.env.NODE_ENV 的值不需要再定義,預設是 development

  • 3.開發模式下支援註釋和提示,並且支援 eval 下的 source maps

const NODE_ENV = process.env.NODE_ENV;
if (NODE_ENV === 'development') { // 開發環境下執行下面程式碼
    console.log('development', NODE_ENV);
} else { // 生產環境則執行以下環境
    console.log('production', NODE_ENV);
}

複製程式碼

DefinePlugn

DefinePlugin 允許建立一個在編譯時可以配置的全域性常量。這可能會對開發模式和釋出模式的構建允許不同的行為非常有用。

webpack配置中區分環境

在專案目錄中新增webpack配置檔案

  • webpack.base.config.js 儲存webpack基礎通用的配置的檔案
  • webpack.dev.config.js 儲存webpack開發環境配置的檔案
  • webpack.prod.config.js 儲存webpack生成環境配置的檔案
  • webpack.config.js webpack執行配置檔案 儲存相應環境的配置和webpack基礎配置檔案合併後的配置

基礎配置 webpack.base.config.js

webpack一些loader配置

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const HappyPack = require('happypack');
const os = require('os'); // 系統操作函式
const happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length}); // 指定執行緒池個數

function resolve(dir) {
    return path.join(__dirname, dir);
}

module.exports = {
    entry: {
        app: './src/main.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                // use: 'babel-loader?cacheDirectory'
                use: 'happypack/loader?id=babel', // 快取loader執行結果
                exclude: /node_modules/, // 排除不要載入的資料夾
                include: path.resolve(__dirname, 'src') // 指定需要載入的資料夾
            }
        ],
        noParse: function(content) { // content 從入口開始解析的模組路徑
            return /no-parser/.test(content); // 返回true則忽略對no-parser.js的解析
        }
    },
    resolve: {
        modules: [ // 優化模組查詢路徑
            resolve('src'),
            resolve('node_modules') // 指定node_modules所在位置 當你import第三方模組式 直接從這個路徑下搜尋
        ],
        alias: {
            funs$: resolve('src/util/funs.js')
        },
        extensions: ['.js', '.vue']
    },
    plugins: [
        new webpack.DefinePlugin({ // 定義環境變數
            "process.env": JSON.stringify(process.env.NODE_ENV)
        }),
        new HappyPack({
            id: 'babel',
            loaders: ['babel-loader?cacheDirectory'],
            threadPool: happyThreadPool,
            verbose: true
        }),
        new HtmlWebpackPlugin({
            template: resolve('index.html'),
            title: 'hello webpack!'
        })
    ]
}
複製程式碼

開發配置webpack.dev.config.js

開發時的輸入輸出以及開發除錯配置 如 devServer devtool 配置

const webpack = require('webpack');

module.exports = {
    plugins: [
       new webpack.DefinePlugin({
           'process.env': {
               'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
           }
       })
    ],
    devServer: {
        contentBase: resolve('dist'),
        compress: true,
        port: 9000
    }
};
複製程式碼

生成環境webpack.prod.config.js

生成環境 進行壓縮 程式碼分離等程式碼優化 線上配置

const webpack = require('webpack');
const path = require('path');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');

module.exports = {
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, 'dll', 'react.manifest.json'))
        }),
        new ParallelUglifyPlugin({
            workerCount: 4, // 開啟幾個子程式去併發的執行壓縮,預設是當前電腦的cpu數量減1
            uglifyJS: {
                output: {
                    beautify: false, // 不需要格式化
                    comments: false // 保留註釋
                },
                compress: {
                    warnings: false, // Uglifyjs 刪除沒有程式碼時,不輸出警告
                    // drop_console: true, // 刪除所有console語句
                    collapse_vars: true,
                    reduce_vars: true
                }
            }
        }),
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'index.html')
        }),
        new HtmlIncludeAssetsPlugin({
            assets: ['/dll/react.dll.js'],
            append: false
        })
    ]
};
複製程式碼

webpack.config.js 配置合併

藉助webpack-merge 將base配置和相應環境配置 合併到'webpack.config.js'

npm i webpack-merge -D
複製程式碼

webpack.config.js

const base = require('./webpack.base.config');
const merge = require('webpack-merge');

let config;
if (process.env.NODE_ENV === 'production') {
    config = require('./webpack.prod.config');
} else {
    config = require('./webpack.dev.config');
}

module.exports = merge(base, config);
複製程式碼

執行生產配置

npm run build
複製程式碼

執行開發配置

npm run dev
複製程式碼

實時重新載入(live reloading) 和 模組熱替換(HMR)

webpack-dev-server 為你提供了一個簡單的 web 伺服器,並且能夠實時重新載入(live reloading)。

讓我們設定以下:

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

配置webpack.dev.config.js

devServer: {
    contentBase: path.join(__dirname, 'dist'), // 將 dist 目錄下的檔案,作為可訪問檔案。
    compress: true, // 開啟Gzip壓縮
    port: 9000, // 埠號
    inline: true // 在打包後檔案裡注入一個websocket客戶端
}
複製程式碼

npm scripts

{
    "scripts": {
        "dev": "cross-env NODE_ENV=development webpack-dev-server --mode development"
    }
}
複製程式碼

啟動server

npm run dev
複製程式碼

瀏覽器訪問localhost:9000 當修改程式碼ctrl+s 將自動重新整理瀏覽器

啟用HMR

模組熱替換(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允許在執行時更新各種模組,而無需進行完全重新整理。

配置webpack.dev.config.js

devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000,
    inline: true,
    hot: true // 開啟HMR
}
複製程式碼

注意 此外我們還需新增NamedModulesPlugin 和 HotModuleReplacementPlugin 外掛

webpack.dev.config.js

{
    plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin()
    ]
}
複製程式碼

其他程式碼和框架開啟熱替換

1.React Hot Loader(實時調整 react 元件)

Install

npm install react-hot-loader
複製程式碼

Getting started

1.1 Add react-hot-loader/babel to your .babelrc:

// .babelrc

{
    "plugins": ["react-hot-loader/babel"]
}
複製程式碼

1.2 Mark your root component as hot-exported:

// App.js
import React from 'react'
import { hot } from 'react-hot-loader'

const App = () => <div>Hello World!</div>

export default hot(module)(App)
複製程式碼

2.Vue loader

vue-cli 已經整合 只需用vue-cli腳手架開發即可

HMR修改樣式表

藉助於style-loader的幫助,CSS的模組熱替換實際上是相當簡單的。

安裝如下loader:

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

HMR參考配置

相關文章