如何打造一个令人愉悦的前端开发环境(四)

Express 结合 Webpack 实现HMR

本篇文件主要讲结合 Webpack 和 Express 实现前后端热更新开发,如果你还不太了解webpack推荐阅读

webpack 官网文档

What

什么是 webpack dev server

Webpack dev server 是一个轻量的node.js express服务器,实现了 webpack 编译代码实时输出更新。在前后端分离的前端项目开发中经常用到。不过这篇文章应该不会讲到它。

webpack dev middleware

Webpack dev middleware 是 WebPack 的一个中间件。它用于在 Express 中分发需要通过 WebPack 编译的文件。单独使用它就可以完成代码的热重载(hot reloading)功能。

特性:

  • 不会在硬盘中写入文件,完全基于内存实现。
  • 如果使用 watch 模式监听代码修改,Webpack 会自动编译,如果在 Webpack 编译过程中请求文件,Webpack dev middleware 会延迟请求,直到编译完成之后再开始发送编译完成的文件。

webpack hot middleware

Webpack hot middleware 它通过订阅 Webpack 的编译更新,之后通过执行 webpack 的 HMR api 将这些代码模块的更新推送给浏览器端。

HMR

HMR 即 Hot Module Replacement 是 Webpack 一个重要的功能。它可以使我们不用通过手动地刷新浏览器页面实现将我们的更新代码实时应用到当前页面中。

HMR 的实现原理是在我们的开发中的应用代码中加入了 HMR Runtime,它是 HMR 的客户端(浏览器端
client)用于和开发服务器通信,接收更新的模块。服务端工作就是前面提到的 Webpack hot middleware
的,它会在代码更新编译完成之后通过以 json 格式输出给HMR Runtime 就会更具 json 中描述来动态更新相应的代码。

How

webpack 配置

先来在webpack配置文件中引入


  1. var webpack = require('webpack'); 
  2. var HotMiddleWareConfig = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000' 
  3.  
  4. module.exports = { 
  5.     context: __dirname, 
  6.     entry: [ 
  7.         // 添加一个和HotMiddleWare通信的客户端              
  8.         HotMiddleWareConfig, 
  9.         // 添加web应用入口文件  
  10.         './client.js' 
  11.     ], 
  12.     output: { 
  13.         path: __dirname, 
  14.         publicPath: '/', 
  15.         filename: 'bundle.js' 
  16.     }, 
  17.     devtool: '#source-map', 
  18.     plugins: [ 
  19.         new webpack.optimize.OccurenceOrderPlugin(), 
  20.         // 在 webpack 插件中引入 webpack.HotModuleReplacementPlugin  
  21.         new webpack.HotModuleReplacementPlugin(), 
  22.         new webpack.NoErrorsPlugin() 
  23.     ], 
  24. };  

webpack-hot-middleware example webpack.config.js

在我们的开发环境中是这样配置的。getEntries 是自动根据我们规则获取到入口文件并加上 webpack hot middle 配置。


  1. var webpack = require('webpack'); 
  2. var path = require('path') 
  3. var merge = require('webpack-merge') 
  4. var baseConfig = require('./webpack.base') 
  5. var getEntries = require('./getEntries') 
  6.  
  7. var publicPath = 'http://0.0.0.0:7799/dist/'; 
  8. var hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; 
  9.  
  10. var assetsInsert = require('./assetsInsert') 
  11.  
  12. module.exports = merge(baseConfig, { 
  13.   entry: getEntries(hotMiddlewareScript), 
  14.   devtool: '#eval-source-map', 
  15.   output: { 
  16.     filename: './[name].[hash].js', 
  17.     path: path.resolve('./public/dist'), 
  18.     publicPath: publicPath 
  19.   }, 
  20.   plugins: [ 
  21.     new webpack.DefinePlugin({ 
  22.       'process.env': { 
  23.         NODE_ENV: '"development"' 
  24.       } 
  25.     }), 
  26.     new webpack.optimize.OccurenceOrderPlugin(), 
  27.     new webpack.HotModuleReplacementPlugin(), 
  28.     new webpack.NoErrorsPlugin(), 
  29.     new assetsInsert() 
  30.   ] 
  31. })  

Express 中的配置

