以前一段時間工做緣由把精力都放在小程序上,趁如今有點空閒時間,恰好官方文檔也補充完整了,我準備重溫一下 webpack 之路了,由於官方文檔已經寫得很是詳細,我會大量引用原文描述,主要重點放在怎麼從零構建 webpack4 代碼上,這不是一個系統的教程,而是從零摸索一步步搭建起來的筆記,因此前期可能bug會後續發現繼續修復而不是修改文章.javascript
webpack4從零開始構建(一)
webpack4+React16項目構建(二)
webpack4功能配置劃分細化(三)
webpack4引入Ant Design和Typescript(四)
webpack4代碼去重,簡化信息和構建優化(五)
webpack4配置Vue版腳手架(六)css
2019/04/16上傳,代碼同步到引入antd webpack4_demo_antdhtml
同級目錄新建文件env.js
文件,咱們將一些環境變量和配置變量獨立開來,儘可能不耦合進webpack文件裏.以下所示前端
const path = require('path'); const isDev = process.env.NODE_ENV !== "DEV", isProd = process.env.NODE_ENV !== "PROD", isServer = process.env.NODE_ENV !== "SERVER", entry = "./src/index.tsx", outputName = "[name].bundle.js", outputPath = path.resolve(__dirname, "../dist"), publicPath = "", title = "test"; module.exports = { isDev, isProd, isServer, entry, outputName, outputPath, publicPath, title };
相關變量替換以下:java
...省略 const { isProd, entry, outputName, outputPath, publicPath, title } = require('./env'); module.exports = { // 入口 entry, // 輸出 output: { // 打包文件名 filename: outputName, // 輸出路徑 path: outputPath, // 資源請求路徑 publicPath }, module: { rules }, plugins: [ // 清除文件 new CleanWebpackPlugin(), // 提取樣式文件 new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: !isProd ? "[name].css" : "style/[name].[contenthash].css", chunkFilename: !isProd ? "[id].css" : "style/[id].[contenthash].css" }), new HtmlWebpackPlugin({ // title title, // 模板 template: "index.html" }) ], ...省略 };
yarn add progress-bar-webpack-plugin ------------------------------------------- new VueLoaderPlugin()
目測沒啥用處,求個視覺效果吧node
new webpack.DefinePlugin({ 'process.env.project': 'demo' }),
plugins部分最終代碼以下:webpack
plugins: [ // 清除文件 new CleanWebpackPlugin(), // 進度條展現 new ProgressBarPlugin(), // 該插件幫助咱們安心地使用環境變量 new webpack.DefinePlugin({ 'process.env.project': 'demo' }), // 提取樣式文件 new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: !isProd ? "[name].css" : "style/[name].[contenthash].css", chunkFilename: !isProd ? "[id].css" : "style/[id].[contenthash].css" }), new HtmlWebpackPlugin({ // title title, // 模板 template: "index.html" }) ],
咱們能夠自定義webpack打包的輸出信息,由於比較多字段,因此同級目錄新增stats.js
文件,具體彷佛出看本身需求自由改動吧git
module.exports = { // 未定義選項時,stats 選項的備用值(fallback value)(優先級高於 webpack 本地默認值) all: undefined, // 添加資源信息 assets: true, // 對資源按指定的字段進行排序 // 你可使用 `!field` 來反轉排序。 // Some possible values: 'id' (default), 'name', 'size', 'chunks', 'failed', 'issuer' // For a complete list of fields see the bottom of the page assetsSort: 'field', // 添加構建日期和構建時間信息 builtAt: true, // 添加緩存(但未構建)模塊的信息 cached: true, // 顯示緩存的資源(將其設置爲 `false` 則僅顯示輸出的文件) cachedAssets: true, // 添加 children 信息 children: true, // 添加 chunk 信息(設置爲 `false` 能容許較少的冗長輸出) chunks: false, // 添加 namedChunkGroups 信息 chunkGroups: false, // 將構建模塊信息添加到 chunk 信息 chunkModules: false, // 添加 chunk 和 chunk merge 來源的信息 chunkOrigins: false, // 按指定的字段,對 chunk 進行排序 // 你可使用 `!field` 來反轉排序。默認是按照 `id` 排序。 // Some other possible values: 'name', 'size', 'chunks', 'failed', 'issuer' // For a complete list of fields see the bottom of the page chunksSort: 'field', // `webpack --colors` 等同於 colors: true, // 顯示每一個模塊到入口起點的距離(distance) depth: false, // 經過對應的 bundle 顯示入口起點 entrypoints: false, // 添加 --env information env: false, // 添加錯誤信息 errors: true, // 添加錯誤的詳細信息(就像解析日誌同樣) errorDetails: true, /* // 將資源顯示在 stats 中的狀況排除 // 這能夠經過 String, RegExp, 獲取 assetName 的函數來實現 // 並返回一個布爾值或以下所述的數組。 excludeAssets: "filter" | /filter/ | (assetName) => true | false | ["filter"] | [/filter/] | [(assetName) => true | false], // 將模塊顯示在 stats 中的狀況排除 // 這能夠經過 String, RegExp, 獲取 moduleSource 的函數來實現 // 並返回一個布爾值或以下所述的數組。 excludeModules: "filter" | /filter/ | (moduleSource) => true | false | ["filter"] | [/filter/] | [(moduleSource) => true | false], // 查看 excludeModules exclude: "filter" | /filter/ | (moduleSource) => true | false | ["filter"] | [/filter/] | [(moduleSource) => true | false], */ // 添加 compilation 的哈希值 hash: true, // 設置要顯示的模塊的最大數量 // maxModules: 15, // 添加構建模塊信息 modules: true, // 按指定的字段,對模塊進行排序 // 你可使用 `!field` 來反轉排序。默認是按照 `id` 排序。 // Some other possible values: 'name', 'size', 'chunks', 'failed', 'issuer' // For a complete list of fields see the bottom of the page modulesSort: 'field', // 顯示警告/錯誤的依賴和來源(從 webpack 2.5.0 開始) moduleTrace: true, // 當文件大小超過 `performance.maxAssetSize` 時顯示性能提示 performance: true, // 顯示模塊的導出 providedExports: false, // 添加 public path 的信息 publicPath: false, // 添加模塊被引入的緣由 reasons: false, // 添加模塊的源碼 source: false, // 添加時間信息 timings: true, // 顯示哪一個模塊導出被用到 usedExports: false, // 添加 webpack 版本信息 version: false, // 添加警告 warnings: true, // 過濾警告顯示(從 webpack 2.4.0 開始), // 能夠是 String, Regexp, 一個獲取 warning 的函數 // 並返回一個布爾值或上述組合的數組。第一個匹配到的爲勝(First match wins.)。 // warningsFilter: "filter" | /filter/ | ["filter", /filter/] | (warning) => true| false }
performance: { hints: false // 取消靜態文件超過250kb的警告 },
設置後不影響打包,只是不會提示github
const HtmlWebpackPlugin = require("html-webpack-plugin"), CleanWebpackPlugin = require("clean-webpack-plugin"), MiniCssExtractPlugin = require("mini-css-extract-plugin"), ProgressBarPlugin = require('progress-bar-webpack-plugin'), webpack = require('webpack'), alias = require("./alias"), rules = require("./rules"), stats = require("./stats"), { isProd, entry, outputName, outputPath, publicPath, title } = require('./env'); module.exports = { // 入口 entry, // 輸出 output: { // 打包文件名 filename: outputName, // 輸出路徑 path: outputPath, // 資源請求路徑 publicPath }, module: { rules }, stats, performance: { hints: false // 取消靜態文件超過250kbkb的警告 }, plugins: [ // 清除文件 new CleanWebpackPlugin(), // 進度條展現 new ProgressBarPlugin(), // 該插件幫助咱們安心地使用環境變量 new webpack.DefinePlugin({ 'process.env.project': 'demo' }), // 提取樣式文件 new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: !isProd ? "[name].css" : "style/[name].[contenthash].css", chunkFilename: !isProd ? "[id].css" : "style/[id].[contenthash].css" }), new HtmlWebpackPlugin({ // title title, // 模板 template: "index.html" }) ], resolve: { // Add '.ts' and '.tsx' as resolvable extensions. extensions: [".ts", ".tsx", ".js", ".json"], // 建立 import 或 require 的別名,來確保模塊引入變得更簡單 alias } };
本來代碼充斥不少重複代碼,將他們提取出來web
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const tsImportPluginFactory = require("ts-import-plugin"); const { isProd, isServer, entry, outputName, outputPath, publicPath, title } = require('./env') const cssMiniLoader = !isServer ? { loader: MiniCssExtractPlugin.loader, options: { // you can specify a publicPath here // by default it use publicPath in webpackOptions.output publicPath: process.env.NODE_ENV === "DEV" ? "./" : "../" } } : "style-loader"; // 使用<style>將css-loader內部樣式注入到咱們的HTML頁面, const postcssLoader = { loader: "postcss-loader", options: { config: { path: "./" // 寫到目錄便可,文件名強制要求是postcss.config.js } } }; const imgLoader = { loader: "url-loader", options: { name: "[name].[hash:5].[ext]", limit: 20 * 1024, // size <= 50kb outputPath: "img" } }; module.exports = [ // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'. { test: /\.tsx?$/, loader: "awesome-typescript-loader", options: { // 利用緩存 useCache: true, // 禁止babel useBabel: false, // !important! // 提供自定義轉換 getCustomTransformers: () => ({ before: [ // ts按需加載庫 tsImportPluginFactory({ libraryName: "antd", libraryDirectory: "lib", style: true }) ] }) }, exclude: [/node_modules\/mutationobserver-shim/g] }, { test: /\.s?css$/, // 匹配文件 use: [ cssMiniLoader, "css-loader", // 加載.css文件將其轉換爲JS模塊 postcssLoader, "sass-loader" // 加載 SASS / SCSS 文件並將其編譯爲 CSS ] }, { test: /antd.*\.less$/, // 匹配文件 use: [ cssMiniLoader, "css-loader", // 加載.css文件將其轉換爲JS模塊 postcssLoader, { loader: "less-loader", options: { javascriptEnabled: true // 是否處理js內樣式 } } ] }, { test: /\.(png|svg|jpe?g|gif)$/i, // 圖片處理 use: isProd ? [ imgLoader, { loader: "image-webpack-loader", options: { // Compress JPEG images mozjpeg: { progressive: true, quality: 65 }, // Compress PNG images optipng: { enabled: false }, // Compress PNG images pngquant: { quality: "65-90", speed: 4 }, // Compress GIF images gifsicle: { interlaced: false } } } ] : [ imgLoader ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, // 字體處理 use: ["file-loader"] }, { test: /\.xml$/, // 文件處理 use: ["xml-loader"] }, { test: /\.(html)$/, use: { loader: "html-loader" } } ];
由於如今已經有awesome-typescript-loader
處理,因此咱們能夠將js規則去掉.同時默認會禁止babel,若是須要的話能夠配置babelOptions
咱們稍微再細緻一下配置,修改以下
const path = require('path'), webpack = require('webpack'), merge = require('webpack-merge'), common = require('./webpack.common.js'), BundleAnalyzerPlugin = require('webpack-bundle-analyzer') .BundleAnalyzerPlugin; module.exports = merge(common, { mode: "development", // 原始源代碼(僅限行) devtool: "cheap-module-eval-source-map", devServer: { clientLogLevel: 'warning', // none,error,warning // 打開模式, Iframe mode和Inline mode最後達到的效果都是同樣的,都是監聽文件的變化,而後再將編譯後的文件推送到前端,完成頁面的reload的 inline: true, progress: true, // 指定了服務器資源的根目錄 contentBase: path.join(__dirname, '../'), // 是否開啓gzip壓縮 compress: false, port: 9000, // 是否開啓熱替換功能 // hot: true, host: 'localhost', // 是否自動打開頁面,能夠傳入指定瀏覽器名字打開 open: false, // 是否開啓部分熱替換功能 hotOnly: true, proxy: { }, // Shows a full-screen overlay in the browser when there are compiler errors or warnings. Disabled by default. If you want to show only compiler errors: overlay: { warnings: false, errors: true }, /* 惰性模式 將不監視文件改動,不想使用自動刷新功能時可設置爲true */ lazy: false, /* 靜默模式,減小沒必要要的信息輸出 necessary for FriendlyErrorsPlugin */ quiet: false, watchOptions: { // Turn on polling by passing true, or specifying a poll interval in milliseconds: poll: false }, // 同stats stats: { builtAt: true, colors: true, modules: false, children: false, chunks: false, chunkModules: false, // 添加時間信息 timings: true } }, plugins: [ // 熱替換模塊 new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. new webpack.NoEmitOnErrorsPlugin() //在編譯出現錯誤時,使用 NoEmitOnErrorsPlugin 來跳過輸出階段。這樣能夠確保輸出資源不會包含錯誤。對於全部資源,統計資料(stat)的 emitted 標識都是 false // 性能可視化 // new BundleAnalyzerPlugin() ] });
當前配置代碼以下
const merge = require("webpack-merge"), common = require("./webpack.common.js"), OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"), ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); module.exports = merge(common, { mode: "production", // 原始源代碼 devtool: "cheap-module-eval-source-map", plugins: [new OptimizeCssAssetsPlugin()] });
能夠看出構建文件和耗費時間
devtool: "none",
簡單將node_modules
和業務代碼分開
optimization: { // Setting optimization.runtimeChunk to true or "multiple" adds an additional chunk to each entrypoint containing only the runtime. This setting is an alias for: runtimeChunk: { name: 'manifest' }, splitChunks: { minSize: 0, cacheGroups: { vendor: { // 優先級高於其餘就不會被打包進其餘chunk,若是想匹配本身定義的拆分規則,則priority須要設置爲正數,優先匹配默認拆分規則就設置爲負數。 priority: 10, name: 'vendor', test: /[\\/]node_modules[\\/]/, chunks: 'all' } } } },
結果以下
這插件能爲多入口文件啓用多線程構建,提升打包速度,這裏demo比較簡單暫時用不到,可是咱們能夠利用他作一些特殊處理
minimizer: [ // This plugin serves to help projects with many entry points speed up their builds. The UglifyJS plugin provided with webpack runs sequentially on each of the output files. This plugin runs uglify in parallel with one thread for each of your available cpus. This can lead to significantly reduced build times as minification is very CPU intensive. new ParallelUglifyPlugin({ // Optional absolute path to use as a cache. If not provided, caching will not be used. cacheDir: '.cache/', // 傳遞給 UglifyJS的參數以下: uglifyJS: { output: { /* 是否輸出可讀性較強的代碼,即會保留空格和製表符,默認爲輸出,爲了達到更好的壓縮效果, 能夠設置爲false */ beautify: false, /* 是否保留代碼中的註釋,默認爲保留,爲了達到更好的壓縮效果,能夠設置爲false */ comments: false }, compress: { /* 是否在UglifyJS刪除沒有用到的代碼時輸出警告信息,默認爲輸出,能夠設置爲false關閉這些做用 不大的警告 */ warnings: false, /* 是否刪除代碼中全部的console語句,默認爲不刪除,開啓後,會刪除全部的console語句 */ drop_console: true, /* 是否內嵌雖然已經定義了,可是隻用到一次的變量,好比將 var x = 1; y = x, 轉換成 y = 5, 默認爲不 轉換,爲了達到更好的壓縮效果,能夠設置爲false */ collapse_vars: true, /* 是否提取出現了屢次可是沒有定義成變量去引用的靜態值,好比將 x = 'xxx'; y = 'xxx' 轉換成 var a = 'xxxx'; x = a; y = a; 默認爲不轉換,爲了達到更好的壓縮效果,能夠設置爲false */ reduce_vars: true } } }) ]
const merge = require("webpack-merge"), common = require("./webpack.common.js"), OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"), ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); module.exports = merge(common, { mode: "production", // 原始源代碼 devtool: "none", optimization: { // Setting optimization.runtimeChunk to true or "multiple" adds an additional chunk to each entrypoint containing only the runtime. This setting is an alias for: runtimeChunk: { name: 'manifest' }, splitChunks: { minSize: 0, cacheGroups: { vendor: { // 優先級高於其餘就不會被打包進其餘chunk,若是想匹配本身定義的拆分規則,則priority須要設置爲正數,優先匹配默認拆分規則就設置爲負數。 priority: 10, name: 'vendor', test: /[\\/]node_modules[\\/]/, chunks: 'all' } } }, minimizer: [ // This plugin serves to help projects with many entry points speed up their builds. The UglifyJS plugin provided with webpack runs sequentially on each of the output files. This plugin runs uglify in parallel with one thread for each of your available cpus. This can lead to significantly reduced build times as minification is very CPU intensive. new ParallelUglifyPlugin({ // Optional absolute path to use as a cache. If not provided, caching will not be used. cacheDir: '.cache/', // 傳遞給 UglifyJS的參數以下: uglifyJS: { output: { /* 是否輸出可讀性較強的代碼,即會保留空格和製表符,默認爲輸出,爲了達到更好的壓縮效果, 能夠設置爲false */ beautify: false, /* 是否保留代碼中的註釋,默認爲保留,爲了達到更好的壓縮效果,能夠設置爲false */ comments: false }, compress: { /* 是否在UglifyJS刪除沒有用到的代碼時輸出警告信息,默認爲輸出,能夠設置爲false關閉這些做用 不大的警告 */ warnings: false, /* 是否刪除代碼中全部的console語句,默認爲不刪除,開啓後,會刪除全部的console語句 */ drop_console: true, /* 是否內嵌雖然已經定義了,可是隻用到一次的變量,好比將 var x = 1; y = x, 轉換成 y = 5, 默認爲不 轉換,爲了達到更好的壓縮效果,能夠設置爲false */ collapse_vars: true, /* 是否提取出現了屢次可是沒有定義成變量去引用的靜態值,好比將 x = 'xxx'; y = 'xxx' 轉換成 var a = 'xxxx'; x = a; y = a; 默認爲不轉換,爲了達到更好的壓縮效果,能夠設置爲false */ reduce_vars: true } } }) ] }, plugins: [new OptimizeCssAssetsPlugin()] });
打包結果