Nuxt.js实现一个SSR的前端博客的示例代码

为什么要用Nuxt.js

公司现有的项目只有落地页是通过前端本身server读取pug文件进行服务端渲染的,当然是为了首屏加载速度以及SEO。Nuxt.js 是一个基于Vue.js的通用应用框架,预设了利用Vue.js开发服务端渲染的应用所需要的各种配置,只需要安装官方文档的要求进行开发,就可以很好的解决SSR的问题。我们以一个简单的博客为例,来实践一下Nuxt.js。

项目介绍

当前基于Nuxt.js的简化版博客,包括注册、登录、文章列表页面、文章详情页、以及用户列表页等几个页面,用户信息使用了Vux进行存储,异步数据使用了asyncData进行获取,配合了nuxtServerInit、cookie来处理刷新页面后Vux数据丢失的问题,同时使用了error模板页面处理常规错误,使用了中间件进行了简单的权限校验。该项目不足点,统一封装了axios的方法,但是没有考虑到服务端请求接口,token的处理。

目录结构

  • assets: 资源文件。用于组织未编译的静态资源如 LESS、SASS或 JavaScript。
  • components: 组件。
  • layouts: page: 模板页面,默认为 default.vue可以在这个目录下创建全局页面的统一布局,或是错误处理页面页,需要提供一个nuxt 标签,类似于router-view
  • middleware: 中间件,放置自定义的中间件,会在加载组件之前调用。可以在页面中调用: middleware: '中间件名称'。
  • pages: 页面,index.vue 为根页面,Nuxt.js 框架读取该目录下所有的 .vue文件并自动生成对应的路由配置,如需要动态参数id,则可以添加_id的文件,必须是下划线加参数名。
  • plugin: 插件,用于组织那些需要在 根Vue.js应用实例化之前需要运行的 Javascript 插件。
  • static: 静态文件,静态文件目录 static用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。
  • store: 用于组织vuex状态管理。具体使用请移步至 官网。
  • nuxt.config.js: nuxt.config.js文件用于组织Nuxt.js 应用的个性化配置,配置head,loading,css,plugins等。

Nuxt.js生命周期

1. incoming Request 浏览器发出的请求)
2. nuxtServerInit 服务端接受请求后,要检查当前有没有 nuxtServerInit配置项,如果有就执行这个函数
3. store action 用来操作vuex
4. middleware 可以做jWT等一些操作。
5. validate() 检验参数,参数检验失败,可以在layout里的error里面进行捕捉。
6. asyncData()& fetch() asyncData用来渲染组件,fetch用来渲染vuex
7. Render

Nuxt扩展以后的生命周期和方法以下:

beforeCreate: ƒ beforeCreate()
components: {NuxtLoading: {…}}
computed: {isOffline: ƒ}
context: {isStatic: false, isDev: true, isHMR: true, app: {…}, payload: undefined, …}
created: ƒ created()
data: ƒ data()
head: {title: "nuxt-meituan-ssr", meta: Array(3), link: Array(1), style: Array(0), script: Array(0)}
methods: {refreshOnlineStatus: ƒ, refresh: ƒ, errorChanged: ƒ, setLayout: ƒ, loadLayout: ƒ}
mounted: ƒ mounted()
nuxt: {…}
render: ƒ render(h, props)
router: VueRouter {app: Vue, apps: Array(1), options: {…}, beforeHooks: Array(2), resolveHooks: Array(0), …}
watch: {nuxt.err: "errorChanged"}

注意:

  • Vue.js生命周期的钩子只有beforeCreate和created会在服务端和客户端渲染。
  • 以上生命周期里都获取不到window对象。
  • asyncData和fetch我们可以拿到数据,不要尝试挂载数据到data上,此时获取不到this对象。

开发总结

如何修改默认启动端口?

可以在package.json下面修改配置,如下。

"config":{
  "nuxt":{
    "host":"127.0.0.1",
    "port":"3304"
  }
}

如何添加全局的样式?

