模板网站搭建缺点-使用vite搭建ssr活动页面框架

前言

最近,我收到了一个要求重组公司活动页面项目的请求。 实现:

SEO MPA启动速度好,构建速度快后端工程浏览器至少兼容IE11

基于这个需求,我选择了vite+react+vite-plugin-ssr

文章后面是ssr的介绍,老手请跳过,直接看最后

SSR 入门 SSR 术语是什么

ssr,全称server side render,服务端渲染

csr,全称client side render,客户端渲染

spa,全称单页应用程序,单页应用程序

mpa,全称多页面应用程序,多页面应用程序

苏维埃社会主义共和国的历史

我的学习习惯是,无论学什么,先了解它的历史背景。存在就是合理的。 知道一项技术为什么形成会让我更容易理解这项技术

最初的网页渲染,前端三剑客:html+css+js,放在服务器上,静态部署可供用户访问。

后来随着网页复杂度的增加,出现了jsp/ejs等一系列模板句型。 服务器获取到数据后,将数据渲染到模板中,最后生成html返回给客户端。 这就是最原始的ssr。

随着后端框架(ng/react/vue)的诞生,越来越多的朋友开始使用框架来开发Web。 利用后端工程等新技术,强化后端工程。 而这些完全前馈的方式也带来了一些问题,比如对SEO非常不友好

企业社会责任的缺点

我们打开一个SPA网页(使用脚手架默认形式构建),右键查看网页源代码

第一个问题:SEO极其不友好。 页面上根本没有任何内容。 爬虫最喜欢这些网页,看一眼就离开。

SPA的工作方式是使用js动态渲染html,所有压力都交给客户端(浏览器)。 正因为如此,第二个问题也出现了:首屏加载速度慢

为什么又出现了ssr的需求

为了更好的SEO,为了更快的加载速度(服务器生成首页静态页面,客户端可以直接显示,然后用JS动态渲染)

前端开发使用react/vue,能熟练开发网页。 cra/vue-cli脚手架创建的默认模板是SPA。

那么我们应该如何实现“需要,而且”(我想要所有前端框架/seo)

如何实现基本的ssr

根据里面的问题,我们希望达到:

由于需要服务端渲染,而服务端是用来执行vue/react js框架的,所以第一反应就是使用nodejs来进行服务端渲染,因为nodejs自然是执行js代码

对于客户端,使用vue(react也可以,不过最近熟悉vue3),对于vue3来说,体积比react小,toC网站更好。 React18 发布了 ssr 的新 API。 开发者可以使用React.lazy和suspense来实现延迟加载,这也提供了良好的用户体验:github.com/reactwg/rea...

以下是一个基本的ssr示例

请注意以下情况:客户端使用esm规范,服务端使用cjs

如果想统一使用esm,可以使用tsx执行node脚本或者更改package.json => type: "module"

模板网站搭建缺点-使用vite搭建ssr活动页面框架

创建服务器

const express = require('express')
const app = express()
app.get('*', (req, res) => {
  res.send('Hello World')
})
app.listen(4000, () => {
  console.log('Server running at http://localhost:4000');
})

启动服务后,打开浏览器http:localhost:4000,可以看到内容

渲染vue

服务器有,但是是返回的字符串。 我们想使用vue来开发,尝试返回一个vue组件

Vue3提供了在服务器端渲染组件的方式,在vue/server-renderer下

const express = require('express')
const { renderToString } = require('vue/server-renderer')
const { createSSRApp } = require('vue')
const app = express()
app.get('*', (req, res) => {
  const vue = createSSRApp({
    data: () => ({ count: 1 }),
    template: ``,
  })
  renderToString(vue).then((html) => {
    res.send(`
    
    
    
      
      
      
      Document
    
    
      
${html}
`
) }) }) app.listen(4000, () => { console.log('Server running at http://localhost:4000') })

此时打开页面,可以看到按钮,但是此时页面是静态的,因为页面已经在服务端渲染好了,但是客户端没有注入vue

右键查看网页源代码,可以看到按钮元素

客户端渲染

我们希望按钮的交互可以移动,此时客户端需要做渲染

const { createSSRApp } = require('vue')
const vue = createSSRApp({
  data: () => ({ count: 1 }),
  template: ``,
})
vue.mount('#app')

这段代码是不是很眼熟? 其实和服务端渲染返回的内容基本是一样的。 所以ssr的本质就是服务端渲染静态html+客户端渲染js

此外,为了在浏览器中加载客户端文件模板网站搭建缺点,我们还需要:

在server.js中添加server.use(express.static('.'))来托管客户端文件。 这里注意,在HTML shell中会添加js执行顺序来加载客户端入口文件,通过在HTML shell中添加Import Map来支持在浏览器中使用import * from 'vue'

const express = require('express')
const { renderToString } = require('vue/server-renderer')
const { createSSRApp } = require('vue')
const app = express()
app.get('/', (req, res) => {
  const vue = createSSRApp({
    data: () => ({ count: 1 }),
    template: ``,
  })
  renderToString(vue).then((html) => {
    res.send(`
    
    
    
      
      
      
      Document
      
      {
        "imports": {
          "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
        }
      }
      
      
    
    
      
${html}
`
) }) }) app.use(express.static('.')) app.listen(4000, () => { console.log('Server running at http://localhost:4000') })

此时打开本地地址,可以看到点击按钮的数字发生了变化

以上是最简单的ssr,在vue官网上可以找到。

