const Vue = require("vue"); const createRouter = require("./router") module.exports = (context) => { const router = createRouter(); const app = new Vue({ router, data:{ message:"Hello,Vue SSR!" }, template:` <div> <h1>{{message}}</h1> <ul> <li>, <router-link to="https://www.jb51.net/">首页<router-link/> </li> <li> <router-link to="/about">关于我<router-link/> </li> </ul> </div> <router-view></router-view> ` }); return { app, router } }
通过上面的改造之后,就可以在entry-server.js中轻松的拿到vue和router的实例了,现在查看一下当前entry-server.js中有那些可用参数,vue,router,提及到的URL从哪里来?既然这个函数是给服务端使用的,那么当服务端去执行这个函数的时候,就可以通过参数形式传递进来,获取到我们想要的参数,我们假设这个参数叫做url,我们需要让路由去做的就是跳转到对应的路由中(这一步很重要),然后再把对router的实例挂载到vue实例中,然后再把vue实例返回出去,供vueServerRender消费。那么就需要导出这个函数,以供服务端使用。
由于我们不能预测到用户所访问的路由就是在vue-router中所配置的,所以需要在onReady的时候进行处理,我们可以通过router的getMatchedComponents这个方法,获取到我们所导入的组件,这些有个我们就可通过判断组件对匹配结果进行渲染。
entry-server.js
const createApp = require("./app.js"); model.exports = (context) => { return new Promise((reslove,reject) => { let {url} = context; let {app,router} = createApp(context); router.push(url); // router回调函数 // 当所有异步请求完成之后就会触发 router.onRady(() => { let matchedComponents = router.getMatchedComponents(); if(!matchedComponents.length){ return reject({ code:404, }); } reslove(app); },reject) }) }
既然实例又发生了变化,需要对应发生变化的index.js同样也需要做出对应的改动。把刚才的引入vue实例的路径改为entey-server.js,由于这里返回的是一个Promise对象,这里使用async/await处理接收一下,并拿到vue实例。不要忘了把router所需要的url参数传递进去。
index.js:
const express = require("express"); const App = require("./src/entry-server.js"); const vueServerRender = require("vue-server-render").creteRender(); const app = express(); app.get('*',async (request,respones) => { respones.status(200); respones.setHeader("Content-Type","text/html;charset-utf-8;"); let {url} = request; // 这里可以传递给vue实例一些参数 let vm = await App({url}); vueServerRender.renderToString(vm).then((html) => { respones.end(html); }).catch(error => console.log(error)); }) app.listen(3000,() => { console.log("服务已启动") });
这下子就完成了,启动项目吧,当访问根路径的时候,就会看到刚才缺少的组件也已经渲染出来了,当然我们也可以切换路由,也是没有问题的。大功告成。。。好像并没有emmmmmmmmm,为什么,细心的话应该会发现,当我们切换路由的时候,地址栏旁边的刷新按钮一直在闪动,这也就是说,我们所做出来的并不是一个单页应用(手动笑哭),出现这样的问题也是难怪的,毕竟我们没有配置前端路由,我们把所有路由的控制权都交给了服务端,每次访问一个路由的时候,都会向服务端发送一个请求,返回路由对应的页面。想要解决这个问题,当处于前端的时候我们需要让服务端把路由的控制权交还给前端路由,让前端去控制路由的跳转。
之前在src文件夹下面添加了两个文件,只用到了服务端的文件,为了在客户端能够交还路由控制权,要对web端路由进行配置。由于在客户端在使用vue的时候需要挂载一个document,因为vue的实例已经创建完成了,所以,这里需要使用$mount这个钩子函数,来完成客户端的挂载。同样为了解决懒加载这种类似的问题so~同样需要使用onReady里进行路由的处理,只有当vue-router加载完成以后再去挂载。
在客户端是使用的时候很简单,只需要把路由挂载到app里面就可以了。
entry-client.js
const createApp = require("./app.js"); let {app,router} = createApp({}); router.onReady(() => { app.$mount("#app") });