服务端渲染和NuxtJS实战

1,116 阅读7分钟

一、服务端渲染基础

1、渲染的本质:把数据+模板拼接到一起
2、传统的服务端渲染

  • 图解过程 image.png
  • 服务端处理
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、客户端渲染

  • 图解过程(后端处理数据接口、前端负责将接口数据渲染到页面中,前端更独立) image.png
  • 缺点
    • 首屏渲染慢: 对比穿透服务端渲染与客户端渲染页面呈现的耗时。
      9.2.png
    • 不利于SEO: 因为在请求一个地址后服务端返回的是空HTML,搜索引擎不能解析JS获取动态页面,在body标签内得不到有用的信息。

4、现代化的服务端渲染(同构渲染)

  • 理解同构渲染
    • 基于React、Vue等框架,客户端渲染和服务器端渲染的结合
      • 在服务器端执行一次,用于实现服务器端渲染(首屏直出)
      • 在客户端再执行一次,用于接管页面交互
    • 核心解决SEO和首屏渲染慢的问题
    • 拥有传统服务端渲染的优点,也有客户端渲染的优点
  • 图解过程 image.png
  • 实现同构渲染的方案
    • 使用Vue、React等框架的官方解决方案
      • 优点:有助于理解原理
      • 缺点:需要搭建环境,比较麻烦
    • 使用第三方解决方案
      • React生态的 Next.js
      • Vue生态的Nuxt.js

5、同构渲染的问题

  • 开发条件所限
    • 浏览器特定的代码只能在某些生命周期钩子函数中使用;
    • 一些外部扩展库可能需要特殊处理才能在服务端渲染应用中运行;
    • 不能在服务端渲染期间操作DOM
    • 某些代码操作需要区分运行环境
  • 涉及构建设置和部署的更多要求,对比如下: | |客户端渲染 |同构渲染 | | -- | -- |-- | | 构建 | 仅构建客户端应用即可 | 需要构建两个端 | | 部署 | 可以部署在任意web服务器中 | 只能部署在Node.js Server |
  • 更多的服务器负载
    • 在Node中渲染完整的应用程序,相比仅仅提供静态文件的服务器需要大量占用CPU资源
    • 如果应用在高流量环境下使用,需要准备相应的服务器负载
    • 需要更多的服务端渲染优化工作处理

二、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、用户的登录状态保存

  • vuexstate中定义user保存当前登录用户的登录状态。为了防止刷新页面数据丢失,通过Cookie把数据持久化(由于cookie客户端和服务端都可以访问)。
  • 客户端通过Cookie.set('user', data.user)配置Cookie
// 仅在客户端加载 js-cookie 包
const Cookie = process.client ? require('js-cookie') : undefined
  • 服务端通过cookieparser解析请求头中的cookie字符串,并转为JavaScript对象。方法定义在vuexaction方法中。
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、vuexstate的定义

  • 在服务端渲染期间运行都是同一个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 应用程序之前执行。通过插件机制获取到上下文对象(queryparamsreqresappstore...)。插件导出函数必须作为 default成员
  • 注册各种插件。在nuxt.config.js文件中配置。
module.exports = {
  plugins: [
    '~/plugins/request.js',
    '~/plugins/dayjs.js'
  ]
}

4、组件的watchQuery属性

  • 观察查询字符串并在更改时执行组件方法(asyncDatafetchvalidatelayout 等)。示例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.zipimage.png
  • 把发布包传到服务端。依次经过图中的7个步骤,其中第4步是通过Linuxscp命令将本地压缩包上传到远程服务器的指定目录/root/realworld-nuxtjs文件夹中。 image.png
  • 解压,如上图中第7步,查看文件信息,由于Linux默认会隐藏.开头的文件,因此要通过ls -a查看。 image.png
  • 安装依赖。通过npm i安装依赖
  • 启动服务,运行npm run start启动服务 image.png
  • 在外网访问。这里不能使用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传参 image.png
  • PM2常用命令 image.png 9、自动化部署--现代化的部署方式(CI持续集成/CD持续部署)
  • 图解过程: image.png
  • 常见的CI/CD服务
    • JenkinsGitlab CIGitHub ActionsTravic CICircle CI... 10、以GitHub Actions为例,演示具体实现
  • 环境准备
    • 首先准备一台Linux服务器
    • 把代码提交到GitHub远程仓库
  • 配置GitHub Actions Token
  • 配置GitHub Actions执行脚本
    • 在项目根目录创建.github/workflows 目录
    • 下载main.yml到workflows目录中,下载地址
    • 修改Secrets相关的配置
      image.png
    • 配置PM2配置文件
{
  "apps": [
    {
      "name": "RealWorld",
      "script": "npm",
      "args": "start"
    }
  ]
}
  • 提交更新,创建以v开头的标签并推送到远程 image.png
  • 查看自动部署状态 image.png
  • 访问网站
  • 提交更新...