webpack4.0 入門實(shí)踐教程
webpack 可以看做是模塊打包機(jī):他做的事情是,分析你的項(xiàng)目結(jié)構(gòu),找到 JavaScript
模塊以及其他的一些瀏覽器不能直接運(yùn)行的擴(kuò)展語(yǔ)言( Scss
、 TypeScript
等),將其打包為合適的格式以供瀏覽器使用
構(gòu)建就是把源代碼轉(zhuǎn)換成發(fā)布到線上可執(zhí)行的 JavaScript
、CSS、HTML 代碼,包括以下內(nèi)容:
- 代碼轉(zhuǎn)換 :
TypeScript
編譯成JavaScript
、SCSS
編譯成 CSS 等等 文件優(yōu)化 :壓縮JavaScript
、CSS、HTML 代碼,壓縮合并圖片等 - 代碼分割 :提取多個(gè)頁(yè)面的公共代碼、提取首屏不需要執(zhí)行部分的代碼讓其異步加載
- 模塊合并 :在采用模塊化的項(xiàng)目有很多模塊和文件,需要構(gòu)建功能把模塊分類合并成一個(gè)文件
- 自動(dòng)刷新 :監(jiān)聽本地源代碼的變化,自動(dòng)構(gòu)建,刷新瀏覽器
- 代碼校驗(yàn) :在代碼被提交到倉(cāng)庫(kù)前需要檢測(cè)代碼是否符合規(guī)范,以及單元測(cè)試是否通過
- 自動(dòng)發(fā)布 :更新完代碼后,自動(dòng)構(gòu)建出線上發(fā)布代碼并傳輸給發(fā)布系統(tǒng)。
構(gòu)建其實(shí)是工程化、自動(dòng)化思想在前端開發(fā)中的體現(xiàn)。把一系列流程用代碼去實(shí)現(xiàn),讓代碼自動(dòng)化地執(zhí)行這一系列復(fù)雜的流程。
webpack 的基本概念
入口(entry point): 指示 webpack 應(yīng)該使用哪個(gè)模塊,來作為構(gòu)建其內(nèi)部依賴圖的開始,webpack 會(huì)找出有哪些模塊和 library 是入口起點(diǎn)(直接和間接)依賴的。
默認(rèn)值是 ./src/index.js
,然而,可以通過在 webpack 配置中配置 entry 屬性,來指定一個(gè)不同的入口起點(diǎn)(或者也可以指定多個(gè)入口起點(diǎn))。
出口 output: 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,主輸出文件默認(rèn)為 ./dist/main.js
,其他生成文件的默認(rèn)輸出目錄是 ./dist
loader: 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉(zhuǎn)換為 webpack 能夠處理的有效模塊,然后你就可以利用 webpack 的打包能力,對(duì)它們進(jìn)行處理。
注意,loader 能夠 import 導(dǎo)入任何類型的模塊(例如 .css 文件),這是 webpack 特有的功能,其他打包程序或任務(wù)執(zhí)行器的可能并不支持。我們認(rèn)為這種語(yǔ)言擴(kuò)展是有很必要的,因?yàn)檫@可以使開發(fā)人員創(chuàng)建出更準(zhǔn)確的依賴關(guān)系圖。
插件 plugins: loader 被用于轉(zhuǎn)換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務(wù)。插件的范圍包括,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量。插件接口功能極其強(qiáng)大,可以用來處理各種各樣的任務(wù)。
模式 mode: 通過選擇 development
或 production
之中的一個(gè),來設(shè)置 mode 參數(shù),你可以啟用相應(yīng)模式下的 webpack 內(nèi)置的優(yōu)化
webpack 構(gòu)建過程
- 從 Entry 里配置的 Module 開始遞歸解析 Entry 依賴的所有 Module。
- 每找到一個(gè) Module, 就會(huì)根據(jù)配置的 Loader 去找出對(duì)應(yīng)的轉(zhuǎn)換規(guī)則。
- 對(duì) Module 進(jìn)行轉(zhuǎn)換后,再解析出當(dāng)前 Module 依賴的 Module。
- 這些模塊會(huì)以 Entry 為單位進(jìn)行分組,一個(gè) Entry 和其所有依賴的 Module 被分到一個(gè)組也就是一個(gè) Chunk。
- 最后 Webpack 會(huì)把所有 Chunk 轉(zhuǎn)換成文件輸出。
- 在整個(gè)流程中 Webpack 會(huì)在恰當(dāng)?shù)臅r(shí)機(jī)執(zhí)行 Plugin 里定義的邏輯。
開發(fā)環(huán)境和生產(chǎn)環(huán)境
我們?cè)谌粘5那岸碎_發(fā)工作中,一般都會(huì)有兩套構(gòu)建環(huán)境:一套開發(fā)時(shí)使用,一套供線上使用。
- development : 用于開發(fā)的配置文件,用于定義
webpack dev server
和其他東西 - production : 用于生產(chǎn)的配置文件,用于定義
UglifyJSPlugin
,sourcemaps
等
簡(jiǎn)單來說,開發(fā)時(shí)可能需要打印 debug 信息,包含 sourcemap
文件,而生產(chǎn)環(huán)境是用于線上的即代碼都是壓縮后,運(yùn)行時(shí)不打印 debug 信息等。譬如 axios、antd 等我們的生產(chǎn)環(huán)境中需要使用到那么我們應(yīng)該安裝該依賴在生產(chǎn)環(huán)境中,而 webpack-dev-server
則是需要安裝在開發(fā)環(huán)境中
平時(shí)我們 npm
中安裝的文件中有 -S -D, -D 表示我們的依賴是安裝在開發(fā)環(huán)境的,而-S 的是安裝依賴在生產(chǎn)環(huán)境中。
本文就來帶你搭建基本的前端開發(fā)環(huán)境,前端開發(fā)環(huán)境需要什么呢?
- 構(gòu)建發(fā)布需要的 HTML、CSS、JS、圖片等資源
- 使用 CSS 預(yù)處理器,這里使用 less
- 配置 babel 轉(zhuǎn)碼器 => 使用 es6+
- 處理和壓縮圖片
- 配置熱加載,HMR
以上配置就可以滿足前端開發(fā)中需要的基本配置。下面是本文打包后的效果圖:
搭建基本的開發(fā)環(huán)境
安裝
mkdir webpack-dev && cd webpack-dev npm init -y npm i webpack webpack-cli -D
添加 scripts
生成了 package.json 文件,在文件中添加
"scripts": { "build": "webpack --mode production" }
-- mode
模式 (必選,不然會(huì)有 WARNING
),是 webpack4
新增的參數(shù)選項(xiàng),默認(rèn)是 production
--mode production
生產(chǎn)環(huán)境
- 提供
uglifyjs-webpack-plugin
代碼壓縮 - 不需要定義
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })
- 默認(rèn)
production
默認(rèn)開啟NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors
, 編譯出錯(cuò)時(shí)跳過輸出,以確保輸出資源不包含錯(cuò)誤 - 默認(rèn)開啟
ModuleConcatenationPlugin
->optimization.concatenateModules
,webpack3
添加的作用域提升(Scope Hoisting
)
--mode development
開發(fā)環(huán)境
- 使用 eval 構(gòu)建 module, 提升增量構(gòu)建速度
- 不需要定義
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
默認(rèn)development
- 默認(rèn)開啟
NamedModulesPlugin -> optimization.namedModules
使用模塊熱替換(HMR)時(shí)會(huì)顯示模塊的相對(duì)路徑
添加了 scripts 之后,新建 src/index.js
,然后執(zhí)行 npm run build
,你就會(huì)發(fā)現(xiàn)新增了一個(gè) dist
目錄,里邊存放的是 webpack 構(gòu)建好的 main.js
文件。
新建 webpack.config.js 文件
要想對(duì) webpack 中增加更多的配置信息,我們需要建立一個(gè) webpack 的配置文件。在根目錄下創(chuàng)建 webpack.config.js
后再執(zhí)行 webpack
命令,webpack 就會(huì)使用這個(gè)配置文件的配置了
配置中具備以下的基本信息:
module.exports = { entry: '', // 打包入口:指示 webpack 應(yīng)該使用哪個(gè)模塊,來作為構(gòu)建其內(nèi)部依賴圖的開始 output: '', // 出口 resolve: {}, // 配置解析:配置別名、extensions 自動(dòng)解析確定的擴(kuò)展等等 devServer: {}, // 開發(fā)服務(wù)器:run dev/start 的配置,如端口、proxy等 module: {}, // 模塊配置:配置loader(處理非 JavaScript 文件,比如 less、sass、jsx、圖片等等)等 plugins: [] // 插件的配置:打包優(yōu)化、資源管理和注入環(huán)境變量 }
配置打包入口和出口
首先我們往 webpack.config.js
添加點(diǎn)配置信息
const path = require('path') module.exports = { // 指定打包入口 entry: './src/index.js', // 打包出口 output: { path: path.resolve(__dirname, 'dist'), // 解析路徑為 ./dist filename: 'bundle.js' } }
上面我們定義了打包入口 ./src/index.js
,打包出口為 ./dist
, 打包的文件夾名字為 bundle.js
,執(zhí)行 npm run build
命令后,index.js 文件會(huì)被打包為 bundle.js
文件。此時(shí)隨便建立一個(gè) html 文件引用這個(gè) bundle.js
就可以看到你在 index.js
寫的代碼了。
path.resolve([...paths]) 方法會(huì)把一個(gè)路徑或路徑片段的序列解析為一個(gè)絕對(duì)路徑。
使用 html-webpack-plugin 創(chuàng)建 html 文件
更多情況下我們不希望打包一次,就新建一次 html 文件來引用打包后的文件,這樣顯得不智能或者說當(dāng)你打包的文件名修改后,引用路徑就會(huì)出錯(cuò)。
這個(gè)時(shí)候我們就可以使用html-webpack-plugin 插件來將 HTML 引用路徑和我們的構(gòu)建結(jié)果關(guān)聯(lián)起來。
npm install html-webpack-plugin -D
創(chuàng)建文件 public/index.html
修改 webpack.config.js
文件
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { //... plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', // 配置輸出文件名和路徑 template: './public/index.html' // 配置要被編譯的html文件 }) ] }
重新執(zhí)行 npm run build
, dist 目錄就會(huì)多個(gè) index.html
并引入了 bundle.js
.
壓縮 html 文件
修改 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { //... plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', // 配置輸出文件名和路徑 template: './public/index.html', // 配置要被編譯的html文件 hash: true, // 壓縮 => production 模式使用 minify: { removeAttributeQuotes: true, //刪除雙引號(hào) collapseWhitespace: true //折疊 html 為一行 } }) ] }
打包 css 文件
我們希望使用 webpack 來進(jìn)行構(gòu)建 css 文件,,為此,需要在配置中引入 loader 來解析和處理 CSS 文件:
npm install style-loader css-loader -D
新建 src/assets/style/color.css
, 修改 webpack.config.js
文件:
module.exports = { //... module: { /** * test: 匹配特定條件。一般是提供一個(gè)正則表達(dá)式或正則表達(dá)式的數(shù)組 * include: 匹配特定條件。一般是提供一個(gè)字符串或者字符串?dāng)?shù)組 * exclude: 排除特定條件 * and: 必須匹配數(shù)組中的所有條件 * or: 匹配數(shù)組中任何一個(gè)條件, * nor: 必須排除這個(gè)條件 */ rules: [ { test: /\.css$/, include: [path.resolve(__dirname, 'src')], use: ['style-loader', 'css-loader'] } ] } //... }
經(jīng)由上述兩個(gè) loader 的處理后,CSS 代碼會(huì)轉(zhuǎn)變?yōu)?JS, 如果需要單獨(dú)把 CSS 文件分離出來,我們需要使用 mini-css-extract-plugin 插件
抽取 css 到獨(dú)立文件, 自動(dòng)添加前綴
npm i mini-css-extract-plugin postcss-loader autoprefixer -D
我們?cè)趯?css 時(shí)不免要考慮到瀏覽器兼容問題,如 transform
屬性,需要添加瀏覽器前綴以適配其他瀏覽器。故使用到 postcss-loader
這個(gè) loader, 下面則是相關(guān)的配置
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { // ... module: { rules: [ { test: /\.css$/, include: [path.resolve(__dirname, 'src')], use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] } } ] } ] }, plugins: [ //... new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
打包 less 文件
開發(fā)中通常會(huì)用到一門預(yù)處理語(yǔ)言,這里以 less
為例,通過 less-loader
可以打包 less 為 css 文件
npm install less less-loader -D
新建 src/assets/style/index.less
, 并且在 src/index.js
中引入 import './assets/style/index.less'
配置 webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { module: { rules: [ // ... { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] // 添加css中的瀏覽器前綴 } }, 'less-loader' ] } ] } //... }
執(zhí)打包命令后就可以發(fā)現(xiàn) index.less
中寫的樣式會(huì)和 color.css
一樣被打包進(jìn) main.css
中。
webpack@v4 升級(jí)踩坑: 關(guān)于使用 mini-css-extract-plugin
的注意點(diǎn)。
打包圖片
npm install file-loader url-loader -D
file-loader:可以用于處理很多類型的文件,它的主要作用是直接輸出文件,把構(gòu)建后的文件路徑返回。
url-loader:如果圖片較多,會(huì)發(fā)很多 http 請(qǐng)求,會(huì)降低頁(yè)面性能。 url-loader
會(huì)將引入的圖片編碼,生成 dataURl。相當(dāng)于把圖片數(shù)據(jù)翻譯成一串字符。再把這串字符打包到文件中,最終只需要引入這個(gè)文件就能訪問圖片了。當(dāng)然,如果圖片較大,編碼會(huì)消耗性能。因此 url-loader
提供了一個(gè) limit 參數(shù),小于 limit 字節(jié)的文件會(huì)被轉(zhuǎn)為 DataURl,大于 limit 的還會(huì)使用 file-loader
進(jìn)行 copy。
- url-loader 可以看作是增強(qiáng)版的 file-loader。
- url-loader 把圖片編碼成 base64 格式寫進(jìn)頁(yè)面,從而減少服務(wù)器請(qǐng)求。
module.exports = { module: { rules: [ // ... { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { outputPath: 'images/', //輸出到images文件夾 limit: 500 //是把小于500B的文件打成Base64的格式,寫入JS } } ] } ] } //... }
url-loader 和 file-loader 是什么關(guān)系呢?
簡(jiǎn)單地說, url-loader
封裝了 file-loader
。 url-loader
不依賴于 file-loader
,即使用 url-loader
時(shí),只需要安裝 url-loader
即可,不需要安裝 file-loader
,因?yàn)?url-loader
內(nèi)置了 file-loader
。
通過上面的介紹,我們可以看到,url-loader 工作分兩種情況:
- 文件大小小于 limit 參數(shù),url-loader 將會(huì)把文件轉(zhuǎn)為 DataURL;
- 文件大小大于 limit,url-loader 會(huì)調(diào)用 file-loader 進(jìn)行處理,參數(shù)也會(huì)直接傳給 file-loader。因此我們只需要安裝 url-loader 即可。
有關(guān) url-loader
和 file-loader
的解析:webpack 之圖片引入-增強(qiáng)的 file-loader:url-loader
配置 babel
babel-loader
Babel
是一個(gè)讓我們能夠使用 ES 新特性的 JS 編譯工具,我們可以在 webpack 中配置 Babel,以便使用 ES6、ES7 標(biāo)準(zhǔn)來編寫 JS 代碼。
Babel 7 的相關(guān)依賴包需要加上 @babel
scope。一個(gè)主要變化是 presets 設(shè)置由原來的 env
換成了 @babel/preset-env
, 可以配置 targets
, useBuiltIns
等選項(xiàng)用于編譯出兼容目標(biāo)環(huán)境的代碼。其中 useBuiltIns
如果設(shè)為 "usage"
,Babel 會(huì)根據(jù)實(shí)際代碼中使用的 ES6/ES7 代碼,以及與你指定的 targets,按需引入對(duì)應(yīng)的 polyfill
,而無(wú)需在代碼中直接引入 import '@babel/polyfill'
,避免輸出的包過大,同時(shí)又可以放心使用各種新語(yǔ)法特性。
npm i babel-loader @babel/core @babel/preset-env -D
筆者這里配的版本號(hào)如下
{ "babel-loader": "^8.0.4", "@babel/core": "^7.1.2", "@babel/preset-env": "^7.1.0" }
babel-loader: 用 babel 轉(zhuǎn)換 ES6 代碼需要使用到 babel-loader
@babel-preset-env: 默認(rèn)情況下是等于 ES2015 + ES2016 + ES2017,也就是說它對(duì)這三個(gè)版本的 ES 語(yǔ)法進(jìn)行轉(zhuǎn)化。
@babel/core:babel 核心庫(kù)
根目錄下新建 .babelrc
文件
{ "presets": [ [ "@babel/preset-env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] }, "useBuiltIns": "usage" } ] ] }
- presets 是一堆 plugins 的預(yù)設(shè),起到方便的作用。
- plugins 是編碼轉(zhuǎn)化工具,babel 會(huì)根據(jù)你配置的插件對(duì)代碼進(jìn)行相應(yīng)的轉(zhuǎn)化。
修改 webpack.config.js
module.exports = { module: { rules: [ //... { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader' } } ] } }
babel/polyfill 和 transform-runtime
Babel 默認(rèn)只轉(zhuǎn)換新的 JavaScript 句法(syntax),而不轉(zhuǎn)換新的 API ,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局對(duì)象,以及一些定義在全局對(duì)象上的方法(比如 Object.assign)都不會(huì)轉(zhuǎn)碼。
babel-polyfill: 如上述所說,對(duì)于新的 API,你可能需要引入 babel-polyfill 來進(jìn)行兼容
關(guān)鍵點(diǎn)
- babel-polyfill 是為了模擬一個(gè)完整的 ES2015+環(huán)境,旨在用于應(yīng)用程序而不是庫(kù)/工具。
- babel-polyfill 會(huì)污染全局作用域
babel-runtime 的作用:
- 提取輔助函數(shù) 。ES6 轉(zhuǎn)碼時(shí),babel 會(huì)需要一些輔助函數(shù),例如 _extend。babel 默認(rèn)會(huì)將這些輔助函數(shù)內(nèi)聯(lián)到每一個(gè) js 文件里, babel 提供了 transform-runtime 來將這些輔助函數(shù)“搬”到一個(gè)單獨(dú)的模塊 babel-runtime 中,這樣做能減小項(xiàng)目文件的大小。
- 提供 polyfill :不會(huì)污染全局作用域,但是不支持實(shí)例方法如 Array.includes
babel-runtime
更像是分散的 polyfill 模塊,需要在各自的模塊里單獨(dú)引入,借助 transform-runtime
插件來自動(dòng)化處理這一切,也就是說你不要在文件開頭 import 相關(guān)的 polyfill
,你只需使用, transform-runtime
會(huì)幫你引入。
對(duì)于開發(fā)應(yīng)用來說,直接使用上述的按需 polyfill
方案是比較方便的,但如果是開發(fā)工具、庫(kù)的話,這種方案未必適合( babel-polyfill
是通過向全局對(duì)象和內(nèi)置對(duì)象的 prototype
上添加方法實(shí)現(xiàn)的,會(huì)造成全局變量污染)。Babel 提供了另外一種方案 transform-runtime
,它在編譯過程中只是將需要 polyfill
的代碼引入了一個(gè)指向 core-js
中對(duì)應(yīng)模塊的鏈接(alias)。關(guān)于這兩個(gè)方案的具體差異和選擇,可以自行搜索相關(guān)教程,這里不再展開,下面提供一個(gè) transform-runtime
的參考配置方案。
首先安裝 runtime 相關(guān)依賴
npm i @babel/plugin-transform-runtime -D npm i @babel/runtime -S
修改 .babelrc
{ //... "plugins": ["@babel/plugin-transform-runtime"] }
打包前清理源目錄文件 clean-webpack-plugin
每次打包,都會(huì)生成項(xiàng)目的靜態(tài)資源,隨著某些文件的增刪,我們的 dist 目錄下可能產(chǎn)生一些不再使用的靜態(tài)資源,webpack 并不會(huì)自動(dòng)判斷哪些是需要的資源,為了不讓這些舊文件也部署到生產(chǎn)環(huán)境上占用空間,所以在 webpack 打包前最好能清理 dist 目錄。
npm install clean-webpack-plugin -D
修改 webpack.config.js
文件
const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { plugins: [new CleanWebpackPlugin(['dist'])] }
提取公用代碼
假如你 a.js
和 b.js
都 import 了 c.js
文件,這段代碼就冗雜了。為什么要提取公共代碼,簡(jiǎn)單來說,就是減少代碼冗余,提高加載速度。
module.exports = { //... optimization: { splitChunks: { cacheGroups: { commons: { // 抽離自己寫的公共代碼 chunks: 'initial', name: 'common', // 打包后的文件名,任意命名 minChunks: 2, //最小引用2次 minSize: 0 // 只要超出0字節(jié)就生成一個(gè)新包 }, styles: { name: 'styles', // 抽離公用樣式 test: /\.css$/, chunks: 'all', minChunks: 2, enforce: true }, vendor: { // 抽離第三方插件 test: /node_modules/, // 指定是node_modules下的第三方包 chunks: 'initial', name: 'vendor', // 打包后的文件名,任意命名 // 設(shè)置優(yōu)先級(jí),防止和自定義的公共代碼提取時(shí)被覆蓋,不進(jìn)行打包 priority: 10 } } } } }
hash
hash 是干嘛用的? 我們每次打包出來的結(jié)果可能都是同一個(gè)文件,那我上線的時(shí)候是不是要替換掉上線的 js,那我怎么知道哪是最新的呢,我們一般會(huì)清一下緩存。而 hash 就是為了解決這個(gè)問題而存在的
我們此時(shí)在改一些 webpack.config.js 的配置
module.exports = { //... output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[hash:8].js' }, //... plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash:8].css', chunkFilename: '[id].[hash:8].css' }) ] }
減少 resolve 的解析,配置別名
如果我們可以精簡(jiǎn) resolve
配置,讓 webpack
在查詢模塊路徑時(shí)盡可能快速地定位到需要的模塊,不做額外的查詢工作,那么 webpack
的構(gòu)建速度也會(huì)快一些
module.exports = { resolve: { /** * alias: 別名的配置 * * extensions: 自動(dòng)解析確定的擴(kuò)展, * 比如 import 'xxx/theme.css' 可以在extensions 中添加 '.css', 引入方式則為 import 'xxx/theme' * @default ['.wasm', '.mjs', '.js', '.json'] * * modules 告訴 webpack 解析模塊時(shí)應(yīng)該搜索的目錄 * 如果你想要添加一個(gè)目錄到模塊搜索目錄,此目錄優(yōu)先于 node_modules/ 搜索 * 這樣配置在某種程度上可以簡(jiǎn)化模塊的查找,提升構(gòu)建速度 @default node_modules 優(yōu)先 */ alias: { '@': path.resolve(__dirname, 'src'), tool$: path.resolve(__dirname, 'src/utils/tool.js') // 給定對(duì)象的鍵后的末尾添加 $,以表示精準(zhǔn)匹配 }, extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx'], modules: [path.resolve(__dirname, 'src'), 'node_modules'] } }
webpack-dev-serve
上面講到了都是如何打包文件,但是開發(fā)中我們需要一個(gè)本地服務(wù),這時(shí)我們可以使用 webpack-dev-server
在本地開啟一個(gè)簡(jiǎn)單的靜態(tài)服務(wù)來進(jìn)行開發(fā)。
webpack-dev-server
是 webpack 官方提供的一個(gè)工具,可以基于當(dāng)前的 webpack 構(gòu)建配置快速啟動(dòng)一個(gè)靜態(tài)服務(wù)。當(dāng) mode
為 development
時(shí),會(huì)具備 hot reload
的功能,即當(dāng)源碼文件變化時(shí),會(huì)即時(shí)更新當(dāng)前頁(yè)面,以便你看到最新的效果。...
npm install webpack-dev-server -D
package.json 中 scripts 中添加
"start": "webpack-dev-server --mode development"
默認(rèn)開啟一個(gè)本地服務(wù)的窗口 http://localhost:8080/ 便于開發(fā)
配置開發(fā)服務(wù)器
我們可以對(duì) webpack-dev-server
做針對(duì)性的配置
module.exports = { // 配置開發(fā)服務(wù)器 devServer: { port: 1234, open: true, // 自動(dòng)打開瀏覽器 compress: true // 服務(wù)器壓縮 //... proxy、hot } }
- contentBase: 服務(wù)器訪問的根目錄(可用于訪問靜態(tài)資源)
- port: 端口
- open: 自動(dòng)打開瀏覽器
模塊熱替換(hot module replacement)
模塊熱替換( HMR - Hot Module Replacement
)功能會(huì)在應(yīng)用程序運(yùn)行過程中替換、添加或刪除模塊,而無(wú)需重新加載整個(gè)頁(yè)面。主要是通過以下幾種方式,來顯著加快開發(fā)速度:
- 保留在完全重新加載頁(yè)面時(shí)丟失的應(yīng)用程序狀態(tài)。
- 只更新變更內(nèi)容,以節(jié)省寶貴的開發(fā)時(shí)間。
- 調(diào)整樣式更加快速 - 幾乎相當(dāng)于在瀏覽器調(diào)試器中更改樣式。
上面我們 npm start
后修改一次文件,頁(yè)面就會(huì)刷新一次。這樣就存在很大問題了,比如我們使用 redux
, vuex
等插件,頁(yè)面一刷新那么存放在 redux
, vuex
中的東西就會(huì)丟失,非常不利于我們的開發(fā)。
HMR 配合 webpack-dev-server ,首先我們配置下 webpack.config.js
const webpack = require('webpack') module.exports = { devServer: { //... hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() //... ] }
配置后還不行,因?yàn)?webpack 還不知道你要更新哪里, 修改 src/index.js
文件, 添加
if (module.hot) { module.hot.accept() }
重啟服務(wù), npm start
之后,修改引入 index.js
文件后,頁(yè)面就不會(huì)重新刷新了,這便實(shí)現(xiàn)了 HMR
但是但是有個(gè)問題是,你修改 css/less 等樣式文件并未發(fā)生改變, what ?
HMR 修改樣式表 需要借助于 style-loader
, 而我們之前用的是 MiniCssExtractPlugin.loader
, 這也好辦,修改其中一個(gè) rules 就可以了,我們可以試試改
module.exports = { module: { rules: [ { test: /\.less$/, use: [ // MiniCssExtractPlugin.loader, 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] // 添加css中的瀏覽器前綴 } }, 'less-loader' ] } ] } }
這樣我們修改 less 文件就會(huì)發(fā)現(xiàn) HMR 已經(jīng)實(shí)現(xiàn)了。
其實(shí),我們可以發(fā)現(xiàn),dev 下配置的 loader 為 style-loader
, 而生產(chǎn)環(huán)境下則是需要 MiniCssExtractPlugin.loader
這就涉及到了不同環(huán)境之間的配置??梢酝ㄟ^ process.env.NODE_ENV
獲取當(dāng)前是開發(fā)環(huán)境或者是生產(chǎn)環(huán)境,然后配置不同的 loader,這里就不做展開了。下一篇文章打算在做一個(gè) react-cli
或者 vue-cli
的配置,將開發(fā)環(huán)境的配置與生產(chǎn)環(huán)境的配置分開為不同的文件。
結(jié)語(yǔ)
前面講到的知識(shí)都是 webpack 的一些基礎(chǔ)的知識(shí),更多的資料可以查詢webpack 中文官網(wǎng),官網(wǎng)講的比較詳細(xì),我這里也是講最常的配置,也是一篇入門系列的文章,文中涉及的知識(shí)點(diǎn)還有很多地方還需要完善,譬如 優(yōu)化 webpack 的構(gòu)建速度, 減小打包的體積等等。
學(xué)習(xí) webpack 4.0
還需要多實(shí)踐,多瞎搞,筆者也是剛剛學(xué)習(xí) webpack 的配置,不對(duì)之處請(qǐng)各位指出。
下一篇文章打算從零配置一個(gè)腳手架,以加深自己對(duì) webpack 的理解。
本文產(chǎn)生的代碼: webpack-dev
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用html+js+css 實(shí)現(xiàn)頁(yè)面輪播圖效果(實(shí)例講解)
下面小編就為大家?guī)硪黄褂胔tml+js+css 實(shí)現(xiàn)頁(yè)面輪播圖效果(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09js設(shè)置document.domain實(shí)現(xiàn)跨域的注意點(diǎn)分析
這篇文章主要介紹了js設(shè)置document.domain實(shí)現(xiàn)跨域的注意點(diǎn),較為詳細(xì)的分析了document.domain跨域的相關(guān)技巧,需要的朋友可以參考下2015-05-05輕松實(shí)現(xiàn)HTML和JS之間的轉(zhuǎn)化的代碼
輕松實(shí)現(xiàn)HTML和JS之間的轉(zhuǎn)化的代碼...2007-09-09javascript 使用for循環(huán)時(shí)該注意的問題-附問題總結(jié)
所謂for循環(huán)就是重復(fù)的執(zhí)行一段代碼,for循環(huán)也是希望在創(chuàng)建循環(huán)時(shí)常會(huì)用到的工具,這篇內(nèi)容主要給大家介紹javascript 使用for循環(huán)時(shí)該注意的問題-附問題總結(jié),需要的朋友可以參考下2015-08-08TypeScript與JavaScript項(xiàng)目里引入MD5校驗(yàn)和
這篇文章主要介紹了TypeScript與JavaScript項(xiàng)目里引入MD5校驗(yàn)和,MD5校驗(yàn)和可以用于驗(yàn)證網(wǎng)絡(luò)文件傳輸?shù)耐暾砸约胺乐刮募蝗舜鄹?。下文我們就一起來學(xué)習(xí)TypeScript與JavaScript項(xiàng)目里引入MD5校驗(yàn)和_MD5校驗(yàn),需要的朋友可以參考一下2022-02-02使用Plupload實(shí)現(xiàn)直接上傳附件至七牛云存儲(chǔ)
這篇文章主要介紹了使用Plupload實(shí)現(xiàn)直接上傳附件至七牛云存儲(chǔ),需要的朋友可以參考下2014-12-12