在 Express 的配置主要就4个步骤:

  • 引入 webpack 的配置文件和 生成 webpack 的编译器
  • 将编译器连接至 webpack dev middleware
  • 将编译器连接至 webpack hot middleware
  • 定义 express 配置

  1. var http = require('http'); 
  2.  
  3. var express = require('express'); 
  4.  
  5. var app = express(); 
  6.  
  7. app.use(require('morgan')('short')); 
  8.  
  9. // ************************************ 
  10. // This is the real meat of the example 
  11. // ************************************ 
  12. (function() { 
  13.   // Step 1: 引入 webpack 的配置文件和 生成 webpack 的编译器 
  14.   var webpack = require('webpack'); 
  15.   var webpackConfig = require(process.env.WEBPACK_CONFIG ? process.env.WEBPACK_CONFIG : './webpack.config'); 
  16.   var compiler = webpack(webpackConfig); 
  17.   // Step 2: 将编译器挂载给 webpack dev middleware 
  18.   app.use(require("webpack-dev-middleware")(compiler, { 
  19.     noInfo: true, publicPath: webpackConfig.output.publicPath 
  20.   })); 
  21.  
  22.   // Step 3: 将编译器挂载给 webpack hot middleware 
  23.   app.use(require("webpack-hot-middleware")(compiler, { 
  24.     log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000 
  25.   })); 
  26. })(); 
  27.  
  28. // 定义 express 配置 
  29.  
  30. app.get("/", function(req, res) { 
  31.   res.sendFile(__dirname + '/index.html'); 
  32. }); 
  33. app.get("/multientry", function(req, res) { 
  34.   res.sendFile(__dirname + '/index-multientry.html'); 
  35. }); 
  36.  
  37. if (require.main === module) { 
  38.   var server = http.createServer(app); 
  39.   server.listen(process.env.PORT || 1616, function() { 
  40.     console.log("Listening on %j", server.address()); 
  41.   }); 
  42. }  

webpack-hot-middleware example server.js

区分开发和生产环境

要注意的是一定要在定义 express router 前定义 webpack 相关的中间件。还有一点是这里server.js 只是开发环境中使用,在生成环境中我们就不需要再用到它们了。所以在我们实际的使用中需要通过定义环境变量来区分开发和生产环境


  1. var NODE_ENV = process.env.NODE_ENV || 'production'; 
  2. var isDev = NODE_ENV === 'development'; 
  3.  
  4. if (isDev) { 
  5.     var webpack = require('webpack'), 
  6.         webpackDevMiddleware = require('webpack-dev-middleware'), 
  7.         webpackHotMiddleware = require('webpack-hot-middleware'), 
  8.         webpackDevConfig = require('./build/webpack.config.js'); 
  9.  
  10.     var compiler = webpack(webpackDevConfig); 
  11.  
  12.     app.use(webpackDevMiddleware(compiler, { 
  13.         publicPath: webpackDevConfig.output.publicPath, 
  14.         noInfo: true, 
  15.         stats: { 
  16.             colors: true 
  17.         } 
  18.     })); 
  19.  
  20.     app.use(webpackHotMiddleware(compiler)); 
  21.  
  22.     routerConfig(app, { 
  23.         dirPath: __dirname + '/server/routes/', 
  24.         map: { 
  25.             'index': '/', 
  26.             'api': '/api/*', 
  27.             'proxy': '/proxy/*' 
  28.         } 
  29.     }); 
  30.  
  31.     var reload = require('reload'); 
  32.     var http = require('http'); 
  33.  
  34.     var server = http.createServer(app); 
  35.     reload(server, app); 
  36.  
  37.     app.use(express.static(path.join(__dirname, 'public'))); 
  38.  
  39.     server.listen(port, function() { 
  40.         console.log('App (dev) is now running on port ' + port + '!'); 
  41.     }); 
  42. } else { 
  43.     routerConfig(app, { 
  44.         dirPath: __dirname + '/server/routes/', 
  45.         map: { 
  46.             'index': '/', 
  47.             'api': '/api/*', 
  48.             'proxy': '/proxy/*' 
  49.         } 
  50.     }); 
  51.     app.use(express.static(path.join(__dirname, 'public'))); 
  52.  
  53.     app.listen(port, function() { 
  54.         console.log('App (dev) is now running on port ' + port + '!'); 
  55.     }); 
  56. }  

supervisor

