【译】关于Webpack中一些让人困惑的地方的解答

原文链接:Webpack — The Confusing Partscss


Webpack是React和Redux项目的主要模块加载器。我认为使用Angular2和其余的框架的人在现在也大量使用Webpack进行开发。html

当我第一次查看Webpack的配置文件时,我是懵逼的。在使用过一段时间之后,我以为这是由于Webpack有着独一无二的语法和标新立异的哲学思想,因此在刚开始使用的时候可能会形成必定的困惑。凑巧的是,这些哲学思想也是让它如此受欢迎的缘由。node

正由于Webpack的起步比较容易产生困惑,因此我但愿写一些什么出来,好让更多人更容易上手而且体验它强大的特性。react

接下来是第一部分。webpack

Webpack的核心哲学思想

两个核心哲学思想是:web

  1. 一切都是模块——就像JS文件能够视做“模块”同样,其余全部的一切(CSS,图片,HTML)均可以被视做模块。也就是说,你能够require(“myJSfile.js”)或者require(“myCSSfile.css”)。这意味着咱们能够把任何静态资源分割成可控的模块,以供重复使用等不一样的操做。npm

  2. 只加载“你须要的”和“你什么时候须要”的——典型的模块加载器会把全部的模块最终打包生成一个巨大的“bundle.js”文件。但在不少实际的项目当中,这个“bundle.js”文件体积可能会达到10MB~15MB,而且会一直不停进行加载!因此Webpack经过大量的特性去分割你的代码,生成多个“bundle”片断,而且异步地加载项目的不一样部分,所以只会为你加载“你须要的”和“你什么时候须要”的部分。json

OK,让咱们一块儿来看看那些“让人困惑”的部分吧。segmentfault

1. 开发环境 VS 生产环境

第一件须要意识到的事情是Webpack拥有着大量的特性。有一些是“开发环境专用”的,一些是“生产环境专用”的,还有一些是“通用”的。
图片描述数组

通常来讲,大部分的项目都使用了许多Webpack的特性,因此它们一般有两个大的webpack config文件,用于区分开发环境和生产环境。

2. webpack CLI Vs webpack-dev-server

明白Webpack这个模块加载器拥有两个接口是很是重要的:

  1. Webpack CLI tool ——默认的接口(和Webpack一并被安装)

  2. webpack-dev-server tool ——这个工具经过来自CLI和配置文件(默认:webpack.config.js)的配置项来控制Webpack的打包动做。

你刚开始学习Webpack的时候多是从CLI入手的,但你接下来极可能只会用它去创建生产环境的项目。

使用方法:

OPTION 1: 
//全局安装
npm install webpack --g

//在终端使用
$ webpack //<--经过webpack.bundle.js进行打包

OPTION 2 :
//本地安装并写入package.json依赖
npm install webpack --save

//添加到package.json的script内
“scripts”: {
 “build”: “webpack --config webpack.config.prod.js -p”,
 ...
 }

//开始构建
npm run build

Webpack-dev-server (有利于开发环境使用)

这是一个运行在8080端口的基于Express的node.js服务器。这个服务器会在内部调用Webpack。它的优点是提供了额外的能力——相似能够刷新浏览器的“Live Reloading”,以及(或者)局部更新模块的“模块热重载”功能(HMR)

使用方法:

OPTION 1:
//全局安装
npm install webpack-dev-server --save

//终端使用
$ webpack-dev-server --inline --hot

OPTION 2:
//添加到package.json的script内
“scripts”: {
 “start”: “webpack-dev-server --inline --hot”,
 ...
 }

//输入下列命令行进行使用
$ npm start

浏览器打开下列地址
http://localhost:8080

Webpack Vs webpack-dev-server options

值得注意的是,有一些选项好比“inline”和“hot”仅用于webpack-dev-server,而好比“hide-modules”仅用于CLI。

webpack-dev-server CLI options Vs config options

另一件须要知道的事情是你能够经过两种方式对webpack-dev-server进行配置:

  1. 经过webpack.config.js的“devServer”对象。

  2. 经过CLI选项。

//使用CLI
webpack-dev-server --hot --inline

