[譯]Webpack 2和模板打包的初學者指南(下)

Plugins

咱們已經看到一個內置webpack插件的示例,npm run bulid腳本調用的webpack -p使用UglifyPlugin,它附帶了webpack,能夠再生產過程當中縮減bundlecss

loaders對單個文件進行轉換操做時,插件會在較大的代碼塊上操做。html

Common code

commons-chunk-plugin 是webpack附帶的另外一個核心插件,可用於建立一個具備多入口點的共享代碼的獨立模塊。到目前爲止,咱們一直使用單個入口點和單個輸出包,有不少real-world scenarios,咱們能夠從中分離出多個條目和輸出文件,而獲得好處。python

若是在你的應用程序裏有兩個不一樣的區域都共享模塊,例如面向公衆的應用程序app.js和管理區域的admin.js,則能夠爲其建立單獨的入口點。webpack

// webpack.config.js
const webpack = require('webpack')
const path = require('path')

const extractCommons = new webpack.optimize.CommonsChunkPlugin({
  name: 'commons',
  filename: 'commons.js'
})

const config = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    app: './app.js',
    admin: './admin.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
  },
  module: {
    // ...
  },
  plugins: [
    extractCommons
  ]
}

module.exports = config

注意到output.filename的更改,如今包含[name],這被替換成塊名稱,因此咱們能夠期待這個配置的兩個輸出bundle:app.bundle.jsadmin.bundle.js,爲咱們的兩個入口點。ios

commonschunk插件生成的第三個文件common.js,其中包括來自咱們入口的共享模塊。git

// src/app.js
import './style.scss'
import {groupBy} from 'lodash/collection'
import people from './people'

const managerGroups = groupBy(people, 'manager')