以上在前端我们实现了前端文件的热更新,但是我们在修改服务端文件的时候,并不会使Node自动重启,所以我们使用 supervisor 来作为监听文件修改事件来自动重启 Node服务。supervisor 需要 全局安装


  1. npm install supervisor -g 

安装完成之后我们就可以在命令行中使用我们在 package.json 的 scripts 中写好常用的命令,之后只用 npm run xxx 即可使用


  1. "scripts": { 
  2.     "dev": "export NODE_ENV=development && supervisor -w server,app.js app", 
  3.     "build": "node build/build.js", 
  4.     "start": "node app" 
  5.   },  

node-supervisor


  1. supervisor [options] <program> 
  2. supervisor -w server,app.js app  

-w 就是一个 options 配置项,它用于监听指定目录或者文件的变更,可以使用,分隔,监听多个目录或者文件,这就是监听了 server 目录和根目录的 app.js 到变更之后就会重启我们的 Express 入口文件 app。

我自己的解读

首先解释下他原文代码几个难以解释的点:

  • getEntries 这个是我们自己的载入入口文件的统一方法,具体内容其实我前面的文章提到过,就是规定好了的,文件夹目录下的main.js就是我们的入口文件,其他全部忽略,原因也说过,这里再说一次,规定死了,简单,方便,利于合作。
  • var publicPath = 'http://0.0.0.0:7799/dist/';
    这里publicPath和大家平时配置Webpack的publicPath
    不太一样的原因,是需要Express能够认出绝对地址来,因为你项目是Express大于Webpack的。
  • routerConfig 这个方法是我们自己的一个方法,内容前面文章提到过,就是我写的一个用来加载所有路由的方法,免得重复写各种引用。npm地址
  • export NODE_ENV=development 注意这里,windows的环境可能会失败,可以替换成 cross-env NODE_ENV=development

其次说下这么做的原因

  • 前面文章也提到了,我司目前前端的整体架构是使用Node做中间层,那么问题就是Node渲染层会高于Webpack层,而且很多时候,不一定使用SPA的方式,要兼容这个架构,所以才需要这些配置。
  • 这个配置也很好解决了我们开发中的几个痛点,一个Node自动重新,一个文件热更新,结合起来,基本不需要自己不停的手动刷新浏览器,而且能保存当前状态,这点很关键,能节省不少时间,提升开发效率。
  • 当然也有痛点,例如要多一个模板文件,而且文件目录要根据规范来,要不是不会渲染的。

最后记得这是一个连续的文章系列,只看这里你不一定能配置成功!!!!

作者:乖小鬼YQ

来源:51CTO

时间: 2024-09-03 01:05:40

如何打造一个令人愉悦的前端开发环境(四)的相关文章

如何打造一个令人愉悦的前端开发环境(一)

文章来源 最近几年,前端发展越来越迅速,各种萌新加入了前端这个大家庭,大有赶IOS.超Android的趋势呀!同时,萌新们提出了各种前端工作问题,除了最基础的html.css.js三板斧之外,最让人头疼的应该是关于环境的配置问题,所以以环境作为切入点,开始一系列的前端开发环境配置文章. 主要会涉及到打包.构建.编程工具.debug等等前端环境,以及前后端分离.Nodejs中间层使用伸展面. 工欲善其事必先利其器 以编程工具而言WebStorm.sublime.Atom.VS Code.Brack

如何打造一个令人愉悦的前端开发环境(二)

前情提要 上一篇文章介绍了目前前端比较流行的各种编辑器,以及各种流行的打包方式,最后给了一个Gulp的例子,这个例子还是14年的时候写的,还有一些可以优化的空间,就不讨论了,这篇文章主要讲目前火热的打包构建方式--Webpack的使用方式. 主菜--没有开胃汤 其实Webpack的入门指导文章非常多,配置方式也各有各样,这里我推荐题叶大神的入门级指南--Webpack 入门指迷,如果不知道Webpack是什么或者不是很清楚各项配置含义的开发者,可以看此文章扫扫盲.毕竟我这篇文章并不是特别基础.

如何打造一个令人愉悦的前端开发环境(三)

