什么是服务端渲染?
服务器端渲染(Server-Side Rendering)是指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。
目前主流的前端应用都是单页面应用,大部分都是客户端渲染(CSR),服务侧只返回基本html,主体内容通过js控制文档片段加载。
服务端渲染主要解决客户端渲染两大缺点:
1、首屏渲染慢
2、SEO难度高,不利于搜索引擎优化
目前主流框架vue、react等都支持了ssr渲染。主流SSR有基于vue的nuxtjs以及基于react的nextjs。 由于这些框架的流行,必须要了解“同构”这个概念
同构实际上是客户端渲染和服务器端渲染的一个整合。我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互。
注意:在同构中,组件的代码可以公用,而路由这样的代码是没有办法公用,是因为在服务器端需要通过请求路径,找到路由组件,而在客户端需通过浏览器中的网址,找到路由组件,是完全不同的两套机制,所以这部分代码是肯定无法公用。
下面就使用nuxtjs新建一个SSR应用。
1、新建项目
npx create-nuxt-app my-project
,按照官方提示选择对应插件,最后得到应用目录。
然后执行启动命令
打开本地3000端口可以看见
打开控制台network可以看见,此时返回的是构建渲染好的页面,而不是像single-spa那样的一个单纯的html文档。
我们再看看代码层面:
<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'
},