服务端渲染之nuxt.js

487 阅读3分钟
什么是服务端渲染?

服务器端渲染(Server-Side Rendering)是指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。

目前主流的前端应用都是单页面应用,大部分都是客户端渲染(CSR),服务侧只返回基本html,主体内容通过js控制文档片段加载。

服务端渲染主要解决客户端渲染两大缺点:

1、首屏渲染慢

2、SEO难度高,不利于搜索引擎优化

目前主流框架vue、react等都支持了ssr渲染。主流SSR有基于vue的nuxtjs以及基于react的nextjs。 由于这些框架的流行,必须要了解“同构”这个概念

同构实际上是客户端渲染和服务器端渲染的一个整合。我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互。

image.png

注意:在同构中,组件的代码可以公用,而路由这样的代码是没有办法公用,是因为在服务器端需要通过请求路径,找到路由组件,而在客户端需通过浏览器中的网址,找到路由组件,是完全不同的两套机制,所以这部分代码是肯定无法公用。

下面就使用nuxtjs新建一个SSR应用。

1、新建项目

npx create-nuxt-app my-project,按照官方提示选择对应插件,最后得到应用目录。

image.png

然后执行启动命令

image.png

打开本地3000端口可以看见

image.png

打开控制台network可以看见,此时返回的是构建渲染好的页面,而不是像single-spa那样的一个单纯的html文档。

image.png

我们再看看代码层面:

<template>
  <div class="container">
    <div>
      <NuxtLogo />
      <h1 class="title">
        Welcome to the Vant + Nuxt.js template
      </h1>
      <div class="links">
        <van-button type="primary" url="https://nuxtjs.org/">
          Documentation
        </van-button>
        <van-button url="https://github.com/nuxt/nuxt.js">
          GitHub
        </van-button>
        <van-button url="https://vant-contrib.gitee.io/vant">
          Vant UI
        </van-button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'IndexPage'
}
</script>

<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family:
    Quicksand,
    "Source Sans Pro",
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    "Helvetica Neue",
    Arial,
    sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

以平时CSR写vue的方式写SSR。

import Vue from 'vue'
import Router from 'vue-router'
import { normalizeURL, decode } from 'ufo'
import { interopDefault } from './utils'
import scrollBehavior from './router.scrollBehavior.js'

const _62083809 = () => interopDefault(import('../pages/index.vue' /* webpackChunkName: "pages/index" */))

const emptyFn = () => {}

Vue.use(Router)

export const routerOptions = {
  mode: 'history',
  base: '/',
  linkActiveClass: 'nuxt-link-active',
  linkExactActiveClass: 'nuxt-link-exact-active',
  scrollBehavior,

  routes: [{
    path: "/",
    component: _62083809,
    name: "index"
  }],

  fallback: false
}

export function createRouter (ssrContext, config) {
  const base = (config._app && config._app.basePath) || routerOptions.base
  const router = new Router({ ...routerOptions, base  })

  // TODO: remove in Nuxt 3
  const originalPush = router.push
  router.push = function push (location, onComplete = emptyFn, onAbort) {
    return originalPush.call(this, location, onComplete, onAbort)
  }

  const resolve = router.resolve.bind(router)
  router.resolve = (to, current, append) => {
    if (typeof to === 'string') {
      to = normalizeURL(to)
    }
    return resolve(to, current, append)
  }

  return router
}

nuxt的路由是根据pages目录自动生成的。 假设 pages 的目录结构如下:

pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

那么,Nuxt.js 自动生成的路由配置如下:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
    },
    {
      name: 'user-one',
      path: '/user/one',
      component: 'pages/user/one.vue'
    }
  ]
}

在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。

以下目录结构:

pages/
--| _slug/
-----| index.vue
--| users/
-----| _id.vue

Nuxt.js 生成对应的路由配置表为

router: {
  routes: [
  
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },