iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue中SSR如何实现
  • 765
分享到

Vue中SSR如何实现

2024-04-02 19:04:59 765人浏览 薄情痞子
摘要

这篇文章主要为大家展示了“Vue中SSR如何实现”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue中SSR如何实现”这篇文章吧。思路Vue 提供了一个官方 D

这篇文章主要为大家展示了“Vue中SSR如何实现”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue中SSR如何实现”这篇文章吧。

思路

Vue 提供了一个官方 Demo,该 Demo 优点是功能大而全,缺点是对新手不友好,容易让人看蒙。因此,今天我们来写一个更加容易上手的 Demo。总共分三步走,循序渐进。

  1. 写一个简单的前端渲染 Demo(不包含 ajax 数据);

  2. 将前端渲染改成后端渲染(仍然不包含 Ajax 数据);

  3. 在后端渲染的基础上,加上 Ajax 数据的处理;

第一步:前端渲染 Demo

这部分比较简单,就是一个页面中包含两个组件:Foo 和 Bar。

<!-- index.html -->
<body>
<div id="app">
 <app></app>
</div>
<script src="./dist/WEB.js"></script> <!--这是 app.js 打包出来的 JS 文件 -->
</body>
// app.js,也是 webpack 打包入口
import Vue from 'vue';
import App from './App.vue';
var app = new Vue({
 el: '#app',
 components: {
 App
 }
});
// App.vue
<template>
 <div>
 <foo></foo>
 <bar></bar>
 </div>
</template>
<script>
 import Foo from './components/Foo.vue';
 import Bar from './components/Bar.vue';
 export default {
 components:{
  Foo,
  Bar
 }
 }
</script>
// Foo.vue
<template>
 <div class='foo'>
 <h2>Foo</h2>
 <p>Component </p>
 </div>
</template>
<style>
 .foo{
 background: yellow;
 }
</style>
// Bar.vue
<template>
 <div class='bar'>
 <h2>Bar</h2>
 <p>Component </p>
 </div>
</template>
<style>
 .bar{
 background: blue;
 }
</style>

最终渲染结果如下图所示,源码请参考这里。

Vue中SSR如何实现

第二步:后端渲染(不包含 Ajax 数据)

第一步的 Demo 虽不包含任何 Ajax 数据,但即便如此,要把它改造成后端渲染,亦非易事。该从哪几个方面着手呢?

  1. 拆分 JS 入口;

  2. 拆分 Webpack 打包配置;

  3. 编写服务端渲染主体逻辑。

1. 拆分 JS 入口

在前端渲染的时候,只需要一个入口 app.js。现在要做后端渲染,就得有两个 JS 文件:entry-client.js 和 entry-server.js 分别作为浏览器和服务器的入口。

先看 entry-client.js,它跟第一步的 app.js 有什么区别吗? → 没有区别,只是换了个名字而已,内容都一样。

再看 entry-server.js,它只需返回 App.vue 的实例。

// entry-server.js
export default function createApp() {
 const app = new Vue({
 render: h => h(App)
 });
 return app; 
};

entry-server.js 与 entry-client.js 这两个入口主要区别如下:

  1. entry-client.js 在浏览器端执行,所以需要指定 el 并且显式调用 $mount 方法,以启动浏览器的渲染。

  2. entry-server.js 在服务端被调用,因此需要导出为一个函数。


2. 拆分 Webpack 打包配置

在第一步中,由于只有 app.js 一个入口,只需要一份 Webpack 配置文件。现在有两个入口了,自然就需要两份 Webpack 配置文件:webpack.server.conf.js 和 webpack.client.conf.js,它们的公共部分抽象成 webpack.base.conf.js。

关于 webpack.server.conf.js,有两个注意点:

  1. libraryTarget: 'commonjs2' → 因为服务器是 node,所以必须按照 commonjs 规范打包才能被服务器调用。

  2. target: 'node' → 指定 Node 环境,避免非 Node 环境特定 api 报错,如 document 等。

3. 编写服务端渲染主体逻辑

Vue SSR 依赖于包 vue-server-render,它的调用支持两种入口格式:createRenderer 和 createBundleRenderer,前者以 Vue 组件为入口,后者以打包后的 JS 文件为入口,本文采取后者。

