React开发神器Webpack

编者按:自2013年Facebook发布以来,React吸引了愈来愈多的开发者,基于它的衍生技术,如React Native、React Canvas等也层出不穷。InfoQ精心策划“深刻浅出React”系列文章,为读者剖析React开发的技术细节。javascript

上一篇咱们对React有了一个整体的认识,在介绍其中的技术细节以前,咱们首先来了解一下用于React开发和模块管理的主流工具Webpack。称之为React开发神器有点标题党了,不过Webpack确实是笔者见过的功能最为强大的前端模块管理和打包工具。虽然Webpack是一个通用的工具,并不仅适合于React,可是不少React的文章或者项目都使用了Webpack,尤为是react-hot-loader这样的神器存在,让Webpack成为最主流的React开发工具。css

CommonJS和AMD是用于JavaScript模块管理的两大规范,前者定义的是模块的同步加载,主要用于NodeJS;然后者则是异步加载,经过requirejs等工具适用于前端。随着npm成为主流的JavaScript组件发布平台,愈来愈多的前端项目也依赖于npm上的项目,或者自身就会发布到npm平台。所以,让前端项目更方便的使用npm上的资源成为一大需求。因而诞生了相似browserify这样的工具,代码中可使用require函数直接以同步语法形式引入npm模块,打包后再由浏览器执行。html

Webpack其实有点相似browserify,出自Facebook的Instagram团队,但功能比browserify更为强大。其主要特性以下:前端

  1. 同时支持CommonJSAMD模块(对于新项目,推荐直接使用CommonJS);
  2. 串联式模块加载器以及插件机制,让其具备更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持;
  3. 能够基于配置或者智能分析打包成多个文件,实现公共模块或者按需加载;
  4. 支持对CSS,图片等资源进行打包,从而无需借助Grunt或Gulp;
  5. 开发时在内存中完成打包,性能更快,彻底能够支持开发过程的实时打包需求;
  6. 对sourcemap有很好的支持,易于调试。

Webpack将项目中用到的一切静态资源都视之为模块,模块之间能够互相依赖。Webpack对它们进行统一的管理以及打包发布,其官方主页用下面这张图来讲明Webpack的做用:java

能够看到Webpack的目标就是对项目中的静态资源进行统一管理,为产品的最终发布提供最优的打包部署方案。本文就将围绕React对其相关用法作一个整体介绍,从而能让你将其应用在本身的实际项目之中。node

安装Webpack,并加载一个简单的React组件

Webpack通常做为全局的npm模块安装:react

npm install -g webpack

以后便有了全局的webpack命令,直接执行此命令会默认使用当前目录的webpack.config.js做为配置文件。若是要指定另外的配置文件,能够执行:jquery

webpack —config webpack.custom.config.js

尽管Webpack能够经过命令行来指定参数,但咱们一般会将全部相关参数定义在配置文件中。通常咱们会定义两个配置文件,一个用于开发时,另一个用于产品发布。生产环境下的打包文件不须要包含sourcemap等用于开发时的代码。配置文件一般放在项目根目录之下,其自己也是一个标准的CommonJS模块。webpack

一个最简单的Webpack配置文件webpack.config.js以下所示:git

module.exports = { entry:[ './app/main.js' ], output: { path: __dirname + '/assets/', publicPath: "/assets/", filename: 'bundle.js' } };

其中entry参数定义了打包后的入口文件,数组中的全部文件会按顺序打包。每一个文件进行依赖的递归查找,直到全部相关模块都被打包。output参数定义了输出文件的位置,其中经常使用的参数包括:

  • path: 打包文件存放的绝对路径
  • publicPath: 网站运行时的访问路径
  • filename: 打包后的文件名

如今来看如何打包一个React组件。假设有以下项目文件夹结构:

- react-sample
  + assets/
   - js/
     Hello.js
     entry.js
   index.html
   webpack.config.js

其中Hello.js定义了一个简单的React组件,使用ES6语法:

var React = require('react'); class Hello extends React.Component { render() { return ( <h1>Hello {this.props.name}!</h1> ); } }

entry.js是入口文件,将一个Hello组件输出到界面:

var React = require('react'); var Hello = require('./Hello'); React.render(<Hello name="Nate" />, document.body);

index.html的内容以下:

<html> <head></head> <body> <script src="/assets/bundle.js"></script> </body> </html>

在这里Hello.js和entry.js都是JSX组件语法,须要对它们进行预处理,这就要引入webpack的JSX加载器。所以在配置文件中加入以下配置:

module: { loaders: [ { test: /\.jsx?$/, loaders: ['jsx?harmony']} ] }

加载器的概念稍后还会详细介绍,这里只须要知道它能将JSX编译成JavaScript并加载为Webpack模块。这样在当前目录执行webpack命令以后,在assets目录将生成bundle.js,打包了entry.js的内容。当浏览器打开当前服务器上的index.html,将显示“Hello Nate!”。这是一个很是简单的例子,演示了如何使用Webpack来进行最简单的React组件打包。

加载AMD或CommonJS模块

在实际项目中,代码以模块进行组织,AMD是在CommonJS的基础上考虑了浏览器的异步加载特性而产生的,可让模块异步加载并保证执行顺序。而CommonJS的require函数则是同步加载。在Webpack中笔者更加推荐CommonJS方式去加载模块,这种方式语法更加简洁直观。即便在开发时,咱们也是加载Webpack打包后的文件,经过sourcemap去进行调试。

除了项目自己的模块,咱们也须要依赖第三方的模块,如今比较经常使用的第三方模块基本都经过npm进行发布,使用它们已经无需单独下载管理,须要时执行npm install便可。例如,咱们须要依赖jQuery,只需执行:

npm install jquery —save-dev

更多状况下咱们是在项目的package.json中进行依赖管理,而后经过直接执行npm install来安装全部依赖。这样在项目的代码仓库中并不须要存储实际的第三方依赖库的代码。

安装以后,在须要使用jquery的模块中须要在头部进行引入:

var $ = require('jquery'); $('body').html('Hello Webpack!');

能够看到,这种以CommonJS的同步形式去引入其它模块的方式代码更加简洁。浏览器并不会实际的去同步加载这个模块,require的处理是由Webpack进行解析和打包的,浏览器只须要执行打包后的代码。Webpack自身已经能够彻底处理JavaScript模块的加载,可是对于React中的JSX语法,这就须要使用Webpack的扩展加载器来处理了。

Webpack开发服务器

除了提供模块打包功能,Webpack还提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,均可以直接使用这个开发服务器进行开发。在开发过程当中,开发服务器会监听每个文件的变化,进行实时打包,而且能够推送通知前端页面代码发生了变化,从而能够实现页面的自动刷新。

Webpack开发服务器须要单独安装,一样是经过npm进行:

npm install -g webpack-dev-server

以后即可以运行webpack-dev-server命令来启动开发服务器,而后经过localhost:8080/webpack-dev-server/访问到页面了。默认状况下服务器以当前目录做为服务器目录。在React开发中,咱们一般会结合react-hot-loader来使用开发服务器,所以这里不作太多介绍,只须要知道有这样一个开发服务器能够用于开发时的内容实时打包和推送。详细配置和用法能够参考官方文档

Webpack模块加载器(Loaders)

Webpack将全部静态资源都认为是模块,好比JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等等,从而能够对其进行统一管理。为此Webpack引入了加载器的概念,除了纯JavaScript以外,每一种资源均可以经过对应的加载器处理成模块。和大多数包管理器不同的是,Webpack的加载器之间能够进行串联,一个加载器的输出能够成为另外一个加载器的输入。好比LESS文件先经过less-load处理成css,而后再经过css-loader加载成css模块,最后由style-loader加载器对其作最后的处理,从而运行时能够经过style标签将其应用到最终的浏览器环境。

对于React的JSX也是如此,它经过jsx-loader来载入。jsx-loader专门用于载入React的JSX文件,Webpack的加载器支持参数,jsx-loader就能够添加?harmony参数使其支持ES6语法。为了让Webpack识别什么样的资源应该用什么加载器去载入,须要在配置文件进行配置:经过正则表达式对文件名进行匹配。例如:

module: { preLoaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'jsxhint' }], loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'react-hot!jsx-loader?harmony' }, { test: /\.less/, loader: 'style-loader!css-loader!less-loader' }, { test: /\.(css)$/, loader: 'style-loader!css-loader' }, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }] }

能够看到,该使用什么加载器彻底取决于这里的配置,即便对于JSX文件,咱们也能够用js做为后缀,从而全部的JavaScript均可以经过jsx-loader载入,由于jsx自己就是彻底兼容JavaScript的,因此即便没有JSX语法,普通JavaScript模块也可使用jsx-loader来载入。

加载器之间的级联是经过感叹号来链接,例如对于LESS资源,写法为style-loader!css-loader!less-loader。对于小型的图片资源,也能够将其进行统一打包,由url-loader实现,代码中url-loader?limit=8192含义就是对于全部小于8192字节的图片资源也进行打包。这在必定程度上能够替代Css Sprites方案,用于减小对于小图片资源的HTTP请求数量。

除了已有加载器,你也能够本身实现本身的加载器,从而可让Webpack统一管理项目特定的静态资源。如今也已经有不少第三方的加载器实现常见静态资源的打包管理,能够参考Webpack主页上的加载器列表

React开发神器:react-hot-loader

Webpack自己具备运行时模块替换功能,称之为Hot Module Replacement (HMR)。当某个模块代码发生变化时,Webpack实时打包将其推送到页面并进行替换,从而无需刷新页面就实现代码替换。这个过程相对比较复杂,须要进行多方面考虑和配置。而如今针对React出现了一个第三方react-hot-loader加载器,使用这个加载器就能够轻松实现React组件的热替换,很是方便。其实正是由于React的每一次更新都是全局刷新的虚拟DOM机制,让React组件的热替换能够成为通用的加载器,从而极大提升开发效率。

要使用react-hot-loader,首先经过npm进行安装:

npm install —save-dev react-hot-loader

以后,Webpack开发服务器须要开启HMR参数hot,为了方便,咱们建立一个名为server.js的文件用以启动Webpack开发服务器:

var webpack = require('webpack'); var WebpackDevServer = require('webpack-dev-server'); var config = require('../webpack.config'); new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, hot: true, noInfo: false, historyApiFallback: true }).listen(3000, '127.0.0.1', function (err, result) { if (err) { console.log(err); } console.log('Listening at localhost:3000'); });

为了热加载React组件,咱们须要在前端页面中加入相应的代码,用以接收Webpack推送过来的代码模块,进而能够通知全部相关React组件进行从新Render。加入这个代码很简单:

entry: [ 'webpack-dev-server/client?http://127.0.0.1:3000', // WebpackDevServer host and port 'webpack/hot/only-dev-server', './scripts/entry' // Your appʼs entry point ]

须要注意的是,这里的client?http://127.0.0.1:3000须要和在server.js中启动Webpack开发服务器的地址匹配。这样,打包生成的文件就知道该从哪里去获取动态的代码更新。下一步,咱们须要让Webpack用react-hot-loader去加载React组件,如上一节所介绍,这经过加载器配置完成:

loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'react-hot!jsx-loader?harmony' }, … ]

作完这些配置以后,使用Node.js运行server.js:

node server.js

便可启动开发服务器并实现React组件的热加载。为了方便,咱们也能够在package.json中加入一节配置:

"scripts": { "start": "node ./js/server.js" }

从而经过npm start命令便可启动开发服务器。示例代码也上传在Github上,你们能够参考。

这样,React的热加载开发环境即配置完成,任何修改只要以保存,就会在页面上马上体现出来。不管是对样式修改,仍是对界面渲染的修改,甚至事件绑定处理函数的修改,均可以马上生效,不得不说是提升开发效率的神器。

将Webpack开发服务器集成到已有服务器

尽管Webpack开发服务器能够直接用于开发,但实际项目中咱们可能必须使用本身的Web服务器。这就须要咱们能将Webpack的服务集成到已有服务器,来使用Webpack提供的模块打包和加载功能。要实现这一点其实很是容易,只须要在载入打包文件时指定完整的URL地址,例如:

<script src="http://127.0.0.1:3000/assets/bundle.js"></script>

这就告诉当前页面应该去另一个服务器得到脚本资源文件,在以前咱们已经在配置文件中指定了开发服务器的地址,所以打包后的文件也知道应该经过哪一个地址去创建Socket IO来动态加载模块。整个资源架构以下图所示:

打包成多个资源文件

将项目中的模块打包成多个资源文件有两个目的:

  1. 将多个页面的公用模块独立打包,从而能够利用浏览器缓存机制来提升页面加载效率;
  2. 减小页面初次加载时间,只有当某功能被用到时,才去动态的加载。

Webpack提供了很是强大的功能让你可以灵活的对打包方案进行配置。首先来看如何建立多个入口文件:

{ entry: { a: "./a", b: "./b" }, output: { filename: "[name].js" }, plugins: [ new webpack.CommonsChunkPlugin("init.js") ] }

能够看到,配置文件中定义了两个打包资源“a”和“b”,在输出文件中使用方括号来得到输出文件名。而在插件设置中使用了CommonsChunkPlugin,Webpack中将打包后的文件都称之为“Chunk”。这个插件能够将多个打包后的资源中的公共部分打包成单独的文件,这里指定公共文件输出为“init.js”。这样咱们就得到了三个打包后的文件,在html页面中能够这样引用:

<script src="init.js"></script> <script src="a.js"></script> <script src="b.js"></script>

除了在配置文件中对打包文件进行配置,还能够在代码中进行定义:require.ensure,例如:

require.ensure(["module-a", "module-b"], function(require) { var a = require("module-a"); // ... });

Webpack在编译时会扫描到这样的代码,并对依赖模块进行自动打包,运行过程当中执行到这段代码时会自动找到打包后的文件进行按需加载。

小结

本文结合React介绍了Webpack的基本功能和用法,但愿能让你们对这个新兴而强大的模块管理工具备一个整体的认识,并能将其应用在实际的项目开发中。笔者也将其应用在以前提供的React示例组件项目中,你们能够参考。除了这里介绍的功能,Webpack还有许多强大的特性,例如插件机制、支持动态表达式的require、打包文件的智能重组、性能优化、代码混淆等等。限于篇幅再也不一一介绍,其官方文档也很是完善,须要时能够参考。

相关文章
相关标签/搜索