从手写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是一个空盒子。
传统vue渲染的问题
- seo的问题,让搜索引擎找到你的网站。- 爬虫去爬你的网站,只会爬取html,但是html是一个空盒子,没有具体页面实现的代码。
- 首屏速度过慢的问题 - 异步组件
- 加大浏览器负担,我们给浏览器的不是现成的html,而是js
引出SSR
由于以上问题,我们引出了SSR。
手写一个SSR
首先我们需要知道SSR核心要做的事情是什么?
- 怎么把一个VUE实例变成html字符串
- 在Node服务,返回对应的页面给浏览器
这里介绍一个vue 官方提出的库vue-server-renderer,可以将vue实例变成HTML字符串。
SSR需要哪些东西
看完上面画的这张图,我们可以开始写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",
......
},
复制代码
目前可以通过指令实现打包了。
接下来要做的就是图中最右边的部分,启动一个服务器。(我们整个手写过程都是根据这张图来的,所以要实时的回顾一下)
创建一个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. 目录结构
我们可以发现这里面没有路由文件,nuxt会自动生成。当我们每次在page文件夹中新建文件(例如page1.vue),都会生成一个路由(/page1)
3. 打包、启动
npm run build
打包文件 npm run start
启动项目
总结
以上手写SSR可以对原理的理解更加深刻。如果有写的不对的地方,欢迎指正,相互学习。
作者:新时代农名工小陈
链接:https://juejin.cn/post/7016885850084474910
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
共有 0 条评论