vue3预渲染和服务端渲染(同构)示例讲解

简介: vue3预渲染和服务端渲染(同构)示例讲解

大家好,我是 17

SSR 特别指支持在 Node.js 中运行相同应用程序的前端框架(例如 React、Preact、Vue 和 Svelte),将其预渲染成 HTML,最后在客户端 hydrating。

下面是关于 vue3 预渲染和服务端渲染的示例讲解。

本示例虽然是用 hotpack 工具,但原理是相通的,与工具无关。


示例项目


hotpack 为服务端渲染(SSR)提供了内建支持。下面的范例包含了Vue3 的 SSR 示例,可以作为本指南的参考。



源码结构


一个典型的 SSR 应用应该有如下的源文件结构


c9e9dd5550b946e684107c4a93b2bf5e_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

index 是一个页面的文件夹,里面包含index页面需要的内容

  • vue vue组件
  • index.b.js 客户端专用入口  b 是 browser的第一个字母
  • index.html html模板
  • index.s.js 服务端专用入口  s 是 server的第一个字母


情景体验


hotpack 可以进行浏览器渲染,预渲染和服务端渲染,支持多页,单页,可以自由选择

为了体验全部功能,我们先准备一下环境

  1. 安装 node 最低版本要求 14.0
  2. 安装 hotpack 并下载 vue3 ssr 示例项目


npm install -g hotpack
git clone https://github.com/duhongwei/hotpack-tpl-vue3.git my-app
cd my-app/main
npm install
复制代码

环境准备完毕,请保证在 my-app 项目的 main 目录执行后续的命令

hotpack 项目中 多页和单页并没有区别,单页只是在多页的基础上增加了路由而已。先从简单的多页说起


多页浏览器渲染


hotpack dev
复制代码

执行hotpack dev会启动开发环境,默认使用 3000 端口


hotpack dev -p 3001 指定为 3001端口

hotpack 的默认命令是 dev 所以 hotpack dev 也可以写成 hotpack

打开浏览器输入网址 localhost:3000 显示如下内容

404819ca4214475db69ae1e591f3f86a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


在页面上右键,选择 显示网页源代码 页面上只有空的 div,内容是浏览器请求到 js 后填充的

<div id='app' pre-ssr></div>
复制代码

pre-ssr 表示 可以 使用预渲染。但现在还没有起作用。

多页预渲染

预渲染不像服务器渲染那样即时编译 HTML,它只在构建时为了特定的路由生成特定的几个静态页面。

在开发环境,即使有 pre-ssr 标记,预渲染也是不开启的(开发环境配置文件一般配置为不开启,因为在我们开发页面的时候,是不需要预渲染的)在开发环境要启用预渲染很简单

hotpack dev -r
复制代码

默认还是 3000 端口, localhost:3000 打开页面后,在页面上右键,选择 显示网页源代码 我们清楚的看到页面已经渲染好了。

<div id='app'><div class="index"><h1>Hotpack Vue3 Multi Page Egxample</h1>...
复制代码

vue3 与 vue2 不同,在 div 上并没有渲染标记

多页服务端渲染

与预渲染不同,服务端渲染会时时编译生成 HTML,根据路径和数据实时渲染页面。

打开 page/index/index.html

<div id='app' pre-ssr ></div>
复制代码

修改 pre-ssr 为 ssr

<div id='app' ssr ></div>
复制代码

ssr 表示 可以 使用服务端渲染。在开发环境,即使有 ssr 标记,和预渲染原因一样,服务端渲染也是不开启的。开发环境默认都走浏览器渲染,这样开发效率较高。在命令行加上 -r 开启服务端渲染

hotpack dev -r
复制代码

默认 3000 端口, localhost:3000 打开页面后,在页面上右键,选择 显示网页源代码

<div id='app' ssr></div>
复制代码

还是空的div , 明明已经启用服务端渲染了呀!

其实是没错的,因为现在的server是 hotpack 的 server 并不是服务端的 server,hotpack 的 server 并没有根据数据时时编译的功能。在开发环境,编译好的文件都发到了 dev 目录,为了避免受到干扰,把 dev目录copy到上一级,我们可以到这里查看效果

cp -r dev ../
cd ../dev
npm install
node index.js
复制代码

查看源文件,果然已经渲染好了。

<div id='app'><div class="index"><h1>Hotpack Vue3 Multi Page Egxample</h1>...
复制代码


能不能实时编译呢?当然是可以的。

只不过...


为了简化项目,数据现在是固定的,直接在 api 函数里返回。所以就不能看到实时编译的效果了。如果大家有兴趣话,我会再写一篇时时请求真实数据的例子。

多页体验完了,下面我们体验下单页


单页浏览器渲染


打开浏览器输入网址 localhost:3000/single.html 显示如下内容