可以在assets里添加全局Css文件,如在assets下的Css文件夹目录下添加了一个index.css文件,然后在nuxt-config.js里配置该css文件路径即可。 css:['~assers/css/index.css']

通过别名访问图片在template里是正确的,为何在Css设置背景图却报错?

在css配置的是,需要将'~/'后面的'/'去除掉。

  <img src="~/static/logo.jpg"/>
  backround-image:url('~static/logo.jpg');

如何添加路由动画?

同样,我们在Css文件里添加一些动画代码,一般样式会在其后面添加-active和-leave-active,其实和Vue动画形式一致。其中以page开头的动画,默认会作用于全部页面,如果想给特定的页面加动画,可以在对应的页面script里引用,如 transitions: 'bounce'即可。

.page-enter-active, .page-leave-active {
  transition: opacity .3s
 }
 .page-enter, .page-leave-active {
  opacity: 0
 }
 .bounce-enter-active {
  animation: bounce-in .8s;
 }
 .bounce-leave-active {
  animation: bounce-out .5s;
 }
 @keyframes bounce-in {
  0% { transform: scale(1) }
  50% { transform: scale(1.01) }
  100% { transform: scale(1) }
 }
 @keyframes bounce-out {
  0% { transform: scale(1) }
  50% { transform: scale(1.01) }
  100% { transform: scale(1) }
 }

路由参数如何传递?

同Vue-router,有声明式和编程式两种方式,无非是标签变成了 router.push(...)

  nuxt-link :to="{name:'article',params:{id:1234}}" >声明式</nuxt-link>
  // 编程式
  this.$router.push({
    name:'article',
    params:{
      id:1234
    }
  })

动态路由如何进行参数检验?

Nuxt.js提供了一个validate的生命周期钩子,可以在此进行参数的校验。以文章详情校验id为例,我们需要判断传入的id是否是数字,可以像下面这样处理。

 validate({ params }) {
  return /^\d+$/.test(params.id)
 }

如何添加404等错误页面?

可以在layout下新建一个error.vue页面,内容如下,当访问一个不存在的页面的时候,或者参数检验失败的时候,或者我们在middleware中间件处理抛出异常的时候,都会跳转到该页面。

<template>
 <div class="container">
  <h1 v-if="error.statusCode === 404">页面不存在</h1>
  <h1 v-else>应用发生错误异常</h1>
  <nuxt-link to="/">首 页</nuxt-link>
 </div>
</template>

<script>
export default {
 props: ['error'],
 layout: 'blog' // 指定模板页面
}
</script>

middleware中的文件抛出错误

export default function({ store, error, redirect }) {
  if (!store.state.user.userInfo.auth) {
      error({
       message: '没有权限哦!',
      statusCode: 403
     })
  }
}

顶部进度条如何设置?

loading 属性配置 可以在nuxt-config.js设置loading的颜色,使用了this. loading可能无法在created里立即使用。此种配置loading有严重的缺陷,无法知道真正的加载进度。也可以自定义加载组件,loading: '~components/loading.vue'。

export default {
 mounted () {
  this.$nextTick(() => {
   this.$nuxt.$loading.start()
   setTimeout(() => this.$nuxt.$loading.finish(), 500)
  })
 }
 }

异步数据如何获取?

Nuxt.js提供了两个函数,asyncData和fetch函数。asyncData 获取组件的数据,fetch 在渲染页面之前获取数据填充应用的状态树(store)。

asyncData可以使用promise也可以使用async函数,记住,此时返回的东西需要用一个对象进行包裹,不能挂载到data里,此时没有this对象。

// 方式一
 asyncData({ app,params,route,query,error}) {
   return getUserlist({}).then(res => {
    let user = [];
     user = res.list
     console.log(user,'user')
     return {user}
    })
     .catch(err => {
      console.log(err)
    })
},

// 方式二
async asyncData({ app }) {
  let data = await getUserlist({});
  let user = data.list;
  return { user }
}