往期回顾 前面2期都讲得是浏览器端的东西比较多,包括Webpack,虽然是Node处理的,但是还是浏览器端用的多,对于现在的前端开发来说,不懂一点服务端的东西,简直没办法活,一般的招聘要求都会加上要懂一门服务端的语言,例如:PHP,Java之类的啦.如图所示: 所以我们这期就讲Node的东西. 程咬金的三板斧 一.劈脑袋 -- 工具链 Node在前端领域使用最为广泛的就是工具链了,一期提到的构建工具都是Node写的,当然还有其他很多工具,比如:京东工程化,百度开源构建工具FIS3,微信发布的工作

在 windows(cygwin) 或 mac 下安装git、node、ruby等前端开发环境

我记得每一年技术部年会都会由老大说出一个技术主题,之前有服务化.模块化.工具化.数据化等等等等,今年的主题是全栈.遥想当年,在供职过的两家公司什么抗机器.装机架:什么安装系统.加固.搭建Web或任务应用的前后端环境.打包发布.网络配置:什么JavaScript.CSS.VB..NET.PHP.JAVA等等语言:什么Mootools.JQuery.Ext.Lucene.Solr.Hibernate.Spring:什么Oracle.Mysql.SQLServer.MongoDB.PL/SQL --

用 Docker 快速配置前端开发环境

本文讲的是用 Docker 快速配置前端开发环境[编者的话]最近在公司实践了一下 Docker,记录成了一篇文章,发出来和大家交流下.我基本上是个 Docker 新手,如果有什么地方说得不对请大家指出~目前的方案还比较粗糙,大家有什么改进建议也请告诉我,我多和大家学习 今天是你入职第一天. 你起了个大早,洗漱干净带着材料去入职. 签了合同,领了机器,坐到工位,泡一杯袋装红茶,按下开机键,输入密码, 然后,下载 Chrome.Postman.Sublime.盗版 PS.NodeJS.配置 NODE

搭建前端开发环境——docker篇

一.解决痛点 免搭建前端静态环境 分支切换,无需重新启动编译(package.json或gulpfile.js文件改变除外) nginx可自行配置,满足不同项目的需求 二.前端静态搭建思路 基于ubuntu系统环境,利用nginx静态资源服务器经过docker暴露出来的端口进行请求转发,这样后端的开发机上面只需要安装docker就能够访问前端的静态资源,不需要访问前端开发机. 三.具体解决方案 用 Kitematic 客户端实现跨平台运行 Docker 用端口映射预览 Docker 里的文件 用

《深入浅出iPhone/iPad开发(第2版)》——Xcode是一个全功能的集成开发环境

Xcode是一个全功能的集成开发环境 深入浅出iPhone/iPad开发(第2版) Xcode远不只是一个文本编辑器.正如你所看到的,Xcode含有模板,帮助你开始一个应用程序的开发.根据你的应用程序,你可以使用所有的模板,或者只是使用一部分,但你总是能够以它们中的一个作为模板开始.一旦你选择了基础应用程序模板,你可以使用Xcode做更多的事. 维护你的项目资源 Xcode会为你的项目创建一个新的目录,并将各种文件放入这个子目录中.你没有必要坚持默认的布局,但是如果你决定要整理它们,完全可以用X

用 Ubuntu 搭建一个 Ruby on Rails 本地开发环境

想要开发 Ruby on Rails 应用吗?虽然已经有一些(初级的)Ruby on Rails 教程了,但是似乎在如何搭建一个简洁而更新的本地开发环境方面还有些不甚确定的地方. 这个教程将引导你通过几个步骤来搭建一个基于 Ubuntu 的 Ruby on Rails 本地开发环境.而这个教程的即将发布的第二部分,将帮助你搭建一个 Ubuntu VPS.当前你暂时知道 VPS 是虚拟私有服务器的意思就可以了,让我们先将注意力转回到如何搭建本地开发环境中. Bf Frontpage in Set

单机搭建WinCE开发环境(四)

     前几天一直在测试VS2005下WinCE6.0中文模拟器的使用,基本上没有问题了.今天便在VS2008中安装了一下WinCE6.0中文模拟器的SDK,又遇到了一些小问题,贴出来跟大家分享一下,也算是对单机搭建WinCE开发环境的一个补充. 第一次安装时,一路默认,开始一切正常,到安装快完成的时候出现了状况,先后出现了下面的两个提示,点完OK后就Roll Back了.                      重新试了两遍,都是如此.难道VS2008不支持WinCE6.0?这似乎不大可能