以前一直用vue+webpack在寫前端項目,最近換了個環境後,項目都是用react來開發的,因此也想搗鼓下框架啥的東西,最近也看了下webpack4,感受這玩意兒愈來愈集成化了,有些插件已經被丟棄了或者集成了,不過在實際項目開發中,也存在一些依賴版本方面的問題,本文將帶你走進webpack的世界,一塊兒看看那些坑吧。css
本人寫的實例,已上傳至GitHub,點擊項目地址能夠查看詳情,歡迎star哦。html
webpack做爲時下前端最流行的自動化構建工具,其版本更新也是一路備受關注,目前大多數新項目都會採用webpack4.0+去構建,接下來在瞭解如何用webpack從零開始搭建本身的項目以前,咱們先仍是熟悉下webpack的基本概念:前端
webpack: 本質上,webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle文件被瀏覽器識別使用。vue
gulp和grunt是基於流的一種管理工具,經過創建一個個task任務,配置執行用戶須要的功能,如格式檢驗,代碼壓縮等,值得一提的是,通過這二者處理的代碼只是局部變量名被替換簡化,總體並無發生改變,仍是你的代碼。node
webpack是基於入口文件,識別模塊依賴關係,分析代碼,轉換代碼,編譯代碼,輸出代碼,通過打包後的代碼基本已是認不出來了,有點像壓縮的jq那樣的感受,webpack它自己也是一個node的模塊,webpack.config.js是以commonjs形式書寫的(node中的模塊化是commonjs規範的)。react
熟悉了webpck的一些基本概念後,接下來,咱們從零開始真正的配置下webpack.config.js文件。webpack
mkdir react-webpack4
cd react-webpack4
mkdir src
mkdir dist
npm init -y
// 安裝webpack和相關模塊
yarn add webpack webpack-cli webpack-dev-server -D //webpack4.X 把webpack拆分了
touch webpack.config.js // mac上建立文件
echo test>webpack.config.js // win上建立文件
module.exports = {
entry: ["./src/index.js"], // 項目文件入口
output: {
// 輸出目錄
path: path.resolve(__dirname, "../dist")
},
module: {},
plugins:[],
devServer: {}
}
複製代碼
webpack.config.js的文件結構就是如上,可是在項目開發時, 每每開發環境和生產環境在配置上會有些不一樣,因此爲了區分開來,咱們在項目根目錄下建立一個build的文件夾,在該文件夾下面建立如下三個文件:git
webpack.base.config.js // 基本配置
webpack.dev.config.js // 開發環境
webpack.prod.config.js // 生產環境
複製代碼
接下來先看下webpack.base.config.js基本配置:es6
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
entry: ["./src/index.js"],
output: {
// 輸出目錄
path: path.resolve(__dirname, "../dist")
},
resolve: {
extensions: [".js", ".jsx"], // 擴展
alias: {
'@': path.resolve(__dirname, 'src'),
'@pages': path.resolve(__dirname, 'src/pages'),
'@router': path.resolve(__dirname, 'src/router'),
'@assets': path.resolve(__dirname, 'src/assets')
}
},
module: {
rules: [
{
test: /\.(js|jsx?)$/,
exclude: /node_modules/,
use: [
{
loader: "happypack/loader?id=happyBabel"
}
]
},
{
test: /\.(le|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // 編譯css
"postcss-loader", // 使用 postcss 爲 css 加上瀏覽器前綴
"less-loader", // 編譯less
]
},
{
test: /\.(png|jpg|jpeg|gif|svg)/,
use: {
loader: "url-loader",
options: {
outputPath: "images/", // 圖片輸出的路徑
limit: 10 * 1024
}
}
},
{
test: /\.(eot|woff2?|ttf|svg)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 5000, // 使用base64進行轉換, 大小限制小於5KB, 不然使用svg輸出
publicPath: 'fonts/',
outputPath: 'fonts/'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title:'webpack配置', // 項目標題
filename: "index.html", // 最終建立的文件名
template: path.resolve(__dirname, '..', "src/index.html"), // 指定模板路徑
minify: {
collapseWhitespace: true // 去除空白
}
}),
// 單獨生成css文件和js文件分離開來 加快頁面渲染
new MiniCssExtractPlugin({
filename: "[name]-[hash:5].css",
chunkFilename: "[id]-[hash:5].css"
}),
// happypack
new HappyPack({
//用id來標識 happypack處理那裏類文件
id: 'happyBabel',
//如何處理 用法和loader 的配置同樣
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享進程池threadPool: HappyThreadPool 表明共享進程池,即多個 HappyPack 實例都使用同一個共享進程池中的子進程去處理任務,以防止資源佔用過多。
threadPool: happyThreadPool,
//容許 HappyPack 輸出日誌
verbose: true,
}),
],
performance: false // 關閉性能提示
};
複製代碼
以上配置,可能你見過,可是不必定知道有些東西用來幹嗎的,作個解釋:github
yarn add html-webpack-plugin
複製代碼
new HtmlWebpackPlugin({
title:'webpack配置', // 網站標題
filename: "index.html", // 最終建立的文件名
template: path.resolve(__dirname, '..', "src/index.html"), // 指定模板路徑
minify: {
collapseWhitespace: true // 去除空白
}
}),
複製代碼
使用 HtmlWebpackPlugin插件,來生成 html, 並將每次打包的js自動插入到你的 index.html 裏面去,並且它還能夠基於你的某個 html 模板來建立最終的 index.html,也就是說能夠指定模板
yarn add mini-css-extract-plugin
複製代碼
若是不作該配置,咱們的css是直接打包進js裏面的,咱們但願能單獨生成css文件。 由於單獨生成css,css能夠和js並行下載,提升頁面加載效率。 特別說明:目前 extract-text-webpack-plugin 最新版本不支持 Webpack 4.3.0 版本. Webpack 4.2.0 一下可用。 目前從 extract-text-webpack-plugin issues 瞭解, 將來 extract-text-webpack-plugin 將廢棄,做者建議使用 mini-css-extract-plugin
{
test: /\.(le|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // 編譯css
"postcss-loader", // 使用 postcss 爲 css 加上瀏覽器前綴
"less-loader", // 編譯less
]
},
複製代碼
new MiniCssExtractPlugin({
filename: "[name]-[contenthash:5].css",
chunkFilename: "[id]-[contenthash:5].css"
}),
複製代碼
hash: 若是都使用hash的話,由於這是工程級別的,即每次修改任何一個文件,全部文件名的hash至都將改變。因此一旦修改了任何一個文件,整個項目的文件緩存都將失效。
chunkhash: 根據不一樣的入口文件(Entry)進行依賴文件解析、構建對應的chunk,生成對應的哈希值。在生產環境裏把一些公共庫和程序入口文件區分開,單獨打包構建,接着咱們採用chunkhash的方式生成哈希值,那麼只要咱們不改動公共庫的代碼,就能夠保證其哈希值不會受影響,可是同一個模塊,就算將js和css分離,其哈希值也是相同的,修改一處,js和css哈希值都會變,同hash,沒有作到緩存意義。
contenthash:表示由文件內容產生的hash值,內容不一樣產生的contenthash值也不同。在項目中,一般作法是把項目中css都抽離出對應的css文件來加以引用。(只要文件內容不同,產生的哈希值就不同)
因此css文件最好使用contenthash。
yarn add happypack
複製代碼
運行在 Node.之上的Webpack是單線程模型的,也就是說Webpack須要一個一個地處理任務,不能同時處理多個任務。 Happy Pack 就能讓Webpack作到這一點,它將任務分解給多個子進程去併發執行,子進程處理完後再將結果發送給主進程。
plugins:[
new HappyPack({
//用id來標識 happypack處理那裏類文件
id: 'happyBabel',
//如何處理 用法和loader 的配置同樣
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享進程池threadPool: HappyThreadPool 表明共享進程池,即多個 HappyPack 實例都使用同一個共享進程池中的子進程去處理任務,以防止資源佔用過多。
threadPool: happyThreadPool,
//容許 HappyPack 輸出日誌
verbose: true,
})
]
複製代碼
以上是對webpack.base.config.js基礎配置的一個解釋說明,接下來我們再看看再開發環境:webpack.dev.config.js
const path = require("path");
const merge = require('webpack-merge')
const commonConfig = require('./webpack.base.config.js')
const webpack = require("webpack");
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
module.exports = merge(commonConfig, {
mode: "development", //webpack 4.0 要申明這個
devtool: 'cheap-module-eval-soure-map',
output: {
// 輸出目錄
path: path.resolve(__dirname, "../dist"),
// 文件名稱
filename: "bundle.js",
chunkFilename: '[name].js'
},
plugins: [
//開啓HMR(熱替換功能,替換更新部分,不重載頁面!) 至關於在命令行加 --hot
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'ENV': JSON.stringify("development"),
'process.env': {
VUEP_BASE_URL: '/'
}
}),
//識別某些類別的webpack錯誤
new FriendlyErrorsPlugin({
// 運行成功
compilationSuccessInfo: {
message: ['你的應用程序在這裏運行http://localhost:8001'],
notes: ['有些附加說明要在成功編輯時顯示']
},
// 運行錯誤
onErrors: function(severity, errors){
//您能夠收聽插件轉換和優先級的錯誤,嚴重性能夠是'錯誤'或'警告'
},
//是否每次編譯之間清除控制檯,默認爲true
clearConsole: true,
//添加格式化程序和變換器(見下文)
additionalFormatters: [],
additionalTransformers: []
})
],
devServer: {
contentBase: path.resolve(__dirname, "../dist"), // 指定訪問資源目錄
historyApiFallback: true, // 該選項的做用全部的404都鏈接到index.html
disableHostCheck: true, // 繞過主機檢查
inline: true, // 改動後是否自動刷新
host: 'localhost', // 訪問地址
port: 8001, // 訪問端口
overlay: true, // 出現編譯器錯誤或警告時在瀏覽器中顯示全屏覆蓋
stats: "errors-only", // 顯示捆綁軟件中的錯誤
compress: true, // 對全部服務啓用gzip壓縮
open: true, // 自動打開瀏覽器
progress: true, // 顯示編譯進度
proxy: {
// 代理到後端的服務地址
"/api": "http://localhost:8000"
}
}
});
複製代碼
devServer: {
contentBase: path.resolve(__dirname, "../dist"), // 指定訪問資源目錄
historyApiFallback: true, // 該選項的做用全部的404都鏈接到index.html
disableHostCheck: true, // 繞過主機檢查
inline: true, // 改動後是否自動刷新
host: 'localhost', // 訪問地址
port: 8001, // 訪問端口
overlay: true, // 出現編譯器錯誤或警告時在瀏覽器中顯示全屏覆蓋
stats: "errors-only", // 顯示捆綁軟件中的錯誤
compress: true, // 對全部服務啓用gzip壓縮
open: true, // 自動打開瀏覽器
progress: true, // 顯示編譯進度
proxy: {
// 代理到後端的服務地址
"/api": "http://localhost:8000"
}
}
複製代碼
webpack4已經集成了open-browser-webpack-plugin 和 progress-bar-webpack-plugin這兩個插件,因此不須要再單獨引入,直接將 devServer.open 和devServer.progress設置爲true便可。
mode: "development/production", webpack 4.0+ 要申明這個
output: {
// 輸出目錄
path: path.resolve(__dirname, "../dist"),
// 文件名稱
filename: "bundle.js",
chunkFilename: '[name].js'
},
複製代碼
yarn add friendly-errors-webpack-plugin
複製代碼
Friendly-errors-webpack-plugin識別某些類別的webpack錯誤,並清理,聚合和優先級,以提供更好的開發人員體驗。
//識別某些類別的webpack錯誤
new FriendlyErrorsPlugin({
// 運行成功
compilationSuccessInfo: {
message: ['你的應用程序在這裏運行http://localhost:8001'],
notes: ['有些附加說明要在成功編輯時顯示']
},
// 運行錯誤
onErrors: function(severity, errors){
//您能夠收聽插件轉換和優先級的錯誤, 嚴重性能夠是'錯誤'或'警告'
},
//是否每次編譯之間清除控制檯,默認爲true
clearConsole: true,
//添加格式化程序和變換器(見下文)
additionalFormatters: [],
additionalTransformers: []
})
複製代碼
最後,我們看看再實際生產環境中,webpack.dev.config.js的配置:
const path = require("path");
const webpack = require("webpack");
const merge = require('webpack-merge')
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const commonConfig = require('./webpack.base.config.js')
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
const WorkboxPlugin = require('workbox-webpack-plugin') // 引入 PWA 插件
module.exports = merge(commonConfig, {
mode: "production", //webpack 4.0 要申明這個
output: {
// 輸出目錄
path: path.resolve(__dirname, "../dist"),
// 文件名稱
filename: '[name].[contenthash:5].js',
chunkFilename: '[name].[contenthash:5].js'
},
devtool: 'cheap-module-source-map',
optimization: {
usedExports: true, //js Tree Shaking 清除到代碼中無用的js代碼,只支持import方式引入,不支持commonjs的方式引入
splitChunks: {
chunks: "all", // 全部的 chunks 代碼公共的部分分離出來成爲一個單獨的文件
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors'
}
}
},
},
plugins: [
new CleanWebpackPlugin(),// 打包時刪除以前的文件
// 清除無用 css---生產環境---csstree-shaking
new PurifyCSS({
paths: glob.sync([
// 清除無用 css---生產環境-- 對於 css的tree shaking 使用 purifycss-webpack 配合 glob-all來實現 。
path.resolve(__dirname, '..', 'src/*.html'),
path.resolve(__dirname, '..', 'src/*.js'),
path.resolve(__dirname, '..', 'src/**/*.jsx'),
])
}),
// PWA配置,生產環境才須要,PWA優化策略,在你第一次訪問一個網站的時候,若是成功,作一個緩存,當服務器掛了以後,你依然可以訪問這個網頁 ,這就是PWA。那相信你也已經知道了,這個只須要在生產環境,才須要作PWA的處理,以防不測。
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
}),
]
});
複製代碼
你執行yarn run build的時候,每次都會在dist目錄下邊生成一堆文件,可是上一次的打包的文件還在,這個咱們須要每次打包時清除 dist 目錄下舊版本文件
yarn add clean-webpack-plugin
複製代碼
注意: 這個引入的坑,最新版的須要這樣引入,而不是直接
// 報錯:const CleanWebpackPlugin = require('clean-webpack-plugin')
複製代碼
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin()
]
複製代碼
webpack中devtool選項用來控制是否生成,以及如何生成 source map。簡言之,source map就是幫助咱們定位到錯誤信息位置的文件。正確的配置source map,可以提升開發效率,更快的定位到錯誤位置。
devtool:"cheap-module-eval-source-map",// 開發環境配置
devtool:"cheap-module-source-map", // 線上生成配置
複製代碼
yarn add glob-all purify-css purifycss-webpack
複製代碼
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
// 清除無用 css
new PurifyCSS({
paths: glob.sync([
// 要作 CSS Tree Shaking 的路徑文件
path.resolve(__dirname, './src/*.html'), // 請注意,咱們一樣須要對 html 文件進行 tree shaking
path.resolve(__dirname, './src/*.js')
])
})
]
複製代碼
在webpack4中已經移除了UglifyJsPlugin,只須要配置mode爲"production",便可顯式激活 UglifyjsWebpackPlugin 插件。
清除到代碼中無用的js代碼,只支持import方式引入,不支持commonjs的方式引入
只要mode是production就會生效,develpoment的tree shaking是不生效的,由於webpack爲了方便你的調試
optimization: {
usedExports:true,
}
複製代碼
yarn add workbox-webpack-plugin
複製代碼
PWA的做用就是當你第一次成功訪問網站是,作一個緩存,那麼在服務器掛了的狀況下,你依然能夠訪問這個網頁,這個只須要在生產環境下處理,以防不測。
const WorkboxPlugin = require('workbox-webpack-plugin')
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
}),
複製代碼
以上是關於本人在webpack在開發環境和生產環境的一些配置和優化,歡迎你們繼續補充和指正,你們對webpack有興趣的也能夠先去官網瞭解下webpack的一些知識點。
因爲篇幅太長,本文分爲兩部分來寫,這一篇主要講解下webpack方面的配置。
下一篇 將寫一些關於基於react-router4.0實現路由的按需加載。