MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue CLI 服务端渲染(SSR)与静态生成(SSG)的支持

2023-09-267.3k 阅读

Vue CLI 服务端渲染(SSR)与静态生成(SSG)的支持

在前端开发领域,Vue.js 凭借其简洁易用和高效的特点深受开发者喜爱。随着应用规模的扩大和对性能、SEO 等方面要求的提升,服务端渲染(SSR)和静态生成(SSG)技术逐渐成为构建现代 Web 应用的重要手段。Vue CLI 为开发者提供了便捷的方式来支持 SSR 和 SSG,下面我们将深入探讨这两种技术以及 Vue CLI 对它们的支持。

服务端渲染(SSR)

SSR 指的是在服务器端将 Vue 组件渲染为 HTML 字符串,然后将其发送到客户端。这样做的主要好处在于,用户在首次访问页面时能够更快地看到内容,因为无需等待客户端 JavaScript 加载和渲染完成。同时,SSR 对搜索引擎优化(SEO)也更为友好,因为搜索引擎爬虫可以直接获取完整的 HTML 内容。

在 Vue CLI 中开启 SSR 支持,首先需要确保已经安装了 Vue CLI。如果没有安装,可以使用以下命令进行全局安装:

npm install -g @vue/cli

然后,通过 Vue CLI 创建一个新的项目并启用 SSR:

vue create -p vuejs-templates/ssr my-ssr-app
cd my-ssr-app

上述命令中,vue create -p vuejs-templates/ssr my-ssr-app 使用了 Vue CLI 的官方 SSR 模板来创建项目。进入项目目录后,我们可以看到项目结构如下:

my-ssr-app
├── build
│   ├── webpack.base.js
│   ├── webpack.client.js
│   └── webpack.server.js
├── client
│   └── main.js
├── dist
├── public
├── server
│   ├── entry.js
│   └── app.js
├── src
│   ├── App.vue
│   ├── components
│   └── router
│       └── index.js
├── test
├── babel.config.js
├── package.json
└── README.md
  1. 服务器端入口文件server/entry.js 是服务器端的入口文件,它主要负责创建 Vue 应用实例,并将其渲染为 HTML 字符串。以下是一个简单的示例:
import Vue from 'vue'
import createApp from '../client/main'

export default context => {
  return new Promise((resolve, reject) => {
    const { app, router } = createApp()

    router.push(context.url)

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      resolve(app)
    }, reject)
  })
}

在上述代码中,createApp 函数从客户端入口文件 client/main.js 引入,用于创建 Vue 应用实例。通过 router.push(context.url) 将当前请求的 URL 推进路由,然后等待路由准备就绪。当路由准备好后,检查是否匹配到组件,如果没有匹配到则返回 404 错误,否则将应用实例 app 作为 Promise 的 resolve 值返回。

  1. 客户端入口文件client/main.js 负责创建 Vue 应用实例,并在客户端启动应用。示例代码如下:
import Vue from 'vue'
import App from '../src/App.vue'
import router from '../src/router'

export function createApp() {
  const app = new Vue({
    router,
    render: h => h(App)
  })
  return { app, router }
}

if (process.env.NODE_ENV === 'production') {
  const { initGA } = require('./ga')
  initGA()
}

if (process.env.NODE_ENV === 'development') {
  if (module.hot) {
    module.hot.accept()
  }
}

const { app } = createApp()
app.$mount('#app')

在这段代码中,createApp 函数创建并返回 Vue 应用实例 app 和路由实例 router。在生产环境下,可以在这里初始化一些统计代码,如 Google Analytics(GA);在开发环境下,启用热模块替换(HMR)功能。最后,将应用挂载到 HTML 页面中的 #app 元素上。

  1. Webpack 配置:在 build 目录下有三个 Webpack 配置文件,webpack.base.js 包含了通用的配置,webpack.client.js 用于客户端构建,webpack.server.js 用于服务器端构建。以 webpack.client.js 为例,其核心配置如下:
const path = require('path')
const merge = require('webpack-merge')
const base = require('./webpack.base')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

module.exports = merge(base, {
  entry: path.resolve(__dirname, '../client/main.js'),
  output: {
    filename: 'client.js',
    path: path.resolve(__dirname, '../dist/client')
  },
  plugins: [
    new VueSSRClientPlugin()
  ]
})

