上一篇文章介紹了React+Typescript的基礎環境搭建,並無作任何優化配置,以及根據不一樣的開發環境拆分配置,這篇文章主要就是介紹這些,而且全部配置都是在上篇文章的基礎上,若是有什麼問題或者不對的地方,但願大佬們能及時指出,最後有項目地址~css
在上篇webpack.common.js中繼續添加和更新咱們的配置。html
有的時候須要在不一樣的環境定義不一樣的變量,就像vue-cli3建立的項目中的.env文件同樣。vue
首先在 build 文件夾下新建一個 env.json 文件夾,並在裏面寫上你可能用到的全局變量。node
{
"dev": {
"APP_ENVO": "dev",
"BASEURL": "https://xxxx.xxxx.com/api/"
},
"test": {
"APP_ENVO": "test",
"BASEURL": "https://xxxx.xxxx.com/api/"
},
"pre": {
"APP_ENVO": "pre",
"BASEURL": "https://xxxx.xxxx.com/api/"
},
"prod": {
"APP_ENVO": "prod",
"BASEURL": "https://xxxx.xxxx.com/api/"
}
}
複製代碼
接下來須要用到 yargs-parser
這個插件,yargs-parser: 用於將咱們的npm scripts中的命令行參數轉換成鍵值對的形式如 --mode development會被解析成鍵值對的形式mode: "development",便於在配置文件中獲取參數。react
而後在 package.json 中的scripts 腳本中加上咱們的環境參數 --env test
等,例如:webpack
"scripts": {
"dev": "webpack-dev-server --config build/webpack.dev.js --mode development --open",
"test-build": "webpack --config build/webpack.prod.js --mode production --env test",
"pre-build": "webpack --config build/webpack.prod.js --mode production --env pre",
"prod-build": "webpack --config build/webpack.prod.js --mode production --env prod"
},
複製代碼
而後在 webpack.common.js 中拿到這個參數,並利用 webpack.DefinePlugin
這個插件將這些變量配置進去git
const argv = require('yargs-parser')(process.argv.slice(4))
const APP_ENV = argv.env || 'dev'
const env = require('./env.json')
const oriEnv = env[config.APP_ENV]
Object.assign(oriEnv, {
APP_ENV: config.APP_ENV
})
const defineEnv = {}
for (let key in oriEnv) {
defineEnv[`process.env.${key}`] = JSON.stringify(oriEnv[key])
}
module.exports={
// ... 省略了其餘配置
plugins: [
new webpack.DefinePlugin(defineEnv)
]
}
複製代碼
以後在項目啓動後就能夠經過 process.env.${key}
對應的鍵,拿到相應的值了。github
修改咱們打包後的 js 輸出目錄以及名稱,讓它看起來清晰一些。web
module.exports={
output: {
filename: 'js/[name].[chunkhash].js',
path: path.join(__dirname, '../dist')
}
}
複製代碼
首先在 build 下新建一個 webpack.dev.js,而後須要安裝 webpack-merge
來合併配置。vuex
yarn add webpack-merge -D
複製代碼
接下來引入它以及公共配置文件,把以前的 devServer 移到這裏,並引入 webpack.HotModuleReplacementPlugin
用於啓用局部模塊熱重載方便咱們開發,若是要配置代理的話,須要配置 devServer 下的 proxy,具體每一個字段的意思,能夠參照官網。
關於 source-map
的話,能夠理解它爲你的源碼與打包後代碼的一個映射,由於打包後的代碼都是通過壓縮的,尋找錯誤調試會很麻煩,因此須要它,這裏使用 eval-source-map
,對應的配置 devtool 選項。
const webpack=require('webpack')
const merge = require('webpack-merge')
const baseConfig=require('./webpack.common')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const devConfig={
mode: 'development',
devtool: 'eval-source-map',
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'public/index.html',
inject: true
}),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
host: 'localhost',
port: 3000,
historyApiFallback: true,
overlay: {//當出現編譯器錯誤或警告時,就在網頁上顯示一層黑色的背景層和錯誤信息
errors: true
},
inline: true,
hot: true,
// proxy: {
// '/api/v1': {
// target: '',
// ws: true,
// changeOrigin: true,
// pathRewrite: {
// '^/api/v1': '/api/v1'
// }
// }
// }
},
}
module.exports=merge(baseConfig,devConfig)
複製代碼
而後在 package.json 中 scripts 添加咱們啓動開發環境的命令,以後就能夠啓動項目了。
"dev": "webpack-dev-server --config build/webpack.dev.js --mode development --open"
複製代碼
首先在 build 下新建一個 webpack.prod.js,跟開發環境同樣,都須要引入公共配置,而後一點點的引入插件。
const merge = require('webpack-merge')
const baseConfig = require('./webpack.common')
const webpack = require('webpack')
const prodConfig = {
mode: 'production',
devtool: 'source-map'
}
module.exports = merge(baseConfig, prodConfig)
複製代碼
開頭介紹過它,用於自動生成html,並默認將打包生成的js、css引入到html文件中,其中minify 配置項有不少,具體能夠參照html-minifier
const HtmlWebpackPlugin = require('html-webpack-plugin')
const prodConfig = {
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'public/index.html',
inject: true,
minify: {
removeComments: true, // 去掉註釋
collapseWhitespace: true, // 去掉多餘空白
removeAttributeQuotes: true // 去掉一些屬性的引號,例如id="moo" => id=moo
}
})
]
}
複製代碼
使用mini-css-extract-plugin來將css從js裏分離出來,而且支持chunk css。
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// ...
const prodConfig = {
// ...
plugins: [
// ...
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: assetsPath('css/[name].[contenthash].css'),
chunkFilename: assetsPath('css/[name].[id].[contenthash].css')
})
]
}
複製代碼
除此以外還要配置 webpack.commom.js, 把 style-loader
換成這個插件提供的 loader,固然也能夠區分一下環境,開發環境仍然使用 style-loader
,以 css 文件爲例。
{
test: /\.css$/, // 正則匹配文件路徑
exclude: /node_modules/,
use: [
//
APP_ENV !== 'dev' ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader', // 解析 @import 和 url() 爲 import/require() 方式處理
options: {
importLoaders: 1 // 0 => 無 loader(默認); 1 => postcss-loader; 2 => postcss-loader, sass-loader
}
},
'postcss-loader'
]
}
複製代碼
用於清除本地文件,在進行生產環境打包的時候,若是不清除dist文件夾,那麼每次打包都會生成不一樣的js文件或者css文件堆積在文件夾中,注意版本帶來的使用不一樣。
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// ...
const prodConfig = {
// ...
plugins: [
// ...
new CleanWebpackPlugin(),
]
}
複製代碼
在webpack打包時優化壓縮css代碼,主要使用 cssnano 壓縮器,這個就不是配置在 plugins 裏了,而是 optimization
下的 minimizer
。
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...
const prodConfig = {
// ...
optimization: { // 性能配置
// ...
minimizer: [
new OptimizeCssAssetsPlugin({
cssProcessor: require('cssnano'), // 使用 cssnano 壓縮器
cssProcessorOptions: {
reduceIdents: false,
autoprefixer: false,
safe: true,
discardComments: {
removeAll: true
}
}
})
]
}
}
複製代碼
optimize-css-assets-webpack-plugin 用於壓縮 css 代碼,而它用來壓縮 js 代碼,以前用到的是 uglifyjs-webpack-plugin 這一個,可是它好像須要 babel 的支持,並且如今官方推薦用 terser-webpack-plugin, 不過在使用上差很少,並且它不須要安裝。
const TerserPlugin = require('terser-webpack-plugin')
// ...
const prodConfig = {
// ...
optimization: { // 性能配置
// ...
minimizer: [
new TerserPlugin({
cache: true,
// parallel: true,
terserOptions: {
compress: {
warnings: true,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log'] // 移除console
}
},
sourceMap: true
}),
]
}
}
複製代碼
它能夠將包含chunks 映射關係的 list單獨從 app.js裏提取出來,由於每個 chunk 的 id 基本都是基於內容 hash 出來的,因此你每次改動都會影響它,若是不將它提取出來的話,等於app.js每次都會改變。緩存就失效了。在 webpack4 中,無需手動引入插件,配置 runtimeChunk 便可。
const prodConfig = {
// ...
optimization: { // 性能配置
// ...
{
runtimeChunk: true;
}
}
}
複製代碼
打包生成的 runtime.js很是的小,gzip 以後通常只有幾 kb,但這個文件又常常會改變,咱們每次都須要從新請求它,它的 http 耗時遠大於它的執行時間了,因此建議不要將它單獨拆包,有關優化就是將他將它內聯到咱們的 index.html 之中。
這裏使用了 script-ext-html-webpack-plugin。
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
// 注意必定要在HtmlWebpackPlugin以後引用
// inline 的name 和你 runtimeChunk 的 name保持一致
new ScriptExtHtmlWebpackPlugin({
//`runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
});
複製代碼
這個配置能讓咱們以必定規則抽離想要的包,webpack4 有一套默認的代碼分包策略。
關於按需加載跟頁面初始加載就對應到 webpack.splitChunks.chunks
它表示將選擇哪些塊進行優化,async 表示只優化動態導入的包,而 initial 表示初始加載時導入的包,還有一個值 all 表示都會優化,默認是 async,也就是說若是你動態導入了一個包,壓縮前大於30kb,而且你在代碼中有超過5個地方引用了它,那麼 webpack 就會將它單獨打包出來。
一般咱們須要將 node_modules 下的比較大的基礎類庫包抽出來,好比 vuex、vue之類的,或者像比較大的UI 組件庫,好比 antd、element-ui 之類的也抽出來,以及本身寫的可能會在多個頁面間用到屢次的組件。下面給一個我這裏的配置,注意:拆包的時候不要過度的追求顆粒化,資源的加載策略並沒什麼徹底的方案,都須要結合本身的項目找到最合適的拆包策略。
const prodConfig = {
// ...
optimization: { // 性能配置
// ...
splitChunks: {
chunks: 'async', // 提取的 chunk 類型,all: 全部,async: 異步,initial: 初始
// minSize: 30000, // 默認值,新 chunk 產生的最小限制 整數類型(以字節爲單位)
// maxSize: 0, // 默認值,新 chunk 產生的最大限制,0爲無限 整數類型(以字節爲單位)
// minChunks: 1, // 默認值,新 chunk 被引用的最少次數
// maxAsyncRequests: 5, // 默認值,按需加載的 chunk,最大數量
// maxInitialRequests: 3, // 默認值,初始加載的 chunk,最大數量
// name: true, // 默認值,控制 chunk 的命名
cacheGroups: { // 配置緩存組
vendor: {
name: 'vendor',
chunks: 'initial',
priority: 10, // 優先級
reuseExistingChunk: false, // 容許複用已經存在的代碼塊
test: /node_modules\/(.*)\.js/, // 只打包初始時依賴的第三方
},
common: {
name: 'common',
chunks: 'initial',
// test: resolve("src/components"), // 可自定義拓展你的規則
minChunks: 2,
priority: 5,
reuseExistingChunk: true
}
}
}
}
}
複製代碼
像 React 相關基礎運行環境,將這些基礎模塊打到一個包裏,只要這些包的包的版本沒升級,之後每次打包就不須要再編譯這些模塊,提升打包的速率,這裏咱們就能夠用到 webpack.DllPlugin,而後使用 webpack.DllReferencePlugin 將這個 dll 包關聯到當前的編譯中去。
在 build 文件夾下新建一個 webpack.dll.js 文件,並寫入下面的配置
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode:'production',
entry: {
// 還有redux 之類的也能夠放進來
vendor: ['react', 'react-dom', 'react-router-dom']
},
output: {
filename: '[name].dll.[hash:8].js',
path: path.join(__dirname, '../dll'),
// 連接庫輸出方式 默認'var'形式賦給變量
libraryTarget: 'var',
// 全局變量名稱 導出庫將被以var的形式賦給這個全局變量 經過這個變量獲取到裏面模塊
library: '_dll_[name]_[hash:8]'
},
plugins: [
// 每次運行時清空以前的 dll 文件
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.join(__dirname, '../dll/**/*')]
}),
new webpack.DllPlugin({
// path 指定manifest文件的輸出路徑
path: path.join(__dirname, '../dll/[name].manifest.json'),
// 和library 一致,輸出的manifest.json中的name值
name: '_dll_[name]_[hash:8]'
})
]
}
複製代碼
下面修改 webpack.prod.js 使用DllReferencePlugin告訴 Webpack 使用了哪些動態連接庫,而後並使用下面介紹的 add-asset-html-webpack-plugin 將其放入資源列表 html webpack插件注入到生成的 html 中。
其中 vendor.manifest.json 是由 DllPlugin 生成出,用於描述動態連接庫文件中包含哪些模塊。
// ...
const prodConfig = {
// ...
plugins: [
// ...
// 告訴 Webpack 使用了哪些動態連接庫
new webpack.DllReferencePlugin({
manifest: path.join(__dirname, `../dll/vendor.manifest.json`)
})
]
}
複製代碼
以後在 package.json 中scripts再加一個命令
"scripts": {
"dll": "webpack --config build/webpack.config.dll.js",
}
複製代碼
而後運行它,就能夠發現根目錄下dll生成了兩個文件 vendor.dll.xxxxxxxx.js,vendor.manifest.json
咱們使用它來將給定的靜態資源css或者js引入到html-webpack-plugin生成的html文件中。
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
// ...
const prodConfig = {
// ...
plugins: [
// ...
new AddAssetHtmlPlugin({
filepath: resolve(`${DLL_PATH}/**/*.js`),
includeSourcemap: false
}),
]
}
複製代碼
若是你想看你webpack打包以後輸出文件的大小佔比,可使用這個插件,在webpack.prod.js 中加入以下配置,若是你想控制這個插件是否引入,可使用一個變量:
if (config.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
prodConfig.plugins.push(new BundleAnalyzerPlugin())
}
複製代碼
這樣在打包結束後,會自動打開一個瀏覽器窗口,並展現輸出文件的大小佔比。
若是想要在打包或者開發過程當中展現一些性能提示,能夠在 webpack.common.js 中加入以下配置。
module.exports={
// ...
performance: { // 性能提示,能夠提示過大文件
hints: "warning", // 性能提示開關 false | "error" | "warning"
maxAssetSize: 100000, // 生成的文件最大限制 整數類型(以字節爲單位)
maxEntrypointSize: 100000, // 引入的文件最大限制 整數類型(以字節爲單位)
assetFilter: function(assetFilename) {
// 提供資源文件名的斷言函數
return (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetFilename))
}
}
}
複製代碼
到這裏生產開發環境的配置基本上就結束了,若是有漏掉的或者配置不對的地方,但願大佬指出。
最後附上地址 項目地址,若是有不對的地方但願各位指出,感謝。
參考的文章: