成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Webpack 4 和單頁(yè)應(yīng)用入門

Zoom / 1471人閱讀

摘要:但由于和技術(shù)過于和復(fù)雜,并沒能得到廣泛的推廣。但是在瀏覽器內(nèi)并不適用。依托模塊化編程,的實(shí)現(xiàn)方式更為簡(jiǎn)單清晰,一個(gè)網(wǎng)頁(yè)不再是傳統(tǒng)的類似文檔的頁(yè)面,而是一個(gè)完整的應(yīng)用程序。到了這里,我們的主角登場(chǎng)了年此處應(yīng)有掌聲。和差不多同期登場(chǎng)的還有。

Github:https://github.com/fenivana/w...

webpack 更新到了 4.0,官網(wǎng)還沒有更新文檔。因此把教程更新一下,方便大家用起 webpack 4。

寫在開頭

先說說為什么要寫這篇文章,最初的原因是組里的小朋友們看了 webpack 文檔后,表情都是這樣的:摘自 webpack 一篇文檔的評(píng)論區(qū))

和這樣的:

是的,即使是外國(guó)佬也在吐槽這文檔不是人能看的?;叵肫甬?dāng)年自己啃 webpack 文檔的血與淚的往事,覺得有必要整一個(gè)教程,可以讓大家看完后愉悅地搭建起一個(gè) webpack 打包方案的項(xiàng)目。

官網(wǎng)新的 webpack 文檔現(xiàn)在寫的很詳細(xì)了,能看英文的小伙伴可以直接去看官網(wǎng)。

可能會(huì)有人問 webpack 到底有什么用,你不能上來就糊我一臉代碼讓我馬上搞,我照著搞了一遍結(jié)果根本沒什么用,都是騙人的。所以,在說 webpack 之前,我想先談一下前端打包方案這幾年的演進(jìn)歷程,在什么場(chǎng)景下,我們遇到了什么問題,催生出了應(yīng)對(duì)這些問題的工具。了解了需求和目的之后,你就知道什么時(shí)候 webpack 可以幫到你。我希望我用完之后很爽,你們用完之后也是。

先說說前端打包方案的黑暗歷史

在很長(zhǎng)的一段前端歷史里,是不存在打包這個(gè)說法的。那個(gè)時(shí)候頁(yè)面基本是純靜態(tài)的或者服務(wù)端輸出的,沒有 AJAX,也沒有 jQuery。那個(gè)時(shí)候的 JavaScript 就像個(gè)玩具,用處大概就是在側(cè)欄弄個(gè)時(shí)鐘,用 media player 放個(gè) mp3 之類的腳本,代碼量不是很多,直接放在

以上是 AMD 規(guī)范的基本用法,更詳細(xì)的就不多說了(反正也淘汰了~),有興趣的可以看 這里。

js 模塊化問題基本解決了,css 和 html 也沒閑著。什么 less,sass,stylus 的 css 預(yù)處理器橫空出世,說能幫我們簡(jiǎn)化 css 的寫法,自動(dòng)給你加 vendor prefix。html 在這期間也出現(xiàn)了一堆模板語(yǔ)言,什么 handlebars,ejs,jade,可以把 ajax 拿到的數(shù)據(jù)插入到模板中,然后用 innerHTML 顯示到頁(yè)面上。

托 AMD 和 CSS 預(yù)處理和模板語(yǔ)言的福,我們的編譯腳本也洋洋灑灑寫了百來行。命令行腳本有個(gè)不好的地方,就是 windows 和 mac/linux 是不通用的,如果有跨平臺(tái)需求的話,windows 要裝個(gè)可以執(zhí)行 bash 腳本的命令行工具,比如 msys(目前最新的是 msys2),或者使用 php 或 python 等其他語(yǔ)言的腳本來編寫,對(duì)于非全棧型的前端程序員來說,寫 bash / php / python 還是很生澀的。因此我們需要一個(gè)簡(jiǎn)單的打包工具,可以利用各種編譯工具,編譯 / 壓縮 js、css、html、圖片等資源。然后 Grunt 產(chǎn)生了(2012 年),配置文件格式是我們最愛的 js,寫法也很簡(jiǎn)單,社區(qū)有非常多的插件支持各種編譯、lint、測(cè)試工具。一年多后另一個(gè)打包工具 gulp 誕生了,擴(kuò)展性更強(qiáng),采用流式處理效率更高。

