webpack-react之webpack篇(上)

構(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ù)目錄(原生寫的)

Paste_Image.png

可以看到蛙紫,整個(gè)項(xiàng)目有一些js文件拍屑,一堆img文件,一個(gè)css文件坑傅。將來我們也要一步步親自實(shí)現(xiàn)他們丽涩,這個(gè)目錄展示了整個(gè)項(xiàng)目大概需要些什么。讓我們使用webpack構(gòu)建工作流來管理未來我們將要寫的代碼吧裁蚁。

webpack安裝與配置

1.npm

創(chuàng)建一個(gè)文件夾矢渊,并在文件夾下打開命令行

Paste_Image.png

我們需要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)行了懦底。
  },

注意,不推薦全局安裝 webpacknpm 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)打包堆特殊的名字。

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.pathinfoloader項(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模式船响,請看:

實(shí)時(shí)刷新測試
實(shí)時(shí)刷新測試2.png

但是躬拢,難道不能啟動iframe模式了么?非也见间,請看

iframe

那么聊闯,我們并沒有啟動iframe模式啊米诉?這與上邊的hotOnly那一塊說的一個(gè)意思菱蔬,是我們手動啟動了服務(wù)

我們可以關(guān)閉默認(rèn)啟動的inline模式:--no-inline命令

iframe2

但是想要看到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í)瓶您,首先從緩存列表查找麻捻,沒有再去請求。這會提高性能呀袱。

緩存1.png

很明顯贸毕,我們的bundle.js并不是從磁盤緩存中來的(細(xì)心的同學(xué)會說,哇夜赵,bundle.js為什么那么大明棍?!這牽扯甚廣寇僧,又是一篇文章的量疤浮)
緩存2

點(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中引用了它。

Paste_Image.png

接下來羡铲,必須將webpack.config.js中蜂桶,output下的publicPath與devServer下的publicPath設(shè)置為一樣

// webpack.config.js
···
output: {
  ···
  publicPath: '/',
  ···
}
···
devServer: {
  ···
  publicPath: '/',
  ···
}
···

下面關(guān)于兩種配置方式

  • cmd
    --hot
    設(shè)置它之后,啟動服務(wù)犀勒,在瀏覽器可以看到:

    hot模塊

    • 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)證了。
Paste_Image.png
熱加載nodeapi
  • 關(guān)于webpack/hot/only-dev-server前文也介紹了檐盟,雖然不加沒錯(cuò)褂萧,但是還是加上比較好。

  • 在研究熱加載的過程中葵萎,有一些很讓我生氣的事

下邊的寫法是錯(cuò)誤的5加獭3!;蚜 ?闹纭!
webpack/hot/dev-server添加到entry中

熱加載錯(cuò)誤.png

把它改為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;
react熱替換成功唤冈!

很多朋友還在使用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庫秽浇,也放上來

一些我覺得應(yīng)該仔細(xì)看看的文章:

未完待續(xù),敬請期待

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末富玷,一起剝皮案震驚了整個(gè)濱河市璧坟,隨后出現(xiàn)的幾起案子既穆,更是在濱河造成了極大的恐慌,老刑警劉巖雀鹃,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幻工,死亡現(xiàn)場離奇詭異,居然都是意外死亡褐澎,警方通過查閱死者的電腦和手機(jī)会钝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門伐蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來工三,“玉大人,你說我怎么就攤上這事先鱼〖笳” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵焙畔,是天一觀的道長掸读。 經(jīng)常有香客問我,道長宏多,這世上最難降的妖魔是什么儿惫? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮伸但,結(jié)果婚禮上肾请,老公的妹妹穿的比我還像新娘。我一直安慰自己更胖,他們只是感情好铛铁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著却妨,像睡著了一般饵逐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彪标,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天倍权,我揣著相機(jī)與錄音,去河邊找鬼捞烟。 笑死薄声,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坷襟。 我是一名探鬼主播奸柬,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼婴程!你這毒婦竟也來了廓奕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桌粉,沒想到半個(gè)月后蒸绩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铃肯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年患亿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片押逼。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡步藕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挑格,到底是詐尸還是另有隱情咙冗,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布漂彤,位于F島的核電站雾消,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挫望。R本人自食惡果不足惜立润,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望媳板。 院中可真熱鬧桑腮,春花似錦、人聲如沸拷肌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巨缘。三九已至添忘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間若锁,已是汗流浹背搁骑。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留又固,地道東北人仲器。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像仰冠,于是被迫代替她去往敵國和親乏冀。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺洋只,特此分享以備自己日后查看辆沦,也希望更多的人看到...
    小小字符閱讀 8,160評論 7 35
  • GitChat技術(shù)雜談 前言 本文較長昼捍,為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack肢扯,它要...
    蕭玄辭閱讀 12,689評論 7 110
  • webpack 介紹 webpack 是什么 為什么引入新的打包工具 webpack 核心思想 webpack 安...
    yxsGert閱讀 6,463評論 2 71
  • 汽修巷的女人活得比男人久妒茬,男人多死于癌癥。 大修廠還是當(dāng)?shù)赜忻膰髸r(shí)蔚晨,男人們在工廠里面干活乍钻。修車,修大卡車铭腕,一汽...
    麥克不姓麥閱讀 333評論 0 1
  • 開學(xué)第二天星期二银择,所謂的情人節(jié),對我們來說就是尋常日子谨履,不尋常的是我們評選出了“寒假奎文行動”的優(yōu)勝者欢摄,我們班有5...
    野百合c閱讀 541評論 0 0