const root = document.querySelector('#root')
root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`
// src/admin.js
import people from './people'

const root = document.querySelector('#root')
root.innerHTML = `<p>There are ${people.length} people.</p>`

入口點能夠輸出以下的文件:github

  • app.bundle.js 包括stylelodash/collection模塊web

  • admin.bundle.js 不包括額外的模塊npm

  • commons.js 包括咱們的people模塊json

接着,咱們能夠在下面兩個地方包含commons塊:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/app.bundle.js"></script>
  </body>
</html>
<!-- admin.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/admin.bundle.js"></script>
  </body>
</html>

嘗試在瀏覽器中加載index.htmlindex.html,以查看它們是否與自動建立的公共模塊一塊兒運行。

Extracting CSS

另外一個流行的插件是extract-text-webpack-plugin,能夠用來將模塊提取到本身的輸出文件中。

下面將修改咱們的.scss規則來編譯咱們的SASS,記載CSS,將每一個提取到本身的CSS包,而後再咱們的JavaScript包中刪除它們。

npm install extract-text-webpack-plugin@2.0.0-beta.4 --save-dev
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const extractCSS = new ExtractTextPlugin('[name].bundle.css')

const config = {
  // ...
  module: {
    rules: [{
      test: /\.scss$/,
      loader: extractCSS.extract(['css-loader','sass-loader'])
    }, {
      // ...
    }]
  },
  plugins: [
    extractCSS,
    // ...
  ]
}

從新啓動webpack,你應該能夠拉到一個新的包app.bundle.js,你能夠像往常同樣直接連接。

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello webpack</title>
    <link rel="stylesheet" href="dist/app.bundle.css">
  </head>
  <body>
    <div id="root"></div>
    <script src="dist/commons.js"></script>
    <script src="dist/app.bundle.js"></script>
  </body>
</html>

刷新頁面,確保咱們的CSS已經編譯並從app.bundle.js中移動到app.bundle.css,成功!

Code Splitting

咱們已經見過幾種分割代碼的方法:

  • 手動建立單獨的entry points

  • 把共享代碼自動拆分爲commons chunk

  • 使用extract-text-webpack-plugin從咱們編譯的bundle中提取塊

另外一種拆分咱們bundle的方法是使用System.import或者require.ensure。經過這些函數中包裝的代碼段,你能夠建立一個在運行時按需加載的塊,這能夠經過在開始時不向客戶端發送全部內容來顯著減小加載時間,提升加載性能。System.import將模塊名稱做爲參數,並返回一個Promiserequire.ensure獲取依賴的列表——回調和塊的可選名稱。

若是你的應用程序的某一部分具備沉重的依賴關係,而其他部分不須要,這是把它分割成本身的捆綁包的好場景。咱們能夠經過一個名爲dashboard.js(須要d3)的新模塊來演示這一點.

npm install webpack-dev-server@2.2.0-rc.0 --save-dev
// src/dashboard.js
import * as d3 from 'd3'

console.log('Loaded!', d3)

export const draw = () => {
  console.log('Draw!')
}

app.js的底部導入dashboard.js

// ...

const routes = {
  dashboard: () => {
    System.import('./dashboard').then((dashboard) => {
      dashboard.draw()
    }).catch((err) => {
      console.log("Chunk loading failed")
    })
  }
}

// demo async loading with a timeout
setTimeout(routes.dashboard, 1000)

由於咱們添加了模塊的異步加載,因此咱們須要在配置中使用output.publicPath屬性,以便webpack知道在哪裏獲取它們。

// webpack.config.js

const config = {
  // ...
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist/',
    filename: '[name].bundle.js'
  },
  // ...
}

從新啓動構建,你會看到一個神祕的包0.bundle.js
圖片描述

注意到,webpack經過突出顯示[big]包來獲取你的關注,從而保持你的忠誠。

0.bundle.js將根據須要使用JSONP請求獲取,所以文件系統直接加載文件不會再削減它。咱們須要運行一個服務器,任何服務器都會作。

python -m SimpleHTTPServer 8001

打開 http://localhost:8001/
加載完後,你應該能夠看到一個GET請求咱們動態生成的包/dist/0.bundle.js和「Loaded!」記錄在控制檯上,成功!

Webpack Dev Server

實時從新加載——每當文件更改時自動刷新,能夠真正改善開發者的體驗。只須要安裝它,並啓動它和webpack-dev-server,你就能夠進行你的比賽了。

npm install webpack-dev-server@2.2.0-rc.0 --save-dev

修改package.json中的start

"start": "webpack-dev-server --inline",

運行npm start開啓服務器,在瀏覽器中打開 http://localhost:8080

嘗試經過更改任何的src文件,例如更改people.js中的名稱或者style.scss中的樣式,查看它在你眼前的變化。

Hot Module Replacement

若是你偏心現場從新加載,那麼hot module replacement(HMB)會讓你歎爲觀止。

這是2017年,可能你已經在去全球的單頁面應用程序工做。在開發期間,你可能對組件進行不少小修改,並但願在瀏覽器中看到這些修改——你能夠在其中看到輸出並與其進行交互。經過手動刷新頁面或使用實時從新加載,你的全局狀態就不存在,你須要從頭開始。Hot module replacement永遠更改了這一點。

在你夢想中的開發人員工做流程中,你能夠更改模塊,並在運行時編譯和交換,而不刷新瀏覽器(不改變本地緩存)或影響到其餘模塊。雖然HMR有時仍然須要你手動刷新,但其依舊能夠爲你節省大量時間,感受就像將來。

package.json中的start進行最後一次修改:

"start": "webpack-dev-server --inline --hot",

app.js頭部告訴webpack接受此模塊及其任何依賴的HMR:

if (module.hot) {
  module.hot.accept()
}

// ...

注意:webpack-dev-server --hot設置module.hottrue,其中包括此僅供開發。當在生產模式中把module.hot設置爲false,這些就都會被剝離出來。

NamedModulesPlugin添加到webpack.config.js中的插件列表,以改進控制檯的輸出日誌。

plugins: [
  new webpack.NamedModulesPlugin(),
  // ...
]

最後,向頁面中添加一個<input>元素,還能夠添加一些文本,以確保咱們對模塊進行更改時不會發生整個頁面的刷新。

<body>
  <input />
  <div id="root"></div>
  ...

運行npm start重啓服務器,看那熱重裝。

能夠嘗試這樣作:在input中輸入HMR規則」,而後再people.js中改變名稱,以查出是否更換了,而沒有刷新頁面(沒有丟失input的輸入狀態)。

這是一個簡單的例子,但能夠幫助你看到這是多麼有用。對於使用基於組件的開發(例如React)尤爲有用,其中不少"dumb"組件與其狀態分離,組件能夠被交換出來並從新呈現而不丟失狀態,所以你能夠得到即時反饋循環。

Hot Reloading CSS

更改style.css<pre>元素的背景顏色,你會注意到它沒有被HMR替換。

pre {
  background: red;
}

事實證實,CSS的HMR是免費的,當你使用style-loader,你不須要作什麼特別的事情。咱們只是經過將CSS模塊提取到外部CSS文件中而沒法被替換,從而打破了鏈中的link

若是咱們將Sass規則恢復到原始狀態,並從插件列表中刪除extractCSS,你能夠看到熱重載加載你的Sass。

{
  test: /\.scss$/,
  loader: ['style-loader', 'css-loader','sass-loader']
}

HTTP/2

使用像webpack這樣的模塊打包工具的主要好處之一視它能夠幫助你提升性能,讓你能夠控制若是構建資源,並能夠在客戶端上獲取資源。多年來,人們認爲最佳實踐是鏈接文件以減小須要在客戶端上進行的請求數。這仍然有效,但HTTP/2 now allows multiple files to be delivered in a single request,因此並置再也不是一個銀子彈。實際上你的應用程序可能受益於多個小文件的單獨緩存,客戶端能夠獲取一個更改的模塊,而沒必要從新獲取一個完整的包——大多數內容是相同的。

webpack的創造者Tobias Koppers撰寫了一篇資料性的文章,解釋了我什麼打包是很重要的,即便在HTTP/2時代。

有關這方面的詳細內容,請點閱webpack & HTTP/2

Over to You

我很是但願你發現了本文,而且可以使用它進行你的偉大創造。它可能須要一些時間使你的頭腦圍繞webpack的配置、加載程序和插件,但學習這個工具是值得的。

文檔扔在更新中,若是你想將現有的webpack1項目遷移到新的熱點中,將會有一個方便從v1遷移到v2的指南。

做者:Mark Brown
原文連接:A Beginner’s Guide to Webpack 2 and Module Bundling

相關文章
相關標籤/搜索