该配置文件首先引入基础配置 webpack.base.js,然后通过 merge 方法合并客户端特有的配置。设置客户端入口文件为 client/main.js,输出文件为 dist/client/client.js,并使用 VueSSRClientPlugin 插件来生成客户端特定的资源清单。

  1. SSR 的运行流程:当用户请求页面时,服务器接收到请求,在服务器端通过 server/entry.js 创建 Vue 应用实例,并根据请求的 URL 进行路由匹配和渲染,将渲染后的 HTML 字符串返回给客户端。客户端接收到 HTML 后,通过 client/main.js 中的代码将静态 HTML 激活为可交互的 Vue 应用,这个过程称为 “客户端激活”。通过这种方式,用户可以快速看到页面内容,同时后续的交互操作依然可以享受到 Vue 的响应式和组件化带来的便利。

静态生成(SSG)

静态生成(SSG)是在构建时将页面渲染为静态 HTML 文件,这些文件可以直接部署到静态文件服务器上。与 SSR 不同,SSG 不需要在运行时进行实时渲染,而是提前生成好 HTML 页面,因此具有更高的性能和更好的可扩展性。

在 Vue CLI 中支持 SSG,可以使用 @vue/cli-plugin-ssg 插件。首先安装该插件:

vue add @vue/cli-plugin-ssg

安装完成后,项目结构会有一些变化,新增了 generate.js 文件,该文件用于配置静态生成的相关选项。以下是一个简单的 generate.js 示例:

module.exports = {
  routes: () => [
    '/',
    '/about'
  ]
}

在上述示例中,routes 数组指定了需要生成静态页面的路由。这里配置了根路径 '/''/about' 两个页面。

  1. 页面组件改造:对于需要静态生成的页面组件,可能需要一些特殊的处理。例如,在页面组件中获取数据的逻辑,在 SSG 场景下通常需要使用 asyncData 方法(这是一个自定义的约定,并非 Vue 官方 API)。假设我们有一个文章列表页面 PostList.vue
<template>
  <div>
    <h1>文章列表</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      posts: []
    }
  },
  async asyncData() {
    const response = await fetch('https://api.example.com/posts')
    const data = await response.json()
    return { posts: data }
  }
}
</script>

在这个组件中,asyncData 方法在构建时会被调用,通过 fetch 获取文章数据,并将其作为组件的初始数据返回。这样在静态生成时,组件就可以使用这些数据渲染出包含文章列表的 HTML 页面。

  1. 构建与部署:在安装好 SSG 插件并配置好 generate.js 和页面组件后,可以使用以下命令进行静态生成:
npm run generate

执行该命令后,Vue CLI 会根据 generate.js 中的配置,对指定的路由页面进行静态生成,生成的静态 HTML 文件会放在 dist 目录下。这些文件可以直接部署到任何静态文件服务器上,如 Netlify、Vercel 等。

  1. 增量静态再生(ISR):一些现代的静态生成方案还支持增量静态再生(ISR)。这意味着在构建后,可以在运行时重新生成静态页面,而无需重新构建整个项目。在 Vue 生态中,通过结合一些后端服务或者中间件,可以实现类似的功能。例如,可以设置一个定时任务,定期重新生成某些页面的静态内容,以保证数据的实时性。假设我们有一个新闻列表页面,数据变化相对频繁,可以通过编写一个脚本,利用 @vue/cli-plugin-ssg 提供的 API 在服务器端定时重新生成该页面的静态 HTML 文件。

SSR 与 SSG 的比较与选择

  1. 性能方面

    • SSR:在首次加载时,由于服务器端直接返回渲染好的 HTML,性能较好,特别是对于内容较多且需要实时数据的应用。但是,SSR 需要服务器具备一定的计算资源来实时渲染页面,对于高并发场景可能会对服务器造成较大压力。
    • SSG:构建时生成静态 HTML 文件,在部署后几乎不需要服务器进行额外的计算,性能极高,尤其适合内容更新不频繁的网站,如博客、文档网站等。但如果数据变化频繁,需要频繁重新构建或采用增量静态再生等方案来保证数据实时性。
  2. SEO 方面

    • SSR:搜索引擎爬虫可以直接获取完整的 HTML 内容,SEO 效果较好。
    • SSG:同样因为静态 HTML 文件的存在,搜索引擎爬虫可以顺利抓取内容,SEO 也有良好的支持。
  3. 开发与维护方面

    • SSR:开发过程相对复杂,需要处理服务器端和客户端的代码逻辑,并且要注意同构代码的编写,以保证在服务器端和客户端都能正常运行。维护时也需要关注服务器的性能和稳定性。
    • SSG:开发相对简单,专注于页面组件的开发和静态生成配置。维护时主要关注内容更新以及可能的增量静态再生逻辑。
  4. 适用场景

    • SSR:适用于实时数据较多、交互性强的应用,如电子商务应用、社交网络应用等,这些应用需要根据用户的实时操作和数据变化来动态渲染页面。
    • SSG:适用于内容驱动型的网站,如博客、产品文档网站等,这些网站内容相对固定,更新频率较低,通过 SSG 可以获得极高的性能和良好的 SEO 效果。

