从手写SSR实现到轻松使用NUXT

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

相信对于学习前端的同学来说,SSR并不陌生。但很多时候我们只是知道或者使用过SSR,因此我们对这块知识很容易忘记。这篇文章带你手写一个SSR,相信写完后会对这块知识更加了解,记忆也会更加深刻。

什么项目要做SSR?

我们说所有 to C(面对普通用户) 的项目都要用到SSR,而后台的系统就没必要做SSR了。

什么是SSR?

SSR(Server-Side Rendering) 服务端渲染。页面渲染过程是在服务端完成,最终的HTML字符串,直接通过请求发送给客户端

传统的vue渲染

首先我们了解一下传统的vue实例是通过浏览器端渲染:

浏览器发送请求,访问某个页面,服务器会返回app.bundle.js,index.html给浏览器,而index.html是一个空盒子。

纯浏览器渲染.png

传统vue渲染的问题

  1. seo的问题,让搜索引擎找到你的网站。- 爬虫去爬你的网站,只会爬取html,但是html是一个空盒子,没有具体页面实现的代码。
  2. 首屏速度过慢的问题 - 异步组件
  3. 加大浏览器负担,我们给浏览器的不是现成的html,而是js

引出SSR

由于以上问题,我们引出了SSR。

服务端渲染.png

手写一个SSR

首先我们需要知道SSR核心要做的事情是什么?

  • 怎么把一个VUE实例变成html字符串
  • 在Node服务,返回对应的页面给浏览器

这里介绍一个vue 官方提出的库vue-server-renderer,可以将vue实例变成HTML字符串。

SSR需要哪些东西

手写ssr线路图.png

看完上面画的这张图,我们可以开始写SSR了。创建一个vue项目,我们用脚手架创建的项目都是单入口的,只有一个main.js,上图可知我们需要两个入口,所以在main.js的同级目录下创建一个serve.js

服务端入口

serve.js 代码如下:

import { createApp } from "./main"

// context 相当于地址信息,req.url
export default context => {
  return new Promise((resolve, reject) => {
    const { app, router } = createApp() // 拿到App实例,和路由
    router.push(context.url) // 路由跳转到相应的页面
    router.onReady(() => { // 当路由准备好后,
      resolve(app)
    }, reject)
  })
}
复制代码

ssr的情况下,每一次访问都会创建一个新的vue实例.

main.js 代码如下:

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'

Vue.config.productionTip = false
// !! ssr的情况下,每一次访问都会创建一个新的vue实例

var router = createRouter() // 调用方法,创建路由
export function createApp () {
  const app = new Vue({
    router,
    render: h => h(App)
  })
  return { app, router }
}
复制代码

以上服务端入口就写好了,下面来写客户端入口。

服务端入口

创建一个client.js文件

import { createApp } from './main'

const { app, router } = createApp()
router.onReady(() => {
  app.$mount('#app')
})
复制代码

现在服务端和客户端都写好了,下一步就是打包。

打包

服务端打包文件如下:

webpack.buildserver.js

const merge = require('webpack-merge') // 合并webpack配置
const baseWebpackConfig = require('./webpack.base.conf') // 引入基础webpack配置
const VueSSRServerPlugin = require('vue-server-renderer/Server-plugin') //vue 官方库,可以将vue实例变成HTML字符串

// webpack直接打包,会以浏览器为目标。但是我们现在是用node启动文件
// 因此这里需要设置一下
const ServerConfig = merge(baseWebpackConfig, {
  entry: {
    app:'./src/serve.js' // 服务端入口
  },
  output: {
    libraryTarget: "commonjs2"
  },
  target: "node",
  plugins: [
    new VueSSRServerPlugin()
  ]
})
module.exports = ServerConfig
复制代码

客户端打包文件如下:

webpack.buildclient.js

const merge = require('webpack-merge') // 合并webpack配置
const baseWebpackConfig = require('./webpack.base.conf') // 引入基础webpack配置
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') //vue 官方库,可以将vue实例变成HTML字符串


const ClientConfig = merge(baseWebpackConfig, {
  entry: {
    app:'./src/client.js' // 客户端入口
  },
  plugins: [
    new VueSSRClientPlugin()
  ]
})
module.exports = ClientConfig
复制代码

设置命令

在package.json 中设置命令

"scripts": {
  .....
  "build:client": "webpack --config build/webpack.buildclient.js",
  "build:server": "webpack --config build/webpack.buildserver.js",
  ......
},
复制代码

目前可以通过指令实现打包了。

接下来要做的就是图中最右边的部分,启动一个服务器。(我们整个手写过程都是根据这张图来的,所以要实时的回顾一下) 手写ssr线路图.png

创建一个server.js文件

const vue = require('vue')
const express = require('express')
const server = express()
const { createBundleRenderer} = require('vue-server-renderer')
const fs = require('fs')
const path = require('path')

const serverBundle = require(path.resolve(__dirname, './dist/vue-ssr-server-bundle.json'))
const clientManifest = require(path.resolve(__dirname, './dist/vue-ssr-client-manifest.json'))
// 引入HTML文件模板,因为目前只能获得DOM节点的字符串
const template = fs.readFileSync(path.resolve(__dirname, './index.ssr.html'))

// 这里的renderer 相当于生成了app实例
const renderer = createBundleRenderer(serverBundle,{
  clientManifest: clientManifest,
    template: template
})

server.get('*', (req,res) => {
  if (req.url != '/favicon.icon') {
    const context = {
      url: req.url
    }
    renderer.renderToString(context).then((html) => {
      res.end(html)
    })
  }
})
server.listen(1000)
复制代码

现在node server.js 就可以启动服务了。通过访问localhost:1000,即可访问。我们怎么知道这时候的服务端渲染呢?当我们切换路由时会重新刷新页面,正如上面的代码,每一次都会创建一个新的实例,

而我们使用的nuxt,就相当于把上述过程封装了一遍,直接用就行了。

使用NUXT

1. 创建nuxt项目

npx create-nuxt-app nuxtdemo

2. 目录结构

企业微信截图_16336959801426.png 我们可以发现这里面没有路由文件,nuxt会自动生成。当我们每次在page文件夹中新建文件(例如page1.vue),都会生成一个路由(/page1)

3. 打包、启动

npm run build 打包文件 npm run start 启动项目

总结

以上手写SSR可以对原理的理解更加深刻。如果有写的不对的地方,欢迎指正,相互学习。

猜你喜欢

转载自juejin.im/post/7016885850084474910