【學(xué)習(xí)筆記】Webpack

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

1. npm init

生成一個(gè)默認(rèn)的項(xiàng)目配置文件 package.json.

2. 安裝 eslint, 用來檢查語法報(bào)錯(cuò)

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è)試工具用到的包弯菊。

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

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

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

3. npm install

讀取 package.json 中的 devDependencies 和 dependencies 字段, 把記錄的包的相應(yīng)版本下載下來阔馋。

4. 建立 src/index.html 文件.

注意這里我們不需要自己寫<script src="index.js"></script>, 因?yàn)榇虬蟮奈募吐窂娇赡軙?huì)變, 所以我們用webpack插件幫我們自動(dòng)加上.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  </body>
</html>

5. 建立 src/index.js 文件.

// 引入作為全局對(duì)象儲(chǔ)存空間的global.js, js文件可以省略后綴

import g from './global'

// 引入頁面文件
import foo from './views/foo'
import bar from './views/bar'

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

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

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

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

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

// new一個(gè)路由對(duì)象, 賦值為g.router, 這樣我們?cè)谄渌鹙s文件中可以引用到
g.router = new Router()
// 啟動(dòng)
g.router.start()

在webpack配置后蓄氧,當(dāng)我們?cè)L問http://localhost:8100/foo的時(shí)候, 路由會(huì)加載 ./views/foo/index.js文件

// 引入全局對(duì)象
import g from '../../global'

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

// 引入css, 會(huì)生成<style>塊插入到<head>頭中
import './style.css'

// 導(dǎo)出類
export default class {
  mount(container) {
    document.title = 'foo'
    container.innerHTML = template
    container.querySelector('.foo__gobar').addEventListener('click', () => {
      // 調(diào)用router.go方法加載 /bar 頁面
      g.router.go('/bar')
    })
  }
}

借助webpack插件, 我們可以import html, css等其他格式的文件, 文本類的文件會(huì)被儲(chǔ)存為變量打包進(jìn)js文件, 其他二進(jìn)制類的文件, 比如圖片, 可以自己配置, 小圖片作為Data URI打包進(jìn)js文件, 大文件打包為單獨(dú)文件.

6. 安裝webpack和Babel

npm install webpack webpack-dev-server html-webpack-plugin html-loader css-loader style-loader file-loader url-loader --save-dev

webpack-dev-server是webpack提供的用來開發(fā)調(diào)試的服務(wù)器, 讓你可以用 http://127.0.0.1:8080/ 這樣的url打開頁面來調(diào)試, 有了它就不用配置nginx了, 方便很多.

html-webpack-plugin, html-loader,css-loader,style-loader等看名字就知道是打包html文件, css文件的插件, 大家在這里可能會(huì)有疑問, html-webpack-pluginhtml-loader有什么區(qū)別, css-loader和style-loader有什么區(qū)別, 我們等會(huì)看配置文件的時(shí)候再講.

file-loaderurl-loader是打包二進(jìn)制文件的插件, 具體也在配置文件章節(jié)講解.

接下來, 為了能讓不支持ES6的瀏覽器(比如IE)也能照常運(yùn)行, 我們需要安裝babel, 它會(huì)把我們寫的ES6源代碼轉(zhuǎn)化成ES5, 這樣我們?cè)创a寫ES6, 打包時(shí)生成 ES5.

npm install babel-core babel-preset-env babel-loader --save-dev

這里babel-core顧名思義是babel的核心編譯器.babel-preset-env是一個(gè)配置文件, 我們可以使用這個(gè)配置文件轉(zhuǎn)換ES2015/ES2016/ES2017到ES5.

但是光安裝了babel-preset-env, 在打包時(shí)是不會(huì)生效的, 需要在package.json加入babel 配置:

{
  "babel": {
    "presets": [
      "env"
    ]
  }
}
打包時(shí)babel會(huì)讀取package.json中babel字段的內(nèi)容, 然后執(zhí)行相應(yīng)的轉(zhuǎn)換.