fetch函数同上,可以使用promise也可以使用async函数,通常会commit一个mutation。

export default {
 fetch ({ store, params }) {
  return axios.get('http://my-api/stars')
  .then((res) => {
   store.commit('setStars', res.data)
  })
 }
}
</script>
// 或者使用 async 或 await 的模式简化代码如下:
<template>
 <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
 async fetch ({ store, params }) {
  let { data } = await axios.get('http://my-api/stars')
  store.commit('setStars', data)
 }
}
</script>

如何动态修改title的内容?

如果是写死的,可以直接修改head的配置。

 head() {
  return {
   // title: '',这里一旦声明,在asyncdata里修改也不起作用,直接以这个为准
   meta: [
    {
     hid: 'description', // nuxt.config 替换唯一标识 hid  { hid: 'description', name: 'description', content: 'Nuxt.js project' }
     name: 'content',
     content: '文章详情'
    }
   ]
  }
 },

如果是动态数据从数据源里获取,然后通过asynData里的app对象,动态修改head的title。

 asyncData({ app, params }) {
  const id = params.id;
  return getArticleDetail({ id })
   .then(result => {
      app.head.title = result.title;
   })
   .catch(err => {})
 }

如何进行权限JWT验证?

登录成功以后,我们会在cookie和Vuex中缓存token信息,当界面刷新的时候,会走store里的nuxtServerInit 函数,该函数仅在每个服务器端渲染中运,可以使用req.headers.cookie获取浏览器的cookie,再次更新store里的值,接着会走到中间件,中间件进行验证,如果有token信息则继续,没有则跳转到登录页。
1. 为什么要在nuxtServerInit更新store的值?
需要在middleware里使用,否则刷新后store里的值为空了。
2. 客户端调用接口可以拿到token,服务器端如何拿到?
可以通过nuxtServerInit里的req拿到请求信息的cookie,然后请求接口。
3. 前后端分离,刷新的时候如何保证用户名、token等信息依然存在?
可以像上面一样,每次取cookie的值再次更新store,但这样有一个问题,cookie可能会被篡改,后端代码需要做验证。也可以每次刷新重新通过token请求接口,更新用户信息。

store代码

import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
import { COOKIE_KEY } from '~/assets/js/constant.js';
Vue.use(Vuex);
const store = () =>
  new Vuex.Store({
    modules: {
      user
    },
    actions: {
      async nuxtServerInit({ commit, dispatch }, { req, app }) {
        if (req.headers.cookie) {
          let parsedResult = {};
          req.headers.cookie.split(';').forEach(cookie => {
            const currentCookie = cookie.split('=');
            parsedResult[currentCookie[0].trim()] = (currentCookie[1] || '').trim();
          });
          const userInfo = {
            name: parsedResult[COOKIE_KEY.NAME],
            token: parsedResult[COOKIE_KEY.TOKEN]
          };
          commit('user/setUserInfo',userInfo);
        }
      }
    }
  });

export default store;

中间件代码

export default function({ store, error, redirect }) {
  if (!store.state.user.userInfo.token || !store.state.user.userInfo.name) {
    //  error({
    //   message: 'You are not connected',
    //   statusCode: 403
    //  })
    redirect('/');
  }
}

nginx部署

npm run build
选择build以后的四个文件: .nuxt, static, nuxt.config.js, package.json上传到服务器。
pm2 pm2 start npm --name 'package.json.name' -- run start
nginx配置

查看网页源代码可以看到:

   server{
      listen 3000;
      server_name  felix12345.club;
      gzip on;
      gzip_buffers 32 4K;
      gzip_comp_level 6;
      gzip_min_length 100;
      gzip_types application/javascript text/css text/xml;
      gzip_disable "MSIE [1-6]\.";
      gzip_vary on;
      proxy_buffer_size 64k;
      proxy_buffers  32 32k;
      proxy_busy_buffers_size 128k;
      location / {
        root  /data/ww/nuxt;
        proxy_pass  http://127.0.0.1:3002;
        proxy_set_header X-Real-IP $remote_addr;
      }
    }

