来源:qiuzhx时间:2019-09-18
服务端渲染的模式就是:就是将本来要放在浏览器执行创建的组件,放到服务端先创建好,然后生成对应的html,在请求一个网址的时候,服务端收到请求之后把html的内容先生成好然后再返回给浏览器。这样子搜索引擎就可以通过你返回的a标签抓取到网站的其他页面了,最后将这些静态标记"激活"为客户端上完全可交互的应用程序
Vue SSR 相比 SPA(单页应用)好处及坏处
好处
更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
更快的内容到达时间(time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。
坏处
开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数(lifecycle hook)中使用;一些外部扩展库(external library)可能需要特殊处理,才能在服务器渲染应用程序中运行。
涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序(SPA)不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源(CPU-intensive - CPU 密集),因此如果你预料在高流量环境(high traffic)下使用,请准备相应的服务器负载,并明智地采用缓存策略。
SSR原理
这是vue.js官方的SSR原理介绍图,从这张图片,我们可以知道:我们需要通过Webpack打包生成两份bundle文件:
Client Bundle,给浏览器用。和纯Vue前端项目Bundle类似
Server Bundle,供服务端SSR使用,一个json文件
不管你项目先前是什么样子,是否是使用vue-cli生成的。都会有这个构建改造过程。在构建改造这里会用到 vue-server-renderer 库,这里要注意的是 vue-server-renderer 版本要与Vue版本一样。
Vue SSR实例(构建基于vue-cli3 的SSR应用程序)
创建一个vue项目
vue create ssr-example
............这里就i不再过多的说明了,就正常的创建一个vue项目的流程
进行SSR改造—安装需要的包
安装 vue-server-renderer
安装 lodash.merge
安装 webpack-node-externals
安装 cross-env
npm install vue-server-renderer lodash.merge webpack-node-externals cross-env --save-dev
进行SSR改造—在服务器中集成
安装koa2,然后在项目根目录下新建一个server.js
npm install koa koa-static --save
进行SSR改造—在koa2集成ssr
源码:
----------------------------------------------------------------------------------
// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const Koa = require('koa')
const app = new Koa()
// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()
// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
const vm = new Vue({
data: {
title: "ssr example",
url: ctx.url
},
template: `<div>访问的 URL 是: {{ url }}</div>`
})
// 将 Vue 实例渲染为 HTML
renderer.renderToString(vm, (err, html) => {
if(err){
res.status(500).end('Internal Server Error')
return
}
ctx.body = html
})
})
const port = 3000
app.listen(port, function() {
console.log(`server started at localhost:${port}`)
})
--------------------------------------------------------------------------------------
看到这个说明一个简单的ssr构建成功了。
不过到目前为止,我们并没有将客户端的.vue文件通过服务端进行渲染,那么如何将前端的.vue文件与后端node进行结合呢?
进行SSR改造—改造前端配置,以支持SSR
修改源码结构
在src目录下新建两个文件,一个entry-client.js 仅运行于浏览器 一个entry-server.js 仅运行于服务器
修改main.js
main.js 是我们应用程序的「通用 entry」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实例,并直接挂载到 DOM。但是,对于服务器端渲染(SSR),责任转移到纯客户端 entry 文件。app.js 简单地使用 export 导出一个 createApp 函数:
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from "./router"
import store from './store'
Vue.config.productionTip = false
// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
const router = createRouter()
const app = new Vue({
router,
store,
// 根实例简单的渲染应用程序组件。
render: h => h(App)
})
return { app }
}
修改entry-client.js
客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中
import { createApp } from './main'
// 客户端特定引导逻辑……
const { app } = createApp()
// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')
修改entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。
import { createApp } from './main'
export default context => {
// 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
// 以便服务器能够等待所有的内容在渲染前,
// 就已经准备就绪。
return new Promise((resolve, reject) => {
const { app, router, store } = createApp()
// 设置服务器端 router 的位置
router.push(context.url)
// 等到 router 将可能的异步组件和钩子函数解析完
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// 匹配不到的路由,执行 reject 函数,并返回 404
if (!matchedComponents.length) {
return reject({
code: 404
})
}
// Promise 应该 resolve 应用程序实例,以便它可以渲染
resolve(app)
}, reject)
})
}
修改router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history', // 一定要是history模式
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
}
]
})
修改webpack配置
在vue-cli3创建的vue项目,已经没有了之前的webpack.base.conf.js、webpack.dev.conf.js、webpack.prod.conf.js。那么如何进行webpack的配置呢?
在vue-cli官网上也说明了如何使用。 调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象,该对象将会被 webpack-merge 合并入最终的 webpack 配置。
在项目根目录下,新建一个vue.config.js
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin")
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin")
const nodeExternals = require("webpack-node-externals")
const merge = require("lodash.merge")
const TARGET_NODE = process.env.WEBPACK_TARGET === "node"
const target = TARGET_NODE ? "server" : "client"
module.exports = {
configureWebpack: () => ({
// 将 entry 指向应用程序的 server / client 文件
entry: `./src/entry-${target}.js`,
// 对 bundle renderer 提供 source map 支持
devtool: 'source-map',
target: TARGET_NODE ? "node" : "web",
node: TARGET_NODE ? undefined : false,
output: {
libraryTarget: TARGET_NODE ? "commonjs2" : undefined
},
// https://webpack.js.org/configuration/externals/#function
// https://github.com/liady/webpack-node-externals
// 外置化应用程序依赖模块。可以使服务器构建速度更快,
// 并生成较小的 bundle 文件。
externals: nodeExternals({
// 不要外置化 webpack 需要处理的依赖模块。
// 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
// 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
whitelist: [/.css$/]
}),
optimization: {
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 2,
maxAsyncRequests: 5,
maxInitialRequests: 3
}
},
plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
}),
chainWebpack: config => {
config.module
.rule("vue")
.use("vue-loader")
.tap(options => {
merge(options, {
optimizeSSR: false
})
})
}
}
修改package,在scripts下面新增三个脚本来生成bundle.json
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build",
"build:win": "npm run build:server && move dist\\vue-ssr-server-bundle.json bundle && npm run build:client && move bundle dist\\vue-ssr-server-bundle.json",
执行命令
npm run build:win
在dist目录下会生成两个json文件
改造server.js 代码
const fs = require('fs')
const Koa = require('koa')
const path = require('path')
const koaStatic = require('koa-static')
const app = new Koa()
const resolve = file => path.resolve(__dirname, file)
// 开放dist目录
app.use(koaStatic(resolve('./dist')))
// 第 2 步:获得一个createBundleRenderer
const { createBundleRenderer } = require('vue-server-renderer')
const bundle = require('./dist/vue-ssr-server-bundle.json')
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
const renderer = createBundleRenderer(bundle, {
runInNewContext: false,
template: fs.readFileSync(resolve('./src/index.temp.html'), 'utf-8'),
clientManifest: clientManifest
})
function renderToString(context) {
return new Promise((resolve, reject) => {
renderer.renderToString(context, (err, html) => {
err ? reject(err) : resolve(html)
})
})
}
// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
const context = {
title: 'ssr test',
url: ctx.url
}
// 将 context 数据渲染为 HTML
const html = await renderToString(context)
ctx.body = html
})
const port = 3000
app.listen(port, function() {
console.log(`server started at localhost:${port}`)
})
运行server.js
node server.js
访问 localhost:3000,可以看到页面的数据都是又服务端渲染完成后返回的。到这一步已经基本算完成了SSR的构建了。
如果有问题的话,可以把dist目录下的index.html文件删了。避免直接访问到了该html文件
恩尼斯网站开发定制网站转载
下一篇:如何自行搭建个人网站