这可能是最简单的改造方案了

简介

SSR是Server Side Render简称。
在服务端使用node渲染出html页面,再传输到客户端。常规单页应用html只返回极少部分dom字符串+script动态生成网页,不利于google 爬虫的抓取。因此为了google更好的搜录网页,需要在服务端(node)执行js以渲染出html给google爬虫收录。

通用代码

  1. 由于js会在node端执行,所以不能包含客户端浏览器植入的对象,比如window,location,localStorage,navigator,document等对象。
  2. 服务端避免状态污染
    官网说明
    解释: 下面一段代码记录当前用户发起的请求数量

    export const http = axios.create({baseURL:'/api'})
    let count = 0
    http.interceptors.request.use((config) => {
     count++
     return config
    })

上述写法将使count持久化,当第二个用户发起请求时,会累加第一个用户的count,导致统计不正确。

  1. 不能在node端运行setInterval 等代码,因为此类代码对于服务端染没有任何意义,只会造成node内存泄漏,必须限制在客户端运行。

必须的api

  1. renderToString 可以将组件渲染成html字符串(可以用renderToNodeStream等流式渲染方法)
  2. Suspense 组件,此组件为node端获取数据后渲染的关键,使得由此组件包裹的后代组件,可以使用async setup函数
  3. vite.ssrLoadModule 利用vite将vue打包后执行,此为调试模式关键。

官网示例代码
但还有2个问题

  1. 如何获取数据后渲染
  2. 如何将服务端的请求数据持久化,避免客户端二次请求
1.获取数据后渲染的方法

在About组件中需要获取数据后渲染:

<script setup lang="ts">
const get = () => {
  return new Promise(res => {
    setTimeout(() => {
      res(Math.random())
    }, 3000)
  })
}
const b = await get()
</script>
<template>
<div>{{b}}</div>
</template>

app组件:

<template>
    <Suspense>
          <About/>
    </Suspense>
</template>

服务端代码:

const ctx = {}
const html = await renderToString(App,ctx)

上述html会渲染出b的数值。其中在组件中可以使用useSSRContext获取ctx。ctx.modules 含有当前所渲染页面需要的依赖js,css,图片等。可将这些依赖加入预下载,加快渲染速度。

2. 解决客户端重复请求数据

在服务端,请求数据后,将数据保存到window变量中,在客户端再次请求时,发现已经有数据了,那么就不必在此请求。直接使用window中的数据。是否删除次数据,依照情况而定。
此处附一个axios的请求做法。
ssrCache.ts

import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, Method } from 'axios'
import md5 from 'md5'
type MapData = {
    [k: string]: {
        data: any,
        count: number
    }
}
let map = {} as MapData
const getKey = (config: AxiosRequestConfig<any>) => {
    const { url, data, baseURL, params } = config
    let paramsStr = ''
    for (let key in params) {
        if (Object.prototype.hasOwnProperty.call(params, key))
            paramsStr += `${key}=${params[key]}`
    }
    return md5(baseURL || '' + url + data + paramsStr).substring(0, 8)
}


export const createProxy = (http: AxiosInstance) => {
    const ssr = typeof window === 'undefined'
    const request = async (config: AxiosRequestConfig<any>) => {
        const key = getKey(config)

        if (ssr) {
            if (map[key]) {
                // 只有当页面的数据请求数据做缓存。并且记录请求次数
                map[key].count++
                return map[key].data
            }
            try {
                const res = await http(config)
                let data = res
                if (res.request) data = res.data
                map[key] = { data, count: 1 }
                return res
            } catch (err) {
                return Promise.reject(err)
            }

        }
        if (window.__SSRDATA__ !== undefined) {
            const map = window.__SSRDATA__
            if (map[key]) {
                map[key].count-- 
                if (map[key].count === 0) {
                    delete map[key]
                }
                return map[key].data
            }
        }

        return http(config)
    }
    const fn = (method: string) => {
        return (url: string, config: AxiosRequestConfig<any>) => {
            return request({ url, method, ...config })
        }
    }

    const set = new Set(["get", "post", "options", "delete", "put", "patch"])
    // 此处做一层proxy,因为axios可以axios()使用,也可以axios.get()使用
    // 需要将他们转换成一样的请求模式
    return new Proxy(http, {
        get(target, key: keyof AxiosInstance) {
            if (set.has(key.toLowerCase())) {
                return fn(key)
            }
            return http[key]
        },
        apply(v) {
            return request(v)
        }
    }) as AxiosInstance
}

export const collect =() => {
    map={}
    return () => `<script>window.__SSRDATA__=${JSON.stringify(map)}</script>`
}

使用:

const http = createProxy(axios.create({baseURL:''}))

当在ssr中使用http获取数据,会将所有返回记录到map中,再将其转换为字符串潜入到html字符串中

const getScriptString = collect()
let html = await renderToString(App,ctx)
html = getScriptString() + html

吃个石头
34 声望0 粉丝

bug满天飞