// server.js 服务端渲染主体逻辑
// dist/server.js 就是以 entry-server.js 为入口打包出来的 JS 
const bundle = fs.readFileSync(path.resolve(__dirname, 'dist/server.js'), 'utf-8'); 
const renderer = require('vue-server-renderer').createBundleRenderer(bundle, {
 template: fs.readFileSync(path.resolve(__dirname, 'dist/index.ssr.html'), 'utf-8')
});

server.get('/index', (req, res) => {
 renderer.renderToString((err, html) => {
 if (err) {
  console.error(err);
  res.status(500).end('服务器内部错误');
  return;
 }
 res.end(html);
 })
});

server.listen(8002, () => {
 console.log('后端渲染服务器启动,端口号为:8002');
});

这一步的最终渲染效果如下图所示,从图中我们可以看到,组件已经被后端成功渲染了。源码请参考这里。

Vue中SSR如何实现

第三步:后端渲染(预获取 Ajax 数据)

这是关键的一步,也是最难的一步。

假如第二步的组件各自都需要请求 Ajax 数据的话,该怎么处理呢?官方文档给我们指出了思路,我简要概括如下:

  1. 在开始渲染之前,预先获取所有需要的 Ajax 数据(然后存在 Vuex 的 Store 中);

  2. 后端渲染的时候,通过 Vuex 将获取到的 Ajax 数据分别注入到各个组件中;

  3. 把全部 Ajax 数据埋在 window.INITIAL_STATE 中,通过 HTML 传递到浏览器端;

  4. 浏览器端通过 Vuex 将 window.INITIAL_STATE 里面的 Ajax 数据分别注入到各个组件中。

下面谈几个重点。

我们知道,在常规的 Vue 前端渲染中,组件请求 Ajax 一般是这么写的:“在 mounted 中调用 this.fetchData,然后在回调里面把返回数据写到实例的 data 中,这就 ok 了。”

在 SSR 中,这是不行的,因为服务器并不会执行 mounted 周期。那么我们是否可以把 this.fetchData

提前到 created 或者 beforeCreate 这两个生命周期中执行?同样不行。原因是:this.fetchData 是异步请求,请求发出去之后,没等数据返回呢,后端就已经渲染完了,无法把 Ajax 返回的数据也一并渲染出来。

所以,我们得提前知道都有哪些组件有 Ajax 请求,等把这些 Ajax 请求都返回了数据之后,才开始组件的渲染。

// store.js
function fetchBar() {
 return new Promise(function (resolve, reject) {
 resolve('bar ajax 返回数据');
 });
}

export default function createStore() {
 return new Vuex.Store({
 state: {
  bar: '',
 },
 actions: {
  fetchBar({commit}) {
  return fetchBar().then(msg => {
   commit('setBar', {msg})
  })
  }
 },
 mutations:{
  setBar(state, {msg}) {
  Vue.set(state, 'bar', msg);
  }
 }
 })
}
// Bar.uve
asyncData({store}) {
 return store.dispatch('fetchBar');
},
computed: {
 bar() {
 return this.$store.state.bar;
 }
}

组件的 asyncData 方法已经定义好了,但是怎么索引到这个 asyncData 方法呢?先看我的根组件 App.vue 是怎么写的。

// App.vue
<template>
 <div>
 <h2>App.vue</h2>
 <p>vue with vue </p>
 <hr>
 <foo1 ref="foo_ref"></foo1>
 <bar1 ref="bar_ref"></bar1>
 <bar2 ref="bar_ref2"></bar2>
 </div>
</template>
<script>
 import Foo from './components/Foo.vue';
 import Bar from './components/Bar.vue';

 export default {
 components: {
  foo1: Foo,
  bar1: Bar,
  bar2: Bar
 }
 }
</script>

从根组件 App.vue 我们可以看到,只需要解析其 components 字段,便能依次找到各个组件的 asyncData 方法了。

// entry-server.js 
export default function (context) {
 // context 是 vue-server-render 注入的参数
 const store = createStore();
 let app = new Vue({
 store,
 render: h => h(App)
 });

 // 找到所有 asyncData 方法
 let components = App.components;
 let prefetchFns = [];
 for (let key in components) {
 if (!components.hasOwnProperty(key)) continue;
 let component = components[key];
 if(component.asyncData) {
  prefetchFns.push(component.asyncData({
  store
  }))
 }
 }

 return Promise.all(prefetchFns).then((res) => {
 // 在所有组件的 Ajax 都返回之后,才最终返回 app 进行渲染
 context.state = store.state;
 // context.state 赋值成什么,window.__INITIAL_STATE__ 就是什么
 return app;
 });
};

