前端性能优化成神之路--SSR(服务端渲染)
Nuxt.js的介绍
Nuxt.js概述
nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(服务器端渲染).Vue.js是开发SPA(单页应用)的,Nuxt.js这个框架,用Vue开发多页应用,并在服务端完成渲染,可以直接用命令把我们制作的vue项目生成为静态html
通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染
我们的目标是创建一个灵活的应用框架,你可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。
Nuxt.js 预设了利用Vue.js开发服务端渲染的应用所需要的各种配置。还提供了一种命令叫:nuxt generate,为基于 Vue.js 的应用提供生成对应的静态站点的功能
作为框架,Nuxt.js 为 客户端/服务端
这种典型的应用架构模式提供了许多有用的特性:例如异步数据加载、中间件支持、布局支持等
压缩并 gzip 后,总代码大小为:57kb (如果使用了 Vuex 特性的话为 60kb)
官网地址:https://zh.nuxtjs.org/guide
服务器端渲染到底有什么好处
主要的原因时SPA(单页应用)不利于搜索引擎的SEO操作,Nuxt.js适合作新闻、博客、电影、咨询这样的需要搜索引擎提供流量的项目。如果你要作移动端的项目,就没必要使用这个框架了
什么是SSR
SSR,即服务器渲染,就是在服务器端将对Vue页面进行渲染生成html文件,将html页面传递给浏览器
SSR的两个优点
SEO 不同于SPA的HTML只有一个无实际内容的HTML和一个app.js,SSR生成的HTML是有内容的,这让搜索引擎能够索引到页面内容
更快内容到达时间 传统的SPA应用是将bundle.js从服务器获取,然后在客户端解析并挂载到dom。而SSR直接将HTML字符串传递给浏览器。大大加快了首屏加载时间
特性
基于 Vue.js。自动代码分层。服务端渲染。强大的路由功能,支持异步数据。静态文件服务。ES2015+ 语法。支持打包和压缩 JS 和 CSS。HTML头部标签管理。本地开发支持热加载。集成ESLint。支持各种样式预处理器: SASS、LESS、 Stylus等等。支持HTTP/2 推送
Nuxt.js 框架是如何运作的
基于 Vue、Webpack 和 Babel。Nuxt.js 集成了以下组件/框架,用于开发完整而强大的 Web 应用:Vue2,Vue Router,Vuex(当配置了 Vuex 状态树配置项 时才会引入),Vue Server Renderer ,vue-mate
Nuxt.js 使用 Webpack 和 vue-loader 、 babel-loader 来处理代码的自动化构建工作(如打包、代码分层、压缩等等)
Nuxt.js的工作流
下图阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 <nuxt-link>
切换路由渲染页面)的流程:
Incoming Request:浏览器发出一个请求
nuxtServerInit:服务端接收到请求,检查当前有没有nuxtServerInit这个配置项,如果有的化就先执行这个方法,这个方法是用来操作vuex的
middleware:这是一个中间件,跟路由相关,这里可以做任何想要的功能
validate():验证,配合高级动态路由去做一些验证,比如A页面是否允许跳转到B页面去,如果没有验证通过可以跳转到别的页面之类的校验
asyncData()&fetch():获取数据。asyncData()获取的数据是用来渲染vue组件的,fetch通常用来修改vuex的数据
Render:渲染
Naviage:如果发起了一个非Server 的路由,那么在执行一遍middleware——Render的过程
服务端渲染
你可以使用 Nuxt.js 作为你项目的UI渲染框架。
当运行 nuxt
命令时,会启动一个支持 热加载
和 服务端渲染
(基于Vue.js的 vue-server-renderer
模块)的开发服务器。
更多命令了解:https://zh.nuxtjs.org/guide/commands/
单页应用程序 (SPA)
如果您不想使用服务器端渲染或需要应用程序提供静态托管,则可以使用nuxt --spa
命令即可使用SPA
模式。 它为您提供了强大的SPA部署机制,无需使用Node.js
来运行应用程序或任何特殊的服务器端处理。
可以查看Nuxt.js提供的各种命令来了解更多相关使用信息。
如果你的项目有自己的Web服务器(例如用 Express.js 启动的Web服务),你仍然可以将 Nuxt.js 当作是中间件来使用,负责UI渲染部分的功能。在开发通用的Web应用过程中,Nuxt.js 是可插拔的,没有太多的限制,
可通过 开发编码中使用Nuxt.js 了解更多的信息
Nuxt环境搭建
Nuxt.js 十分简单易用。一个简单的项目只需将 nuxt
添加为依赖组件即可,为了快速入门,Nuxt.js团队创建了脚手架工具 create-nuxt-app
确保安装了npx(npx在NPM版本5.2.0默认安装了)
npx create-nuxt-app <项目名>
它会让你进行一些选择
项目名称
项目描述
在集成的服务器端框架之间进行选择
选择一些插件
是否选择单页面应用
添加 EsLint 以在保存时代码规范和错误检查您的代码。
添加 Prettier 以在保存时格式化/美化您的代码
选择您喜欢的UI框架
当运行完时,它将安装所有依赖项,因此下一步是启动项目,应用现在运行在 http://localhost:3000 上运行
npm run dev
如果不使用 Nuxt.js 提供的 starter 模板,我们也可以从头开始新建一个 Nuxt.js 应用项目,过程非常简单,只需要 1个文件和1个目录。如下所示:
mkdir <项目名> cd <项目名>
新建 package.json 文件,使用下面命令创建package.josn文件
npm init --yes
安装 nuxt:
一旦 package.json
创建好, 可以通过以下npm命令将 nuxt
安装至项目中
npm install --save nuxt
Nuxt.js 会依据 pages
目录中的所有 *.vue
文件生成应用的路由配置
创建 pages
目录——创建我们的第一个页面 pages/index.vue
<template> <h1>Hello world!</h1> </template>
然后启动项目,应用运行在 http://localhost:3000 上运行
npm run dev
项目目录结构
assets: 资源目录,用于组织未编译的静态资源如 LESS
、SASS
或 JavaScript
components:组件目录,用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData
方法的特性
layouts:布局目录 layouts
用于组织应用的布局组件。该目录名为Nuxt.js保留的,不可更改
middleware:中间件目录,目录用于存放应用的中间件
pages:页面目录 用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue
文件并自动生成对应的路由配置。该目录名为Nuxt.js保留的,不可更改
plugins:插件目录 用于组织那些需要在 根vue.js应用
实例化之前需要运行的 Javascript 插件
static:静态文件目录 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 /
下。该目录名为Nuxt.js保留,不可更改。
nuxt.config.js 文件:文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。该文件名为Nuxt.js保留的,不可更改
Store 目录:store
目录用于组织应用的 Vuex 状态树 文件。 Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store
目录下创建一个 index.js
文件可激活这些配置。该目录名为Nuxt.js保留,不可更改
package.json
:文件用于描述应用的依赖关系和对外暴露的脚本接口。该文件名为Nuxt.js保留的,不可更改。
别名
默认情况下,src目录
和根目录
相同
vue
模板中, 如果需要引入 assets
或者 static
目录, 使用 ~/assets/your_image.png
和 ~/static/your_image.png
方式
Nuxt.js配置
详情查看文档:https://zh.nuxtjs.org/guide/configuration
路由使用的示例和参数传递
将路由级别的页面都定义在pages目录下,Nuxt.js会自动配置路由,创建了如下目录和vue文件
index.vue中添加两个路由链接和引入一个公共的组件。search.vue页面。about.vue页面引入两个组件
<template> <section class="container"> <div> <logo /> <h2 class="subtitle"> My best Nuxt.js project </h2> <div class="links"> <nuxt-link class="search-page" to="/search">Search</nuxt-link> <nuxt-link class="about-page" to="/about/about">About</nuxt-link> </div> </div> </section> </template> <script> import Logo from '~/components/Logo.vue' export default { components: { Logo } } </script> <style> .container { margin: 0 auto; min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; } .subtitle { font-weight: 300; font-size: 42px; color: #526488; word-spacing: 5px; padding-bottom: 15px; } .links { padding-top: 15px; } </style>
<template> <div class="search-page">This is Search Page</div> </template> <script> export default { name: 'search' } </script> <style scoped></style>
<template> <div class="about-page"> This is About Page <about1></about1> <about2></about2> </div> </template> <script> import About1 from './components/about1' import About2 from './components/about2' export default { name: 'about', components: { About1, About2 } } </script> <style scoped></style>
params传递参数
路由经常需要传递参数,我们可以简单的使用params来进行传递参数,我们现在向新闻页面(news)传递个参数,然后在新闻页面进行简单的接收
下面index.vue页面跳转到search.vue页面,传递一个id参数
<template> <section class="container"> <div> <div class="links"> <nuxt-link class="search-page" :to="{ name: 'search', params: { Id: 3306 } }"> Search </nuxt-link> <nuxt-link class="about-page" to="/about/about">About</nuxt-link> </div> </div> </section> </template> <script> export default {} </script> <style> .container { margin: 0 auto; } .links { padding-top: 15px; } </style>
search.vue页面中接收传递过来的参数
<template> <div class="search-page"> This is Search Pages <div>ID:{{ $route.params.Id }}</div> </div> </template> <script> export default { name: 'Search' } </script> <style scoped></style>
Nuxt的动态路由和参数校验
动态路由,其实动态路由就是带参数的路由。比如我们现在新闻模块下面有很多新闻详细页
页面模板使用示例
页面模板的作用就是将一个所有页面都会引入的一个组件放在页面模板中,这样就不用在每个页面中去引入这个公共的组件
layouts下会有一个default.vue组件,如果pages里的页面组件没有指定其他页面模板,那么默认就是default.vue模板,再页面模板中也可以引入其他公共的组件
layouts/default.vue
<template> <div> 我是默认的模板 <nuxt /> </div> </template> <style> html { font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 16px; word-spacing: 1px; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; box-sizing: border-box; } </style>
pages/index.vue
<template> <section class="container"> <div> <div class="links"> <nuxt-link class="search-page" to="/search">Search</nuxt-link> <nuxt-link class="about-page" to="/about/about">About</nuxt-link> </div> </div> </section> </template> <script> export default {} </script> <style> .container { margin: 0 auto; min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; } .links { padding-top: 15px; } </style>
pages/search.vue
<template> <div class="search-page">This is Search Page</div> </template> <script> export default { name: 'search' } </script> <style scoped></style>
pages/about.vue
<template> <div class="about-page"> This is About Page <about1></about1> <about2></about2> </div> </template> <script> import About1 from './components/about1' import About2 from './components/about2' export default { name: 'about', components: { About1, About2 } } </script> <style scoped></style>
自定义页面模板
在layouts文件夹中创建一个vue文件info.vue
<template> <div> <h1 class="info-header">我是默认的头部</h1> <nuxt /> <footer class="info-footer">我是默认的底部</footer> </div> </template> <style> .info-header { color: red; } .info-footer { color: blue; } </style>
然后再需要的页面组件中使用layout:'xxx'来声明
<template> <div class="search-page">This is Search Page</div> </template> <script> export default { name: 'search', layout: 'info' } </script> <style scoped></style>
没有声明的还是使用default默认的页面模板
异步数据的使用
首先再服务端写一个接口,如果服务端使用的是别的语言或者只是前端开发可以不用这个步骤,首先创建文件和目录
city.js
const Router = require('koa-router') // 接口的配置 const router = new Router({ prefix: '/city' // 接口的前缀 }) // 具体的接口 router.get('/list',async (ctx) => { ctx.body = { list: ['北京', '深圳', '上海', '广州'] } }) module.exports = router
服务端入口文件index.js
引入模块并且使用use调用
const Koa = require('koa') const consola = require('consola') const { Nuxt, Builder } = require('nuxt') const config = require('../nuxt.config.js') const cityInterface = require('./interface/city.js') const app = new Koa() // Import and Set Nuxt.js options config.dev = !(app.env === 'production') async function start() { // Instantiate nuxt.js const nuxt = new Nuxt(config) const { host = process.env.HOST || '127.0.0.1', port = process.env.PORT || 3000 } = nuxt.options.server // Build in development if (config.dev) { const builder = new Builder(nuxt) await builder.build() } else { await nuxt.ready() } app.use(cityInterface.routes()).use(cityInterface.allowedMethods()) app.use(ctx => { ctx.status = 200 ctx.respond = false // Bypass Koa's built-in response handling ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash nuxt.render(ctx.req, ctx.res) }) app.listen(port, host) consola.ready({ message: `Server listening on http://${host}:${port}`, badge: true }) } start()
接口已经编写好了,可以调用这个接口,使用正常的方式请求这个接口的数据
<template> <div class="search-page"> This is Search Page <div v-for="(item, index) in list" :key="index">{{ item }}</div> </div> </template> <script> import axios from 'axios' export default { name: 'Search', data() { return { list: [] } }, async created() { const that = this const { status, data: { list } } = await axios.get('/city/list') if (status === 200) { that.list = list } } } </script> <style scoped></style>
接下来我们使用SSR的方式去渲染,注意asyncData是用来处理组件数据的,fetch是用来处理Vuex的,下面如果使用fetch能请求回来数据但渲染不出来
<template> <div class="search-page"> This is Search Pages <div v-for="(item, index) in list" :key="index">{{ item }}</div> </div> </template> <script> import axios from 'axios' export default { name: 'Search', data() { return { list: [] } }, async asyncData() { const { status, data: { list } } = await axios.get('/city/list') if (status === 200) { return { list } } } } </script> <style scoped></style>
Vuex 状态树
Nuxt.js 会尝试找到应用根目录下的 store
目录,如果该目录存在,它将做以下的事情:引用 vuex
模块,将 vuex
模块 加到 vendors 构建配置中去,设置 Vue
根实例的 store
配置项
Nuxt.js 支持两种使用 store
的方式
普通方式: store/index.js
返回一个 Vuex.Store 实例
模块方式: store
目录下的每个 .js
文件会被转换成为状态树指定命名的子模块 (当然,index
是根模块)
const state = () => ({ list: ['a', 'b'] }) const mutations = { add(state, text) { state.list.push(text) } } const action = { add: ({ commit }, text) => { commit('add', text) } } module.exports = { namespace: true, state, mutations, action }
import Vue from 'vue' import Vuex from 'vuex' import city from './modules/city' Vue.use(Vuex) const stroe = () => new Vuex.Store({ modules: { city }, actions: { // nuxtServerInit({ commit }, { req }) { // if (req.session.user) { // commit('city', req.session.user) // } // } } }) export default stroe
<template> <div class="search-page"> This is Search Pages <div v-for="(item, index) in $store.state.city.list" :key="index"> {{ item }} </div> </div> </template> <script> export default { name: 'Search' } </script> <style scoped></style>
前端性能优化成神之路--SSR(服务端渲染)的更多相关文章
- 前端性能优化成神之路—资源合并与压缩减少HTTP请求
资源合并与压缩减少HTTP请求的概要 资源合并与压缩减少HTTP请求主要的两个优化点是减少HTTP请求的数量和减少请求资源的大小 http协议是无状态的应用层协议,意味着每次http请求都需要建立通信 ...
- 前端性能优化成神之路--图片懒加载(lazyload image)
图片懒加载(当然不仅限于图片,还可以有视频,flash)也是一种优化前端性能的方式.使用懒加载可以想要看图片时才加载图片,而不是一次性加载所有的图片,从而在一定程度从减少服务端的请求 什么是懒加载 懒 ...
- 前端性能优化成神之路-HTTP压缩开启gzip
什么是HTTP压缩 HTTP压缩是指: Web服务器和浏览器之间压缩传输的”文本内容“的方法. HTTP采用通用的压缩算法,比如gzip来压缩HTML,Javascript, CSS文件. 能大大减少 ...
- 前端性能优化成神之路--vue组件懒加载(Vue Lazy Component )
---恢复内容开始--- 使用组件懒加载的原因 我们先来看看这样的一个页面,页面由大量模块组成,所有模块是同时进行加载,模块中图片内容较多,每个模块的依赖资源较多(包括js文件.接口文件.css文件等 ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十五║初探SSR服务端渲染(个人博客二)
缘起 时间真快,现在已经是这个系列教程的下半部 Vue 第 12 篇了,昨天我也简单思考了下,可能明天再来一篇,Vue 就基本告一段落了,因为什么呢,这里给大家说个题外话,当时写博文的时候,只是想给大 ...
- 实现ssr服务端渲染
前言 前段时间寻思做个个人网站,然后就立马行动了. 个人网站如何实现选择什么技术方案,自己可以自由决定. 刚好之前有大致想过服务端渲染,加载速度快,还有 SEO 挺适合个人网站的. 所以就自己造 ...
- 用prerender-spa-plugin插件Vue项目优化SEO做ssr服务端渲染及预渲染
今天在做公交的时候没干,用手机看看文章,偶然发现了一个关于Vue优化seo的文章,我先是在Vue的官方文档看了一篇关于Vue做SEO优化的文章. 上面提到了nuxt.js这个框架,这个框架我做过一个小 ...
- 实现ssr服务端渲染demo
最近在研究SSR服务器端渲染,自己写了的小demo. 项目布局 ├── build // 配置文件 │ │── webpack.base // 公共配置 │ │── webpack.clien ...
- 使用 PHP 来做 Vue.js 的 SSR 服务端渲染
对于客户端应用来说,服务端渲染是一个热门话题.然而不幸的是,这并不是一件容易的事,尤其是对于不用 Node.js 环境开发的人来说. 我发布了两个库让 PHP 从服务端渲染成为可能.spatie/se ...
随机推荐
- frp 初探
条件: (1) 服务器端要有公网 IP (2) 客户端能上网,能够访问服务器的公网 IP 下载 https://github.com/fatedier/frp/releases 根据服务器和客户端的操 ...
- No application encryption key has been specified.
环境:php7.1.10laravel5.5出现: 解决:在根目录下执行: php artisan key:generate OK问题解决
- Base64字符保存图片,图片转换成Base64字符编码
//文件转换成Base64编码 public static String getFileBase64Str(String filePath) throws IOException { String f ...
- Python3 系列之 环境包管理神器 pipenv
环境说明:Windows 10 build 17763 + Python 3.7.2 介绍 pipenv 是在 pip 与 virtualenv 基础上发展而来的,弥补了之前 virtualenv 通 ...
- hihocoder编程练习赛75
题目1 : 工作城市分配 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 H公司在北京和上海两个城市各有一间办公室.该公司最近新招募了2N名员工,小Hi负责把这2N名员工 ...
- 关于openSetting通过tap的调用
问题模块 框架类型 问题类型 API/组件名称 终端类型 微信版本 基础库版本 API和组件 小程序 Bug openSetting 工具 6.7.2 2.3.0 - 当前 Bug 的表现(可附上截图 ...
- 我写的Java相关的文章
此文正在更新中... Activiti 升级到Activiti7了. Web service/Soap Java如何调用.net写的asmx服务
- 极简】如何在服务器上安装SSL证书?
本文适合任何人了解,图形化操作.下面以腾讯云为例,并且服务器(linux)也安装了宝塔面板. 1.登陆腾讯云账号进入控制台,找到SSL的产品 2.按要求申请并填写表单,记住私钥密码 3.提交后,待腾讯 ...
- eNSP 常用操作
1.eNSP关闭保存文件的提示信息 总是提示如下信息: Oct 12 2017 23:49:24-08:00 Huawei DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4. ...
- Keystone, Start, Failed to Load Bson
If you have installed the Keystone.js, and properly installed mongodb, but when tried to start the k ...