acd3881c2b114000a954d718734f7f01_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


查看源文件

<div id='app' ssr></div>
复制代码

并没有执行服务端渲染,原因和预渲染一样,开发环境需要加参数 -r 开启,并copy到上层目录查看效果

hotpack dev -rs
cp -r dev ../
cd ../dev
npm install
node index.js
复制代码

hotpack dev -s -s 参数在开发环境会阻止启动 server

查看原文件,内容在服务端已经渲染好了。


情景逻辑


浏览器渲染


浏览器渲染的入口在 index.b.js

import './index.html=>index.html'
复制代码

当前目录下的 index.html 作为模板,经过转换,发布到 /index.html web 目录,因为路径都是以 web 根目录为基准,所以 / 一律省略不写。

多页浏览器渲染

查看 page/index/index.b.js

if (window.__state__) {
  store.initState = window.__state__
}
else {
  store.dispatch('init')
}
复制代码

初始化数据。如果已经预渲染或服务端渲染,初始化的数据会保存在 window._state 中和页面一起发送到浏览器中。也就是客户端 hydrating。

多页比较简单,不需要路由。

store.dispatch('user') 是为了和 store.dispatch('init') 做对比。多页的初始化数据是可以预见的,所以把它们放在一起处理。并不是所有的数据都适合用同步的方式,异步数据可以单独请求。

单页浏览器渲染

单页在感觉上只是多了一个路由,但是复杂度可是增加了好多。如果window._state有数据,

if (window.__state__) {
  storeInfo.state = window.__state__
}
复制代码

如果没有window._state并不能象多页那样直接发一个 store.dispatch('init') 完事。单页是有客户端路由的,需要哪些数据是由路由决定的,一个想法是根据路由信息直接获取数据,但更好的做法是把获取数据的方法放在模块中。

page/single/vue/index