我们甚至没有考虑后端路由、状态管理等,一个完整的ssr还需要一系列的设置。

网络路由

ssr web路由有两种形式

服务器路由 客户端路由 服务器路由

服务端路由是利用Web框架的路由能力,当匹配到某个路由时,返回相应的html代码,并加载相应的客户端代码,例如:

import express from 'express'
const router = express.Router()
router.get('/some-page', (req, res) => {
  // 返回 some-page 的html
  res.send(`
    
    
      
      
      
      Document
      
    
    
      
要渲染的html字符串
`
) })

服务器端路由跳转可以直接使用a标签

客户端路由

对于客户端路由,需要使用后端框架对应的路由库,vue-router/react-router等。

可以参考官方的反例

比较两种形式

服务器端路由适合页面碎片化的项目,比如活动页面,每次路由重定向都会刷新整个页面

客户端路由适合页面间交互较强的项目,例如产品页面,跳转路由不会刷新页面

使用vite作为ssr

Vue官方推荐了几个ssr的反例,包括Nuxt/Quasar等重度框架。为了对项目进行细粒度的控制,我使用了vite+ vite-plugin-ssr方案来做

vite-插件-ssr

类似于 Next.js / Nuxt,但作为“做一件事”的 Vite 插件。

一个类似于 Next/Nuxt 但只做一件事并且做得很好的 vite 插件

这个插件的文档非常详尽,github上也有很多反例。

插件的具体功能我就不详细说了。 你可以阅读官方文档。 这里我就讲一下这个插件(v0.3x)常规路由的工作原理。 下面的vite-plugin-ssr简称为vps

vps 的传统路由

VPS建议使用文件夹名称作为路由,这也是最方便的。 活动页面中页面之间没有交互,所以我选择了默认形式。

VPS指定一系列文件命名作为开发/构建遍历的条件。 以下4个名称将被vps收集,每个文件都有其独特的功能。我们不要只用page来命名文件。***

// Vite resolves globs with micromatch: https://github.com/micromatch/micromatch
// Pattern `*([a-zA-Z0-9])` is an Extglob: https://github.com/micromatch/micromatch#extglobs
export const pageFiles = {
  //@ts-ignore
  '.page': import.meta.glob('/**/*.page.*([a-zA-Z0-9])'),
  //@ts-ignore
  '.page.client': import.meta.glob('/**/*.page.client.*([a-zA-Z0-9])'),
  //@ts-ignore
  '.page.server': import.meta.glob('/**/*.page.server.*([a-zA-Z0-9])'),
  //@ts-ignore
  '.page.route': import.meta.glob('/**/*.page.route.*([a-zA-Z0-9])'),
}

dev阶段和build阶段的项目较大后,打包速度慢怎么办?

对于活动页面,每个页面之间没有连接。 其实我希望打包是增量打包,但是如果公共文件发生变化,很难防止全打包。 所以如果能够缓存打包的文件,就可以提高打包速度。

理想是美好的模板网站搭建缺点,但现实往往是相反的。 rollup2 不支持内容哈希,但好消息是 rollup3 支持,并将于近期发布

目前我们只能通过hack的形式实现内容hash,比如使用node的crypto模块做md5hash

import { createHash } from 'crypto'
import type { PreRenderedChunk } from 'rollup'
export function getContentHash(chunk: string | Uint8Array) {
  return createHash('md5').update(chunk).digest('hex').substring(0, 6)
}
export function getHash(chunkInfo: PreRenderedChunk) {
  return getContentHash(
    Object.values(chunkInfo.modules)
      .map((m) => m.code)
      .join(),
  )
}

然后在汇总输出中设置文件的名称

rollupOptions: {
  treeshake: 'smallest',
  output: {
    format: 'es',
    assetFileNames: (assetInfo) => {
      let extType = path.extname(assetInfo.name || '').split('.')[1]
      if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType!)) {
          extType = 'img'
          }
      const hash = getContentHash(assetInfo.source)
      return `assets/${extType}/[name].${hash}.[ext]`
    },
    
    chunkFileNames: (chunkInfo) => {
      const server = chunkInfo.name.endsWith('server') ? 'server-' : ''
      const name = chunkInfo.facadeModuleId?.match(/src/pages/(.*?)//)?.[1] || chunkInfo.name
      
      if (chunkInfo.isDynamicEntry || chunkInfo.name === 'vendor') {
        const hash = getHash(chunkInfo)
        return `assets/js/${name}-${server}${hash}.chunk.js`
      } else {
        return `assets/js/${name}-${server}[hash].chunk.js`
      }
    },
    entryFileNames: (chunkInfo) => {
      if (chunkInfo.name === 'pageFiles') {
        return '[name].js'
      }
      const hash = getHash(chunkInfo)
      return `assets/js/entry-${hash}.js`
    },
  },
},

做了content-hash之后,打包率会大大提高,因为虽然有rollup,但是缓存的文件不会进行transform,而恰好transform是一个非常长期的步骤。

我尝试打包1000个文件,花了40+s,在我可以接受的范围内

快速创建页面模板

活动页面会有更多的相似之处,所以直接根据模板创建页面代码,开发效率更高(又可以钓鱼了)代码地址

做得不好的地方

记录两个ssr探索过程,我想实现,但最终没有实现

部署

对于部署,我打算使用docker来进行,下一篇文章会讲到

源地址

反应+服务端响应

vue3+ssr

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 模板插件 模板网站搭建缺点-使用vite搭建ssr活动页面框架 https://www.wkzy.net/game/172812.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务