本文偏入門&實踐,從零開始配置 Webpack; 實際項目開發,零配置是不存在的。
快速初始化配置文件 package.json
javascript
// npm i yarn -g yarn init -y // yarn init --yes // yarn init --yes=true // 即所有選項默認爲 yes
接下來將 webpack
添加到 package.json
=> devDependencies
css
yarn add webpack -D
安裝成功後,建立目錄 src/index.js
並添加以下內容 (默認入口爲 src
)html
document.write("Hello webpack4!");
命令行輸入:java
webpack --mode=development
成功後顯示,打開 dist
文件夾會看到 main.js
(默認輸出到 dist
)node
Hash: 771a2645c2d430fa3bb4 Version: webpack 4.5.0 Time: 128ms Built at: 2020-4-10 03:14:23 Asset Size Chunks Chunk Names main.js 2.81 KiB main [emitted] main Entrypoint main = main.js [./index.js] 34 bytes {main} [built]
--mode
模式 (必選,否則會有WARNING
),是webpack4
新增的參數選項,默認是production
--mode production
生產環境react
uglifyjs-webpack-plugin
代碼壓縮new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })
默認 production
optimization.noEmitOnErrors
, 編譯出錯時跳過輸出,以確保輸出資源不包含錯誤optimization.concatenateModules
, webpack3 添加的做用域提高(Scope Hoisting)--mode development
開發環境webpack
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
默認 development
optimization.namedModules
使用模塊熱替換(HMR)時會顯示模塊的相對路徑接下來建立 dist/index.html
並引入 main.js
, 瀏覽器中打開看內容。git
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>webpack-simple</title> </head> <body> <script type="text/javascript" src="./main.js"></script> </body> </html>
再建立一個文件 src/content.js
, 在 src/index.js
中引入該模塊github
// content.js module.exports = 'Looooooooooooooong content!';
// index.js document.write(`Hello webpack4!${require('./content.js')}`);
再次執行 webpack --mode=development
完了打開 index.html
web
// 內容 Hello webpack4!Looooooooooooooong content!
webpack.config.js
安裝 webpack-cli
來初始化配置
yarn add webpack-cli -D
webpack-cli init 1. Will your application have multiple bundles? No // 單入口 string, 多頁面 object 2. Which module will be the first to enter the application? [example: './src/index'] ./src/index // 程序入口 3. What is the location of "app"? [example: "./src/app"] './src/index' // 程序主文件 4. Which folder will your generated bundles be in? [default: dist]: // 輸出目錄,默認 dist 5. Are you going to use this in production? No // (Yes 第9步默認'config', No 則爲 'prod') 6. Will you be using ES2015? Yes // 會添加 ES6 => ES5 的配置 7. Will you use one of the below CSS solutions? CSS // 選一種樣式語言,會生成對應的 loader 配置 8. If you want to bundle your CSS files, what will you name the bundle? (press enter to skip) // 回車跳過 9. Name your 'webpack.[name].js?' [default: 'config']: // webpack.config.js Congratulations! Your new webpack configuration file has been created!
配置生成OK,以下
// webpack.config.js const webpack = require('webpack'); const path = require('path'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: ['env'] } }, { test: /\.css$/, use: [ { loader: 'style-loader', options: { sourceMap: true } }, { loader: 'css-loader' } ] } ] }, plugins: [new UglifyJSPlugin()] // 這款插件用於壓縮 JS 代碼,減小資源體積大小 };
再度執行編譯一切OK, 打開 index.html
查看內容
webpack --mode=development Hash: c30d4f489db4d568ee0b Version: webpack 4.5.0 Time: 1308ms Built at: 2020-4-11 04:14:23 Asset Size Chunks Chunk Names app.38de904fed135db4bf0a.js 1.17 KiB app [emitted] app Entrypoint app = app.38de904fed135db4bf0a.js [./src/content.js] 62 bytes {app} [built] [./src/index.js] 80 bytes {app} [built]
接下來就是在這份配置上,作一些實踐。
html-webpack-plugin
建立 html 文件index.html
, 它會與 JS 生成在同一目錄 dist
並引入 app.38de904fed135db4bf0a.js
。yarn add html-webpack-plugin -D
安裝完成後,在 webpack.config.js
下配置 更多可選的配置項
// webpack.config.js + const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new UglifyJSPlugin(), + new HtmlWebpackPlugin({ title: 'webpack-cli' }), ]
從新執行 webpack --mode=development
, dist
目錄就會多個 index.html
並引入了 main.bundle.js
.
上面配置中的 module.rules
babel-loader 的應用
babel-loader
將 ES6* 代碼轉化爲 ES5 代碼Babel 默認只轉換新的 JavaScript 句法 (
syntax
), 而不轉換新的 API, 好比Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise
等全局對象,以及一些定義在全局對象上的方法(好比Object.assign
)都不會轉碼。舉例來講,ES6 在
Array
對象上新增了Array.from
方法。Babel
就不會轉碼這個方法。若是想讓這個方法運行,必須使用babel-polyfill
,爲當前環境提供一個墊片。—— 摘自 阮一峯 Babel 入門教程
yarn add react react-dom babel-preset-react
babel-preset-react
用於解析react
的語法;
babel-preset-env
初始化配置時已經安裝。它的前身是babel-preset-es2015/es2016/es2017
之後要用新特性這個包就能夠搞定一切。
安裝完成,修改 src/index.js
的內容爲
import React from 'react'; import { render } from 'react-dom'; render(<h1>Hello world!</h1>, document.querySelector('#root'));
把 webpack.config.js module.rules
babel-loader
配置 presets
刪掉。
在項目根目錄新建 .babelrc
文件,內容以下
// .babelrc { "presets": [ "env", "react" ] }
// webpack.config.js plugins: [ new HtmlWebpackPlugin({ + template: './index.html' // 添加模版文件 }), ]
// index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Webpack4-react16</title> </head> <body> <div id="root" /> </html>
再次執行 webpack --mode=development
, ok!
yarn add webpack-dev-server -D
打開 package.json
添加構建腳本
--open
自動打開瀏覽器並定向至 http://localhost:8080/
"scripts": { "dev": "webpack-dev-server --mode=development --open --hot" "//": "webpack-dev-server --mode=development --host 0.0.0.0" "//": "使用本機 IP 訪問項目 [Your IP]:8080 => 192.168.0.111:8080" },
執行 yarn dev
, 自動刷新完成。
即在不重載頁面的狀況下,實時替換更新修改的模塊。提升開發效率。
本文使用 React, 因此用 react-hot-loader
yarn add react-hot-loader -D
項目根目錄下新建文件 .babelrc
, 添加內容:
{ + "plugins": ["react-hot-loader/babel"] }
在 src
目錄下添加文件 App.js
// src/App.js import React from 'react'; import { hot } from 'react-hot-loader'; const App = () => <div>Hello World!</div>; export default hot(module)(App)
應用入口引入 App.js
// src/index.js import React from 'react'; import { render } from 'react-dom'; import App from './App'; render(<App />, document.querySelector('#root'));
從新執行 yarn dev
, 修改下 App.js
的代碼,注意看瀏覽器與 console
.
[HMR] - ./src/App.js log.js:24 [HMR] App is up to date.
若是 hot(module)(App)
與 render
一個文件則會收到警告
在 4.x 版本以前,用的是
extract-text-webpack-plugin
,不過 webpack@4.3.0 不支持使用。
yarn add mini-css-extract-plugin -D
// module.rules { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { } } ] } plugins: [ new MiniCssExtractPlugin({ filename: "[name].[contenthash].css", chunkFilename: "[id].[contenthash].css" }) ],
yarn add react-loadable yarn add babel-preset-stage-2 -D // for 動態 import() 語法
import Loadable from 'react-loadable'; const Loading = () => 'Loading...'; const Home = Loadable({ loader: () => import('./Home'), loading: Loading });
效果如圖
按需加載OK,不過發現個問題,這個 Header
組件被多處調用,樣式&JS都存在屢次加載。
接下來要作的就是把共用的代碼提取出來。
配置以下
// webpack.config.js optimization: { splitChunks: { cacheGroups: { commons: { name: 'commons', priority: 10, chunks: 'initial' }, styles: { name: 'styles', test: /\.css$/, chunks: 'all', minChunks: 2, enforce: true } } } }
entry: { app: './src/index.js', + ramda: ['ramda'], } new HtmlWebpackPlugin({ template: './index.html', + chunks: ['app', 'commons', 'ramda'] }) 2.e9dc7e430f6a31c868b2.css 45 bytes 2 [emitted] app.bundle.js 9.6 KiB app [emitted] app 0.decbf5b19337a4ce4aac.css 61 bytes 0 [emitted] 0.bundle.js 4.01 KiB 0 [emitted] + ramda.bundle.js 7.99 KiB ramda [emitted] ramda index.html 393 bytes [emitted]
yarn add antd yarn add less less-loader babel-plugin-import -D
// .babelrc 添加 { "plugins": [ [ "import", { "style": true, "libraryName": "antd" } ] ] }
// webpack.config.js module.rules 添加 { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'less-loader', options: { sourceMap: true, javascriptEnabled: true, modifyVars: { 'primary-color': '#531dab' } } } ] }
display: -webkit-box; display: -ms-flexbox; display: flex;
yarn add autoprefixer postcss-loader -D
項目根目錄新建 postcss.config.js
// postcss.config.js module.exports = { plugins: [ require('autoprefixer')({ 'browsers': ['> 1%', 'last 2 versions'] }) ] };
// webpack.config.js module.rules { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', + 'postcss-loader' ] }
Demo: webpack4-react16-react-router4
Uncaught Error: [HMR] Hot Module Replacement is disabled.
運行
webpack-dev-server --mode=development
報錯。
把 webpack.config.js devSever
hot: true, inline: true
刪掉,
添加 webpack-dev-server --mode=development --hot --inline
或者
plugins: [ + new webpack.HotModuleReplacementPlugin(), ]
ERROR in 0.js from UglifyJs TypeError: Cannot read property 'sections' of null
TypeError: Cannot read property 'sections' of null 👉 Remove `new UglifyJsPlugin` from plugins part schema id ignored LoaderOptionsPlugin 👉 Remove `new LoaderOptionsPlugin` plugin from config schema id ignored SourceMapDevToolPlugin 👉 Remove `devtool` config
ERROR in chunk app [entry] [name].[chunkhash].js
Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode
A tale of Webpack 4 and how to finally configure it in the right way
webpack 4: mode and optimization
webpack split chunks
webpack init
Webpack HMR 原理解析
精讀《webpack4.0 升級指南》
擠時間敲了幾天,教程終於告一段落!後續還會繼續完善其餘的配置(HappyPack, DllReferencePlugin...)實踐; 本文若有錯誤,歡迎指正,很是感謝。