7. 配置webpack

創(chuàng)建webpack配置文件webpack.config.js, 注意這個(gè)文件是在node.js中運(yùn)行的, 因此不支持ES6的import語法.

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // 配置頁面入口js文件
  entry: './src/index.js',

  // 配置打包輸出相關(guān)
  output: {
    // 打包輸出目錄
    path: resolve(__dirname, 'dist'),

    // 入口js的打包輸出文件名
    filename: 'index.js'
  },

  module: {
    /*
    配置各種類型文件的加載器, 稱之為loader
    webpack當(dāng)遇到import ... 時(shí), 會(huì)調(diào)用這里配置的loader對(duì)引用的文件進(jìn)行編譯
    */
    rules: [
      {
        /*
        使用babel編譯ES6/ES7/ES8為ES5代碼
        使用正則表達(dá)式匹配后綴名為.js的文件
        */
        test: /\.js$/,

        // 排除node_modules目錄下的文件, npm安裝的包不需要編譯
        exclude: /node_modules/,

        /*
        use指定該文件的loader, 值可以是字符串或者數(shù)組.
        這里先使用eslint-loader處理, 返回的結(jié)果交給babel-loader處理. loader的處理順序是從最后一個(gè)到第一個(gè).
        eslint-loader用來檢查代碼, 如果有錯(cuò)誤, 編譯的時(shí)候會(huì)報(bào)錯(cuò).
        babel-loader用來編譯js文件.
        */
        use: ['babel-loader', 'eslint-loader']
      },

      {
        // 匹配.html文件
        test: /\.html$/,
        /*
        使用html-loader, 將html內(nèi)容存為js字符串, 比如當(dāng)遇到
        import htmlString from './template.html'
        template.html的文件內(nèi)容會(huì)被轉(zhuǎn)成一個(gè)js字符串, 合并到j(luò)s文件里.
        */
        use: 'html-loader'
      },

      {
        // 匹配.css文件
        test: /\.css$/,

        /*
        先使用css-loader處理, 返回的結(jié)果交給style-loader處理.
        css-loader將css內(nèi)容存為js字符串, 并且會(huì)把background, @font-face等引用的圖片,
        字體文件交給指定的loader打包, 類似上面的html-loader, 用什么loader同樣在loaders對(duì)象中定義, 等會(huì)下面就會(huì)看到.
        */
        use: ['style-loader', 'css-loader']
      },

      {
        /*
        匹配各種格式的圖片和字體文件
        上面html-loader會(huì)把html中<img>標(biāo)簽的圖片解析出來, 文件名匹配到這里的test的正則表達(dá)式,
        css-loader引用的圖片和字體同樣會(huì)匹配到這里的test條件
        */
        test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,

        /*
        使用url-loader, 它接受一個(gè)limit參數(shù), 單位為字節(jié)(byte)

        當(dāng)文件體積小于limit時(shí), url-loader把文件轉(zhuǎn)為Data URI的格式內(nèi)聯(lián)到引用的地方
        當(dāng)文件大于limit時(shí), url-loader會(huì)調(diào)用file-loader, 把文件儲(chǔ)存到輸出目錄, 并把引用的文件路徑改寫成輸出后的路徑

        比如 views/foo/index.html中
        ![](smallpic.png)
        會(huì)被編譯成
        [站外圖片上傳中……(2)]

        而
        [站外圖片上傳中……(3)]
        會(huì)被編譯成
        [站外圖片上傳中……(4)]
        */
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      }
    ]
  },

  /*
  配置webpack插件
  plugin和loader的區(qū)別是, loader是在import時(shí)根據(jù)不同的文件名, 匹配不同的loader對(duì)這個(gè)文件做處理,
  而plugin, 關(guān)注的不是文件的格式, 而是在編譯的各個(gè)階段, 會(huì)觸發(fā)不同的事件, 讓你可以干預(yù)每個(gè)編譯階段.
  */
  plugins: [
    /*
    html-webpack-plugin用來打包入口html文件
    entry配置的入口是js文件, webpack以js文件為入口, 遇到import, 用配置的loader加載引入文件
    但作為瀏覽器打開的入口html, 是引用入口js的文件, 它在整個(gè)編譯過程的外面,
    所以, 我們需要html-webpack-plugin來打包作為入口的html文件
    */
    new HtmlWebpackPlugin({
      /*
      template參數(shù)指定入口html文件路徑, 插件會(huì)把這個(gè)文件交給webpack去編譯,
      webpack按照正常流程, 找到loaders中test條件匹配的loader來編譯, 那么這里html-loader就是匹配的loader
      html-loader編譯后產(chǎn)生的字符串, 會(huì)由html-webpack-plugin儲(chǔ)存為html文件到輸出目錄, 默認(rèn)文件名為index.html
      可以通過filename參數(shù)指定輸出的文件名
      html-webpack-plugin也可以不指定template參數(shù), 它會(huì)使用默認(rèn)的html模板.
      */
      template: './src/index.html'
    })
  ],

  /*
  配置開發(fā)時(shí)用的服務(wù)器, 讓你可以用 http://127.0.0.1:8080/ 這樣的url打開頁面來調(diào)試
  并且?guī)в袩岣碌墓δ? 打代碼時(shí)保存一下文件, 瀏覽器會(huì)自動(dòng)刷新. 比nginx方便很多
  如果是修改css, 甚至不需要刷新頁面, 直接生效. 這讓像彈框這種需要點(diǎn)擊交互后才會(huì)出來的東西調(diào)試起來方便很多.
  */
  devServer: {
    // 配置監(jiān)聽端口, 因?yàn)?080很常用, 為了避免和其他程序沖突, 我們配個(gè)其他的端口號(hào)
    port: 8100,

    /*
    historyApiFallback用來配置頁面的重定向

    SPA的入口是一個(gè)統(tǒng)一的html文件, 比如
    http://localhost:8010/foo
    我們要返回給它
    http://localhost:8010/index.html
    這個(gè)文件

    配置為true, 當(dāng)訪問的文件不存在時(shí), 返回根目錄下的index.html文件
    */
    historyApiFallback: true
  }
}

