一、服务端渲染基础
1、渲染的本质:把数据+模板拼接到一起
2、传统的服务端渲染
- 图解过程
- 服务端处理
app.get('/', (req, res) => {
// 1. 获取页面模板
const templateStr = fs.readFileSync('./index.html', 'utf-8')
// 2. 获取数据
const data = JSON.parse(fs.readFileSync('./data.json', 'utf-8'))
// 3. 渲染:数据 + 模板 = 最终结果
const html = template.render(templateStr, data)
// 4. 把渲染结果发送给客户端
res.send(html)
})
- 缺点
- 前后端代码完全耦合在一起,不利于开发和维护
- 前端没有足够的发挥空间
- 服务端压力大
- 用户体验一般(每次请求新页面都需要刷新页面)
3、客户端渲染
- 图解过程(后端处理数据接口、前端负责将接口数据渲染到页面中,前端更独立)
- 缺点
- 首屏渲染慢: 对比穿透服务端渲染与客户端渲染页面呈现的耗时。
- 不利于
SEO: 因为在请求一个地址后服务端返回的是空HTML,搜索引擎不能解析JS获取动态页面,在body标签内得不到有用的信息。
- 首屏渲染慢: 对比穿透服务端渲染与客户端渲染页面呈现的耗时。
4、现代化的服务端渲染(同构渲染)
- 理解同构渲染
- 基于React、Vue等框架,客户端渲染和服务器端渲染的结合
- 在服务器端执行一次,用于实现服务器端渲染(首屏直出)
- 在客户端再执行一次,用于接管页面交互
- 核心解决SEO和首屏渲染慢的问题
- 拥有传统服务端渲染的优点,也有客户端渲染的优点
- 基于React、Vue等框架,客户端渲染和服务器端渲染的结合
- 图解过程
- 实现同构渲染的方案
- 使用Vue、React等框架的官方解决方案
- 优点:有助于理解原理
- 缺点:需要搭建环境,比较麻烦
- 使用第三方解决方案
- React生态的 Next.js
- Vue生态的Nuxt.js
- 使用Vue、React等框架的官方解决方案
5、同构渲染的问题
- 开发条件所限
- 浏览器特定的代码只能在某些生命周期钩子函数中使用;
- 一些外部扩展库可能需要特殊处理才能在服务端渲染应用中运行;
- 不能在服务端渲染期间操作DOM
- 某些代码操作需要区分运行环境
- 涉及构建设置和部署的更多要求,对比如下: | |客户端渲染 |同构渲染 | | -- | -- |-- | | 构建 | 仅构建客户端应用即可 | 需要构建两个端 | | 部署 | 可以部署在任意web服务器中 | 只能部署在Node.js Server |
- 更多的服务器负载
- 在Node中渲染完整的应用程序,相比仅仅提供静态文件的服务器需要大量占用
CPU资源 - 如果应用在高流量环境下使用,需要准备相应的服务器负载
- 需要更多的服务端渲染优化工作处理
- 在Node中渲染完整的应用程序,相比仅仅提供静态文件的服务器需要大量占用
二、NuxtJS基础
1、官网学习
- 一个基于Vue.js 生态的第三方开源服务端渲染应用框架
- 它可以帮我们轻松的使用Vue.js技术栈构建同构应用 备注
2、Nuxt.js的异步数据--asyncData方法
- 基本用法
- 它会将
asyncData返回的数据融合组件data方法返回数据一并给组件 - 调用时机:服务端渲染期间和客户端路由更新之前
- 它会将
- 注意事项
- 只能在页面组件中使用
- 没有this,因为它是在组件初始化之前被调用的
3、具体实现
// 当你想要动态页面内容有利于 SEO 或者是提升首屏渲染速度的时候,就在 asyncData 中发请求拿数据
async asyncData () {
console.log('asyncData')
console.log(this)
const res = await axios({
method: 'GET',
url: 'http://localhost:3000/data.json'
})
return res.data
},
// 如果是非异步数据或者普通数据,则正常的初始化到 data 中即可
data () {
return {
foo: 'bar'
}
}
4、Nuxt.js的异步数据--上下文对象 官方参考
async asyncData (context) {
console.log(context)
const { data } = await axios({
method: 'GET',
url: 'http://localhost:3000/data.json'
})
const id = Number.parseInt(context.params.id)
return {
article: data.posts.find(item => item.id === id)
}
}
三、NuxtJS实战
1、用户的登录状态保存
vuex的state中定义user保存当前登录用户的登录状态。为了防止刷新页面数据丢失,通过Cookie把数据持久化(由于cookie客户端和服务端都可以访问)。- 客户端通过
Cookie.set('user', data.user)配置Cookie
// 仅在客户端加载 js-cookie 包
const Cookie = process.client ? require('js-cookie') : undefined
- 服务端通过
cookieparser解析请求头中的cookie字符串,并转为JavaScript对象。方法定义在vuex的action方法中。
export const actions = {
// nuxtServerInit 是一个特殊的 action 方法
// 这个 action 会在服务端渲染期间自动调用
// 作用:初始化容器数据,传递数据给客户端使用
nuxtServerInit ({ commit }, { req }) {
let user = null
// 如果请求头中有 Cookie
if (req.headers.cookie) {
// 使用 cookieparser 把 cookie 字符串转为 JavaScript 对象
const parsed = cookieparser.parse(req.headers.cookie)
try {
user = JSON.parse(parsed.user)
} catch (err) {
// No valid cookie found
}
}
// 提交 mutation 修改 state 状态
commit('setUser', user)
}
}
2、vuex中state的定义
- 在服务端渲染期间运行都是同一个
store实例,为了防止数据冲突,务必要把 state 定义成一个函数,返回数据对象。
export const state = () => {
return {
// 当前登录用户的登录状态
user: null
}
}
3、nuxtJS中的中间件
- 项目根目录新建
middleware文件夹,其中定义各种中间件,应用在组件中。
/* 验证是否登录的中间件 */
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.user) {
return redirect('/login')
}
}
/* 在pages页面组件中使用中间件,限制登陆后才可访问 */
export default {
middleware: 'authenticated',
name: 'UserProfile'
}
3、nuxtJS中的插件
- 项目根目录新建
plugins文件夹,其中定义各种插件(可以作用于全局),在运行 Vue.js 应用程序之前执行。通过插件机制获取到上下文对象(query、params、req、res、app、store...)。插件导出函数必须作为default成员 - 注册各种插件。在
nuxt.config.js文件中配置。
module.exports = {
plugins: [
'~/plugins/request.js',
'~/plugins/dayjs.js'
]
}
4、组件的watchQuery属性
- 观察查询字符串并在更改时执行组件方法(
asyncData、fetch、validate、layout等)。示例watchQuery: ['page', 'tag', 'tab']
5、设置页面meto优化SEO
- Nuxt.js 允许你在
nuxt.config.js里定义应用所需的所有默认 meta 标签,在head字段里配置就可以了。
// 一个使用自定义 `viewport` 和 `谷歌字体` 的配置示例
head: {
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
],
link: [
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto' }
]
}
- 个性化特定页面的
Meta标签,在组件中配置。
// 为了避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签
// 而产生重复的现象,建议利用 `hid` 键为 meta 标签配一个唯一的标识编号
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'My custom description'
}
]
}
}
6、项目打包部署
| 命令 | 描述 |
|---|---|
| nuxt | 启动一个热加载的 Web 服务器(开发模式) localhost:3000。 |
| nuxt build | 利用 webpack 编译应用,压缩 JS 和 CSS 资源(发布用)。 |
| nuxt start | 以生产模式启动一个 Web 服务器 (需要先执行nuxt build)。 |
7、简单的发布部署(传统方式)
- 配置
Host+Port。在nuxt.config.js中配置如:server: { host: '0.0.0.0', port: 3000 }, - 压缩发布包。选中项目中的某些文件,这些文件都是必要的文件,如
static是项目的静态资源,并合并压缩为realworld-nuxtjs.zip。 - 把发布包传到服务端。依次经过图中的7个步骤,其中第4步是通过
Linux的scp命令将本地压缩包上传到远程服务器的指定目录/root/realworld-nuxtjs文件夹中。 - 解压,如上图中第7步,查看文件信息,由于
Linux默认会隐藏.开头的文件,因此要通过ls -a查看。 - 安装依赖。通过
npm i安装依赖 - 启动服务,运行
npm run start启动服务 - 在外网访问。这里不能使用
http://172.17.215.90:3000(当前链接是在服务端的局域网地址)进行访问,应该用服务器对应的公网IP+端口号访问。此处应访问http://39.105.28.5:3000/,就可以测试网页功能了。
8、使用PM2启动Node服务,解决nodeJS进程的管理,实现后台运行 - GitHub仓库地址: github.com/Unitech/om2
- 官方文档: pm2.io/
- 安装: npm install—global pm2
- 启动: pm2 start 脚本路径,如下步骤 使用
npm通过-- start传参 - PM2常用命令
9、自动化部署--现代化的部署方式(CI持续集成/CD持续部署)
- 图解过程:
- 常见的CI/CD服务
Jenkins、Gitlab CI、GitHub Actions、Travic CI、Circle CI... 10、以GitHub Actions为例,演示具体实现
- 环境准备
- 首先准备一台
Linux服务器 - 把代码提交到
GitHub远程仓库
- 首先准备一台
- 配置
GitHub Actions Token- 生成
Token: github.com/settings/to… - 配置到项目的
Secrets中: github.com/lipengzhou/… nuxtis/settings/secrets
- 生成
- 配置
GitHub Actions执行脚本- 在项目根目录创建.github/workflows 目录
- 下载main.yml到workflows目录中,下载地址:
- 修改
Secrets相关的配置
- 配置PM2配置文件
{
"apps": [
{
"name": "RealWorld",
"script": "npm",
"args": "start"
}
]
}
- 提交更新,创建以
v开头的标签并推送到远程 - 查看自动部署状态
- 访问网站
- 提交更新...