依托 AMD 模塊化編程,SPA(Single-page application) 的實(shí)現(xiàn)方式更為簡(jiǎn)單清晰,一個(gè)網(wǎng)頁(yè)不再是傳統(tǒng)的類似 word 文檔的頁(yè)面,而是一個(gè)完整的應(yīng)用程序。SPA 應(yīng)用有一個(gè)總的入口頁(yè)面,我們通常把它命名為 index.html、app.html、main.html,這個(gè) html 的 一般是空的,或者只有總的布局(layout),比如下圖:

布局會(huì)把 header、nav、footer 的內(nèi)容填上,但 main 區(qū)域是個(gè)空的容器。這個(gè)作為入口的 html 最主要的工作是加載啟動(dòng) SPA 的 js 文件,然后由 js 驅(qū)動(dòng),根據(jù)當(dāng)前瀏覽器地址進(jìn)行路由分發(fā),加載對(duì)應(yīng)的 AMD 模塊,然后該 AMD 模塊執(zhí)行,渲染對(duì)應(yīng)的 html 到頁(yè)面指定的容器內(nèi)(比如圖中的 main)。在點(diǎn)擊鏈接等交互時(shí),頁(yè)面不會(huì)跳轉(zhuǎn),而是由 js 路由加載對(duì)應(yīng)的 AMD 模塊,然后該 AMD 模塊渲染對(duì)應(yīng)的 html 到容器內(nèi)。

雖然 AMD 模塊讓 SPA 更容易地實(shí)現(xiàn),但小問題還是很多的:

不是所有的第三方庫(kù)都是 AMD 規(guī)范的,這時(shí)候要配置 shim,很麻煩。

雖然 RequireJS 支持通過插件把 html 作為依賴加載,但 html 里面的 的路徑是個(gè)問題,需要使用絕對(duì)路徑并且保持打包后的圖片路徑和打包前的路徑不變,或者使用 html 模板語(yǔ)言把 src 寫成變量,在運(yùn)行時(shí)生成。

不支持動(dòng)態(tài)加載 css,變通的方法是把所有的 css 文件合并壓縮成一個(gè)文件,在入口的 html 頁(yè)面一次性加載。

SPA 項(xiàng)目越做越大,一個(gè)應(yīng)用打包后的 js 文件到了幾 MB 的大小。雖然 r.js 支持分模塊打包,但配置很麻煩,因?yàn)槟K之間會(huì)互相依賴,在配置的時(shí)候需要 exclude 那些通用的依賴項(xiàng),而依賴項(xiàng)要在文件里一個(gè)個(gè)檢查。

所有的第三方庫(kù)都要自己一個(gè)個(gè)的下載,解壓,放到某個(gè)目錄下,更別提更新有多麻煩了。雖然可以用 npm 包管理工具,但 npm 的包都是 CommonJS 規(guī)范的,給后端 Node.js 用的,只有部分支持 AMD 規(guī)范,而且在 npm 3 之前,這些包有依賴項(xiàng)的話也是不能用的。后來有個(gè) bower 包管理工具是專門的 web 前端倉(cāng)庫(kù),這里的包一般都支持 AMD 規(guī)范。

AMD 規(guī)范定義和引用模塊的語(yǔ)法太麻煩,上面介紹的 AMD 語(yǔ)法僅是最簡(jiǎn)單通用的語(yǔ)法,API 文檔里面還有很多變異的寫法,特別是當(dāng)發(fā)生循環(huán)引用的時(shí)候(a 依賴 b,b 依賴 a),需要使用其他的 語(yǔ)法 解決這個(gè)問題。而且 npm 上很多前后端通用的庫(kù)都是 CommonJS 的語(yǔ)法。后來很多人又開始嘗試使用 ES6 模塊規(guī)范,如何引用 ES6 模塊又是一個(gè)大問題。

項(xiàng)目的文件結(jié)構(gòu)不合理,因?yàn)?grunt/gulp 是按照文件格式批量處理的,所以一般會(huì)把 js、html、css、圖片分別放在不同的目錄下,所以同一個(gè)模塊的文件會(huì)散落在不同的目錄下,開發(fā)的時(shí)候找文件是個(gè)麻煩的事情。code review 時(shí)想知道一個(gè)文件是哪個(gè)模塊的也很麻煩,解決辦法比如又要在 imgs 目錄下建立按模塊命名的文件夾,里面再放圖片。

到了這里,我們的主角 webpack 登場(chǎng)了(2012 年)(此處應(yīng)有掌聲)。

和 webpack 差不多同期登場(chǎng)的還有 Browserify。這里簡(jiǎn)單介紹一下 Browserify。Browserify 的目的是讓前端也能用 CommonJS 的語(yǔ)法 require("module") 來加載 js。它會(huì)從入口 js 文件開始,把所有的 require() 調(diào)用的文件打包合并到一個(gè)文件,這樣就解決了異步加載的問題。那么 Browserify 有什么不足之處導(dǎo)致我不推薦使用它呢? 主要原因有下面幾點(diǎn):

最主要的一點(diǎn),Browserify 不支持把代碼打包成多個(gè)文件,在有需要的時(shí)候加載。這就意味著訪問任何一個(gè)頁(yè)面都會(huì)全量加載所有文件。

Browserify 對(duì)其他非 js 文件的加載不夠完善,因?yàn)樗饕鉀Q的是 require() js 模塊的問題,其他文件不是它關(guān)心的部分。比如 html 文件里的 img 標(biāo)簽,它只能轉(zhuǎn)成 Data URI 的形式,而不能替換為打包后的路徑。

因?yàn)樯厦嬉稽c(diǎn) Browserify 對(duì)資源文件的加載支持不夠完善,導(dǎo)致打包時(shí)一般都要配合 gulp 或 grunt 一塊使用,無(wú)謂地增加了打包的難度。

Browserify 只支持 CommonJS 模塊規(guī)范,不支持 AMD 和 ES6 模塊規(guī)范,這意味舊的 AMD 模塊和將來的 ES6 模塊不能使用。

基于以上幾點(diǎn),Browserify 并不是一個(gè)理想的選擇。那么 webpack 是否解決了以上的幾個(gè)問題呢? 廢話,不然介紹它干嘛。那么下面章節(jié)我們用實(shí)戰(zhàn)的方式來說明 webpack 是怎么解決上述的問題的。

上手先搞一個(gè)簡(jiǎn)單的 SPA 應(yīng)用

一上來步子太大容易扯到蛋,讓我們先弄個(gè)最簡(jiǎn)單的 webpack 配置來熱一下身。

安裝 Node.js

webpack 是基于我大 Node.js 的打包工具,上來第一件事自然是先安裝 Node.js 了,傳送門 ->。

初始化一個(gè)項(xiàng)目

我們先隨便找個(gè)地方,建一個(gè)文件夾叫 simple, 然后在這里面搭項(xiàng)目。完成品在 examples/simple 目錄,大家搞的時(shí)候可以參照一下。我們先看一下目錄結(jié)構(gòu):

├── dist                      打包輸出目錄,只需部署這個(gè)目錄到生產(chǎn)環(huán)境
├── package.json              項(xiàng)目配置信息
├── node_modules              npm 安裝的依賴包都在這里面
├── src                       我們的源代碼
│   ├── components            可以復(fù)用的模塊放在這里面
│   ├── index.html            入口 html
│   ├── index.js              入口 js
│   ├── shared                公共函數(shù)庫(kù)
│   └── views                 頁(yè)面放這里
└── webpack.config.js         webpack 配置文件

打開命令行窗口,cd 到剛才建的 simple 目錄。然后執(zhí)行這個(gè)命令初始化項(xiàng)目:

npm init

命令行會(huì)要你輸入一些配置信息,我們這里一路按回車下去,生成一個(gè)默認(rèn)的項(xiàng)目配置文件 package.json。

給項(xiàng)目加上語(yǔ)法報(bào)錯(cuò)和代碼規(guī)范檢查

我們安裝 eslint, 用來檢查語(yǔ)法報(bào)錯(cuò),當(dāng)我們書寫 js 時(shí),有錯(cuò)誤的地方會(huì)出現(xiàn)提示。

npm install eslint eslint-config-enough eslint-loader --save-dev

npm install 可以一條命令同時(shí)安裝多個(gè)包,包之間用空格分隔。包會(huì)被安裝進(jìn) node_modules 目錄中。

--save-dev 會(huì)把安裝的包和版本號(hào)記錄到 package.json 中的 devDependencies 對(duì)象中,還有一個(gè) --save, 會(huì)記錄到 dependencies 對(duì)象中,它們的區(qū)別,我們可以先簡(jiǎn)單的理解為打包工具和測(cè)試工具用到的包使用 --save-dev 存到 devDependencies, 比如 eslint、webpack。瀏覽器中執(zhí)行的 js 用到的包存到 dependencies, 比如 jQuery 等。那么它們用來干嘛的?

因?yàn)橛行?npm 包安裝是需要編譯的,那么導(dǎo)致 windows / mac /linux 上編譯出的可執(zhí)行文件是不同的,也就是無(wú)法通用,因此我們?cè)谔峤淮a到 git 上去的時(shí)候,一般都會(huì)在 .gitignore 里指定忽略 node_modules 目錄和里面的文件,這樣其他人從 git 上拉下來的項(xiàng)目是沒有 node_modules 目錄的,這時(shí)我們需要運(yùn)行

npm install

它會(huì)讀取 package.json 中的 devDependenciesdependencies 字段,把記錄的包的相應(yīng)版本下載下來。

這里 eslint-config-enough 是配置文件,它規(guī)定了代碼規(guī)范,要使它生效,我們要在 package.json 中添加內(nèi)容:

{
  "eslintConfig": {
    "extends": "enough",
    "env": {
      "browser": true,
      "node": true
    }
  }
}

業(yè)界最有名的語(yǔ)法規(guī)范是 airbnb 出品的,但它規(guī)定的太死板了,比如不允許使用 for-offor-in 等。感興趣的同學(xué)可以參照 這里 安裝使用。

eslint-loader 用于在 webpack 編譯的時(shí)候檢查代碼,如果有錯(cuò)誤,webpack 會(huì)報(bào)錯(cuò)。

項(xiàng)目里安裝了 eslint 還沒用,我們的 IDE 和編輯器也得要裝 eslint 插件支持它。

Visual Studio Code 需要安裝 ESLint 擴(kuò)展

atom 需要安裝 linter 和 linter-eslint 這兩個(gè)插件,裝好后重啟生效。

WebStorm 需要在設(shè)置中打開 eslint 開關(guān):

寫幾個(gè)頁(yè)面

我們寫一個(gè)最簡(jiǎn)單的 SPA 應(yīng)用來介紹 SPA 應(yīng)用的內(nèi)部工作原理。首先,建立 src/index.html 文件,內(nèi)容如下:



  
    
  

  
  

它是一個(gè)空白頁(yè)面,注意這里我們不需要自己寫 , 因?yàn)榇虬蟮奈募吐窂娇赡軙?huì)變,所以我們用 webpack 插件幫我們自動(dòng)加上。

src/index.js:

// 引入 router
import router from "./router"

// 啟動(dòng) router
router.start()

src/router.js:

// 引入頁(yè)面文件
import foo from "./views/foo"
import bar from "./views/bar"

const routes = {
  "/foo": foo,
  "/bar": bar
}

// Router 類,用來控制頁(yè)面根據(jù)當(dāng)前 URL 切換
class Router {
  start() {
    // 點(diǎn)擊瀏覽器后退 / 前進(jìn)按鈕時(shí)會(huì)觸發(fā) window.onpopstate 事件,我們?cè)谶@時(shí)切換到相應(yīng)頁(yè)面
    // https://developer.mozilla.org/en-US/docs/Web/Events/popstate
    window.addEventListener("popstate", () => {
      this.load(location.pathname)
    })

    // 打開頁(yè)面時(shí)加載當(dāng)前頁(yè)面
    this.load(location.pathname)
  }

  // 前往 path,變更地址欄 URL,并加載相應(yīng)頁(yè)面
  go(path) {
    // 變更地址欄 URL
    history.pushState({}, "", path)
    // 加載頁(yè)面
    this.load(path)
  }

  // 加載 path 路徑的頁(yè)面
  load(path) {
    // 首頁(yè)
    if (path === "/") path = "/foo"
    // 創(chuàng)建頁(yè)面實(shí)例
    const view = new routes[path]()
    // 調(diào)用頁(yè)面方法,把頁(yè)面加載到 document.body 中
    view.mount(document.body)
  }
}

// 導(dǎo)出 router 實(shí)例
export default new Router()

src/views/foo/index.js:

// 引入 router
import router from "../../router"

// 引入 html 模板,會(huì)被作為字符串引入
import template from "./index.html"

// 引入 css, 會(huì)生成 

      <