上一篇文章介紹了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)) } } } 複製代碼
到這裏生產開發環境的配置基本上就結束了,若是有漏掉的或者配置不對的地方,但願大佬指出。
最後附上地址 項目地址,若是有不對的地方但願各位指出,感謝。
參考的文章: