Webpack
用于前端工程化代碼的底層構(gòu)建坪哄,angular奸腺、react、vue三大框架都采用了webpack來(lái)做構(gòu)建诵肛。webpack具備的功能:tree shaking、懶加載疆栏、代碼分割等等曾掂。其核心定義是一個(gè)模塊打包工具;
能夠識(shí)別所有模塊引入方式壁顶,例如:
- es6 module import引入珠洗;
- commonJs 中module.exports=Header;
- AMD若专;
- CMD许蓖;
Webpack 4
的構(gòu)建速度更快,在構(gòu)建大型的項(xiàng)目時(shí)调衰,甚至可以提升90%
的構(gòu)建速度膊爪。Nodejs和Webpack版本盡可能用最新穩(wěn)定(LTS)版本,也可以提高打包速度嚎莉;
會(huì)涉及的知識(shí)點(diǎn)如下圖:
安裝
新建一個(gè)米酬,webpack01
文件夾,并cd webpack01
進(jìn)入文件夾趋箩,安裝好需要的包webpack和webpack-cli赃额,并指定版本安裝(新版本尚未研究差異,暫且用老版本)叫确,其中webpack-cli
用于實(shí)現(xiàn)命令行功能跳芳。
當(dāng)沒(méi)有使用-g
進(jìn)行全局安裝時(shí),執(zhí)行webpack -v
會(huì)提示命令不存在竹勉;需要使用npx webpack -v
來(lái)執(zhí)行飞盆,npx
會(huì)進(jìn)入到項(xiàng)目中的node_module
目錄,尋找到webpack
包來(lái)執(zhí)行;
//不推薦全局安裝-g
npm i webpack@4.43.0 webpack-cli@3.3.12 --save-dev
npm i webpack@4.43.0 webpack-cli@3.3.12-D
設(shè)置淘寶鏡像
在根目錄下創(chuàng)建.npmrc
配置文件:
registry=https://registry.npm.taobao.rog/
其他方法:
1次乓、npm config set registry https://registry.npm.taobao.org
2吓歇、npm config set registry https://registry.npm.taobao.org/
重置webpack打包命令**
修改package.json文件的script屬性值,執(zhí)行npm run dev
命令時(shí)票腰,等同于npx webpack
"scripts": {
"dev":"npx webpack",
...
},
看一個(gè)入門(mén)小例子
-
目錄結(jié)構(gòu)
image.png -
代碼
index.html
的代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首頁(yè)</title>
</head>
<body>
<div id="root"></div>
<script src="./index.js"></script>
</body>
</html>
index.js
的代碼
import Header from "./header";
import Content from "./content";
import Footer from "./footer";
new Header();
new Content();
new Footer();
header.js
的代碼
function Header() {
var dom = document.getElementById("root");
var header = document.createElement("div");
header.innerText = "header";
dom.append(header);
}
export default Header;
content.js
的代碼
function Content() {
var dom = document.getElementById("root");
var content = document.createElement("div");
content.innerText = "content";
dom.append(content);
}
export default Content;
footer.js
的代碼
function Footer() {
var dom = document.getElementById("root");
var footer = document.createElement("div");
footer.innerText = "footer";
dom.append(footer);
}
export default Footer;
-
運(yùn)行
執(zhí)行命令npx webpack index.js
完成后照瘾,會(huì)在項(xiàng)目根目錄下生成一個(gè)dist
目錄,里面 有個(gè)main.js
文件丧慈。打開(kāi)index.html
,頁(yè)面顯示如下:
image.png
Webpack默認(rèn)配置文件webpack.config.js
1. 入口(entry)
當(dāng)沒(méi)有創(chuàng)建webpack.config.js
文件,或者此文件中為空時(shí)逃默,直接執(zhí)行npx webpack
鹃愤,終端控制臺(tái)報(bào)錯(cuò);寫(xiě)入如下代碼:
module.exports = {
entry: "./index.js"http://等同于{main:"./index.js"}完域,會(huì)在輸出目錄中打包生成main.js文件软吐。
};
再次執(zhí)行npx webpack
,則在根目錄生成'/dist/main.js'吟税,效果等同于npx webpack index.js
凹耙。
2. 出口(output)
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
執(zhí)行npx webpack
,生成文件如下:
常用配置:
- publicPath:"http://cdn.com.cn"肠仪,打包完成后肖抱,會(huì)在index.html中引入的script路徑中加上這個(gè)域名;image.png
3. mode模式
production
:默認(rèn)异旧,代碼會(huì)被壓縮
development
:代碼不會(huì)被壓縮
4. loader
webpack
不能識(shí)別非 JavaScript 文件意述,需要loader
來(lái)識(shí)別;如果看到引入的模塊的后綴不是.js
吮蛹,就需要想到用對(duì)應(yīng)的loader
來(lái)引入荤崇;loader中支持配置各種options
參數(shù),例如:file-loader的選項(xiàng)值潮针;loader的執(zhí)行順序是由右往左
的术荤。
常用的loader:
- file-loader:指示webpack發(fā)出所需的對(duì)象文件,并返回其公共URL;可用于加載iconfont中的字體文件。
-
url-loader:
url-loader
功能類似于file-loader
每篷,但是在文件大邪昶荨(單位 byte)低于指定的限制時(shí),可以返回一個(gè) DataURL雳攘。
test: /\.(png|jpg|gif)$/, use: {
loader: "url-loader",
options: {
name: 'images/[name].[ext]',
limit: 8192//單位B带兜,如果不設(shè)置,則會(huì)把所有圖片都用base64
}
}
//js引入圖片需要使用require吨灭,直接寫(xiě)相對(duì)路徑無(wú)效
var img = new Image();
img.src = require('./image/img1.jpg').default;
document.getElementById("main").appendChild(img);
- style-loader:將css文件中的內(nèi)容解析提取出來(lái)刚照,并掛載到head中style里面;
-
css-loader:解釋
@import
和url()
喧兄,會(huì)import/require()
后再解析(resolve)它們无畔。例如css文件中使用@import "./avator.css"
; -
sass-loader:加載一個(gè)SASS / SCSS文件,編譯成 CSS吠冤。安裝
npm install sass-loader node-sass --save-dev
-
postcss-loader:
PostCSS
利用JavaScript
的強(qiáng)大編程能力對(duì)CSS
代碼進(jìn)行轉(zhuǎn)換浑彰。數(shù)以百計(jì)的PostCSS
插件可以用來(lái)為CSS
屬性添加特定于瀏覽器廠商的前綴、壓縮工具cssnano拯辙、支持未來(lái) CSS 語(yǔ)法郭变、模塊颜价。如果給class添加廠商前綴 -
babel-loader:用來(lái)處理ES6語(yǔ)法,將其編譯為瀏覽器可以執(zhí)行的js語(yǔ)法诉濒;使用
userBuildIns:'usage'
配置周伦,可以有效減少打包文件大小(只打包用到的es6語(yǔ)法)未荒。使用target
設(shè)定目標(biāo)瀏覽器版本专挪,打包的時(shí)候就會(huì)對(duì)這些版本下支持的es6語(yǔ)法免打包;
再結(jié)合"@babel/polyfill"
片排,可以完全解決es6在老瀏覽器中運(yùn)行問(wèn)題寨腔,但是打包文件也會(huì)因此而變大,因?yàn)橄到y(tǒng)會(huì)把很多老瀏覽器不支持的方法率寡,以自定義代碼的形式打包到文件中迫卢。所以要使用useBuiltIns:'usage'
,設(shè)置如果用到了就打包勇劣,沒(méi)用到就不打包靖避。高階前端工程師需要學(xué)習(xí)babel各種配置,了解抽象語(yǔ)法樹(shù)比默。
如果是寫(xiě)的項(xiàng)目代碼幻捏,那么可以用babel+@babel/polyfill來(lái);如果是發(fā)布上線的組件或插件命咐,就不能用這種方式篡九,會(huì)污染全局。需要改用
@babel/plugin-transform-runtime
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"
}]
]
}
},
babel-loader
中options
配置項(xiàng)的代碼也可以單獨(dú)放入跟目錄下的.babelrc
文件中醋奠,執(zhí)行效果是一樣的榛臼。
//vue init webpack my-project腳手架項(xiàng)目默認(rèn).babelrc文件代碼
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
5. plugins
插件目的在于解決 loader
無(wú)法實(shí)現(xiàn)的其他事;可以在webpack
運(yùn)行到某個(gè)時(shí)刻的時(shí)候窜司,幫你做一些事情沛善;類似于生命周期鉤子;例如html-webpack-plugin
查看塞祈,就是在webpack
打包完成的時(shí)刻金刁,自動(dòng)生成一個(gè)index.html
到打包目錄中去。
常用plugins:
- html-webpack-plugin:自動(dòng)生成一個(gè)HTML文件
- clean-webpack-plugin:清空打包目錄
- webpack.ProvidePlugin议薪,例如引入jquery
//安裝
npm install jquery --save-dev
//引用尤蛮,修改webpack.config.js文件。
const webpack = require('webpack')
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
//使用
$("#header").html("init");
6. sourceMap
devtool
用于打包前和打包后文件代碼位置映射斯议,方便代碼出錯(cuò)時(shí)定位到源代碼位置产捞;
當(dāng) webpack 打包源代碼時(shí),可能會(huì)很難追蹤到錯(cuò)誤和警告在源代碼中的原始位置哼御。例如坯临,如果將三個(gè)源文件(a.js
, b.js
和 c.js
)打包到一個(gè) bundle(bundle.js
)中焊唬,而其中一個(gè)源文件包含一個(gè)錯(cuò)誤,那么堆棧跟蹤就會(huì)簡(jiǎn)單地指向到 bundle.js
看靠。這并通常沒(méi)有太多幫助求晶,因?yàn)槟憧赡苄枰獪?zhǔn)確地知道錯(cuò)誤來(lái)自于哪個(gè)源文件。
為了更容易地追蹤錯(cuò)誤和警告衷笋,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼矩屁。如果一個(gè)錯(cuò)誤來(lái)自于 b.js
辟宗,source map 就會(huì)明確的告訴你。 devtool詳細(xì)配置吝秕、JavaScript Source Map 詳解
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'cheap-module-eval-source-map', //調(diào)試版本建議用
//devtool: 'cheap-module-source-map', //打包后版本建議用
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
7. devServer
用于調(diào)試階段項(xiàng)目熱重載泊脐;
- a.安裝;
npm i webpack-dev-server -D
- b.修改
webpack.config.js
devServer: {
contentBase: path.join(__dirname, "dist"),//
compress: true,
open: true,
port: 9000
},
- c.修改
package.json
"scripts": {
"watch": "webpack --watch",//觀察者模式烁峭。源代碼更改后容客,dist中代碼會(huì)自動(dòng)打包;
"start": "webpack-dev-server"http://npm run start啟動(dòng)后约郁,會(huì)自動(dòng)打開(kāi)一個(gè)端口為9000的頁(yè)面缩挑,并且源代碼修改后,頁(yè)面會(huì)熱重載鬓梅;
}
package.json配置
- 修改打包命令
"scripts": {
"bundle": "webpack"http://可以不用寫(xiě)npx webpack供置,因?yàn)閟cript腳本命令,會(huì)優(yōu)先到當(dāng)前工程目錄中查找是否支持此命令绽快,沒(méi)找到再到全局查找芥丧;
"watch": "webpack --watch",//監(jiān)聽(tīng)模式,如發(fā)現(xiàn)源代碼改變了坊罢,則將文件自動(dòng)打包到dist目錄(頁(yè)面不會(huì)自動(dòng)刷新)
"start": "webpack-dev-server"http://自動(dòng)熱重載续担,如果發(fā)現(xiàn)域代碼改變了,則自動(dòng)打包到內(nèi)存活孩,并自定刷新瀏覽器頁(yè)面
},
執(zhí)行npm run bundle
物遇,等同于執(zhí)行npx webpack
。
附件
webpack.config.js文件代碼:
const path = require('path');
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
mode: "development",//production
devtool: 'cheap-module-eval-source-map', //調(diào)試版本建議用
plugins: [
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),//熱更新
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"
}]
]
}
},
{
test: /\.(png|jpg|gif)$/, use: {
loader: "url-loader",
options: {
name: 'images/[name].[ext]',
limit: 8192
}
}
},
{
test: /\.css$/, use: ["style-loader", "css-loader"]
},
{
test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"]
}
],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
open: true,
port: 9000,
hot:true, //改了哪個(gè)文件诱鞠,就更新哪個(gè)文件挎挖,最小化更新,提交開(kāi)發(fā)效率航夺;
hotOnly: true, //啟用熱模塊替換蕉朵,而無(wú)需頁(yè)面刷新作為構(gòu)建失敗時(shí)的回退。
proxy: {
"/api": {
target: "http://localhost:8081"http://dev環(huán)境設(shè)置代理阳掐,方便測(cè)試始衅;
}
}
},
};
package.json代碼
{
"name": "jquerytowebpack4",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.6.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"postcss-loader": "^3.0.0",
"sass-loader": "^9.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"babel-loader": "^8.1.0",
"jquery": "^3.5.1"
}
}
問(wèn)題匯總:
1冷蚂、安裝 xxx-loader成功后,打包報(bào)錯(cuò):this.getOption is not function
由于xxx-loader
版本過(guò)高導(dǎo)致汛闸,降版本就行了蝙茶,可以看npm上的版本記錄,找前個(gè)版本的穩(wěn)定版诸老;
2隆夯、配置browserslist
node項(xiàng)目中不同位置設(shè)置browserslist對(duì)postcss-loader影響的權(quán)重
前端工程基礎(chǔ)知識(shí)點(diǎn)--Browserslist (基于官方文檔翻譯)