当前位置: 首页 > 科技观察

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

时间:2023-03-16 23:04:29 科技观察

Express结合Webpack实现HMR本文档主要讲Webpack与Express结合实现前后端热更新开发。如果你对webpack了解不多,建议阅读webpack官网文档WhatiswebpackdevserverWebpackdevserver是一个轻量级的node.jsexpressserver,实现webpack编译后代码的实时输出更新。常用于前后端分离的前端项目开发中。不过这篇文章不应该讲。webpackdev中间件Webpackdev中间件是WebPack的中间件。它用于在Express中分发需要通过WebPack编译的文件。可以单独使用,完成代码的热重载功能。特点:不会向硬盘写入任何文件,完全基于内存。如果你使用watch模式监控代码变化,Webpack会自动编译。如果在Webpack编译期间请求文件,Webpackdev中间件会延迟请求,直到编译完成后再发送编译好的文件。webpack热中间件Webpack热中间件订阅Webpack的编译更新,然后通过执行webpack的HMRapi将这些代码模块的更新推送到浏览器端。HMRHMR即HotModuleReplacement是Webpack的一个重要功能。它允许我们将更新代码实时应用到当前页面,而无需手动刷新浏览器页面。HMR的实现原理是在开发中的应用代码中加入HMRRuntime。它是HMR客户端(浏览器客户端)模块,用于与开发服务器通信并接收更新。服务端的工作就是前面提到的Webpack热中间件,它会在代码更新编译完成后,以json格式输出到HMRRuntime,动态更新相应的代码。webpack如何配置首先介绍一下varwebpack=require('webpack');varHotMiddleWareConfig='webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000'module.exports={context:__dirname,entry:[//添加一个客户端HotMiddleWareConfig那个与HotMiddleWare通信,//添加web应用入口文件'./client.js'],output:{path:__dirname,publicPath:'/',filename:'bundle.js'},devtool:'#source-map',plugins:[newwebpack.optimize.OccurenceOrderPlugin(),//引入webpack.HotModuleReplacementPluginnewwebpack.HotModuleReplacementPlugin(),newwebpack.NoErrorsPlugin()],};webpack-hot-middleware示例webpack在webpack插件.config.js中配置在我们的开发环境中是这样的。getEntries根据我们的规则自动获取入口文件,并添加webpack热中配置。varwebpack=require('webpack');varpath=require('path')varmerge=require('webpack-merge')varbaseConfig=require('./webpack.base')vargetEntries=require('./getEntries')varpublicPath='http://0.0.0.0:7799/dist/';varhotMiddlewareScript='webpack-hot-middleware/client?reload=true';varassetsInsert=require('./assetsInsert')module.exports=merge(baseConfig,{entry:getEntries(hotMiddlewareScript),devtool:'#eval-source-map',output:{filename:'./[name].[hash].js',path:path.resolve('./public/dist'),publicPath:publicPath},插件:[newwebpack.DefinePlugin({'process.env':{NODE_ENV:'"development"'}}),newwebpack.optimize.OccurenceOrderPlugin(),newwebpack.HotModuleReplacementPlugin(),newwebpack.NoErrorsPlugin(),newassetsInsert()]})ExpressConfigurationinExpress主要包含4个步骤:导入webpack配置文件和生成webpack编译器连接编译器到webpackdev中间件连接编译器到webpack热中间件定义express配置varhttp=require('http');变量表达式ess=require('express');varapp=express();app.use(rerequire('morgan')('short'));//*****************************************//Thisistherealmeatoftheexample//******************************************(function(){//Step1:引入webpack配置文件,生成webpack编译器webpackConfig);//Step2:挂载编译器到webpackdevmiddlewareapp.use(require("webpack-dev-middleware")(compiler,{noInfo:true,publicPath:webpackConfig.output.publicPath}));//Step3:挂载compilertowebpackhotmiddlewareapp.use(require("webpack-hot-middleware")(compiler,{log:console.log,path:'/__webpack_hmr',heartbeat:10*1000}));})();//定义表达配置app.get("/",function(req,res){res.sendFile(__dirname+'/index.html');});app.get("/multientry",function(req,res){res.sendFile(__dirname+'/index-multientry.html');});if(require.main===module){varserver=http.createServer(app);server.listen(process.env.PORT||1616,函数n(){console.log("Listeningon%j",server.address());});}webpack-hot-middleware示例server.js区分开发环境和生产环境。需要注意的是,在定义expressrouter之前定义webpack相关的中间件还有一点就是这里的server.js只是在开发环境中使用,在生产环境中我们不需要使用它们。所以在我们实际使用中,需要通过定义环境变量varNODE_ENV=process.env.NODE_ENV||'production';varisDev=NODE_ENV==='development';if(isDev){varwebpack=require来区分开发环境和生产环境('webpack'),webpackDevMiddleware=require('webpack-dev-middleware'),webpackHotMiddleware=require('webpack-hot-middleware'),webpackDevConfig=require('./build/webpack.config.js');varcompiler=webpack(webpackDevConfig);app.use(webpackDevMiddleware(编译器,{publicPath:webpackDevConfig.output.publicPath,noInfo:true,stats:{colors:true}}));app.use(webpackHotMiddleware(compiler));routerConfig(app,{dirPath:__dirname+'/server/routes/',map:{'index':'/','api':'/api/*','proxy':'/proxy/*'}});varreload=require('reload');varhttp=require('http');varserver=http.createServer(app);reload(server,app);app.use(express.static(path.join(__dirname,'public')));server.listen(port,function(){console.log('App(dev)isnowrunningonport'+port+'!');});}else{routerConfig(app,{dirPath:__dirname+'/server/routes/',map:{'index':'/','api':'/api/*','proxy':'/proxy/*'}});app.use(express.static(path.join(__dirname,'public')));app.listen(port,function(){console.log('App(dev)isnowrunningonport'+port+'!');});}上面的supervisor在前端实现了前端文件的热更新,但是当我们修改服务端文件时,Node不会自动restart,所以我们使用Supervisor是用来监听文件修改事件来自动重启Node服务的。supervisor需要全局安装npminstallsupervisor-g安装完成后,我们可以使用我们在package.json的scripts中写的常用命令,然后只用npmrunxxx就可以使用"scripts":{"dev":"exportNODE_ENV=development&&supervisor-wserver,app.jsapp","build":"nodebuild/build.js","start":"nodeapp"},node-supervisorsupervisor[options]supervisor-wserver,app.jsapp-w是一个options配置项,用于监听指定目录或文件的变化。它可用于分离和监视多个目录或文件。这是监听服务器目录和根目录下的app.js发生变化后,我们的Express入口文件app会重启。我自己的解读先说明他原代码中的几个难点:getEntries这个是我们自己统一的加载入口文件的方法。具体内容其实在我之前的文章里有提到,是规定的。文件夹目录下的main.js是我们的入口文件,其他的都忽略。原因已经说了,这里又是规则死板,简单方便,有利于合作。varpublicPath='http://0.0.0.0:7799/dist/';这里的publicPath之所以和大家平时给Webpack配置的publicPath不一样,是因为Express需要能够识别绝对地址,因为你的项目是Express而不是Webpack的。routerConfig这个方法是我们自己的方法。上一篇文章提到过,是我写的一个方法,加载所有路由,以免重复写各种引用。npmaddressexportNODE_ENV=development这里要注意,windows环境可能会失效,可以换成cross-envNODE_ENV=development其次,这样做的原因在上一篇文章中也有提到。我们目前的整体前端架构都是采用Node作为中间层,那么问题就是Node渲染层会高于Webpack层,而且很多情况下不一定要用SPA的方式,必须是与此架构兼容,因此需要这些配置。这个配置也解决了我们开发中的几个痛点。Node自动重启,文件热更新。结合起来,基本不需要手动刷新浏览器,而且可以保存当前状态,这点很重要,可以节省大量时间,提高开发效率。当然也有痛点,比如多了一个模板文件,文件目录一定要按照规范,否则无法渲染。***记住这是一个连续的系列文章,只看这里不一定能配置成功!!!!