我們已經(jīng)完成了基于React的腳手架工程的創(chuàng)建,現(xiàn)在讓我們來(lái)進(jìn)一步增強(qiáng)我們的工程,已方便日后的開發(fā)。
增強(qiáng)Webpack
清理 /dist 文件夾
dist文件夾里包含了我們生成的各種打包文件,我們更新生成配置后,如果不即使刪除里面的殘留文件,可能導(dǎo)致舊打包文件和新打包文件共存的情況。
我們可以在編譯時(shí)添加腳本刪除,如下所示:
"build": "rd /S /Q dist && webpack"
也可以通過(guò)Webpack的clean-webpack-plugin插件來(lái)實(shí)現(xiàn)。
安裝插件yarn add -D clean-webpack-plugin
修改webpack.config.js文件,加入html-webpack-plugin插件
// 管理插件,通過(guò)插件實(shí)現(xiàn)增強(qiáng)功能
plugins: [
// 每次編譯前清除dist文件夾
new CleanWebpackPlugin(),
],
區(qū)分開發(fā)和生成環(huán)境
開發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)的構(gòu)建目標(biāo)差異很大。在開發(fā)環(huán)境中,我們需要具有強(qiáng)大的、具有實(shí)時(shí)重新加載(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server。而在生產(chǎn)環(huán)境中,我們的目標(biāo)則轉(zhuǎn)向于關(guān)注更小的 bundle,更輕量的 source map,以及更優(yōu)化的資源,以改善加載時(shí)間。由于要遵循邏輯分離,我們通常建議為每個(gè)環(huán)境編寫彼此獨(dú)立的 webpack 配置。
基于以上原理,我們將原有的webpack.config.js一個(gè)配置文件分割為webpack.common.js,webpack.dev.js和webpack.prod.js三個(gè)配置文件。
首先安裝yarn add -D webpack-merge
根據(jù)原有webpack.config.js文件將對(duì)應(yīng)配置分散到三個(gè)配置文件中
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack')
module.exports = {
// 入口文件
entry: './src/index.tsx',
// 需要解析的文件后綴名
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
// 管理插件,通過(guò)插件實(shí)現(xiàn)增強(qiáng)功能
plugins: [
// 自動(dòng)清理dist
new CleanWebpackPlugin(),
// 根據(jù)模板生成html
new HtmlWebpackPlugin({
title: 'My App',
template: './src/index.html'
}),
],
// 配置項(xiàng)目處理的不同文件及模塊
module: {
rules: [
{
test: /\.(ts|tsx)?$/,
loader: 'awesome-typescript-loader'
},
]
}
};
webpack.dev.js
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const webpack = require('webpack')
const path = require('path');
module.exports = merge(common, {
// 標(biāo)識(shí)配置為開發(fā)用
mode: 'development',
// 控制是否生成,以及如何生成 source map
devtool: 'cheap-module-eval-source-map',
// 管理開發(fā)服務(wù)器
devServer: {
// 查找文件路徑
contentBase: 'dist',
// 啟用 HMR
hot: true,
},
plugins: [
// 當(dāng)開啟 HMR 的時(shí)候使用該插件會(huì)顯示模塊的相對(duì)路徑,建議用于開發(fā)環(huán)境
new webpack.NamedModulesPlugin(),
// 啟用 HMR 熱更新,建議用于開發(fā)環(huán)境
new webpack.HotModuleReplacementPlugin()
],
// 管理輸出
output: {
// 定義輸出文件名規(guī)則
filename: '[name].bundle.js',
// 定義輸出文件名路徑
path: path.resolve(__dirname, 'dist'),
},
})
webpack.prod.js
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const webpack = require('webpack')
const path = require('path');
module.exports = merge(common, {
// 標(biāo)識(shí)配置為生產(chǎn)用
mode: 'production',
// 控制是否生成,以及如何生成 source map
devtool: 'source-map',
// 管理輸出
output: {
// 定義輸出文件名規(guī)則
filename: '[name].[contenthash].js',
// 定義輸出文件名路徑
path: path.resolve(__dirname, 'dist'),
},
})
最后修改package.json:
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev": "webpack-dev-server --config webpack.dev.js"
},
Source Map說(shuō)明
Webpack的打包過(guò)程中,會(huì)對(duì)源代碼進(jìn)行壓縮,合并,編譯等操作,導(dǎo)致編譯后的代碼和源碼差異較大。為了更容易地追蹤錯(cuò)誤和警告,通過(guò)JavaScript 提供的source map功能,能夠?qū)⒕幾g后的代碼映射到源代碼上,便于錯(cuò)誤定位。
在Webpack中,是通過(guò)devtool配置項(xiàng)來(lái)指定構(gòu)造的source map類型。配置項(xiàng)很多,但大致可以劃分為5類,其余配置項(xiàng)可以是其任意組合。
- eval: 使用eval包裹模塊代碼,尾部通過(guò)souceURL來(lái)定位原始代碼位置,不產(chǎn)生.map文件
- source-map: 產(chǎn)生.map文件,通過(guò).map文件定位
- cheap: 不包含列信息(debug時(shí)注意點(diǎn)都在行數(shù)上,不用關(guān)心列數(shù))也不包含loader的sourcemap(意味著如果使用了類似bable類編譯工具,定位到的就是編譯后的代碼而非原始代碼)
- module: 包含loader的sourcemap(比如babel的sourcemap)
- inline: 將.map作為DataURI嵌入,不單獨(dú)生成.map文件
綜上所示,我們?cè)陂_發(fā)環(huán)境選用cheap-module-eval-source-map,就能夠定位到各個(gè)原始文件上。生產(chǎn)環(huán)境則考慮使用source-map。
代碼壓縮
Webpack4的mode是production時(shí),會(huì)自動(dòng)開啟js的壓縮,不過(guò)這里我們還是使用uglifyjs-webpack-plugin插件。
安裝
yarn add -D uglifyjs-webpack-plugin
修改webpack.prod.js文件
module.exports = merge(common, {
// 標(biāo)識(shí)配置為生產(chǎn)用
mode: 'production',
// 控制是否生成,以及如何生成 source map
devtool: 'source-map',
plugins: [
new UglifyJSPlugin({
sourceMap: true
}),
],
// 管理輸出
output: {
// 定義輸出文件名規(guī)則
filename: '[name].[contenthash].js',
// 定義輸出文件名路徑
path: path.resolve(__dirname, 'dist'),
},
})
這里需要注意一點(diǎn),UglifyJSPlugin壓縮插件不能解析es6語(yǔ)法,需要引入bable來(lái)處理es6,es7的js文件的編譯。
指定環(huán)境
程序中使用的某些第三方library可能會(huì)和process.env.NODE_ENV環(huán)境變量關(guān)聯(lián),比如在生產(chǎn)環(huán)境中增加額外的日志記錄或測(cè)試,在生產(chǎn)環(huán)境中增加一些優(yōu)化。因此我們根據(jù)不同的環(huán)境預(yù)設(shè)好process.env.NODE_ENV的值。NODE_ENV本質(zhì)上是Node.js露給執(zhí)行腳本的系統(tǒng)環(huán)境變量,同時(shí)并不能在Webpack.prod.js類似的構(gòu)建腳本中預(yù)設(shè)NODE_ENV的值,但我們可以通過(guò)webpack內(nèi)置的 DefinePlugin為所有的依賴定義這個(gè)變量。
對(duì)應(yīng)環(huán)境的webpack配置文件中增加如下所示的插件配置:
// 預(yù)設(shè)程序執(zhí)行環(huán)境
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})