还有几个问题比较有意思:

1、是否必须使用 vue-router?→ 不是。虽然官方给出的 Demo 里面用到了 vue-router,那只不过是因为官方 Demo 是包含多个页面的 SPA 罢了。一般情况下,是需要用 vue-router 的,因为不同路由对应不同的组件,并非每次都把所有组件的 asyncData 都执行的。但是有例外,比如我的这个老项目,就只有一个页面(一个页面中包含很多的组件),所以根本不需要用到 vue-router,也照样能做 SSR。主要的区别就是如何找到那些该被执行的 asyncData 方法:官方 Demo 通过 vue-router,而我通过直接解析 components 字段,仅此而已。

2、是否必须使用 Vuex? → 是,但也不是,请看尤大的回答。为什么必须要有类似 Vuex 的存在?我们来分析一下。

2.1. 当预先获取到的 Ajax 数据返回之后,Vue 组件还没开始渲染。所以,我们得把 Ajax 先存在某个地方。

2.2. 当 Vue 组件开始渲染的时候,还得把 Ajax 数据拿出来,正确地传递到各个组件中。

2.3. 在浏览器渲染的时候,需要正确解析 window.INITIAL_STATE ,并传递给各个组件。

因此,我们得有这么一个独立于视图以外的地方,用来存储、管理和传递数据,这就是 Vuex 存在的理由。

3、后端已经把 Ajax 数据转化为 HTML 了,为什么还需要把 Ajax 数据通过 window.INITIAL_STATE 传递到前端? → 因为前端渲染的时候仍然需要知道这些数据。举个例子,你写了一个组件,给它绑定了一个点击事件,点击的时候打印出 this.msg 字段值。现在后端是把组件 HTML 渲染出来了,但是事件的绑定肯定得由浏览器来完成啊,如果浏览器拿不到跟服务器端同样的数据的话,在触发组件的点击事件的时候,又上哪儿去找 msg 字段呢?

至此,我们已经完成了带 Ajax 数据的后端渲染了。这一步最为复杂,也最为关键,需要反复思考和尝试。具体渲染效果图如下所示,源码请参考这里。

Vue中SSR如何实现

效果

大功告成了吗?还没。人们都说 SSR 能提升首屏渲染速度,下面我们对比一下看看到底是不是真的。(同样在 Fast 3G 网络条件下)。

Vue中SSR如何实现

Vue中SSR如何实现

官方思路的变形

行文至此,关于 Vue SSR Demo便已经结束了。后面是我结合自身项目特点的一些变形,不感兴趣的读者可以不看。

第三步官方思路有什么缺点吗?我认为是有的:对老项目来说,改造成本比较大。需要显式的引入 vuex,就得走 action、mutations 那一套,无论是代码改动量还是新人学习成本,都不低。

有什么办法能减少对旧有前端渲染项目的改动量的吗?我是这么做的。

// store.js
// action,mutations 那些都不需要了,只定义一个空 state
export default function createStore() {
 return new Vuex.Store({
 state: {}
 })
}
// Bar.vue
// tagName 是组件实例的名字,比如 bar1、bar2、foo1 等,由 entry-server.js 注入
export default {
 prefetchData: function (tagName) {
 return new Promise((resolve, reject) => {
  resolve({
  tagName,
  data: 'Bar ajax 数据'
  });
 })
 }
}
// entry-server.js
return Promise.all(prefetchFns).then((res) => {
 // 拿到 Ajax 数据之后,手动将数据写入 state,不通过 action,mutation 那一套
 // state 内部区分的 key 值就是 tagName,比如 bar1、bar2、foo1 等
 res.forEach((item, key) => {
 Vue.set(store.state, `${item.tagName}`, item.data);
 });
 context.state = store.state;
 return app;
});
// ssrmixin.js
// 将每个组件都需要的 computed 抽象成一个 mixin,然后注入
export default {
 computed: {
 prefetchData () {
  let componentTag = this.$options._componentTag; // bar1、bar2、foo1
  return this.$store.state[componentTag];
 }
 }
}

以上是“Vue中SSR如何实现”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网JavaScript频道!

--结束END--

本文标题: Vue中SSR如何实现

