15分钟学会vue项目改造成SSR(小白教程)(3)

官方的例子是定义一个asyncData函数(这个名字不是唯一的哈,是自己定义的,可以随便取,不要理解为内置的函数哈),这个函数写在路由组件里面。
假设有一个Item.vue组件(官网的例子)

<!-- Item.vue --> <template> <div>{{ item.title }}</div> </template> <script> export default { asyncData ({ store, route }) { // 触发 action 后,会返回 Promise return store.dispatch('fetchItem', route.params.id) }, computed: { // 从 store 的 state 对象中的获取 item。 item () { return this.$store.state.items[this.$route.params.id] } } } </script>

2. 服务端入口entry-server.js配置

到这里,asyncData函数,我们知道它是放在哪里了。接下来,我们有了这个函数,我们服务器肯定要去读到这个函数,然后去获取数据吧?我们把目光放到entry-server.js,之前我们提到过,这是服务端的入口页面。那我们是不是能够在这里面处理asyncData呢。下面还是官网的例子:

// entry-server.js import { createApp } from './app' export default context => { return new Promise((resolve, reject) => { const { app, router, store } = createApp() router.push(context.url) router.onReady(() => { const matchedComponents = router.getMatchedComponents() if (!matchedComponents.length) { return reject({ code: 404 }) } // 对所有匹配的路由组件调用 `asyncData()` Promise.all(matchedComponents.map(Component => { if (Component.asyncData) { return Component.asyncData({ store, route: router.currentRoute }) } })).then(() => { // 在所有预取钩子(preFetch hook) resolve 后, // 我们的 store 现在已经填充入渲染应用程序所需的状态。 // 当我们将状态附加到上下文, // 并且 `template` 选项用于 renderer 时, // 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。 context.state = store.state resolve(app) }).catch(reject) }, reject) }) }

简单的读下这段代码。首先为什么是返回Promise呢?因为可能是异步路由和组件,我们得保证,服务器渲染之前,已经完全准备就绪了。 然后注意**matchedComponents **它是通过传入的地址,获取到和路由匹配到的组件,然后如果存在asyncData,我们就去执行它,然后注入到context(渲染上下文,可以在客户端获取)里面。

是不是简单?这一步我们就已经从服务器端取到动态数据了,同时丢到页面里面了。如果不是为了客户端数据同步,这一步我们已经搞完服务端渲染了~ = =

3.客户端入口client-server.js配置

搞完服务器端的配置,该客户端了,毕竟数据要同步嘛。我们来看看客户端的入口文件代码:

const { app, router, store } = createApp() if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) }

之前服务端入口说过,状态将自动序列化为 window.__INITIAL_STATE__,并注入 HTML。
所以客户端我们获取到了,服务端已经搞好了数据了,我们拿过来直接替换现有的vuex就好了。

看到这里,不是已经完成啦,完整的流程。但是到此为止了吗?还没呢,既然是服务端渲染,你总要启动服务器吧…

Ps: 数据预期,我们刚才讲到的只是服务端预取,其实还有客户端预取。什么是客户端预取呢,简单的理解就是,我们可以在路由钩子里面,找有当前路由组件没有asyncData,有的话,就去请求,获取到数据后,填充完之后,再渲染页面。

六、启动服务(server.js)配置

服务端渲染,服务端,肯定要一个启动服务的文件哈,

const express = require("express"); const fs = require('fs'); let path = require("path"); const server = express() const { createBundleRenderer } = require('vue-server-renderer') let renderer const resolve = file => path.resolve(__dirname, file) const templatePath = resolve('./src/index.template.html') function createRenderer (bundle, options) { return createBundleRenderer(bundle, Object.assign(options, { runInNewContext: false })) } const template = fs.readFileSync(templatePath, 'utf-8') const bundle = require('./dist/vue-ssr-server-bundle.json') const clientManifest = require('./dist/vue-ssr-client-manifest.json') renderer = createRenderer(bundle, { template, clientManifest }) server.use(express.static('./dist')) // 在服务器处理函数中…… server.get('*', (req, res) => { const context = { url: req.url } // 这里无需传入一个应用程序,因为在执行 bundle 时已经自动创建过。 renderer.renderToString(context, (err, html) => { // 处理异常…… res.end(html) }) }) server.listen(3001, () => { console.log('服务已开启') })

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ecb88a369c94909a2e06662b090c6c80.html