8.走一個(gè)

./node_modules/.bin/webpack-dev-server -d --hot

npm會(huì)把包的可執(zhí)行文件安裝到./node_modules/.bin/目錄下, 所以我們要在這個(gè)目錄下執(zhí)行命令.

-d參數(shù)是開發(fā)環(huán)境(Development)的意思, 它會(huì)在我們的配置文件中插入調(diào)試相關(guān)的選項(xiàng), 比如打開debug, 打開sourceMap, 代碼中插入源文件路徑注釋.

--hot開啟熱更新功能, 參數(shù)會(huì)幫我們往配置里添加HotModuleReplacementPlugin插件, 雖然可以在配置里自己寫, 但有點(diǎn)麻煩, 用命令行參數(shù)方便很多.

命令執(zhí)行后, 控制臺(tái)的最后一行應(yīng)該是

webpack: bundle is now VALID.

這就代表編譯成功了, 我們可以在瀏覽器打開http://localhost:8100/foo 看看效果.

要退出編譯, 按ctrl+c.

開發(fā)環(huán)境編譯試過之后, 我們?cè)囋嚳淳幾g生產(chǎn)環(huán)境的代碼, 命令是:

./node_modules/.bin/webpack -p

-p參數(shù)會(huì)開啟生產(chǎn)環(huán)境模式, 這個(gè)模式下webpack會(huì)將代碼做壓縮等優(yōu)化.

大家可能會(huì)發(fā)現(xiàn), 執(zhí)行腳本的命令有點(diǎn)麻煩. 因此, 我們可以利用npm的特性, 把命令寫在package.json 中:

{
  "scripts": {
    "dev": "webpack-dev-server -d --hot --env.dev",
    "build": "webpack -p"
  }
}