这样,使用Nuxt.js实现了一个服务端渲染的简易博客。

在线访问地址: http://felix12345.club:3000/article/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Nuxt.js开启SSR渲染的教程详解

    第一节:nuxt.js相关概述 nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(服务器端渲染).Vue.js是开发SPA(单页应用)的,Nuxt.js这个框架,用Vue开发多页应用,并在服务端完成渲染,可以直接用命令把我们制作的vue项目生成为静态html. 1.那服务器端渲染到底有什么好处呢? 主要的原因时SPA(单页应用)不利于搜索引擎的SEO操作,Nuxt.js适合作新闻.博客.电影.咨询这样的需要搜索引擎提供流量的项目.如果你要作移动端的项目,就没必要使用这个框

  • 详解Vue基于 Nuxt.js 实现服务端渲染(SSR)

    直接使用 Vue 构建前端单页面应用,页面源码时只有简单的几行 html,这并不利于网站的 SEO,这时候就需要服务端渲染 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了一个 React 的服务端渲染应用框架 Next.js 几小时后,一个基于 Vue.js 的服务端渲染应用框架应运而生,与 Next.js 异曲同工,这就是Nuxt.js 一.快速模板 在已经安装了 vue-cli 的前提下,可以快速创建一个 nuxt 的项目模板 vue init nuxt-commun

  • Nuxt.js SSR与权限验证的实现

    Nuxt.js是一个建立在Vue.js基础上的服务端渲染框架.它非常易于上手并且可以让你在几分钟内构建你的应用. 服务端渲染是一个解决所有SPA的SEO问题的伟大解决方案,但不幸的是它带来了另一个问题:权限验证成了另一个项目管理中的痛点. Nuxt.js官网提供了一个称为"路由鉴权"的示例(https://nuxtjs.org/examples/auth-routes).它展示了如何通过一个中间件来限定一个页面是否可访问,但是这个检查是在客户端的进行的并且服务端渲染出的内容无论是否进行

  • 详解使用Nuxt.js快速搭建服务端渲染(SSR)应用

    安装 nuxt.js Nuxt.js 官方提功了两种方法来进行项目的初始化,一种是使用Nuxt.js团队的脚手架工具 create-nuxt-app ,一种是根据自己的需求自由配置 使用脚手架适合新手,对 nodejs 后台框架有所了解:按照自己需求自由配置,需要对如何配置 webpack 以及 nodejs 后台框架有所了解. 两种方式比较下就是原生和插件的区别. 使用脚手架安装 需要有 nodejs 或者yarn环境,推荐使用 vscode 自带的控制台输入命令行命令进行操作 在有了环境之后

  • Nuxt.js实现一个SSR的前端博客的示例代码

    为什么要用Nuxt.js 公司现有的项目只有落地页是通过前端本身server读取pug文件进行服务端渲染的,当然是为了首屏加载速度以及SEO.Nuxt.js 是一个基于Vue.js的通用应用框架,预设了利用Vue.js开发服务端渲染的应用所需要的各种配置,只需要安装官方文档的要求进行开发,就可以很好的解决SSR的问题.我们以一个简单的博客为例,来实践一下Nuxt.js. 项目介绍 当前基于Nuxt.js的简化版博客,包括注册.登录.文章列表页面.文章详情页.以及用户列表页等几个页面,用户信息使用

  • 使用vuepress搭建静态博客的示例代码

    什么是vuePress vuePress是以vue驱动的主题系统的简约静态网站生成工具(拥有自己的默认主题). veuPress由vue,vue-router,webpack驱动的单页面应用,每个markdonw文件都使用markdonw-it编译为html文件,然后作为vue组件的模板来处理. 中文官网 VuePress仓库 项目地址 VuePress 有很多优点: 界面简洁优雅(个人感觉比 HEXO 好看) 容易上手(半小时能搭好整个项目) 更好的兼容.扩展 Markdown 语法 响应式布

  • 用实现ajax读博客rss示例代码

    this is test var xmlHttp; function createXMLHttpRequest() { if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } } function readRSS(url) { createXMLHt

  • 用ajax实现读博客rss示例代码

    复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>this is test</title

  • SpringBoot如何实现一个实时更新的进度条的示例代码

    前言 博主近期接到一个任务,大概内容是:导入excel表格批量修改状态,期间如果发生错误则所有数据不成功,为了防止重复提交,做一个类似进度条的东东. 那么下面我会结合实际业务对这个功能进行分析和记录. 正文 思路 前端使用bootstrap,后端使用SpringBoot分布式到注册中心,原先的想法是导入表格后异步调用修改数据状态的方法,然后每次计算修改的进度然后存放在session中,前台jquery写定时任务访问获取session中的进度,更新进度条进度和百分比.但是这存在session在服务

  • 利用js实现Ajax并发请求限制请求数量的示例代码

    出现问题描述:当不确定异步请求个数时,为防止当一瞬间发生上百个http请求时,导致堆积了无数调用栈进而导致内存溢出问题. 要求:将同一时刻并发请求数量控制在3个以内,同时还要尽可能快速的拿到响应的结果. 同面试问题: 实现一个批量请求函数 multiRequest(urls, maxNum),要求如下: 要求最大并发数 maxNum 每当有一个请求返回,就留下一个空位,可以增加新的请求 所有请求完成后,结果按照 urls 里面的顺序依次打出 1.基于Promise.all实现Ajax的串行和并行

  • SpringBoot实现统一封装返回前端结果集的示例代码

    在实际项目开发过程中.事实上我们经常用@restcontroller注释的方式,将相当于将返回数据的基本形式统一为JSON格式的数据.但是,由于我们的项目可能是由很多人开发的,所以我们最好将返回的结果统一起来,这样每个人都可以返回相同的数据格式,这不仅规范了代码,也方便了前端人员调用,否则每个人都会按照自己的风格编写,代码会变得非常混乱. 我们如何封装这个结果?我们应该注意返回到前端的数据中包含的信息. 一般来说,首先,这次必须有一个code来表示接口的状态.例如,0表示成功,1表示失败,2表示

  • Python实现一个简单的毕业生信息管理系统的示例代码

    写在前面: 从昨晚的梦里回忆起数据管理的作业: 实现一个自己的选题---- 毕业生信息管理系统,实现学生个人信息基本的增删改查, 我想了想前段时间刚学习的列表,这个简单啊 ,设计一个学生信息列表,然后列表里面再存每个学生详细信息的列表,然后来实现一个基本的增删查改,这个不难啊!直接开始撸代码! 上代码! def Menu():##菜单主界面 print('*'*22) print("* 查看毕业生列表输入: 1 *") print("* 添加毕业生信息输入: 2 *"

  • Java实现一个简单的文件上传案例示例代码

    Java实现一个简单的文件上传案例 实现流程: 1.客户端从硬盘读取文件数据到程序中 2.客户端输出流,写出文件到服务端 3.服务端输出流,读取文件数据到服务端中 4.输出流,写出文件数据到服务器硬盘中 下面上代码 上传单个文件 服务器端 package FileUpload; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Serve

  • 使用go实现一个超级mini的消息队列的示例代码

    目录 前言 目的 设计 协议 队列 broker 删除消息 生产者 消费者 启动 总结 前言 趁着有空余时间,就想着撸一个mini的生产-消费消息队列,说干就干了.自己是个javer,这次实现,特意换用了go.没错,是零基础上手go,顺便可以学学go. 前置知识: go基本语法 消息队列概念,也就三个:生产者.消费者.队列 目的 没想着实现多复杂,因为时间有限,就mini就好,mini到什么程度呢 使用双向链表数据结构作为队列 有多个topic可供生产者生成消息和消费者消费消息 支持生产者并发写

随机推荐