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


Posted in Javascript onSeptember 06, 2019

为什么要用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生命周期

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

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实现一个SSR的前端博客的示例代码

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

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

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

Javascript 相关文章推荐
jQuery弹出层始终垂直居中相对于屏幕或当前窗口
Apr 01 Javascript
$.getJSON在IE下失效的原因分析及解决方法
Jun 16 Javascript
返回页面顶部top按钮通过锚点实现(自写)
Aug 30 Javascript
ie下$.getJSON出现问题的解决方法
Feb 12 Javascript
js实现Select列表各项上移和下移的方法
Aug 14 Javascript
jQuery1.9.1源码分析系列(十六)ajax之ajax框架
Dec 04 Javascript
java中String类型变量的赋值问题介绍
Mar 23 Javascript
总结十个Angular.js由浅入深的面试问题
Aug 26 Javascript
JS函数修改html的元素内容,及修改属性内容的方法
Oct 28 Javascript
详解JavaScript中this的指向问题
Jan 20 Javascript
bootstrap模态框远程示例代码分享
May 22 Javascript
浅谈react前后端同构渲染
Sep 20 Javascript
layui type2 通过url给iframe子页面传值的例子
Sep 06 #Javascript
使用layui的router来进行传参的实现方法
Sep 06 #Javascript
layui关闭弹窗后刷新主页面和当前更改项的例子
Sep 06 #Javascript
layui关闭层级、简单监听的实例
Sep 06 #Javascript
layui表格内容溢出的解决方法
Sep 06 #Javascript
layui表格 列自动适应大小失效的解决方法
Sep 06 #Javascript
vue中 this.$set的用法详解
Sep 06 #Javascript
You might like
颠覆常识!无色透明的咖啡诞生了(中日双语)
2021/03/03 咖啡文化
强烈声明: 不要使用(include/require)_once
2013/06/06 PHP
PHP实现图片压缩的两则实例
2014/07/19 PHP
jQuery版Tab标签切换
2011/03/16 Javascript
img onload事件绑定各浏览器均可执行
2012/12/19 Javascript
JS鼠标滑过图片时切换图片实现思路
2013/09/12 Javascript
jquery弹出遮掩层效果【附实例代码】
2016/04/28 Javascript
Javascript中indexOf()和lastIndexOf应用方法实例
2016/08/24 Javascript
快速移动鼠标触发问题及解决方法(ECharts外部调用保存为图片操作及工作流接线mouseenter和mouseleave)
2016/08/29 Javascript
Mongoose经常返回e11000 error的原因分析
2017/03/29 Javascript
配置nodejs环境的方法
2017/05/13 NodeJs
Node.js使用gm拼装sprite图片
2017/07/04 Javascript
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
详解js类型判断
2018/05/22 Javascript
使用ng-packagr打包Angular的方法示例
2018/09/21 Javascript
express 项目分层实践详解
2018/12/10 Javascript
Python中用pycurl监控http响应时间脚本分享
2015/02/02 Python
在Mac OS上使用mod_wsgi连接Python与Apache服务器
2015/12/24 Python
详解python脚本自动生成需要文件实例代码
2017/02/04 Python
Python unittest基本使用方法代码实例
2020/06/29 Python
详解Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
2020/08/04 Python
Django配置Bootstrap, js实现过程详解
2020/10/13 Python
Python+OpenCV图像处理——实现直线检测
2020/10/23 Python
纯CSS3制作漂亮带动画效果的主机价格表
2015/04/25 HTML / CSS
哥伦比亚加拿大官网:Columbia Sportswear Canada
2020/09/07 全球购物
护士实习生自我鉴定范文
2013/12/10 职场文书
总经理职责
2013/12/22 职场文书
毕业生简历自我评价范文
2014/04/09 职场文书
酒店管理求职信
2014/06/09 职场文书
领导干部“四风”问题批评与自我批评材料
2014/09/24 职场文书
查摆剖析材料范文
2014/09/30 职场文书
幼儿园教师师德表现自我评价
2015/03/05 职场文书
党员干部廉政承诺书
2015/04/28 职场文书
2015年高校图书馆工作总结
2015/04/30 职场文书
2015年酒店年度工作总结
2015/05/23 职场文书
预备党员转正党小组意见
2015/06/01 职场文书