[译]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

相关文章
相关标签/搜索