#关于Vue SSR的一点看法
栏目: JavaScript · 发布时间: 5年前
内容简介:SSR,意为 Server Side Rendering(服务端渲染),目的是为了解决单页面应用的 SEO 和首屏渲染速度慢的问题,对于一般网站影响不大,但是对于需要做SEO的网站是致命的,搜索引擎无法抓取页面相关内容,也就是用户搜不到此网站的相关信息。客户端渲染需要:加载html=>解析html=>加载js=>解析js=>生成dom节点=>插入html文档,所以很慢; 服务端渲染就是将首屏的html结构构建好直接返回,客户端只需要:加载html=>解析html。所以首屏打开速度大大提高,但同时对服务器的压
SSR,意为 Server Side Rendering(服务端渲染),目的是为了解决单页面应用的 SEO 和首屏渲染速度慢的问题,对于一般网站影响不大,但是对于需要做SEO的网站是致命的,搜索引擎无法抓取页面相关内容,也就是用户搜不到此网站的相关信息。
2.为什么要用SSR?
客户端渲染需要:加载html=>解析html=>加载js=>解析js=>生成dom节点=>插入html文档,所以很慢; 服务端渲染就是将首屏的html结构构建好直接返回,客户端只需要:加载html=>解析html。所以首屏打开速度大大提高,但同时对服务器的压力也比客户端渲染要大。
3.关于实现SSR的几种方式
目前Vue SSR的实现有两种实现,一种是基于官方Vue SSR指南文档的官方方案,一种是vue.js通用应用框架--NUXT。 官方方案具有更直接的控制应用程序的结构,更深入底层,更加灵活,同时在使用官方方案的过程中,也会对Vue SSR有更加深入的了解。 而NUXT提供了平滑的开箱即用的体验,它建立在同等的Vue技术栈之上,但抽象出很多模板,并提供了一些额外的功能,例如静态站点生成。通过NUXT可以根据约定的规则,快速的实现Vue SSR。
(1)nuxt
官方是这么介绍自己的: Nuxt.js 是一个基于 Vue 的通用应用框架。 通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染。
我们的目标是创建一个灵活的应用框架,你可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。 Nuxt.js 预设了利用 Vue 开发服务端渲染的应用所需要的各种配置。
除此之外,我们还提供了一种命令叫:nuxt generate,为基于 Vue 的应用提供生成对应的静态站点的功能。
我们相信这个命令所提供的功能,是向开发集成各种微服务(miscroservices)的 Web 应用迈开的新一步。 作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载、中间件支持、布局支持等。
Nuxt.js是使用 Webpack 和 Node.js 进行封装的基于Vue的SSR框架,使用它,你可以不需要自己搭建一套 SSR 程序,而是通过其约定好的文件结构和API就可以实现一个首屏渲染的 Web 应用。
之所以叫 Nuxt.js 也是因为受到了 Next.js 的启发。 Nuxt.js 是一个 Node 程序,就像上面说的,我们是要把 Vue 跑在服务端,所以必须使用 Node 环境。
我们对 Nuxt.js 应用的访问,实际上是在访问这个 Node.js 程序的路由,程序输出首屏渲染内容 + 用以重新渲染的 SPA 的脚本代码,而路由是由 Nuxt.js 约定好的 pages 文件夹生成的。
所以,整体上,Nuxt.js 通过各个文件夹和配置文件的约束来管理我们的程序,而又不失扩展性,其有自己的插件机制。
- .nuxt : Nuxt自动生成,临时的用于编辑的文件,build
- assets:Webpack 编译的各类资源,// 用于组织未编译的静态资源入LESS、SASS 或 JavaScript
- components:各组件,用于你自己管理公共组件或非公共组件 ,比如滚动组件,日历组件,分页组件
- layouts:宿主布局页面模板组件,用于你可以把不同的页面指定使用不同的布局,不可更改。
- middleware:中间件,首屏渲染和路由跳转前均执行对应中间件,可以返回promise或直接next(很实用!)
- pages:各页面组件,用于生成对应路由,支持嵌套,支持动态路由,存放写的页面,我们主要的工作区域
- plugins:插件,SPA中用的各类第三方组件和一些node模块,JavaScript插件放的地方
- static :/ 用于存放静态资源文件,比如图片
- store:内置了vuex,可以直接返回数据模块或返回一个自建vuex根对象,具体要翻文档,用于组织应用的Vuex 状态管理
- .editorconfig : // 开发 工具 格式配置
- .eslintrc.js : // ESLint的配置文件,用于检查代码格式
- .gitignore : // 配置git不上传的文件
- nuxt.config.json : // 用于组织Nuxt.js应用的个性化配置,已覆盖默认配置
- package-lock.json : // npm自动生成,用于帮助package的统一性设置的,yarn也有相同的操作
- package-lock.json : // npm自动生成,用于帮助package的统一性设置的,yarn也有相同的操作
- package.json : // npm包管理配置文件
- 其他:你可以自定义文件夹和别名映射,文档都有提及,这里有配置代码
nuxt.config.js对程序的扩展管理可大概分为以下类:
- build:主要对应 Webpack 中的各配置项,可以对默认的 Webpack 配置进行扩展,如这里代码
- cache:主要对应内置的组件缓存模块lru-cache的配置对象,有默认值,可选关闭
- css:对应我们在SPA随处引用样式文件的require语句
- dev:用于自定义配置环境变量,对应之前webpack.config.js相关文件中的变量语句
- env:同上息息相关
- generate:对generate命令执行时的行为做一些定制
- head:对应vue-meta插件的全局配置,vue-meta用于VUE/SSR程序的文档元信息的管理
- loading:用于定制化Nuxt.js内置的进度条组件
- performance:用于配置Node.js服务器性能上的配置
- plugins:用于管理和应用对应plugins文件夹中的插件
- rootdir:用于设置 Nuxt.js 应用的根目录(这俩api有很大合并的意义)
- srcdir:用于设置 Nuxt.js 应用的源码目录(这俩api有很大合并的意义)
- router:用于对vue-router的扩展和定制,其中还包括了中间件的配置,但并不完美
- transition:用于定制Nuxt.js内置的页面切换过渡效果的默认属性值
- watchers:用于定制Nuxt.js内置的文件监听模块chokidar和 -Webpack 的相关配置项
同时,Nuxt.js 支持以generate命令将程序直接构建为静态 html ,就像上面说的,可以作为静态资源直接输出。 打包 npm run generate
开发中遇到的坑点
一开始用generate打包上线,发现首屏打开速度超慢,在测服上面大概需要10s+才能打开首页,但打开其他页面都是秒开,然后开始找原先,压缩vendor.js和app.js之后速度大约块一两s,此时首屏打开任很慢,仍然需要8s+才能打开页面,慢点的情况下可能需要30s+,实在不能忍,打开浏览器debug模式,发现首次加载的时候加载了所有的页面的js,这样首页打开速度慢.其他页面秒开就解释的通了,如下图,
原因找到了,接下来就是怎么解决了,于是我翻看官方文档,尝试了以下方法:
在 nuxt.config.js 里面设置:
module.exports = { render: { resourceHints: false, }, ...... ...... } 复制代码
nuxt官方文档上是这样说明的
nuxt的坑还有很多,但是还是值得尝试的
(2)vue-server-renderer
让我们先创建一个项目并安装一些依赖项:
1. mkdir vue-ssr 2. cd vue-ssr 3. npm init 4 .npm i vue@next vue-server-renderer --save 复制代码
package.json:
{ "name": "vue-ssr", "version": "1.0.0", "dependencies": { "vue": "^2.5.17", "vue-server-renderer": "^2.5.17" } } 复制代码
注意:vue版本号要和vue-server-renderer版本号保持一致
webpack.base.config.js
module.exports = { module: { rules: [{ test: /\.vue$/, loader: 'vue-loader', }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] }, plugins: [], resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } } } 复制代码
webpack.server.conf.js
const merge = require('webpack-merge') const nodeExternals = require('webpack-node-externals') const baseConfig = require('./webpack.base.config.js') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') const path = require('path'); const root= path.resolve(__dirname, '..'); module.exports = merge(baseConfig, { // 将 entry 指向应用程序的 server entry 文件 entry: path.join(root, 'entry/entry-server.js'), // 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import), // 并且还会在编译 Vue 组件时, // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。 target: 'node', // 对 bundle renderer 提供 source map 支持 devtool: 'source-map', // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports) output: { libraryTarget: 'commonjs2', path: path.join(root, 'dist'), filename: 'bundle.server.js' }, // https://webpack.js.org/configuration/externals/#function // https://github.com/liady/webpack-node-externals // 外置化应用程序依赖模块。可以使服务器构建速度更快, // 并生成较小的 bundle 文件。 externals: nodeExternals({ // 不要外置化 webpack 需要处理的依赖模块。 // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件, // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单 whitelist: /\.css$/ }), // 这是将服务器的整个输出 // 构建为单个 JSON 文件的插件。 // 默认文件名为 `vue-ssr-server-bundle.json` plugins: [ new VueSSRServerPlugin() ] }) 复制代码
webpack.client.conf.js
const webpack = require('webpack') const merge = require('webpack-merge') const baseConfig = require('./webpack.base.config.js') const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') const path = require('path'); const root= path.resolve(__dirname, '..'); module.exports = merge(baseConfig, { entry: path.join(root, 'entry/entry-client.js'), output: { path: path.join(root, 'dist'), filename: 'bundle.client.js' }, plugins: [ // 重要信息:这将 webpack 运行时分离到一个引导 chunk 中, // 以便可以在之后正确注入异步 chunk。 // 这也为你的 应用程序/vendor 代码提供了更好的缓存。 new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', filename: 'manifest.js', minChunks: Infinity }), // 此插件在输出目录中 // 生成 `vue-ssr-client-manifest.json`。 new VueSSRClientPlugin() ] }) 复制代码
entry-server.js
/* entry-server.js */ import { createApp } from '../src/app' export default context => { return new Promise((resolve, reject) => { const {app, router} = createApp() // 更改路由 router.push(context.url) // 等到 router 将可能的异步组件和钩子函数解析完 router.onReady(() => { const matchedComponents = app.$router.getMatchedComponents() // 匹配不到的路由,执行 reject 函数,并返回 404 if (!matchedComponents.length) { return reject({code: 404}) } // Promise 应该 resolve 应用程序实例,以便它可以渲染 resolve(app) }, reject) }) } 复制代码
entry-client.js
import { createApp } from '../src/app' // 客户端特定引导逻辑…… const {app} = createApp() // 这里假定 App.vue 模板中根元素具有 `id="app"` app.$mount('#app') 复制代码
server.js
const path = require('path') const express = require('express') const app = express() const { createBundleRenderer } = require('vue-server-renderer') // 创建renderer const template = require('fs').readFileSync('./index.ssr.html', 'utf-8') const serverBundle = require('./dist/vue-ssr-server-bundle.json') const clientManifest = require('./dist/vue-ssr-client-manifest.json') // 这个可以动态将生成的js文件渲染到html模版中 const renderer = createBundleRenderer(serverBundle, { runInNewContext: false, // 推荐 template: template, clientManifest: clientManifest }) app.use(express.static(path.join(__dirname, 'dist'))) // 响应路由请求 app.get('*', (req, res) => { const context = { url: req.url } // 创建vue实例,传入请求路由信息 renderer.renderToString(context, (err, html) => { if (err) { return res.state(500).end('运行时错误') } res.send(html) }) }) // 服务器监听地址 app.listen(8099, () => { console.log('服务器已启动!') }) 复制代码
基本上到这个地方关键性构建以及服务模块代码补充完成后,一个简单的基于vue-server-renderer例子就可以运行起来了。
以上所述就是小编给大家介绍的《#关于Vue SSR的一点看法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。