You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ionic 6 Vue项目接入SSR并部署需求求助

Ionic 6 + Vue.js SSR 添加与部署解决方案

一、依赖配置与脚本更新

  1. 安装SSR核心依赖
npm install @vue/server-renderer express
npm install -D vue3-client-only
  1. 修改package.json的脚本命令,添加客户端/服务端构建及启动命令:
{
  "scripts": {
    "dev": "vite",
    "build:client": "vite build --outDir dist/client",
    "build:server": "vite build --ssr src/main.ts --outDir dist/server",
    "build": "npm run build:client && npm run build:server",
    "serve": "node dist/server/server.js"
  }
}

二、重构main.ts为SSR兼容模式

将直接挂载应用的逻辑改为工厂函数,确保服务端每次请求都生成全新的应用实例:

// src/main.ts
import { createSSRApp } from 'vue'
import { createStore } from 'vuex'
import App from './App.vue'
import router from './router'

// 导出应用创建工厂函数,供客户端/服务端调用
export function createApp() {
  // 初始化Vuex Store(保留你的原有状态配置)
  const store = createStore({
    state: () => ({ /* 你的状态定义 */ }),
    mutations: { /* 你的mutations */ },
    actions: { /* 你的actions */ },
    modules: { /* 你的模块 */ }
  })

  const app = createSSRApp(App)
  app.use(store)
  app.use(router)

  return { app, router, store }
}

// 客户端专属挂载逻辑
if (import.meta.env.SSR === false) {
  const { app, router, store } = createApp()
  // 恢复服务端注入的初始状态
  if (window.__INITIAL_STATE__) {
    store.replaceState(window.__INITIAL_STATE__)
  }
  router.isReady().then(() => {
    app.mount('#app')
  })
}

三、创建服务端入口文件server.ts

在项目根目录创建server.ts,用Express处理服务端渲染逻辑:

// server.ts
import express from 'express'
import { renderToString } from '@vue/server-renderer'
import { createApp } from './src/main'
import fs from 'fs'
import path from 'path'

const app = express()
const PORT = process.env.PORT || 3000

// 读取客户端构建的HTML模板
const template = fs.readFileSync(path.resolve(__dirname, './dist/client/index.html'), 'utf-8')

// 静态资源托管
app.use('/assets', express.static(path.resolve(__dirname, './dist/client/assets')))

// 处理所有路由的SSR渲染
app.get('*', async (req, res) => {
  const { app, router, store } = createApp()

  // 设置当前请求路由
  await router.push(req.url)
  await router.isReady()

  // 调用路由组件的异步数据获取逻辑(如果有)
  const matchedComponents = router.currentRoute.value.matched.flatMap(record => Object.values(record.components))
  await Promise.all(
    matchedComponents.map(component => {
      if (component.asyncData) {
        return component.asyncData({ store, route: router.currentRoute.value })
      }
    })
  )

  // 渲染应用为HTML字符串
  const appHtml = await renderToString(app)
  // 序列化Vuex状态,供客户端恢复
  const initialState = JSON.stringify(store.state)

  // 注入渲染内容与初始状态到模板
  const finalHtml = template
    .replace('<!--app-html-->', appHtml)
    .replace('<!--initial-state-->', `<script>window.__INITIAL_STATE__ = ${initialState}</script>`)

  res.send(finalHtml)
})

app.listen(PORT, () => {
  console.log(`SSR服务运行在 http://localhost:${PORT}`)
})

四、调整HTML模板占位符

修改public/index.html,添加服务端渲染的内容与状态占位符:

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ionic Vue SSR App</title>
</head>
<body>
  <div id="app"><!--app-html--></div>
  <!--initial-state-->
  <script type="module" src="/src/main.ts"></script>
</body>
</html>

五、处理Ionic组件的SSR兼容性

部分Ionic组件依赖浏览器DOM环境,需用<ClientOnly>包裹这类组件,确保只在客户端渲染:

<template>
  <ClientOnly>
    <ion-modal :is-open="showModal">
      <!-- 模态框内容 -->
    </ion-modal>
  </ClientOnly>
</template>

<script setup>
import { ClientOnly } from 'vue3-client-only'
import { ref } from 'vue'
const showModal = ref(false)
</script>

或者在组件的onMounted钩子中处理DOM相关逻辑(onMounted仅在客户端执行)。

六、服务器部署步骤

  1. 将项目代码上传至服务器,执行npm install安装依赖
  2. 执行构建命令:npm run build
  3. 启动SSR服务:npm run serve
  4. (可选)用PM2管理进程,避免服务意外中断:
pm2 start dist/server/server.js --name ionic-ssr-app

关键注意事项

  • 确保Vuex状态可序列化,避免存储函数、Symbol等无法转为JSON的内容
  • 路由中的异步组件需确保在服务端渲染前加载完成
  • API请求需在服务端路由就绪后调用(通过asyncData方法),确保数据预取完成再渲染HTML

内容的提问来源于stack exchange,提问作者Khaled Omara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:20:42