Skip to content

Element Plus SSR 服务端渲染支持与配置

学习目标

  • 理解 SSR 在 Element Plus 中的应用场景
  • 掌握 Element Plus SSR 配置方法
  • 解决 SSR 中的常见问题
  • 学习水合错误的避免方法

学习内容

1. SSR 基础概念

1.1 什么是 SSR

  • 服务端渲染:在服务器端生成完整的 HTML
  • 客户端激活:在浏览器端激活交互功能
  • 同构应用:同一套代码在服务端和客户端运行

1.2 SSR 的优势

  • SEO 友好:搜索引擎可以直接抓取内容
  • 首屏加载快:减少白屏时间
  • 更好的用户体验:快速显示内容

2. Element Plus SSR 配置

2.1 基础配置

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@element-plus/nuxt'],
  elementPlus: {
    /** Options */
  }
})

2.2 手动配置

typescript
// plugins/element-plus.client.ts
import { ID_INJECTION_KEY } from '@element-plus/tokens'
import { ElMessage } from 'element-plus'

export default defineNuxtPlugin({
  name: 'element-plus',
  parallel: true,
  setup(nuxtApp) {
    if (process.client) {
      nuxtApp.vueApp.provide(ID_INJECTION_KEY, {
        prefix: Math.floor(Math.random() * 10000),
        current: 0,
      })
    }
  },
})

3. 解决 SSR 常见问题

3.1 ID 不一致问题

typescript
// 服务端和客户端 ID 同步
import { ID_INJECTION_KEY } from '@element-plus/tokens'

// 在应用入口提供 ID 注入
app.provide(ID_INJECTION_KEY, {
  prefix: 1024,
  current: 0,
})

3.2 ZIndex 管理

typescript
// 统一 ZIndex 管理
import { ZINDEX_INJECTION_KEY } from '@element-plus/tokens'

app.provide(ZINDEX_INJECTION_KEY, {
  current: 2000
})

3.3 Teleport 处理

vue
<!-- 确保 Teleport 目标存在 -->
<template>
  <div>
    <!-- 页面内容 -->
    <div id="teleport-target"></div>
    
    <!-- 使用 Teleport 的组件 -->
    <el-dialog v-model="visible" teleported>
      <p>Dialog content</p>
    </el-dialog>
  </div>
</template>

<script setup>
// 确保在客户端渲染时 Teleport 目标已存在
onMounted(() => {
  if (!document.getElementById('teleport-target')) {
    const target = document.createElement('div')
    target.id = 'teleport-target'
    document.body.appendChild(target)
  }
})
</script>

4. 水合错误避免

4.1 条件渲染处理

vue
<template>
  <div>
    <!-- 避免服务端和客户端渲染不一致 -->
    <ClientOnly>
      <el-date-picker v-model="date" />
      <template #fallback>
        <div>Loading...</div>
      </template>
    </ClientOnly>
  </div>
</template>

4.2 动态内容处理

vue
<template>
  <div>
    <!-- 使用 key 强制重新渲染 -->
    <el-table :data="tableData" :key="tableKey">
      <el-table-column prop="name" label="Name" />
    </el-table>
  </div>
</template>

<script setup>
const tableKey = ref(0)
const tableData = ref([])

onMounted(() => {
  // 客户端数据加载完成后更新 key
  loadTableData().then(() => {
    tableKey.value++
  })
})
</script>

5. Nuxt 3 集成

5.1 安装和配置

bash
# 安装 Element Plus Nuxt 模块
npm install @element-plus/nuxt -D
typescript
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@element-plus/nuxt'
  ],
  elementPlus: {
    /** 配置选项 */
    importStyle: 'scss',
    themes: ['dark']
  }
})

5.2 组件使用

vue
<!-- pages/index.vue -->
<template>
  <div>
    <el-button @click="handleClick">Click me</el-button>
    <el-message-box v-model="visible" title="Title">
      Content
    </el-message-box>
  </div>
</template>

<script setup>
const visible = ref(false)

const handleClick = () => {
  ElMessage.success('Hello from SSR!')
}
</script>

6. Next.js 集成

6.1 配置 Next.js

javascript
// next.config.js
const withTM = require('next-transpile-modules')([
  'element-plus'
])

module.exports = withTM({
  // 其他配置
})

6.2 动态导入

typescript
// components/ElementPlusComponents.tsx
import dynamic from 'next/dynamic'

const ElButton = dynamic(
  () => import('element-plus').then(mod => mod.ElButton),
  { ssr: false }
)

export { ElButton }

7. 性能优化

7.1 按需加载

typescript
// 只在需要时加载组件
const LazyDialog = defineAsyncComponent(() => 
  import('element-plus').then(mod => mod.ElDialog)
)

7.2 预加载关键组件

typescript
// 预加载常用组件
const preloadComponents = () => {
  import('element-plus/es/components/button')
  import('element-plus/es/components/input')
  import('element-plus/es/components/form')
}

if (process.client) {
  preloadComponents()
}

8. 实践练习

练习1:Nuxt 3 项目搭建

bash
# 创建 Nuxt 3 项目
npx nuxi@latest init element-plus-ssr
cd element-plus-ssr

# 安装 Element Plus
npm install @element-plus/nuxt -D

# 配置和运行
npm run dev

练习2:解决水合错误

vue
<!-- 创建一个避免水合错误的组件 -->
<template>
  <div>
    <ClientOnly>
      <el-date-picker 
        v-model="selectedDate"
        type="datetime"
        placeholder="Select date and time"
      />
    </ClientOnly>
  </div>
</template>

<script setup>
const selectedDate = ref(new Date())
</script>

练习3:SSR 性能测试

typescript
// 测试 SSR 性能
const measureSSRPerformance = () => {
  const start = performance.now()
  
  // 渲染组件
  const app = createSSRApp(App)
  const html = renderToString(app)
  
  const end = performance.now()
  console.log(`SSR rendering took ${end - start} milliseconds`)
  
  return html
}

学习资源

作业

  1. 搭建一个 Element Plus + Nuxt 3 的 SSR 项目
  2. 解决项目中的水合错误问题
  3. 优化 SSR 性能和首屏加载时间

下一步

明天我们将学习 Element Plus 的 Teleport 机制与弹出层处理。

Element Plus Study Guide