本文链接: https://www.lsjlt.com/news/74258.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue中SSR如何实现
    这篇文章主要为大家展示了“Vue中SSR如何实现”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue中SSR如何实现”这篇文章吧。思路Vue 提供了一个官方 D...
    99+
    2022-10-19
  • vue中如何实现SSR服务端渲染
    本篇内容主要讲解“vue中如何实现SSR服务端渲染”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue中如何实现SSR服务端渲染”吧!一、SSR是什么Server-Side Rendering ...
    99+
    2023-06-29
  • vue如何使用ssr实现预取数据
    这篇文章主要介绍了vue如何使用ssr实现预取数据的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue如何使用ssr实现预取数据文章都会有所收获,下面我们一起来看看吧。Why在 Vue 的服...
    99+
    2023-07-04
  • Vue服务端如何渲染SSR
    这篇文章主要介绍“Vue服务端如何渲染SSR”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Vue服务端如何渲染SSR”文章能帮助大家解决问题。手写Vue服务端渲染概念:放在浏览器进行就是浏览器渲染,...
    99+
    2023-07-02
  • vue项目如何改造成SSR
    这篇文章主要介绍vue项目如何改造成SSR,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、改造技术的分析对比。一般来说,我们做seo有两种方式:1、预渲染我在性能优化的博客中说过,...
    99+
    2022-10-19
  • 如何解决Vue中SSR组件加载问题
    这篇文章主要介绍如何解决Vue中SSR组件加载问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Node 端渲染提示 window/document 没有定义业务场景首先来看一个简单...
    99+
    2022-10-19
  • React SSR中的限流怎么实现
    这篇文章主要介绍“React SSR中的限流怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“React SSR中的限流怎么实现”文章能帮助大家解决问题。为什么要限流如下所示...
    99+
    2023-07-02
  • 如何使用React Router4实现服务端渲染ssr
    这篇文章将为大家详细讲解有关如何使用React Router4实现服务端渲染ssr,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。我们已经熟悉React 服务端渲染(SSR...
    99+
    2022-10-19
  • vue如何实现垂直居中
    目录vue垂直居中vue div让文字内容水平垂直居中思想代码vue垂直居中 有些时候没有使用form表单,文字和输入框/按钮会出现不对齐的情况,此时需要将文字垂直居中。 情况如下:...
    99+
    2022-11-13
  • vue中如何实现弹框dialog
    这篇文章主要为大家展示了“vue中如何实现弹框dialog”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue中如何实现弹框dialog”这篇文章吧。效果如下,...
    99+
    2022-10-19
  • Vue如何实现MVVM
    这篇文章主要为大家展示了“Vue如何实现MVVM”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue如何实现MVVM”这篇文章吧。本文介绍了Vue的MVVM实现...
    99+
    2022-10-19
  • 如何实现vue-router
    这篇文章将为大家详细讲解有关如何实现vue-router,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。开始实现想象一下,如果自己实现了一个 vue-router,会怎么去...
    99+
    2022-10-19
  • Vue2中SSR如何缓存Api 数据
    小编给大家分享一下Vue2中SSR如何缓存Api 数据,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!具体如下:1. 安装缓存依赖...
    99+
    2022-10-19
  • Vue中如何实现路由传参
    本篇文章给大家分享的是有关Vue中如何实现路由传参,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。情景模拟:A页面中,有一些div是根据A中的b...
    99+
    2022-10-19
  • Vue中如何实现3D标签云
    今天小编给大家分享一下Vue中如何实现3D标签云的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2022-10-19
  • vue中如何实现路由跳转
    这篇文章主要介绍vue中如何实现路由跳转,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!跳转方式:1、用“<router-link :to="{..}">”语句;2、用“this.$ro...
    99+
    2023-06-25
  • vue中的vue-print-nb如何实现页面打印
    目录安装在main.js中全局引入页面中使用安装 npm install vue-print-nb --save 在main.js中全局引入 import Print from 'v...
    99+
    2022-11-13
  • Vue如何实现日历
    这篇文章主要介绍了Vue如何实现日历,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。组件(component)是Vue最强大的功能之一。组件可...
    99+
    2022-10-19
  • vue如何实现同步
    本篇内容主要讲解“vue如何实现同步”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue如何实现同步”吧!vue实现同步的方法:1、创建一个vue示例文件;2、通过“data(){return ...
    99+
    2023-07-05
  • vue如何实现v-model
    本文小编为大家详细介绍“vue如何实现v-model”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue如何实现v-model”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。为什么使用v-modelv-model...
    99+
    2023-07-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作