默认已经了解ssr的基本内容和实现,不熟悉的能够先看下vue ssr服务端渲染小白解惑html
网上有关ssr开发环境搭建的文章不算多,就算找到也是比较高级的,不太适合新手入坑;这篇内容只抽取了其中最重要的部分,实现最基础的开发环境搭建;所谓开发环境无非两件事:自动打包·自动刷新页面,叫法比较土,也能够叫热更新,热加载。前端
先看目录结构
没啥东西,新增了一个hot.config.js文件,用来放置热加载的配置;
先看下server.jsvue
//server.js const express = require('express'); const chalk = require('chalk'); const server = express(); let renderer; const hotServer = require('./webpack.hot.config.js') //咱们但愿经过不停的执行下面这样一个函数的回调,重新实例化renderer,已达到自动更新的目的; hotServer(server,({serverBundle,clientManifest})=>{ console.log('hot***************************************************') renderer = require('vue-server-renderer').createBundleRenderer(serverBundle,{ runInNewContext: false, // 推荐 template: require('fs').readFileSync('./index.html', 'utf-8'), clientManifest // (可选)客户端构建 manifest }) }) server.use('/',express.static('./dist')) // 设置访问静态文件路径 server.get('*', (req, res) => { res.set('content-type', "text/html"); const context = { url:req.url } renderer.renderToString(context, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } else { res.end(html) } }) }) server.listen(8080,function(){ let ip = getIPAdress(); console.log(`服务器开在:http://${chalk.green(ip)}:${chalk.yellow(8080)}`) }) function getIPAdress(){//node下的os模块能够拿到启动该文件的服务端的部分信息,细节本身去node上面查 var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } }
跟自动化有关的代码只有和hotServer有关的几行代码,其实也是通过分析,对于这个server服务,有用的文件只有服务端配置vue-ssr-server-bundle.json和客户端配置vue-ssr-client-manifest.json,咱们只要在这两个文件打包以后重新实例化renderer就能够了,应为renderer实例化是经过加载静态文件而生产的一个再也不变化的实例;node
webpack.hot.config.js也能够写在server.js里面,不过看上去就不太好看,也不符合模块开发的原则;webpack
webpack自动打包有watch,或者使用webpack-dev-serve,dev-server会本身启一个服务,然而咱们有本身的server,他们之间通讯不是不能够,只是比较困难,不适用;watch也是同样,比较独立;
webpack能够独立使用,然而它也只是node中的一个模块,能够做为插件使用,并且能够经过其余插件和express结合使用;web
webpack-dev-middleware是实现热加载的核心组件,看一下webpack.hot.config.js的内容express
//webpack.hot.config.js const webpack = require('webpack'); //新增webpack-dev-middleware插件 const webpackDevMiddleware = require('webpack-dev-middleware'); const path=require('path') const clientConfig=require('./webpack.client.config.js') const serverConfig=require('./webpack.server.config.js') //输出一个函数给server使用 module.exports = function(server,callBack){ let b,c; //这里先定义一个run方法,保证vue-ssr-server-bundle.json和vue-ssr-client-manifest.json都有的状况才去执行回调; function run(){ console.log('run'); if(b && c){ console.log('runend ') callBack({serverBundle:JSON.parse(b),clientManifest:JSON.parse(c)}) } } //生成vue-ssr-server-bundle.json //实例化webpack const serverComplier = webpack(serverConfig); middleware = webpackDevMiddleware(serverComplier) server.use(middleware); //serverComplier是webpack返回的实例,plugin方法能够捕获事件,done表示打包完成 serverComplier.plugin('done',complation => { console.log('serverdown') //核心内容,middleware.fileSystem.readFileSync是webpack-dev-middleware提供的读取内存中文件的方法; //不过拿到的是二进制,能够用JSON.parse格式化; let serverBundle=middleware.fileSystem.readFileSync(path.join(serverConfig.output.path, 'vue-ssr-server-bundle.json')) //把拿到的文件复制给b b=serverBundle; run(); }) //生成vue-ssr-client-manifest.json,方法和上面如出一辙 const clientComplier = webpack(clientConfig) clientMiddleware = webpackDevMiddleware(clientComplier),{ noInfo: true, stats: { colors: true } } server.use(clientMiddleware) clientComplier.plugin('done',complation=>{ console.log('clientdown') let clientBundle=clientMiddleware.fileSystem.readFileSync(path.join(clientConfig.output.path, 'vue-ssr-client-manifest.json')) c=clientBundle; run() }) }
webpack-dev-middleware最大的特色就是把打包好的文件放到内存中本身玩,不生成文件,对于大部分项目没有问题,然而ssr须要读取静态文件才能继续玩,webpack-dev-middleware也确实提供了读取文件的方法middleware.fileSystem.readFileSync,相似fs插件;json
简单说明下webpack流程,webpack是单线程,在打包的过程当中会广播不一样的事件告诉你们webpack如今干到哪一步了,webpack()会返回一个实例compalier,经过compalier.plugin('done',()=>{})方式监听webpack的状况,这也是本身编写webpack 插件的核心内容;done就表示打包完了,咱们就这这个时候去拿咱们想要的文件就对了;segmentfault
默认上面的代码没有bug,下面这个插件没什么讲的,知道怎么用就好了;后端
直接上最终版的代码,server,js没有动,修改下webpack.hot.config.js:
//webpack.hot.config.js const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); //新增webpack-hot-middleware插件 const webpackHotMiddleware = require('webpack-hot-middleware'); const path=require('path') const clientConfig=require('./webpack.client.config.js') const serverConfig=require('./webpack.server.config.js') module.exports = function(server,callBack){ let b,c; function run(){ console.log('run'); if(b && c){ console.log('runend ') callBack({serverBundle:JSON.parse(b),clientManifest:JSON.parse(c)}) } } const serverComplier = webpack(serverConfig) ; middleware = webpackDevMiddleware(serverComplier) server.use(middleware); serverComplier.plugin('done',complation => { console.log('serverdown') let serverBundle=middleware.fileSystem.readFileSync(path.join(serverConfig.output.path, 'vue-ssr-server-bundle.json')) b=serverBundle; run(); }) //修改入口文件,固定写法 clientConfig.entry=['./entry-client.js','webpack-hot-middleware/client?reload=true']; //新增自动更新插件,固定写法 clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); //上面两条要写在下面这个实例化以前,很好理解 const clientComplier = webpack(clientConfig) clientMiddleware = webpackDevMiddleware(clientComplier),{ noInfo: true, stats: { colors: true } } server.use(clientMiddleware) clientComplier.plugin('done',complation=>{ console.log('clientdown') let clientBundle=clientMiddleware.fileSystem.readFileSync(path.join(clientConfig.output.path, 'vue-ssr-client-manifest.json')) c=clientBundle; run() }) //只须要加载这一个就好了 server.use(webpackHotMiddleware(clientComplier)); }
node server.js 试一下。讲道理没有问题0.0
上面代码优化的点其实很是多,除了随意的变量名,就是生成两个文件的方式,同样的代码不少,并且vue-ssr-server-bundle.json是否是能够不用webpack-dev-middleware拿。这只是一个简单的例子,能用,但还不够。
实现express+webpack的开发模式就是利用早期的dev-middleware插件,dev-serve只适合纯前端,这里的难点就是怎么从内存中拿到真实的文件,拿到后怎么处理;虽然只是几行代码,理解起来却不太容易;还有就是webpack的打包原理,仍是要学一下;
有关node先后端一块儿开发的开发环境搭建我这里也有一篇入门的文章:
手动搭建vue+node单页面(一)