package.json中的scripts對(duì)象, 可以用來寫一些腳本命令, 命令不需要前綴目錄 ./node_modules/.bin/, npm會(huì)自動(dòng)尋找該目錄下的命令. 我們可以執(zhí)行

npm run dev

來啟動(dòng)開發(fā)環(huán)境.

執(zhí)行

npm run build

來打包生產(chǎn)環(huán)境的代碼.

進(jìn)階配置

上面的項(xiàng)目雖然可以跑起來了, 但有幾個(gè)點(diǎn)我們還沒有考慮到:

  • 指定靜態(tài)資源的url路徑前綴

  • 各個(gè)頁面分開打包

  • 打包時(shí)區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境

  • 輸出的entry文件加上hash

  • 第三方庫和業(yè)務(wù)代碼分開打包

  • 開發(fā)環(huán)境關(guān)閉performance.hints

  • 配置favicon

  • 開發(fā)環(huán)境允許其他電腦訪問

  • 打包時(shí)自定義部分參數(shù)

  • webpack-dev-server處理帶后綴名的文件的特殊規(guī)則

  • 代碼中插入環(huán)境變量

  • 簡(jiǎn)化import路徑

  • 優(yōu)化babel編譯后的代碼性能

  • 使用webpack 2自帶的ES6模塊處理功能

  • 使用autoprefixer自動(dòng)創(chuàng)建css的vendor prefixes

  • 編譯前清空dist目錄

那么, 讓我們?cè)谏厦娴呐渲玫幕A(chǔ)上繼續(xù)完善, 下面的代碼我們只寫出改變的部分. 代碼在examples/advanced目錄.

詳見【 https://zhuanlan.zhihu.com/p/27046322


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗅蔬,隨后出現(xiàn)的幾起案子蜒灰,更是在濱河造成了極大的恐慌授舟,老刑警劉巖牡直,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缀匕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碰逸,警方通過查閱死者的電腦和手機(jī)乡小,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饵史,“玉大人满钟,你說我怎么就攤上這事≡技保” “怎么了零远?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵苗分,是天一觀的道長厌蔽。 經(jīng)常有香客問我,道長摔癣,這世上最難降的妖魔是什么奴饮? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮择浊,結(jié)果婚禮上戴卜,老公的妹妹穿的比我還像新娘。我一直安慰自己琢岩,他們只是感情好投剥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著担孔,像睡著了一般江锨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糕篇,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天啄育,我揣著相機(jī)與錄音,去河邊找鬼拌消。 笑死挑豌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氓英,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼侯勉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了铝阐?” 一聲冷哼從身側(cè)響起壳鹤,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饰迹,沒想到半個(gè)月后芳誓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啊鸭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年锹淌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赠制。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赂摆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钟些,到底是詐尸還是另有隱情烟号,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布政恍,位于F島的核電站汪拥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏篙耗。R本人自食惡果不足惜迫筑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宗弯。 院中可真熱鬧脯燃,春花似錦、人聲如沸蒙保。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邓厕。三九已至逝嚎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邑狸,已是汗流浹背懈糯。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留单雾,地道東北人赚哗。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓她紫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屿储。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贿讹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看够掠,也希望更多的人看到...
    小小字符閱讀 8,164評(píng)論 7 35
  • 最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的...
    My_Oh_My閱讀 8,180評(píng)論 40 247
  • 寫在開頭 先說說為什么要寫這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,286評(píng)論 4 31
  • GitChat技術(shù)雜談 前言 本文較長民褂,為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack疯潭,它要...
    蕭玄辭閱讀 12,691評(píng)論 7 110
  • 晚上和學(xué)弟打了一會(huì)籃球 也在石椅上抽著煙聊了一會(huì) 談?wù)撝磳⑦M(jìn)入社會(huì)的壓力 悄然無語 今夜武漢風(fēng)很是強(qiáng)勁 遠(yuǎn)處的燈...
    病入膏肓wy閱讀 166評(píng)論 0 1