export default {
  name: 'index',
  ssr(store) {
    return store.dispatch('index')
  },
  ...
复制代码

在 index.b.js 中 根据路由找到所有相关的组件,再一一触发组件内的 ssr 方法

router.beforeResolve((to) => {   
  ...
  to.matched.forEach(record => {
    const components = Object.values(record.components)
    components.forEach(item => {
      if (item.ssr) {
        item.ssr(store)
      }
    })
  })
})
复制代码

最后要注意一个问题,如果window._state有数据,并不需要重复请求数据了,在 store 中判断一下就好

page/single/js/store.js


actions: {
    ...
    async index({ commit, state }) {
      if (state.index) return state.index
      const data = await getIndex()
      commit('index', data)
      return data
    }
 }
复制代码

.b.js 结尾的文件只在浏览器中运行


预渲染和服务端渲染


预渲染不需要服务端支持,是编译工具完成的。预渲染的结果是不变的。对于没有数据,或数据不常变化的页面非常适合预渲染。

服务端渲染的页面是时时变化的,是真正的动态页面。

预渲染和服务端渲染都是以 index.s.js 为入口


import './index.html=>index.html'
复制代码


这句除了指明模板路转换之外,还指明,这个 html 文件是渲染入口是 index.s.js。 用这种声明的方式来指明 html 与 js 的关联,是为了灵活性。html模板在源码中放在哪里没有关系,js 放在哪里也没有关系,随你所愿。


hotpack 在编译的时候,发现这句声明并把 html 和 js 的关联信息保存起来,方便后面查用。


.s.js 结尾的文件只在服务端运行


多页预渲染和多页服务端渲染


export default async function () {
    let store = Vuex.createStore(storeInfo)
    const state = await store.dispatch('init')
    let app = await init(component)
    app.use(store)
    return {
        app,
        state
    }
}
复制代码


hotpack 会执行这个函数,函数会返回 vue 的实例 app 和 初始化的数据 state,因为这个初始化数据是作为 store 的 state ,所以就命名 state 了。


对于预渲染,这个函数每编译一次就执行一次,对于服务端渲染,每次请求页面都会渲染一次。


服务渲染只需要初始化同步数据即可,所以这里没有 store.dispatch('user')


单页服务端渲染


对于单页而言,也是可以预渲染的,但是单页除首页外的页面本来就是异步请求的,所以对于异步页面,预渲染所带来的速度优势没有意义。hotpack  并不支持单页面的预渲染,但如果你愿意,是可以对默认首页进行预渲染的。

page/single/index.s.js

相比于预渲染,服务端渲染会传一个 ctx 进来,ctx 包含 url 等 请求相关的信息

export default async function (ctx = {}) {
...
const url = ctx.originalUrl || '/single'
router.push(url)
...
}
复制代码


根据路由找到相关的组件,触发组件内的 ssr 函数,获得相关数据,与浏览器逻辑不同的是,需要等待数据完成,再渲染内容


let components = null
  router.currentRoute.value.matched.flatMap(record => {
       components = Object.values(record.components)
  })
  let promiseList = components.map(item => item.ssr(store, ctx))
  await Promise.all(promiseList)
复制代码

整个页面都可以用插值的来修改内容,比如 title

<title>{{{title}}}</title>
复制代码
return {
      pageData: {
          title: router.currentRoute.value.meta.title
      },
      app,
      state: store.state
  }
复制代码

注意: <div id=“app ssr">这里不要放任何内容</div>

情景选择

不同渲染方式各有利弊。

浏览器渲染

成本最低,是最常用的方式,也是 hotpack 的默认方式。

预渲染

成本稍高,可以获得明显的速度优势,对于静态页面非常推荐。

服务端渲染

成本最高。若非必须,不建议采用。这并不光是成本的问题,还有对开发者能力的要求,需要掌握服务端的各种知识。


在一个应用中,有的页面适合浏览器渲染,有的页面适合预渲染,有的页面适合服务端渲染,有的适合单页,有的适合多页的是 在 hotpack  项目中各种渲染方式和页面形式都是直接支持的,它们之间是渐进的关系,可以随时相互转换。pre-ssr , ssr 标记、配置文件和命令行可以非常灵活的完成转换。

配置文件

配置文件在项目根目录的 .hotpack 文件加下,有三个文件 base.js, dev.js,pro.js,对应公共配置,开发配置和发布配置

预渲染和服务端渲染的配置很简单,如果只是预渲染,src 是可以不写的。

render: {
    //optional,服务端渲染必须,hotpack编译的时候把 render 里的文件 copy 到 dev(dist) 目录
    src: "render",
    //required,必须,是否启用
    enable: false
  },
复制代码

在 dev.js 中 ssr 是关闭的 enable:false。不过 命令行的优先级最高,可以随时在命令行开启 ssr

hotpack dev -r
复制代码

配置和命令行只能影响有标记 pre-ssr , ssr 的页面。

更多配置信息

开发实践

三种渲染方式的开发测试成本是逐渐升高的。用 hotpack 开发应用可以完美的协调成本与体验。

配置文件:开发环境配置为不启用 SSR,发布环境配置为启用 SSR。

开发的时候完全按浏览器渲染的方式开发。开发完成后,增加预渲染服务端渲染入口,通过 hotpack dev -r 查看效果。开发环境没有问题,可以发布  hotpack pro 在发布环境是不需要加 -r 的,因为配置文件中已经启用 了。发布的时候默认不会启动 server ,如果要启动可以 用 -s参数 hotpack pro -s。开发环境和发布环境的 -s 参数效果正好相反。

对于浏览器渲染和预渲染的页面,可以直接查看。查看服务端渲染的页面,需要把整个目录 copy 到一个纯净的环境中。因为发布的目录是需要 copy 到服务器上的,服务器上是一个全新的环境。开发环境发布目录默认是 dev 目录,发布环境默认是 pro 目录,上线的时候把 pro 目录 copy 到服务器上 ,执行 npm install 。服务端需要的文件放在 render 目录下。在示例项目中包含了最基本的文件,hotpack 会自动把 render 下的文件 copy 到 dev 或 pro目录。

/render

结束语

本篇文章主要是让大家体验一下,可能后面有更多详细介绍。

hotpack 并不会转换只能在服务端运行的代码,可以遵循下面的规则来避免环境问题。

  1. 只在服务端运行的文件名以 .s.js 结尾
  2. index.b.js,index.s.js(入口文件的名字可以不叫 index,叫什么并没有限制) 已经从源头上做了隔离,只在务端运行的文件只在 index.s.js中引用,只在浏览器中运行的文件只在 index.s.js 中引用
  3. 只在浏览器中运行的逻辑不要写在 beforeCreate,created 方法里

最后还可以在代码中做逻辑判断

//浏览器环境
if(typeof global==='undefined'){
  ...
}
//node 环境
else{
  ...
}
复制代码


共用的文件 正常命名 xx.js 即可。hotpack 的缓存非常强大,但是也可能会带来问题,你可以用 -c 参数来清除缓存。非必须不要清除缓存。


#清除开发环境缓存
hotpack dev -c
#清除发布环境缓存
hotpack pro -c


目录
相关文章
|
24天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
23天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
20天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
47 7
|
24天前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
35 9
|
21天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
40 3
|
20天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
39 1
|
20天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
42 1
|
22天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
23天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
24天前
|
JavaScript 搜索推荐 前端开发
Vue SSR 预渲染的广泛应用场景及其优势
【10月更文挑战第23天】Vue SSR 预渲染技术在众多领域都有着广泛的应用价值,可以显著提升网站的性能、用户体验和搜索引擎优化效果。随着技术的不断发展和完善,其应用场景还将不断拓展和深化
42 2
下一篇
无影云桌面