1. 服务端渲染 (SSR)
- SSR 是 Server-Side Rendering,即服务端渲染的英文缩写
- 好处:更快的首屏加载、更好的 SEO
- vs 静态站点生成 (Static-Site Generation,缩写为 SSG):也被称为预渲染,页面内容对每个用户相同,则只构建一次。预渲染的页面生成后作为静态 HTML 文件被服务器托管
- 常用实现框架:vue/server-renderer、nuxt、next
2. 三种实现 基本使用
-
vue/server-renderer
import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'
const server = express()
server.get('/', (req, res) => {
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Vue SSR Example</title>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`)
})
})
server.listen(3000, () => {
console.log('ready')
})
-
nuxt
vue架构,asyncData 返回的对象可以像data一样使用
<script>
import axios from 'axios'
export default {
data(){
return {
name:'hello World',
}
},
async asyncData(){
let {data}=await axios.get('https://api.myjson.com/bins/8gdmr')
return {info: data}
}
}
</script>
async asyncData(context) {
let [newDetailRes, hotInformationRes, correlationRes] = await Promise.all([
axios.post('http://www.huanjingwuyou.com/eia/news/detail', {
newsCode: newsCode
}),
axios.post('http://www.huanjingwuyou.com/eia/news/select', {
newsType: newsType, // 资讯类型: 3环评资讯 4环评知识
start: 0, // 从第0条开始
pageSize: 10,
newsRecommend: true
}),
axios.post('http://www.huanjingwuyou.com/eia/news/select', {
newsType: newsType, // 资讯类型: 3环评资讯 4环评知识
start: 0, // 从第0条开始
pageSize: 3,
newsRecommend: false
})
])
return {
newDetailList: newDetailRes.data.result,
hotNewList: hotInformationRes.data.result.data,
newsList: correlationRes.data.result.data,
}
},
-
next
react 架构
- getStaticPaths 决定哪些路径提前渲染
- getInitialProps
function Page({ stars }) {
return <div>Next stars: {stars}</div>
}
Page.getInitialProps = async (ctx) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default Page
-
getServerSideProps
打包的时候,请求接口数据生成页面 -
getStaticProps
每次客户端请求的时候,请求接口更新页面
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
}
}
export default Blog
- useSWR
import useSWR from 'swr'
const fetcher = (...args) => fetch(...args).then((res) => res.json())
function Profile() {
const { data, error } = useSWR('/api/profile-data', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
3. 原理
3.1 流程
1、用户打开浏览器,输入网址请求到Node.js
2、部署在Node.js的应用Nuxt.js接收浏览器请求,并请求服务端获取数据
3、Nuxt.js获取到数据后进行服务端渲染
4、Nuxt.js将html网页响应给浏览器
3.2 nuxt生命周期
- 在服务端生成初始静态html
- 客户端 mounted阶段挂载dom
- mvvm监听数据变化,更新视图
服务端
对于 SSR,将为您的应用程序的每个初始请求执行这些步骤
- 服务器启动 (
nuxt start)
使用静态站点生成时,服务器步骤仅在构建时执行,但对于将生成的每个页面执行一次
-
生成过程开始 (
nuxt generate) -
Nuxt 挂钩
-
服务器中间件
-
服务器端 Nuxt 插件
- 按照 nuxt.config.js 中定义的顺序
-
nuxtServerInit
-
仅在服务器端调用以预填充存储的 Vuex 操作
-
第一个参数是Vuex 上下文,第二个参数是Nuxt 上下文
- 从此处调度其他操作 → 仅为服务器端后续存储操作的“入口点”
-
只能定义在
store/index.js
-
-
中间件
- 全局中间件
- 布局中间件
- 路由中间件
-
asyncData (blocking)
-
beforeCreate (Vue lifecycle method)
-
created (Vue lifecycle method)
-
新的 fetch(从上到下,兄弟 = 并行)
-
状态序列化(
render:routeContextNuxt 挂钩) -
HTML 渲染发生(
render:routeNuxt 钩子) -
render:routeDone当 HTML 被发送到浏览器时钩子 -
generate:beforeNuxt 钩子 -
生成 HTML 文件
- 全静态生成
generate:page( 编辑HTML)generate:routeCreated(路由生成)
-
generate:done当所有 HTML 文件都生成时
客户端
这部分生命周期完全在浏览器中执行,无论您选择了哪种 Nuxt 模式。
-
接收 HTML
-
加载资源(例如 JavaScript)
-
客户端 Nuxt 插件
- 按照 nuxt.config.js 中定义的顺序
-
Vue Hydration
将一些事件绑定和Vue状态等注入到输出的静态的页面中, 同步下发给浏览器的的Vue bundle接管状态, 继续处理接下来的交互逻辑
-
中间件
- 全局中间件
- 布局中间件
- 路由中间件
-
asyncData (blocking)
-
beforeCreate(Vue 生命周期方法)
-
created(Vue生命周期方法)
-
新的 fetch(从上到下,兄弟 = 并行)(非阻塞)
-
beforeMount(Vue 生命周期方法)
-
mounted(Vue生命周期方法)
4.next部署
- node环境
- Dockerfile
# 1.需要装依赖
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
else echo "Lockfile not found." && exit 1; \
fi
# 2.不需要重装依赖
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
# 3.使用生产镜像,已有依赖
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
-
Build your container:
docker build -t nextjs-docker . -
Run your container:
docker run -p 3000:3000 nextjs-docker
欢迎关注我的前端自检清单,我和你一起成长