webpack
說起 Webpack挎扰,大家都知道這是一個模塊化構(gòu)建(打包)工具寞埠,那么究竟什么是模塊化呢?
模塊化
模塊化是指解決一個復(fù)雜問題時自頂向下逐層把系統(tǒng)劃分成若干模塊的過程繁仁,有多種屬性掰曾,分別反映其內(nèi)部特性。(百度百科)
模塊化被越來越多的應(yīng)用到我們的日常生活中倘是,在我的印象中亭枷,小時候家電(比如收音機、電視)壞了都是拿到店里去找老師傅維修搀崭,現(xiàn)在家電都是模塊化的叨粘,檢測下哪里壞了直接換個新模塊就可以了,由此可見瘤睹,模塊化不僅僅是個前端概念(相反升敲,前端模塊化也是這些年剛剛被得到重視的),我們生活場景中大量的充斥著模塊化轰传,讓我們的生活效率更高冻晤。
前端模塊化一般指得是 JavaScript 的模塊,最常見的是 Nodejs 的 NPM 包绸吸,每個模塊可能是最小甚至是最優(yōu)的代碼組合鼻弧,也可能是解決某些問題有很多特定模塊組成的大模塊。如果沒有模塊化锦茁,可能大家編寫代碼當中遇見最多的就是復(fù)制(copy)攘轩,當我們需要某個功能的代碼時,自己由之前在哪個項目寫過码俩,那么我們就會 copy 過來度帮,copy 多了,自然代碼的可維護性就會下降。
我們所接觸的模塊化開發(fā)有CommonJS
和ES6 Module
規(guī)范笨篷。此外還有AMD
瞳秽、CMD
、UMD
(兼容AMD
和CMD
)
說起模塊化又不得不提到一個詞叫做組件化
組件化和模塊化的區(qū)別
Tips:大家可能也經(jīng)常聽到組件化這個名詞率翅,模塊化一般指的是可以被抽象封裝的最小/最優(yōu)代碼集合练俐,模塊化解決的是功能耦合問題;組件化則更像是模塊化進一步封裝冕臭,根據(jù)業(yè)務(wù)特點或者不同的場景封裝出具有一定功能特性的獨立整體腺晾;另外,前端提到組件化更多的是具有模板辜贵、樣式和 js 交互的 UI 組件悯蝉。
工程化
當我們開發(fā)的 Web 應(yīng)用越來越復(fù)雜的時候,會發(fā)現(xiàn)我們面臨的問題會逐漸增多:
- 模塊多了托慨,依賴管理怎么做鼻由;
- 頁面復(fù)雜度提升之后,多頁面厚棵、多系統(tǒng)嗡靡、多狀態(tài)怎么辦;
- 團隊擴大之后窟感,團隊合作怎么做;
- 怎么解決多人研發(fā)中的性能歉井、代碼風格等問題柿祈;
- 權(quán)衡研發(fā)效率和產(chǎn)品迭代的問題。
這些問題就是軟件工程需要解決的問題哩至。工程化的問題需要運用工程化工具來解決躏嚎,得益于 Nodejs 的發(fā)展,前端這些年在工程化上取得了不俗的成績菩貌。前端工程化早期卢佣,是以 Grunt、Gulp 等構(gòu)建工具為主的階段箭阶,這個階段解決的是重復(fù)任務(wù)的問題虚茶,它們將某些功能拆解成固定步驟的任務(wù),然后編寫工具來解決仇参,比如:圖片壓縮嘹叫、地址添加 hash、替換等诈乒,都是固定套路的重復(fù)你性工作罩扇。
而現(xiàn)階段的 Webpack 則更像是從一套解決 JavaScript 模塊化依賴打包開始,利用強大的插件機制,逐漸解決前端資源依賴管理問題喂饥,依附社區(qū)力量逐漸進化成一套前端工程化解決方案消约。
什么是 webpack
本質(zhì)上,Webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(static module bundler)员帮。在 Webpack 處理應(yīng)用程序時或粮,它會在內(nèi)部創(chuàng)建一個依賴圖(dependency graph),用于映射到項目需要的每個模塊集侯,然后將所有這些依賴生成到一個或多個 bundle被啼。
像 Grunt、Gulp 這類構(gòu)建工具棠枉,打包的思路是:遍歷源文件
→匹配規(guī)則
→打包
浓体,這個過程中做不到按需加載,即對于打包起來的資源辈讶,到底頁面用不用命浴,打包過程中是不關(guān)心的。
webpack 跟其他構(gòu)建工具本質(zhì)上不同之處在于:webpack 是從入口文件開始贱除,經(jīng)過模塊依賴加載生闲、分析和打包三個流程完成項目的構(gòu)建。在加載月幌、分析和打包的三個過程中碍讯,可以針對性的做一些解決方案,比如code split
(拆分公共代碼等)扯躺。
當然捉兴,Webpack 還可以輕松的解決傳統(tǒng)構(gòu)建工具解決的問題:
- 模塊化打包,一切皆模塊录语,JS 是模塊倍啥,CSS 等也是模塊;
- 語法糖轉(zhuǎn)換:比如 ES6 轉(zhuǎn) ES5澎埠、TypeScript虽缕;
- 預(yù)處理器編譯:比如 Less、Sass 等蒲稳;
- 項目優(yōu)化:比如壓縮氮趋、CDN;
- 解決方案封裝:通過強大的 Loader 和插件機制江耀,可以完成解決方案的封裝;
webpack-cli體驗零配置打包
溫故而知新凭峡,可以為師矣。
本節(jié)默認認為已經(jīng)安裝過Node和Npm.開發(fā)IDE使用的是WebStorm 2018 3.6
打造一個可以轉(zhuǎn)換ES6為ES5 以及 支持圖片(png,jpg,gif,webp)决记、less摧冀、sass/scss的webpack配置項,支持修改項目文件自動更新的功能。
全局安裝
npm install webpack webpack-cli -g
初始化項目
npm init -y
{
"name": "demo1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
}
}
這里面包含了dependencies
為項目的依賴在通過npm install XXX -S
或者npm install yyy --save
會將xxx及版本號顯示在這個位置
devdependencies
為開發(fā)環(huán)境依賴通過npm install yyy -D
或者npm install yyy --dev-save
創(chuàng)建src/main.js和src/index.html
需求描述索昂,通過jquery實現(xiàn) ul>li無序列表中l(wèi)i的隔行換色功能建车。
安裝jquery 項目中需要使用
npm install jquery -S
實現(xiàn)隔行換色
src/main.js
import $ from "jquery"
$("ul li:even").css({background:'red'})
$("ul li:odd").css({background:'pink'})
執(zhí)行命令
webpack src/main.js -o dist/bundle.js
目錄結(jié)構(gòu)
dist
| ├─bundle.js
src
| ├─main.js
| ├─index.html
package.json
修改index.html引入打包后的js
……
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script src="../dist/bundle.js"></script>
……
章結(jié)
發(fā)現(xiàn),我們要手動把js代碼引入椒惨,并且要手動打開瀏覽器去查看網(wǎng)頁缤至。等我們后面學到webpack-dev-server
中的html-webpack-plugin
插件去解決。
項目源碼
git clone https://github.com/highh5/webpack.git -b lesson-01
webpack結(jié)合npm scripts
我們后面會不斷的修改項目文件康谆,每次修改完要重新執(zhí)行webpack src/main.js -o dist/bundle.js
命令领斥。感覺 命令比較長,比較繁瑣沃暗。
安裝本地項目依賴
npm install webpack webpack-cli -D
{
"name": "demo1",
"version": "1.0.0",
"dependencies": {
"jquery": "^3.4.1"
},
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5"
}
}
npm scripts
我們可以結(jié)合npm scripts
來方便我們書寫命令月洛。修改package.json
文件如下:
{
"name": "demo1",
"version": "1.0.0",
"scripts": {
"start": "webpack src/main.js -o dist/bundle.js"
},
"dependencies": {
"jquery": "^3.4.1"
},
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5"
}
}
啟動命令
npm start 或者npm run start
解釋
npm run xxx
這其中的xxx
就是我們在scripts中定義的key值。
這樣一句命令代表著我們將xxx的值執(zhí)行孽锥。即執(zhí)行 webpack src/main.js -o dist/bundle.js
嚼黔。
這里面的webpack
命令其實是利用的我們項目的開發(fā)環(huán)境的的webpack-cli
模塊。如果本地項目未完裝webpack
惜辑、wepback-cli
則去找全局命令唬涧。
注意
<font color="red">start這個命令可以省去run其它都不可以</font>
問題
上面命令執(zhí)行后是可以進行打包,不過會產(chǎn)生一個警告提示盛撑。按照提示添加mode屬性要么為production(生產(chǎn)環(huán)境)或者development 開發(fā)環(huán)境 碎节。我們在這里了解一下開發(fā)環(huán)境。所以將mode屬性的值設(shè)置為development;
> demo1@1.0.0 start C:\Users\zhaoy\Desktop\react\01\demo1
> webpack src/main.js -o dist/bundle.js
Hash: 4e6df5b73ef9caaba844
Version: webpack 4.35.0
Time: 329ms
Built at: 2019-06-24 7:36:41 PM
Asset Size Chunks Chunk Names
bundle.js 87.6 KiB 0 [emitted] main
Entrypoint main = bundle.js
[1] ./src/main.js 106 bytes {0} [built]
+ 1 hidden module
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option t
o 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configurati
on/mode/
修改npm scripts
webpack src/main.js -o dist/bundle.js --mode development
{
"name": "demo1",
"version": "1.0.0",
"scripts": {
"start": "webpack src/main.js -o dist/bundle.js --mode development"
},
"dependencies": {
"jquery": "^3.4.1"
},
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5"
}
}
項目源碼
git clone https://github.com/highh5/webpack.git -b lesson-02
webpack配置文件
webpack
的配置項除了--output
和--mode
之外還有非常多的選項抵卫。官方也提供了一個更簡便的方式來進行操作狮荔。
配置文件
在項目根目錄上新建webpack.config.js
const path = require('path');
module.exports = {
entry:path.join(__dirname,'./src/main.js'),
output:{
path:path.join(__dirname,'./dist'),
filename:'bundle.js'
}
}
我們利用path.join(__dirname,'path')
將文件的入口文件變?yōu)榻^對地址。將輸出文件地址也改為絕對地址陌僵。這樣更利于管理。
相應(yīng)的npm scripts
也要做一些修改"start": "webpack --config webpack.config.js"
或者"start": "webpack"
完整的文件如下创坞。
{
"name": "demo1",
"version": "1.0.0",
"scripts": {
"start": "webpack --config webpack.config.js"
},
"dependencies": {
"jquery": "^3.4.1"
},
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5"
}
}
執(zhí)行命令
npm start
那么會執(zhí)行webpack --config webpack.config.js
那么上面的代碼的執(zhí)行順序為webpack
讀取到webconfig.js``的配置就可以得知具體的事務(wù)
碗短。
項目源碼
git clone https://github.com/highh5/webpack.git -b lesson-03
webpack-dev-server
我們現(xiàn)在修改需求,把main.js中的”pink“改為"purple" 即粉色改為紫色题涨。發(fā)現(xiàn)并沒有自動打包文件偎谁,我們需要手動執(zhí)行npm start來重新打包。
我們在去看gulp的時候我們在改項目文件時候纲堵,只要項目文件發(fā)生變化 巡雨,那么會自動打包,并且會刷新瀏覽器席函。其實webpack也有相應(yīng)的功能铐望,而且這個功能較gulp更強大。
修改src/main.js
import $ from "jquery"
$("ul li:even").css({background:'red'})
$("ul li:odd").css({background:'purple'})
安裝npm install webpack-dev-server -D
修改npm scripts
"start": "webpack-dev-server --config webpack.config.js --open --port 3000 --hot"
{
"name": "demo1",
"version": "1.0.0",
"scripts": {
"start": "webpack-dev-server --config webpack.config.js --open --port 3000 --hot"
},
"dependencies": {
"jquery": "^3.4.1"
},
"devDependencies": {
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
}
}
解釋配置
--open 自動打開瀏覽器 相當于 --open true
--port 打開的服務(wù)端口號 3000 --port 3000
--hot 自動更新 --hot true
執(zhí)行命令
npm start
執(zhí)行命令我們會發(fā)現(xiàn)會自動打開瀏覽器,但是我們發(fā)現(xiàn)打開的并不是我們的index.html中的文件正蛙。它其它打開的是一個靜態(tài)文件服務(wù)器
同時我們也發(fā)現(xiàn)并不會在我們本地去創(chuàng)建一個dist/bundle.js
那么它是怎么一回事督弓?
其實它是將打包的信息全部放在緩存(內(nèi)存)中,我們是看不到的乒验,那究竟打包的文件在哪里愚隧?打開這個地址就可以找到。http://localhost:3000/bundle.js
那么如何將我們的模板index.html
和bunddle.js
文件結(jié)合锻全?請查看下面
html-webpack-plugin
插件
安裝npm install html-webpack-plugin -D
使用webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:path.join(__dirname,'./src/main.js'),
output:{
path:path.join(__dirname,'./dist'),
filename:'bundle.js'
},
plugins:[
new HtmlWebpackPlugin({
template: path.join(__dirname,'./src/index.html'),
filename: 'index.html'
})
]
}
注意插都是一個構(gòu)造函數(shù)狂塘,都需要new,所以在引入html-webpack-plugin
模塊時我們會使用大駝峰命名法。
利用上面的方式我們重新啟動一下服務(wù)即可完成鳄厌。npm start
除js外的其它模塊
使用css
webpack將所有的文件都認為是模塊所以CSS也不例外荞胡。
創(chuàng)建src/index.css
body{
background:gold;
}
使用css
在main.js
項目入口文件 通過 import './index.css';
import $ from "jquery"
$("ul li:even").css({background:'red'})
$("ul li:odd").css({background:'purple'})
import './index.css'
報錯
ERROR in ./src/index.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> body{
| background:gold;
| }
@ ./src/main.js 5:0-20
webpack
默認是不識別.css
文件作為結(jié)尾的模塊,需要我們通過loader
加載器將.css
文件進行解釋成正確的模塊
css-loader
安裝 npm install css-loader -D
配置webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:path.join(__dirname,'./src/main.js'),
output:{
path:path.join(__dirname,'./dist'),
filename:'bundle.js'
},
plugins:[
new HtmlWebpackPlugin({
template: path.join(__dirname,'./src/index.html'),
filename: 'index.html'
})
],
module:{
rules:[
{test:/\.css$/,use:['css-loader']}
]
}
}
module表示是模塊的意思部翘,rules是規(guī)則的意思硝训,每個規(guī)則對應(yīng)的都是一個對象,其中test字段表示是書寫正則以哪個文件名結(jié)尾新思,use是一個數(shù)組窖梁,表示 當前類型文件用哪種加載器解釋。
疑問
這次我們重新執(zhí)行命令npm start
不再報錯夹囚,但是對于css的樣式并未加載成功原因是為什么纵刘?缺失style-loader
為什么說是缺失style-loader?
css-loader 是將index.css正確解釋 為webpack的模塊,進行打包到了bundle.js荸哟,我們可以佐證假哎,但是該樣式并未成功的顯示到瀏覽器中。
那么style-loader的作用其實就是將打包到bundle.js中的css樣式輸出到瀏覽器中鞍历,以style標簽的形式顯示舵抹。
style-loader
安裝npm install style-loader -D
配置 {test:/\.css$/,use:['style-loader','css-loader']}
注意loader的加載順序是從右向左, 即先將css文件通過css-loader 作為正確的模塊進行解釋劣砍,然后再通過style-loader進行顯示到瀏覽器中惧蛹。
至此CSS文件已經(jīng)完成。
使用less文件
安裝npm install less-loader less -D
配置:{test:/\.less$/,use:['style-loader','css-loader','less-loader']}
注意刑枝,我們需要借助于less編譯香嗓。因為less-loader 依賴less。
使用sass文件
安裝npm install sass-loader node-sass -D
配置:{test:/\.scss$/,use:['style-loader','css-loader','sass-loader']}
rules:[
{test:/\.css$/,use:['style-loader','css-loader']},
{test:/\.less$/,use:['style-loader','css-loader','less-loader']},
{test:/\.scss$/,use:['style-loader','css-loader','sass-loader']},
]
注意
<font color="red">對正則中的.號表示任意字符装畅,千萬小心最好把它轉(zhuǎn)義一下靠娱,再來使用。</font>
項目源碼
git clone https://github.com/highh5/webpack.git -b lesson-04
圖片處理
安裝npm install url-loader -D
配置:{test:/\.(jpg|jpeg|gif|png|webp)$/,use:['url-loader']},
放置一張圖片到src目錄圖片大小為13.54kb
我們給a.scss指定body的背景是這張圖片
$color:green;
body{
background: url("./avatar.jpg");
}
打開瀏覽器后發(fā)現(xiàn)圖片是以base64進行編碼了掠兄。什么是base64編譯的圖片呢像云?通俗的講锌雀,圖片不再像以前通過外鏈的形式打開,而是內(nèi)嵌在網(wǎng)頁上了苫费。圖片越大base64的編碼信息越多汤锨。也就是說越大的圖片會導致我們的代碼越來越長。這是不是好事呢百框?
答案: 肯定不是很好闲礼,我們期望網(wǎng)頁打開越快越好孤页,如果網(wǎng)站當中所有的圖片全是以base64來進行編碼顿乒,那么會導致首頁代碼體積越大除秀,那么打開速度也不會很快豆拨,相反可能會很卡爸吮。
我們一般可以讓小圖片使用base64進行加載剥险,這樣既能讓圖片快速加載纺涤,又會減少http請求五垮。至于大圖片我們一般就直接以往常的外鏈形式存在即可睬棚。
解決問題
修改loader配置項,limit表示小于后面的1000 byte即接近1kb第煮,用base64顯示。超過的限制的話通過外鏈來讀取圖片(需要安裝file-loader 文件加載器)
npm install file-loader -D
{
test: /\.(jpg|jpeg|gif|png|webp)$/, use: [{
loader: 'url-loader',
options:{
limit:1000,
name:'[name].[hash:8].[ext]'
}
}]
},
最終圖片被正確解釋出來
body {
background: url(avatar.9d23d463.jpg);
}
ES6 轉(zhuǎn) ES5
安裝
npm install babel-core babel-loader@7.1.5 babel-plugin-transform-runtime babel-preset-env babel-preset-stage-0 -D
配置loader
{test:/\.js/,use:['babel-loader'],exclude:/node_modules/}
exclude表示排除掉 node_modules下載的依賴項抑党。這樣可以加速網(wǎng)站開發(fā)包警,而且我們也只需要對我們的項目src源文件進行編譯即可。
新增.babelrc
文件
{
"presets":[“env","stage-0"],
"plugins":["transform-runtime"]
}
修改main.js使用es6語法
class Person {
constructor(){
}
}
var p = new Person();
執(zhí)行命令編譯
npm start
編譯后的結(jié)果接近在1335行
\n\n var Person = function Person() {\n (0, _classCallCheck3.default)(this, Person);\n};\n\nvar p = new Person();\n\n
至此關(guān)于webpack的基本配置已經(jīng)到這里底靠。
項目源碼
git clone https://github.com/highh5/webpack.git -b lesson-05
小結(jié)
介紹了模塊化害晦、工程化相關(guān)的概念和發(fā)展現(xiàn)狀,最后介紹了 Webpack 的應(yīng)用場景暑中,以及復(fù)習了webpack如何使用等壹瘟。
面試題(拓展)
學而不思則罔,思而不學則殆
Webpack 與 Grunt鳄逾、Gulp 這類打包工具有什么不同稻轨?
簡單解答:一個是模塊化打包化工具,一個是流程化任務(wù)工具雕凹。
webpack 的工作方式是: 把你的項目當做一個整體殴俱,通過一個指定的主文件名(index.js, 一般是入口文件),webpack 將從這個文件開始找到你的項目所依賴的文件请琳,使 用loaders 來處理它們粱挡,最后打包為一個瀏覽器可識別的js 文件赠幕。
Gulp的工作方式是:stream流
grunt的工作方式是:在一個配置文件中俄精,指明對某些文件進行壓縮、組合榕堰、檢查等任務(wù)的具體步驟竖慧,然后在運行中輸入相應(yīng)的命令嫌套。
與 Webpack 類似的工具還有哪些?談?wù)勀銥槭裁催x擇(或放棄)使用 Webpack圾旨?
同樣是基于入口的打包工具還有以下幾個主流的:
從應(yīng)用場景上來看:
- webpack適用于大型復(fù)雜的前端站點構(gòu)建
- rollup適用于基礎(chǔ)庫的打包踱讨,如vue、react
- parcel適用于簡單的實驗性項目砍的,他可以滿足低門檻的快速看到效果
由于parcel在打包過程中給出的調(diào)試信息十分有限痹筛,所以一旦打包出錯難以調(diào)試,所以不建議復(fù)雜的項目使用parcel