实战案例

  1. 博客应用(SSG):假设我们要构建一个博客应用,文章内容存储在 CMS(内容管理系统)中。首先,我们在 generate.js 中配置需要生成的页面路由,可能包括首页(展示最新文章列表)、文章详情页等。对于文章详情页,其路由可以根据文章的 ID 动态生成。例如:
module.exports = {
  routes: async () => {
    const response = await fetch('https://api.example.com/articles')
    const articles = await response.json()
    const articleRoutes = articles.map(article => `/article/${article.id}`)
    return ['/', ...articleRoutes]
  }
}

在文章详情页组件 Article.vue 中,使用 asyncData 方法获取文章内容:

<template>
  <div>
    <h1>{{ article.title }}</h1>
    <div v-html="article.content"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      article: {}
    }
  },
  async asyncData({ params }) {
    const response = await fetch(`https://api.example.com/articles/${params.id}`)
    const article = await response.json()
    return { article }
  }
}
</script>

这样,在构建时,Vue CLI 会根据 generate.js 的配置,为每一篇文章生成对应的静态 HTML 页面,用户访问文章链接时可以快速加载页面内容。

  1. 电商产品展示应用(SSR):对于电商产品展示应用,产品信息可能实时更新,并且用户需要与页面进行频繁交互,如添加商品到购物车、查看不同规格的产品详情等。在服务器端入口文件 server/entry.js 中,我们可以根据请求的 URL 获取产品信息,并在服务器端渲染页面。例如:
import Vue from 'vue'
import createApp from '../client/main'

export default context => {
  return new Promise((resolve, reject) => {
    const { app, router } = createApp()

    router.push(context.url)

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      const productId = context.url.split('/').pop()
      // 假设这里有一个函数 getProductById 用于获取产品信息
      getProductById(productId).then(product => {
        app.$data.product = product
        resolve(app)
      }).catch(error => {
        reject(error)
      })
    }, reject)
  })
}

在客户端入口文件 client/main.js 中,初始化应用并处理用户交互逻辑。这样,用户访问产品页面时,服务器端会实时渲染包含最新产品信息的页面,提供良好的用户体验。

通过以上对 Vue CLI 中 SSR 和 SSG 的深入探讨以及实战案例,我们可以看到这两种技术为前端开发提供了强大的工具,根据不同的应用需求选择合适的技术,能够显著提升应用的性能、SEO 效果和用户体验。开发者在实际项目中可以根据具体情况灵活运用 SSR 和 SSG,打造出高效、优质的 Web 应用。无论是内容驱动的静态网站,还是交互性强的动态应用,Vue CLI 的 SSR 和 SSG 支持都能满足相应的开发需求。同时,在开发过程中,要注意代码的结构和优化,以充分发挥这两种技术的优势,并且关注服务器资源的合理利用,确保应用在不同场景下都能稳定高效运行。在不断演进的前端开发领域,SSR 和 SSG 技术也在持续发展,未来可能会出现更多优化和创新,开发者需要保持关注,不断学习和实践,以适应新的技术变化,为用户带来更好的产品体验。例如,随着 WebAssembly 等技术的发展,可能会为 SSR 和 SSG 带来新的性能提升和应用场景拓展。同时,对于大型项目,如何更好地管理同构代码(在 SSR 场景下)以及静态生成配置(在 SSG 场景下),也是开发者需要深入思考和探索的方向。总之,掌握 Vue CLI 对 SSR 和 SSG 的支持,是现代前端开发者提升技能和打造优秀应用的重要一环。