構(gòu)建一個(gè)小項(xiàng)目——FlyBird敲街,學(xué)習(xí)webpack和react。
(本文成文于2017/2/25)
從webpack開始
本篇從零開始严望,詳細(xì)記錄webpack的各個(gè)方面多艇。
文章中將會放入很多鏈接以便擴(kuò)展,我也會歸納總結(jié)像吻,不讀擴(kuò)展不會影響到對本文的理解峻黍,但是有時(shí)間還是看看吧。
聲明:
在閱讀本文列出的鏈接文章時(shí)拨匆,若遇到與本文不同的姆涩,因?yàn)槲恼碌臅r(shí)效性問題——
請以本文為標(biāo)準(zhǔn)
當(dāng)前時(shí)間2017/2/26 在此之后出現(xiàn)的文章,讀者請注意對比惭每,自行判斷
- 核心資料:
webpack2官網(wǎng)doc中文版——小書 后文中的簡稱—小書—專指此鏈接
此文檔非常詳細(xì)骨饿,最近一次修訂時(shí)間為——2017 年 2 月十幾號
目前閱讀起來,盡管放心洪鸭。本文為教程样刷,小書為手冊,二合一览爵,棒棒噠置鼻。(您可以親自去看看他是啥時(shí)候又更新了,如果依然比較新蜓竹,或許很多問題箕母,可以在里邊找到答案)
- 其他較完整的小書型資料:
webpack_github庫doc
webpack.js.org(小書的英文原文)
webpack for React英文
webpack傻瓜教程(較老)
webpack中文教程——趙達(dá)(老書)
后邊的雖然是老書,但是俱济,多條資料嘶是,就多一條路,不是么蛛碌?
- 有助于學(xué)習(xí)理解的代碼庫
在文章結(jié)尾
開始
最近在學(xué)習(xí)react聂喇,難免看到網(wǎng)上各種webpack+react的文章,發(fā)現(xiàn)有些很全面的資料蔚携,內(nèi)容卻有些過時(shí)(比如有個(gè)gitbook的書希太,是在react還沒有分離react react-dom的時(shí)候?qū)懙模械馁Y料則雖然挺新酝蜒,但是往往只談一個(gè)方面誊辉。
種種原因,我決定亡脑,結(jié)合官網(wǎng)堕澄,記錄下webpack的各個(gè)方面邀跃,系統(tǒng)學(xué)習(xí)一下。
從零開始構(gòu)建小項(xiàng)目-FlyBird(源代碼可在文章結(jié)尾處找到)
這是原始數(shù)據(jù)目錄(原生寫的)
可以看到蛙紫,整個(gè)項(xiàng)目有一些js文件拍屑,一堆img文件,一個(gè)css文件坑傅。將來我們也要一步步親自實(shí)現(xiàn)他們丽涩,這個(gè)目錄展示了整個(gè)項(xiàng)目大概需要些什么。讓我們使用webpack構(gòu)建工作流來管理未來我們將要寫的代碼吧裁蚁。
webpack安裝與配置
1.npm
創(chuàng)建一個(gè)文件夾矢渊,并在文件夾下打開命令行
我們需要node-npm來安裝和運(yùn)行webpack,關(guān)于node和npm不懂得同學(xué)請自行百度枉证。
擁有node-npm后
在當(dāng)前文件夾初始化npm的package.json文件
npm init
相關(guān)問題隨便填矮男。這會創(chuàng)建 package.json文件,不用擔(dān)心問題填錯(cuò)室谚,你可以之后修改它毡鉴。
這句命令就表示,我們把當(dāng)前文件夾秒赤,初始化為一個(gè)npm包猪瞬,它處于npm的管理之下。
我們可以通過npm下載其他人的包入篮,構(gòu)成了自己包的依賴陈瘦;當(dāng)我們完成了我們的包,也可以發(fā)布它潮售,讓別人下載痊项。最主要的是使用npm來管理依賴。
package.json文件用來配置當(dāng)前包酥诽,配置文件含有很多屬性鞍泉,反映著包的不同信息,后文中肮帐,遇到一個(gè)介紹一個(gè)咖驮,而不做全面介紹。
(詳情請移步package.json屬性詳解)
當(dāng)init后训枢,我們有如下package.json
//package.json
{
"name": "mydemo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
- name托修,version是必須的,也是最重要的肮砾,他們表示包的名稱和版本诀黍,構(gòu)成了包的唯一標(biāo)示袋坑。假如我們只是用npm來管理依賴仗处,他們自然不重要眯勾,但是如果我們在制作一個(gè)供他人使用的工具包,便必須正確的書寫他們婆誓,以便他人查找和使用吃环。具體的規(guī)則請查看之前的網(wǎng)站。
- description main author license
這些屬性洋幻,僅在需要制作工具包時(shí)關(guān)注郁轻。description author license 顧名思義,main則表示當(dāng)別人引用(require)你的工具包時(shí)文留,入口文件在哪好唯。 - scripts則與實(shí)際開發(fā)過程有關(guān),是我們會經(jīng)常用到的燥翅。
通過它骑篙,我們可以定義npm腳本,比如
//只看森书,不用寫后邊需要?jiǎng)邮謱懓卸耍視f
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"
}
當(dāng)我們在命令行npm run build
時(shí),就等于webpack
凛膏,原理很簡單杨名,在執(zhí)行npm run
命令時(shí),會新創(chuàng)建一個(gè)Shell猖毫,并將當(dāng)前目錄的/node_modules/.bin/
加入到PATH
變量(環(huán)境變量)台谍,之后運(yùn)行腳本命令,結(jié)束后吁断,將PATH
恢復(fù)原樣典唇。
詳情請移步:
阮一峰——npm scripts使用指南
npm-scripts官方文檔(英文)
好吧,其實(shí)太高級的并木有卵用胯府,目前介衔,只知道可以懶省事就行啦(o)/~,比如npm run dev
骂因,如果每次都打一長串炎咖,會瘋的。
2.webpack
是時(shí)候安裝webpack了寒波!不過在安裝之前乘盼,還要介紹一些概念。
概念——開發(fā)與發(fā)布
一個(gè)項(xiàng)目通常都會有俄烁,開發(fā)绸栅,發(fā)布這兩種狀態(tài),也就是自己瞎搗鼓页屠,和放網(wǎng)上讓別人用粹胯。不管是哪種狀態(tài)蓖柔,我們的項(xiàng)目都可能會依賴別人的包。那么自然而然风纠,因?yàn)闋顟B(tài)的不同况鸣,依賴的包也不太一樣。比如竹观,在開發(fā)階段镐捧,往往需要進(jìn)行測試,看看能不能跑通臭增,而測試工具顯然在發(fā)布時(shí)是不必要的懂酱。
為了分別管理,npm在package.json提供了這樣的字段
- devDependencies 聲明—僅開發(fā)依賴
- dependencies 依賴包
在下載別人的包時(shí)誊抛,如果只期望下載一個(gè)發(fā)布的可運(yùn)行版本玩焰,而不希望對此包進(jìn)行任何開發(fā),可以利用npm install --production
僅下載dependencies字段中的依賴芍锚。
好吧昔园,對于我們的項(xiàng)目并沒有什么卵用,因?yàn)槲覀儗⑹褂脀ebpack管理整個(gè)工作流程并炮,npm只是用來下載東西(囧)默刚,我只是說明一下。
開發(fā)與部署
開發(fā)到一定階段需要發(fā)布一個(gè)版本逃魄,我們往往需要一個(gè)文件夾來保存整合后的項(xiàng)目荤西。這個(gè)過程,就叫做部署"deploy"伍俘。這也是webpack的工作邪锌,會用到一個(gè)和開發(fā)階段不同的webpack配置文件,它只是將輸出目錄換成了另一個(gè)而已癌瘾。
在我們的小項(xiàng)目進(jìn)入到這個(gè)階段后觅丰,再細(xì)說抗楔。
安裝webpack
npm install webpack --save-dev
這將在本地(當(dāng)前文件夾下)安裝webpack并在開發(fā)依賴字段(devDependencies )中保存信息式廷。
webpack顯然只需要在開發(fā)階段用到
如果想要運(yùn)行它,進(jìn)入node_modules/.bin
例证,并運(yùn)行它webpack
咬荷。
當(dāng)然冠句,我們也可以在上文提到的package.json中的scripts字段中配上
//動手寫
"scripts": {
"build": "webpack" //由于scripts將node_modules/.bin加入到環(huán)境變量PATH中,所以腳本Shell可以搜索到webpack指令幸乒,`npm run a`等價(jià)的`webpack`也就可以運(yùn)行了懦底。
},
注意,不推薦全局安裝 webpack
npm i webpack g
罕扎。這會鎖定 webpack 到指定版本聚唐,并且在使用不同的 webpack 版本的項(xiàng)目中可能會導(dǎo)致構(gòu)建失敗丐重。
webpack的使命
從上邊目錄圖中也可以看到,我們需要用webpack管理很多東西拱层,依賴包,自己寫的jsx宴咧,css根灯,各種各樣的圖片,也許還有字體掺栅。
為了性能烙肺,我們需要根據(jù)依賴關(guān)系,對各種jsx氧卧,css桃笙,img進(jìn)行壓縮整合為數(shù)量跟少的幾個(gè)文件。
為了開發(fā)方便沙绝,我們需要瀏覽器自動刷新搏明,sass/less自動轉(zhuǎn)換的功能等等。
這些闪檬,就是webpack的使命星著,讓我們的開發(fā)更高效。
構(gòu)建目錄
可以簡單的劃分為來源——去處粗悯,如圖
- app文件夾虚循,就所有我們手寫的文件放的地方
- build文件夾,則是經(jīng)過webpack打包样傍,自動生成的文件的去處横缔。
在build文件夾下,新建index.html用來表示我們的索引頁
它長這樣衫哥,其中的div用來給做一些頁面修改什么的用
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FlyBird</title>
</head>
<body>
<div id="div1"></div>
<script src="bundle.js"></script>
</body>
</html>
對于部署文件夾來說茎刚,一般是這樣
- dist文件夾,用來保存我們的發(fā)布版撤逢。
所以斗蒋,最終,在app中寫東西笛质,打包到build中調(diào)試泉沾、看效果,不錯(cuò)的話妇押,發(fā)布到dist文件夾里跷究。
配置webpack
架子已經(jīng)搭好了,現(xiàn)在敲霍,我們需要控制webpack的各種行為俊马,添加各種功能丁存。
可以有三種途徑
- cli——即命令行形式,一般都會動過package.json中寫入scripts字段的形式
- 配置文件
webpack.config.js
文件中寫入字段柴我,webpack在啟動時(shí)會讀取它解寝,并依據(jù)其工作 - node api——其實(shí)配置文件也算node api,更廣義的來講艘儒,node api是一套配置文件的生成系統(tǒng)聋伦,根據(jù)不同的輸入(從cli傳參數(shù)
--a=b
),實(shí)現(xiàn)不同的配置界睁。
對于第三種觉增,本文不做過多介紹,在文章最后翻斟,我會貼出一位前輩的node api模板地址逾礁,有興趣的同學(xué)可以移步文章結(jié)尾。
注意:當(dāng)使用node api同時(shí)又使用了webpack.config.js時(shí)访惜,webpack.config.js將不會生效
我們通過配置文件嘹履,此時(shí)你的目錄應(yīng)該是這樣(新建文件)
因?yàn)槲覀儠鶅蓚€(gè)地方打包,那么自然债热,得倆配置文件了植捎。
-
webpack.config.js
目標(biāo)build -
webpack.production.config.js
目標(biāo)dist
接下來,通過webpack.config.js的配置來詳細(xì)介紹配置屬性(到部署時(shí)候再說后production配置)
進(jìn)行如下配置
//動手寫
// webpack.config.js
var path = require("path");
module.exports = {
entry: path.join(__dirname, '/app/main.js'),
output: {
path: path.join(__dirname, '/build'),//打包后的文件存放的地方
filename: "bundle.js"http://打包后輸出文件的文件名
}
};
__dirname 是當(dāng)前運(yùn)行的js所在的目錄
模塊的依賴書寫方式:
方式取決于模塊系統(tǒng)阳柔,上文中很明顯是commonJS的方式焰枢。
webpack支持最新的模塊系統(tǒng)——es6的模塊系統(tǒng)(ES6 module import),
但是舌剂,注意:這并不是說它支持es6济锄。
也就是說,我們可以直接使用如import/export
語句來導(dǎo)入模塊霍转,但是荐绝,如果我們想要打包含有其他es6語法的模塊時(shí),依然需要babel轉(zhuǎn)換器
關(guān)于模塊系統(tǒng)這里有大略的介紹——webpack中文指南——趙達(dá)(模塊系統(tǒng))
一個(gè)一個(gè)來說
基本配置屬性介紹(一)——entry
1.基本概念
表示模塊的來源避消,入口低滩,起點(diǎn)。它的值可以是
- 字符串
entry: '某模塊'
表示一個(gè)單一模塊作為起點(diǎn)(當(dāng)然岩喷,單一入口也可以用后邊兩種寫)恕沫,把這個(gè)模塊需要的東西打包成一堆 - 數(shù)組
entry: ['模塊1', '模塊2']
模塊1與模塊2互相之間并沒有依賴,但是我們還想把他們打包在一起纱意,此時(shí)就用數(shù)組值的方式婶溯,webpack從左到右把各個(gè)模塊及他們的依賴打包在一起([第一堆,第二堆]),然后從左到右首尾相連的拼接成一個(gè)文件迄委。最終也是打包成一堆褐筛。 - 對象
//只看
entry: {
page1: "./page1",
page2: ["./entry1", "./entry2"]
}
這將會打包成兩堆,每一堆都有一個(gè)[name]屬性叙身,值為對應(yīng)entry中的屬性名渔扎。
//只看
output: {
// Make sure to use [name] or [id] in output.filename
// when using multiple entry points
path: '/build',
filename: "[name].bundle.js",
chunkFilename: "[id].bundle.js"
}
在filename中使用[name]來生成對應(yīng)打包堆特殊的名字。
- 混合使用信轿,不在贅述
entry的官方文檔
2.各種問題
-
entry值的寫法問題
上網(wǎng)大眼一看晃痴,有三種
entry: path.resolve(__dirname, 'app');
entry: path.join(__dirname, 'app');
entry: __dirname + '/app';
在linux,mac環(huán)境下這三種是一樣的虏两,而在windows環(huán)境下愧旦,最后一種是錯(cuò)誤的
這是因?yàn)槭榔剩琻ode的path模塊的方法定罢,在解析路徑時(shí)候,會使用當(dāng)前平臺的路徑分隔符旁瘫,windows的是\
祖凫。
如果我們直接使用+
號拼接,自然發(fā)生錯(cuò)誤酬凳。
-
path.resolve與path.join
join方法僅僅進(jìn)行路徑拼接
resolve方法則會做一些解析工作惠况,它會將參數(shù)從右到左拼起來,直到遇到一個(gè)絕對路徑宁仔。path的node官網(wǎng)文檔總結(jié)一下路徑符號
'.'表示當(dāng)前目錄
`..`表示上一級目錄
`/`表示路徑起點(diǎn)——絕對路徑的標(biāo)志稠屠,通常為當(dāng)前運(yùn)行腳本所處的位置。
所以在使用resolve的時(shí)候要注意翎苫。
- context配置——entry的根目錄
//只看
{
context: path.join(__dirname, 'app'),
entry: "entry",
}
我們也可以通過context來定義entry的根目錄权埠,這也同時(shí)定義了后邊output.pathinfo
、loader項(xiàng)下的reslove(v2版本新改動煎谍,可在小書中查看)
的根目錄攘蔽。
如果我們不聲明context字段,默認(rèn)為process.cwd()
cwd()
是當(dāng)前執(zhí)行node命令時(shí)候的文件夾地址
__dirname
是被執(zhí)行的js 文件的地址
-
小結(jié)語
對于我們的FlyBird項(xiàng)目來說呐粘,顯然僅僅是一個(gè)單頁面應(yīng)用满俗,只需要一個(gè)入口,所以entry設(shè)置極其簡單
//沒啥變化
module.exports = {
entry: path.join(__dirname, '/app/main.js'),
output: {
path: path.join(__dirname, '/build'),
filename: "bundle.js"
}
};
基本配置屬性介紹(二)output
1.基本概念
output規(guī)定了如何將打包后的一堆堆的東西作岖,寫在磁盤里唆垃。
它的內(nèi)容真的非常多,我會挑出之后要用的痘儡,具體說說降盹;想了解具體的,請看小書
2.各種問題
-
output.filename多個(gè)chunk輸出?
也就是上邊entry是對象情況蓄坏,我們必須保證輸出的名字唯一性价捧。每個(gè)chunk都有一些屬性來幫助我們達(dá)到目的-
[name]
最簡單,就是在entry里邊的屬性名 -
[hash]
is replaced by the hash of the compilation -
[chunkhash]
is replaced by the hash of the chunk
后倆不明白涡戳?他們牽扯到了緩存结蟋,我會在下文做簡述,并且之后會專門寫一篇關(guān)于webpack緩存的總結(jié)
了解更多渔彰,請移步這里——hash-chunkhash的理解嵌屎,區(qū)別。(這篇文章從概念上講解了hash和chunkhash恍涂,在下文的緩存簡述里宝惰,我會做進(jìn)一步的說明)
2017/2/25 21:02更新——照現(xiàn)在這進(jìn)度,想寫緩存總結(jié)不知到猴年馬月了再沧,您還是看看下邊的文章吧(下文簡述已經(jīng)寫好了)
-
filename與chunkFIlename區(qū)別尼夺?
請移步——filename與chunkFIlename區(qū)別output.path與output.publicPath?
output.path值為輸出目錄的絕對路徑炒瘸,也可用[hash]
output.publicPath項(xiàng)則被許多Webpack的插件用于在生產(chǎn)模式下更新內(nèi)嵌到css淤堵、html文件里的url值,在熱加載模塊應(yīng)當(dāng)關(guān)注它顷扩,必須通過這個(gè)屬性來告訴熱加載模塊去哪加載
關(guān)于他倆的區(qū)別的具體解釋拐邪,請移步path與publicPath(往下翻,第4條隘截,當(dāng)然前邊也可以看看)
3.小結(jié)語
好了扎阶,關(guān)于output常用就這幾個(gè)。忍不住吐槽下婶芭,它屬性真的太多了东臀,還大部分不知道有啥用(想了解更多,去看小書哦)
到此我們已經(jīng)完成了最簡單的webpack.config.js的配置雕擂,通過這個(gè)配置webpack可以將main.js打包成bundle.js
下面讓我們來試一試是否有效
~~
※配置webpack-dev-server這個(gè)服務(wù)器工具
webpack-dev-server可以讓瀏覽器實(shí)時(shí)刷新啡邑,顯示我們對文件的改動,不如趁著驗(yàn)證main.js和bundle.js的過程井赌,也一并嘗試一下谤逼。
webpack-dev-server是一個(gè)小型的node.js Express服務(wù)器,為webpack打包生成的資源文件提供Web服務(wù)。webpack-dev-server發(fā)送關(guān)于編譯狀態(tài)的消息到客戶端仇穗,客戶端根據(jù)消息作出響應(yīng)流部。
——想看更多可以瀏覽這個(gè)webpack-dev-server解讀
~
如果你不希望使用webpack-dev-server來啟動服務(wù),(要么是你有服務(wù)器了纹坐,要么是你想知道不用它怎么啟動服務(wù))枝冀,詳情請參考
小書——開發(fā)(如果你有服務(wù)器,請翻到此鏈接最后,查看webpack-dev-middleware果漾,本文不會對這種情況做過多討論)
1.安裝
這個(gè)功能是一個(gè)獨(dú)立的模塊實(shí)現(xiàn)的球切,所以我們首先要安裝這個(gè)模塊
npm install --save-dev webpack-dev-server
默認(rèn)情況下,它將在當(dāng)前文件夾下啟動一個(gè)websocket服務(wù)绒障,端口號為8080
兩種方法配置服務(wù)(選擇其一)
1.配置文件 ——Node API
2.cmd指令(推薦)
- 配置文件(Node API)
// webpack.config.js加入
//只看
devServer: {
port: 8080 //設(shè)置監(jiān)聽端口(默認(rèn)的就是8080)
contentBase: "./build",//本地服務(wù)器所加載的頁面所在的目錄
colors: true,//終端中輸出結(jié)果為彩色
historyApiFallback: true,//不跳轉(zhuǎn)吨凑,用于開發(fā)單頁面應(yīng)用,依賴于HTML5 history API 設(shè)置為true點(diǎn)擊鏈接還是指向index.html
}
如果你需要以server.js的形式寫出相關(guān)配置户辱,也不是什么難事
const WebpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.config.js');
const path = require('path');
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
contentBase: 'www',
hot: true,
filename: 'bundle.js',
publicPath: '/',
stats: {
colors: true,
},
});
server.listen(8080, 'localhost', function() {});
關(guān)于如何以server.js方式定義配置鸵钝,將會在文章最后給出模板鏈接,已經(jīng)有前輩做好了一切(伸手就有庐镐,感覺真好恩商!上邊代碼里不認(rèn)識的不要緊,)
- cmd指令
webpack-dev-server --devtool eval-source-map --progress --colors --content-base ./build
// --代表一個(gè)指令必逆,與上邊各個(gè)屬性對應(yīng)
// 此處展示的是經(jīng)常會在網(wǎng)上看到的寫法怠堪,并非我的寫法
--devtool eval-source-map
與webpack-dev-server沒關(guān)系,先不談末患,會在調(diào)試模塊詳細(xì)說明
--progress --colors
:前著表示顯示打包過程研叫,后者是給顯示出來的進(jìn)度加點(diǎn)顏色(一篇綠锤窑,一點(diǎn)紅璧针,手動滑稽~)。
--content-base
:設(shè)置目錄
注意:
有一些在前邊出現(xiàn)的屬性渊啰,并沒有被對應(yīng)的寫在cmd指令中探橱。事實(shí)上,大多數(shù)配置都有兩種寫法绘证,比如:
historyApiFallback: true
對應(yīng)--history-api-fallback
port:8080
對應(yīng)--port 8080
這些并不是我介紹的重點(diǎn)隧膏,如果你想要了解更多細(xì)節(jié),請移步
小書api章節(jié)
如果你希望看到一些具體例子嚷那,請移步
webpack-github的examples文件夾
如果我們使用cmd指令胞枕,每次都打那么長,絕對要瘋魏宽,所以~~~
//動手寫
// package.json
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --devtool eval-source-map --progress --colors --content-base ./build"
},
目前腐泻,最簡單的配置已經(jīng)完備。我們有了一個(gè)服務(wù)器队询,它的端口號默認(rèn)是8080
讓我們寫一些具體的東西派桩,測試一下它的效果
- 首先在app文件夾下新建main.js
它長這樣
// 新建main.js
document.write('我');
里邊的內(nèi)容自然你隨便寫。
現(xiàn)在啟動服務(wù)器
npm run dev
就可以在瀏覽器訪問通過localhost:8080
訪問蚌斩,此時(shí)铆惑,我們在本地做出更改,然后瀏覽器將自動刷新,即可看到效果员魏。
但是丑蛤,如果觀察頁面:
我們的頁面整個(gè)都被刷新了,這顯然是不高效的撕阎,為什么盏阶?
假如工程非常的龐大(實(shí)際開發(fā)中,往往都是‘非常龐大’)闻书,頁面像我這篇文章一樣名斟,很長,魄眉,砰盐,修改一個(gè)很小的地方,如果頁面整個(gè)刷新坑律,第一岩梳,需要等待頁面刷新完,很難過晃择,第二冀值,刷新出來,咱還得滾動半天到咱們修改的地方看效果宫屠。等等列疗。都是不高效的。
所以我們需要另一個(gè)功能
- 熱加載
然而實(shí)現(xiàn)這個(gè)功能的過程中會遇到很多問題浪蹂,接下來我將通過——問與答逐個(gè)說明
問題一:自動刷新功能的兩種模式抵栈?以及如何配置?
這個(gè)是個(gè)用來承上啟下的問題坤次,首先古劲,我們要更深入的了解一下自動刷新功能。
1.兩種模式
webpack-dev-server自帶就有自動刷新功能缰猴,而且它是有兩種啟動模式的
- iframe模式
在iframe模式下:頁面是嵌套在一個(gè)iframe下的产艾,在代碼發(fā)生改動的時(shí)候,這個(gè)iframe會重新加載
關(guān)于iframe是什么請自行百度滑绒,這不是我的重點(diǎn)闷堡。
- inline模式
在inline模式下:一個(gè)小型的webpack-dev-server客戶端會作為入口文件打包,這個(gè)客戶端會在后端代碼改變的時(shí)候刷新頁面
什么叫作為入口文件打包蹬挤?它其實(shí)類似這樣缚窿,請看
entry: [
'webpack-dev-server/client?http://0.0.0.0:9090',//資源服務(wù)器地址
'webpack/hot/only-dev-server',
path.resolve(__dirname, "app")
]
// 數(shù)組第一項(xiàng)就是那個(gè)小型服務(wù)器
這樣的寫法,網(wǎng)上webpack文章上經(jīng)常見焰扳,聯(lián)想一下上文介紹的entry的數(shù)組值的意思倦零。沒錯(cuò)误续,inline模式就是做了這樣的事情。
關(guān)于數(shù)組第二項(xiàng):它是一個(gè)api扫茅,然而它只有node api的寫法蹋嵌,沒有cli的寫法。在瀏覽小書的api章節(jié)葫隙,你會發(fā)現(xiàn)一些類似的栽烂。
hotOnly: true
那么我們還可以像上邊那樣寫,也就是手動的啟動了這個(gè)服務(wù)恋脚。(揪住此功能的實(shí)體腺办,把他強(qiáng)制打包進(jìn)來)。
或許你在網(wǎng)上還見過另一種方式糟描,通過index.html在bundle.js之前插入它怀喉。其實(shí)道理是一樣的。
2.配置
webpack-dev-server默認(rèn)開啟inline模式船响,請看:
但是躬拢,難道不能啟動iframe模式了么?非也见间,請看
那么聊闯,我們并沒有啟動iframe模式啊米诉?這與上邊的hotOnly那一塊說的一個(gè)意思菱蔬,是我們手動啟動了服務(wù)
我們可以關(guān)閉默認(rèn)啟動的inline模式:--no-inline
命令
但是想要看到iframe模式,依然需要再瀏覽器中端口號后加上/webpack-dev-server/
注意:在網(wǎng)上荒辕,你會看到各種各樣的奇怪說法汗销,請依據(jù)你的demo結(jié)果犹褒,結(jié)合本文理解抵窒。(包括本文引用的文章里,好多都說的很奇葩叠骑,就連小書也沒有說清楚這一塊)
最后李皇,總結(jié)一下:
實(shí)時(shí)刷新功能,根本不用我們管宙枷,webpack-dev-server已經(jīng)做好了一切掉房。
問題二:明明設(shè)置正確,卻不會自動刷新慰丛,為什么卓囚?
1.編輯器
一些文本編輯器有“safe write”(安全寫入)功能,并且默認(rèn)啟用诅病。因此哪亿,保存文件后并不總是會導(dǎo)致 webpack 重新編譯粥烁。
每個(gè)編輯器都有不同的方式來禁用這一功能,以下是一些最常見的:
- Sublime Text 3 - 在用戶設(shè)置(preference)中增加 "atomic_save": false蝇棉。
- IntelliJ - 在設(shè)置中查找 “safe write”并且禁用它讨阻。
- Vim - 在設(shè)置中增加 :set backupcopy=yes。
- WebStorm - 在 Preferences > Appearance & Behavior > System Settings 中取消選中 Use "safe write"篡殷。
摘錄自————小書—開發(fā)
2.另一種可能——緩存
緩存簡述
博主遇到過無法自動刷新的問題钝吮,找不到原因,第二天莫名其妙好了板辽。
當(dāng)我想要重現(xiàn)錯(cuò)誤時(shí)奇瘦,它死活不會錯(cuò),關(guān)于是不是緩存導(dǎo)致劲弦,沒有辦法驗(yàn)證链患。
我們都知道瀏覽器會緩存請求的文件,當(dāng)我們再次請求時(shí)瓶您,首先從緩存列表查找麻捻,沒有再去請求。這會提高性能呀袱。
很明顯贸毕,我們的bundle.js并不是從磁盤緩存中來的(細(xì)心的同學(xué)會說,哇夜赵,bundle.js為什么那么大明棍?!這牽扯甚廣寇僧,又是一篇文章的量疤浮)
點(diǎn)開bundle.js查看他的http請求頭,
cache-control:max-age=0
此項(xiàng)用來表示不緩存嘁傀。
如果緩存兴蒸,緩存多久?
-
max-age
指示客戶機(jī)可以接收生存期不大于指定時(shí)間(以秒為單位)的響應(yīng)(不超過這個(gè)秒數(shù)的细办,都用緩存里的) -
Expires
表示存在時(shí)間橙凳,允許客戶端在這個(gè)時(shí)間之前不去檢查(發(fā)請求),等同max-age的
效果笑撞。但是如果同時(shí)存在岛啸,則被Cache-Control的max-age覆蓋。
關(guān)于緩存概念的更多信息請移步————
HTTP頭的Expires與Cache-control
前端緩存策略與基于Webpack的靜態(tài)資源版本管理
緩存是解決性能問題的一大幫手茴肥,我會專門寫一個(gè)文章來分析它(不知到何時(shí)才能成文)坚踩,此處不再多說。
到目前為止瓤狐,并沒有再發(fā)生奇怪的錯(cuò)誤瞬铸。
問題三熱替換Hot Module Replacement卧晓、react-hot-loader與react-transform
1.基本概念
什么是熱替換?就是局部刷新赴捞,這樣逼裆,提高開發(fā)效率,節(jié)約時(shí)間赦政。前邊我們使用inline方式胜宇,每次都會刷新整個(gè)頁面,而iframe模式會刷新整個(gè)iframe標(biāo)簽恢着,顯然不是我們想要的高效
按照大部分網(wǎng)上教的桐愉,弄的我暈頭轉(zhuǎn)向,到最后還做不出來一個(gè)熱替換效果掰派。深受其害从诲,,靡羡,無力吐槽
webpack-dev-server自帶的Hot Module Replacement模塊用來實(shí)現(xiàn)熱替換功能系洛。
兩種方式開啟它:
- cmd
- nodeapi
不管我們用那種方式,只要開啟略步,webpack就會向我們的模塊暴露module.hot
因此描扯,我們可以使用 module.hot
鉤子函數(shù)為特定資源啟用 HMR。這里最重要的 API 是 module.hot.accept趟薄,它指定如何處理對特定依賴的更改绽诚。
所以,我們一步步自己測試一下
// 在main.js的目錄下創(chuàng)建component.js
// component.js
var oDiv = document.querySelector("#div1");
oDiv.textContent = "我是布雷布雷杭煎,你好啊";
// main.js
require("./component.js");
if(module.hot) {
module.hot.accept();
}
我們模擬了一個(gè)組件恩够,并在main.js中引用了它。
接下來羡铲,必須將webpack.config.js中蜂桶,output下的publicPath與devServer下的publicPath設(shè)置為一樣
// webpack.config.js
···
output: {
···
publicPath: '/',
···
}
···
devServer: {
···
publicPath: '/',
···
}
···
下面關(guān)于兩種配置方式
-
cmd
--hot
設(shè)置它之后,啟動服務(wù)犀勒,在瀏覽器可以看到:
- HMR是hot模塊產(chǎn)生
- WDS是webpack-dev-server模塊產(chǎn)生
-
配置文件
Node.js API方式需要做兩個(gè)配置:- 把
new webpack.HotModuleReplacementPlugin()
加入到webpack配置文件的plugins
項(xiàng)屎飘; - 把
hot:true
加入到webpack-dev-server的配置項(xiàng)(devServer)里面。
兩項(xiàng)都必須加大家可以自行驗(yàn)證一下贾费,博主已經(jīng)一一驗(yàn)證了。
- 把
關(guān)于
webpack/hot/only-dev-server
前文也介紹了檐盟,雖然不加沒錯(cuò)褂萧,但是還是加上比較好。在研究熱加載的過程中葵萎,有一些很讓我生氣的事
下邊的寫法是錯(cuò)誤的5加獭3!;蚜 ?闹纭!
將webpack/hot/dev-server
添加到entry中
把它改為
webpack/hot/dev-server.js
名字寫全节猿,就可以了票从。還不清楚為什么報(bào)錯(cuò),有知道的小伙伴滨嘱,請指教一二峰鄙!
說到底還是命令行方式最簡單——像下邊這樣寫就好
//你的應(yīng)該也長這樣
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --devtool eval-source-map --progress --colors --hot --content-base ./build "
},
其他配置選項(xiàng)
--quiet 控制臺中不輸出打包的信息
--compress 開啟gzip壓縮
--progress 顯示打包的進(jìn)度
更多配置信息——官網(wǎng)webpack-dev-server-cli
~~
2.三個(gè)模塊
以上配置好后,我們已經(jīng)可以熱加載實(shí)時(shí)刷新了太雨,但是我們的項(xiàng)目FlyBird(我都快把他給忘了)想要用到react吟榴,組件化的react給熱加載帶來了一些麻煩。
react-hot-loader
來解決問題
我們都知道react組件是狀態(tài)機(jī)囊扳,有state對象來表示狀態(tài)吩翻,假如因?yàn)橐恍┬「膭樱瑢?dǎo)致要渲染整個(gè)組件锥咸,此時(shí)現(xiàn)有狀態(tài)就會丟失仿野。react-hot-loader
就是來解決這個(gè)問題的。所以它是必須的她君。
react-transform
被棄用
react-transform
也是用來解決這個(gè)問題脚作,不過它已經(jīng)老早就停止維護(hù)了,所以不要使用它缔刹,咱們按官網(wǎng)上的球涛,都是使用react-hot-loader
由于react使用的是jsx+es6語法,所以我們不能僅僅只是打個(gè)包完事校镐,還要在打包過程中使用babel對其進(jìn)行編碼轉(zhuǎn)換亿扁。
總結(jié)一下:
- 使用
react-hot-loader
- 使用
babel
(babel不懂的,請自行百度教程)
不再解釋鸟廓,直接上代碼
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-2
因?yàn)榇幼#J(rèn)安裝的還是v1版本的react-hot-loader,我們要使用的是最新的版本引谜,一定要帶上版本號牍陌。
npm install --save-dev react-hot-loader@3.0.0-beta.6
npm install --save react react-dom
新建.babelrc
并配置
// 新建.babelrc
{
"presets": [
["es2015", {"modules": false}],
// webpack現(xiàn)在已經(jīng)支持原生的import語句了, 并且將其運(yùn)用在tree-shaking特性上
"stage-2",
// 規(guī)定JS運(yùn)用的語言規(guī)范層級
// Stage 2 是 "草案", 4 是 "已完成", 0 is "稻草人(strawman)"。
// 詳情查看 https://tc39.github.io/process-document/
"react"
// 轉(zhuǎn)譯React組件為JS代碼
],
"plugins": [
"react-hot-loader/babel"
// 開啟react代碼的模塊熱替換(HMR)
]
}
配置webpack.config.js员咽,因?yàn)槲覀円呀?jīng)使用babel轉(zhuǎn)義es6所以毒涧,盡情使用吧
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
context: __dirname,
entry: [
'react-hot-loader/patch',
'webpack/hot/only-dev-server',
'./app/main.js'
],
output: {
path: resolve(__dirname, 'build'),//打包后的文件存放的地方
filename: "bundle.js",//打包后輸出文件的文件名
publicPath: "/"
},
devServer: {
contentBase: resolve(__dirname, 'build'),
hot: true,
publicPath:'/'
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
'babel-loader',
],
exclude: /node_modules/
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
],
devtool: "cheap-eval-source-map",
};
編寫main.js,寫一段react測試一下贝室,也是好的
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import Cpt from './component';
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById('div1')
)
};
render(Cpt);
if(module.hot) {
module.hot.accept('./component', () => {
render(Cpt)
});
}
最關(guān)鍵的就是module.hot契讲,一定要寫仿吞,它用來告訴webpack怎么去熱替換
這是我們的組件component.js,它長這樣
import React from 'react';
const Cpt = () => (
<div>
<h1>我是蓋世英雄捡偏!</h1>
</div>
);
export default Cpt;
很多朋友還在使用v1版本的react-hot-loader,網(wǎng)上很多教程也都是v1版本的银伟,小伙伴們在看資料時(shí)候一定要注意區(qū)別俭识。
如果你想了解更多——
react-hot-loader的github可以在這里了解到api泌枪,與以前的區(qū)別,還可以看到很多模板。
這里是一些區(qū)別的討論
都是英文的丈甸,小伙伴要有點(diǎn)耐心讀哦
這是react-hot-loader的網(wǎng)站
結(jié)語
1.一些話
戛然而止惭等,掉冶,路幸,好吧,是我的錯(cuò)模孩,我小小的解釋一下尖阔。
寫這篇文章,完全超出了我的預(yù)計(jì)榨咐,我以為兩天就能寫完介却,然而整整用了五天,從早上8點(diǎn)坐下來块茁,一直到晚上11點(diǎn)齿坷,有兩天午飯都忘吃了。
中間遇到了很多困難数焊,永淌,比如研究緩存那一塊,因?yàn)槟鲥e(cuò)又莫名其妙好了佩耳,我想還原錯(cuò)誤遂蛀,猜測是不是緩存問題,就去清緩存干厚,瀏覽器自帶的緩存清理不給力李滴,我就手動把我谷歌下的user data文件夾給刪了。蛮瞄。所坯。刪完之后才意識到,自己書簽還沒備份裕坊,包竹,,唉籍凝,上百個(gè)書簽周瞎,都是心血啊,又花了點(diǎn)錢買數(shù)據(jù)恢復(fù)饵蒂,也沒恢復(fù)過來声诸,又白賠進(jìn)去幾百塊錢。
類似種種吧退盯,這會兒真的有點(diǎn)累彼乌,昨天熬夜到4點(diǎn)多,想著能搞定渊迁,誰知道還是今天才弄完慰照。
當(dāng)然,其實(shí)這些都不是最關(guān)鍵的琉朽,累了毒租,休息休息就好。
最主要的是箱叁,博主還是學(xué)生墅垮,馬上要去找工作了,耕漱,算色,然而,我還沒有開始復(fù)習(xí)螟够。灾梦。。僵硬妓笙。若河。。所以给郊,必須暫時(shí)停下來了牡肉。
2.關(guān)于這個(gè)項(xiàng)目
FlyBird,淆九,统锤,好吧,我都快把它給忘了炭庙。其實(shí)按計(jì)劃饲窿,今天是能夠做完的,因?yàn)橹皇且粋€(gè)簡單的重構(gòu)焕蹄,就像todoMVC一樣逾雄。
忙完這一段我依然會著手完成它,就像做,下面對接下來需要討論的問題匯總一下鸦泳,以備以后使用
未解決的問題:
緩存策略
| 插件银锻、loader
打包策略
| 插件、loader做鹰、同步異步加載
生產(chǎn)環(huán)境構(gòu)建
| 插件击纬、loader
| 使用node api構(gòu)建配置文件生成系統(tǒng)
懶加載
| 插件、loader
測試功能
| 插件钾麸、loader
兼容問題
| 插件更振、loader
v1到v2版本的改動
| 插件、loader饭尝、api接口肯腕、書寫方式等等一大堆
關(guān)于webpack,小書上其實(shí)很全面了钥平,而且比較新实撒,大家可以放心去查閱,當(dāng)然帖池,也要關(guān)注官網(wǎng)的最新的消息奈惑。
react部分,其實(shí)沒有什么好說的睡汹,因?yàn)榫W(wǎng)上的教材也比較全了肴甸,當(dāng)然,這不是我以后偷懶的理由囚巴。
我會回來完善這篇文章的原在,讓它成為一個(gè)完整的,友好的引導(dǎo)彤叉。這是我對自己的承諾庶柿。
3.放在最后的資料
文章小例子github庫——
FlyBird這個(gè)從哪來的?感謝這篇文章帶給我的一些沖動
JavaScript實(shí)現(xiàn)Fly Bird小游戲
有一些很棒的github庫秽浇,也放上來
- webpack-dev-server的github浮庐,可以在examples文件夾里找到例子
- create react app 兩萬多的星。柬焕。审残。
- webpack-hmr-3-ways 名字已經(jīng)說明了一切,很不錯(cuò)
- react-learn
- react-redux-webpack模板
- react-hot-loader的github
- react-static-boilerplate
- react-hot-boilerplate
一些我覺得應(yīng)該仔細(xì)看看的文章: