继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

你不知道的webpack和webpack-dev-server高级玩法

2017-12-04 17:42:2416100浏览

Jokcy

3实战 · 5手记 · 1推荐
TA的实战

相信很多同学都知道webpack,毕竟是现阶段最火的前端打包工具,现在要是不知道webpack,你可能都不好意思出去面试说你是前端工程师,面试官估计也会斜着眼心里嘀咕:又是个页面仔。webpack的基本用法我想大家应该都知道,你写一个配置文件,然后通过webpack --config /path/to/your/config.js启动webpack就可以进行打包,或者通过webpack-dev-server --config /path/to/your/config.js启动dev-server,你就可以花式使用热更替功能提高你的开发效率了。而我今天就为大家来讲一讲,webpack和webpack-dev-server的一些高级玩法。

把webpack作为一个node module使用

大部分情况下,我们都直接命令行启动webpack进行打包,就可以满足我们的需求。但毕竟计划赶不上变化,有时候你会发现用命令行启动webpack变得不是那么方便。比如我们在调试react的服务端渲染的时候,我们不可能每次有文件更新,等着webpack打包完输出到硬盘上某个文件,然后你重启服务度去加载这个新的文件,因为这太浪费时间了,毕竟开发时你随时都可能改代码,而且改动可能还很小。

那么要解决这个问题怎么办呢?我们可以在启动nodejs服务的时候,顺带启动webpack打包服务,这样我们可以在nodejs的执行环境中拿到webpack打包的上下文,就可以不重启服务但每次文件更新都可以拿到最新的bundle。那么具体怎么做呢?很简单,看下面的代码:

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

 const compiler = webpack(webpackConfig)

 compiler.run((err, stats) => {/* ...处理结果 */})
 // or
 compiler.watch({
     // watch options
 }, (err, stats) => {/* ...处理结果 */})

通过给webpack传入配置,可以得到一个webpack的compiler,你可以调用compiler.run来一次性调用webpack的打包服务,他接受一个callback方法,你可以在里面处理他的打包结果。

同时你也可以调用compiler.watch来让webpack监听文件变化,每次更新打包结束都会调用你传入的callback。而第一个参数是watchOptions,跟配置文件里面的watchOptions是一样的。

但是这样做了之后呢,webpack依旧是把文件打包到本地磁盘,显然在开发的时候我们并没有这个需求,我希望的是提高打包速度,而写入磁盘明显是个低效率的操作,我们要避免这个问题。怎么做呢?还在webpack为我们提供了一个配置项,我们可以指定webpack输出的文件系统。

const MemoryFS = require('memory-fs')
const mfs = new MemoryFS()
compiler.outputFileSystem = mfs

memory-fs是一个在内存中存储读取文件的文件系统类库,他的api和nodejs默认的fs一模一样,所以直接给compiler.outputFileSystem之后,所有webpack输出的文件都将存储在内存里面。而随后,我们都可以通过mfs对象,读取到这些文件。速度瞬间上了一个档次有木有。其实webpack-dev-server内部也用了这个方法,他才能快速得帮我们更新代码。

通过nodejs启动webpack-dev-server

同样的道理,我们有时候也需要在nodejs在启动webpack-dev-server,而webpack-dev-server和webpack不同的是,他本身自己也会启动一个服务,并监听独立的端口。但是我们也有办法让他不这么做,我们可以让只启动我们自己的服务,但同时可以具备webpack-dev-server那些牛逼的功能,怎么做呢?

我们要用两个工具:webpack-dev-middlewarewebpack-hot-middleware,这两个是express的中间件,如果你的服务使用express开发的,那么你可以很方便的把这两个工具集成到你的服务上,我们来看看怎么做:

const express = require('express')
const devMiddleware = require('webpack-dev-middleware')
const hotMiddleware = require('webpack-hot-middleware')

const app = express()
app.use(devMiddleware(compiler, {/* dev config */}))
app.use(hotMiddleware(compiler))
// ...your server code here

这里的compiler就是我们上面webpack生成的compiler,devMiddleware的config跟webpack配置里面的devServer的配置基本类似,但是也有几个不一样的配置,具体可以移步这里查看详细api

但是这样还不能实现热更替功能,在使用hotMiddleware的时候,我们需要在我们webpack配置的entry中加入一个文件webpack-hot-middleware/client,这是一共在客户端跟hotMiddleware进行通信的js,有了它之后,我们客户端才能接收到新的代码并覆盖老的代码。

你以为这样就结束了吗?还有呢

虽然通过上面的方式我们可以只起一个服务,但是这也有弊端,就是webpack的打包服务要随着我们重启node服务而重启,而总所周知,如果你的前端应用很复杂,那么webpack打包的速度会很慢,重启webpack的成本还是很高的,如果你经常要改服务端的代码,那这就不划算了。那有没有办法能把这两个服务分开,但又是在node中启动呢?

答案当然是有的,只要你有需求,没有你解决不了的问题,前提是你敢想敢做

const WebpackDevServer = require('webpack-dev-server')

const devServer = new WebpackDevServer(compiler, {/* devServer config */})
devServer.listen(/* port */)

这样子你就可以通过nodejs启动webpack-dev-server了

这是在webpack-dev-server的文档中没有提到的,其实他也是可以直接作为一个node module使用的。

当然如果你的express server和dev server都写在一个js入口文件中启动,那么你每次修改服务端代码,还是顺便会重启dev server。这时候如果你要区分,可以把这两个入口文件分开,然后在一个脚本中通过child_process分别启动两个服务,然后在这个脚本中控制这两个服务什么时候该重启,这一块我就不深入讲了,大家可以自己去研究一下,只要知道了上面这些方法,你就可以随意控制你的服务在何时启动,又在何时结束。

以上就是webpack和webpack-dev-server的一些高级玩法,当然我这里只是抛砖引玉,webpack太过复杂,如果要把每个细节写清楚估计能写很厚一本书。写这篇文章的用意更多是希望大家能抛开很多流水线化的编程习惯,去拥抱变化,每一个强大的工具或者框架总有各种等着你去挖掘的用法,你只有往深入去研究,才能解锁更多姿势。

打开App,阅读手记
28人推荐
发表评论
随时随地看视频慕课网APP

热门评论

您好,请教一个问题哈,是否有办法在 webpack.config.js 中判断当前到底是运行的 webpack 命令、还是运行的 webpack-dev-server ?

难道只能分别针对他们写两个配置文件吗,只有一个一个参数的区别。

老师我看完您的课程,vue 服务端渲染,为什么 context.renderScripts() 获取到的是空的,也就是说服务端渲染的页面出来的但是 没有script标签 https://img2.mukewang.com/5cff8af7000153bf12140662.jpg

https://img2.mukewang.com/5cff8b0e0001f98b12441354.jpg

大佬你好,请问 react ssr 在开发环境需要通过 getModuleFromServer 方法将自定义对 module 和当前环境的 require 传进去,在生产环境直接读取打包后的 server-entry.js,为什么不需要传入module 和 require 呢?

查看全部评论