//使用webpack.config.js
devServer: {
 inline: true,
 hot:true
 }

我发现有时候devServer的配置并无论用!因此我更倾向于把这些选项以CLI的方式写入package.json里面:

//package.json
{
scripts: 
   {“start”: “webpack-dev-server --hot --inline”}
}

注意:确保你木有把hot:true-hot写在一起。

“hot” Vs “inline” webpack-dev-server options

“inline”选项为整个页面提供了“Live reloading”功能。“hot”选项提供了“模块热重载”功能,它会尝试仅仅更新组件被改变的部分(而不是整个页面)。若是咱们把这两个选项都写上,那么当文件被改动时,webpack-dev-server会先尝试HMR,若是这无论用,它就会从新加载整个页面。

//当文件被改动后,下面的三个选项都会生成新的bundle,可是,
 
//1. 页面不会刷新
$ webpack-dev-server

//2. 刷新整个页面
$ webpack-dev-server --inline

//3. 仅仅刷新被改动的部分(HMR),若是HMR失败则刷新整个页面
$ webpack-dev-server  --inline --hot

“entry”——字符串VS数组VS对象

entry告诉Webpack入口文件或者起点在哪里。它能够是一个字符串,一个数组或者一个对象。这可能会使你感到困惑,但不一样的类型适用于不一样的场合。

若是你使用的是单个起点(大部分项目都是如此),那么你可使用任意的类型,它们的结果都会是同样的。
图片描述

entry——数组

可是,若是你想要添加互不依赖的多个文件,你可使用数组的格式。

举个栗子,你的HTML可能须要“googleAnalytics.js”。你能够告诉Webpack在bundle.js的后面把它添加进去:
图片描述

entry——对象

如今,当你有一个包含多个HTML文件的多页应用,而不是单页应用的项目的时候(index.html和profile.html),你能够经过对象格式告诉Webpack去一次性生成多个bundle文件。

下面的配置会生成两个JS文件:indexEntry.jsprofileEntry.js,你能够在index.htmlprofile.html分别使用它们
图片描述

使用方法:

//profile.html
<script src=”dist/profileEntry.js”></script>

//index.html
<script src=”dist/indexEntry.js”></script>

注意:文件名来自“entry”对象的key。

entry——组合格式

你也能够在entry对象中使用数组。下面的例子会生成三个文件:一个包含三个文件的vendor.js,一个index.js和一个profile.js
图片描述

4. output — “path” Vs “publicPath”

output告诉Webpack应该在哪里以怎样的方式去放置打包好的文件。它有两个属性:“path”和“publicPath”,这也许会对用户形成必定的困惑。

“path”会简单地告诉Webpack生成文件输出位置。“publicPath”多被一些Webpack的插件使用,在HTML文件以生产环境方式被构建的时候,更新CSS文件内的URL地址。
图片描述

举个栗子,在你的CSS文件里面,你可能会在URL里面加载./test.png。可是在生产环境中,test.png极可能放在CDN内——好比当你的node.js服务器运行在Heroku的时候。这意味着,你可能在生产环境内不得不手动更新文件内的URL指向

相反,你可使用Webpack的publicPath以及其余适用于这个属性的插件在生产环境中自动地更新文件内部的URL指向。
图片描述

//开发环境:服务器和图片都放在本地
.image { 
  background-image: url(‘./test.png’);
 }

//生产环境:服务器在Heroku而图片在CDN
.image { 
  background-image: url(‘https://someCDN/test.png’);
 }

5. 加载器和链式加载器

加载器是额外的node模块,用于“加载”或者“引入”不一样类型的文件,并把他们转化成浏览器可以识别的格式——好比JS文件、内联样式表或其余格式。另外,加载器也容许以“require”或者ES6的“import”的方式把这些文件引入到JS文件当中。

例如,你可使用babel-loader把ES6代码转化成ES5代码:

module: {
 loaders: [{
  test: /\.js$/, // 判断文件格式,若为“.js”文件则调用loader
  exclude: /node_modules/, // 排除node_modules文件夹
  loader: ‘babel’ // 使用babel(babel-loader的缩写)
 }]

链式加载器(从右到左的顺序进行工做)

不一样的加载器能够链式地在针对同一个文件类型进行工做。链式加载器的工做顺序是从右到左的,而且经过“!”分割

举个栗子,咱们有一个叫作myCssFile.css的CSS文件,如今想把它以<style></style>的方式在咱们的HTML文件中使用,能够经过两个加载器去完成这个需求:css-loaderstyle-loader

module: {
 loaders: [{
  test: /\.css$/,
  loader: ‘style!css’ // style-loader!css-loader的缩写
 }]

这是运行原理:
图片描述

  1. Webpack搜寻被模块所引用的CSS文件。意思是Webpack会检查一个JS文件内是否有require(myCssFile.css),若是有这句话而且找到了这个依赖,它会首先把这个文件交给css-loader

  2. css-loader加载全部的CSS文件及其依赖包(例如经过@import引入的其余CSS文件)到一个JSON文件中。随后Webpack会把结果交给style-loader

  3. style-loader拿到这个JSON文件并把它注入到<style></style>标签当中,并把这个标签添加到index.html文件内。

6.加载器自身是可配置的

加载器其自身能够经过配置不一样的参数实现不一样的功能。

在下面的例子中,咱们配置了url-loader,当图片小于1024byte的时候使用DataURL,当图片大雨1024byte的时候使用URL。咱们经过下面两个传入limit参数的方法来实现这个功能:
图片描述

7. babelrc文件

babel-loader使用presets去规定如何把ES6代码转化为ES5代码,以及如何把React的JSX转化为JS。咱们能够经过query方法进行配置:

module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel',
      query: {
        presets: ['react', 'es2015']
      }
    }
  ]
}

然而,在许多项目中babel的配置项会很是巨大。因此做为替代,你能够把这些配置项写入一个叫作.babelrc的文件中。若是这个文件存在的话bable-loader会自动的加载这个文件。

因此在许多例子中,你会看到:

//webpack.config.js 
module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel'
    }
  ]
}

//.bablerc
{
 “presets”: [“react”, “es2015”]
}

8. 插件

插件是额外的node模块,多用于处理输出文件。

例如,uglifyJSPlugin会压缩并混淆JS代码,使其体积减少。

一样的,extract-text-webpack-plugin会在内部使用css-loaderstyle-loader去把全部的CSS合并为一个文件,而且最终把结果提取到一个分离在外部的style.css文件中,最后在index.html中引用这个CSS文件。

//webpack.config.js
//Take all the .css files, combine their contents and it extract them to a single "styles.css"
var ETP = require("extract-text-webpack-plugin");

module: {
 loaders: [
  {test: /\.css$/, loader:ETP.extract("style-loader","css-loader") }
  ]
},
plugins: [
    new ExtractTextPlugin("styles.css") //Extract to styles.css file
  ]
}

注意,若是你只打算把CSS以行内样式的形式在HTML中引用,你能够仅仅使用css-loaderstyle-loader,像下面的例子:

module: {
 loaders: [{
  test: /\.css$/,
  loader: ‘style!css’ // style-loader!css-loader的缩写
 }]

9. 加载器 VS 插件

正如你可能已经弄明白的,加载器在单个文件的程度上,在打包结束以前或者打包的过程当中运行

插件是在打包或者数据块的程度上,在输出打包文件的过程当中进行运做。一些插件好比commonsChunksPlugins甚至在更早的阶段开始运做,能够用来修改打包的方式。

10. 解析文件扩展名

一些Webpack配置文件带有解析扩展文件名的属性,它们像下面的例子同样,包含了一些空字符串。这些空字符串被用于帮助加载一些没有扩展名的文件,好比require("./myJSFile")或者import myJSFile from './myJSFile'

{
 resolve: {
   extensions: [‘’, ‘.js’, ‘.jsx’]
 }
}

全文完。

感谢Tobias Koppers(Webpack的做者)帮我审阅这篇文章!


感谢你的阅读。我是Jrain,欢迎关注个人专栏,将不按期分享本身的学习体验,开发心得,搬运墙外的干货。下次见啦!

相关文章
相关标签/搜索