Nuxt.js实践(下)

1,809 阅读11分钟

前言

前两篇我们依次从Web的发展了解到了SSR的特点,同时也知道了SSR真正实现的原理以及整个过程,并且我们以Vue为例,完成了基于Vue的同构开发应用,今天就是SSR的最后一部分,我们将进行Nuxt.js框架的实践

PS:Vue-SSR将会分为三部分完成:理解SSR以及实践于Vue(上)、Vue中同构开发SSR应用(中)、Nuxt.js实践(下)

Nuxt.js是什么

Nuxt.js 是⼀个基于 Vue.js 的通⽤应⽤框架。 通过对客户端/服务端基础架构的抽象组织,Nuxt.js主要关注的是应⽤的UI渲染。

结论:

  • nuxt不仅仅⽤于服务端渲染也可⽤于spa应⽤开发;
  • 利⽤nuxt提供的基础项⽬结构、路由⽣成、中间件、插件等特性可⼤幅提⾼开发效率
  • nuxt可⽤于⽹站静态化

PS:www.nuxtjs.cn/guide(官方文档中…

Nuxt.js的特性

nuxt.js特性.png

Nuxt.js的渲染流程

Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 切换路由渲染页面)的流程:

nuxt渲染流程图.png 接下来我们就针对Nuxt.js的特性模块分别进行实践

1、nuxt的安装

安装我们使用官方提供cli

npx create-nuxt-app <项⽬名>

这是安装之中的选择配置项,可以按照自己项目的需要来进行适当的选择:

安装nuxt.png 运行项目:

npm run dev

这样我们就能在浏览器中看到这样的效果,说明我们就已经安装成功了:

启动服务.png

2、项目结构

我们先来看下官方生成的项目结构,简单看下各个文件夹的作用:

目录结构.png 目录结构:

  • assets资源⽬录 assets ⽤于组织未编译的静态资源如 LESS 、 SASS 或 JavaScript
  • components组件⽬录 components ⽤于组织应⽤的 Vue.js 组件。Nuxt.js 不会扩展增强该⽬录下 Vue.js 组件,即这些组件不会像⻚⾯组件那样有 asyncData ⽅法的特性。
  • layouts布局⽬录 layouts ⽤于组织应⽤的布局组件。
  • middleware中间件⽬录⽤于存放应⽤的中间件
  • pages⻚⾯⽬录 pages ⽤于组织应⽤的路由及视图。Nuxt.js 框架读取该⽬录下所有的 .vue⽂件并⾃动⽣成对应的路由配置。(PS:新建文件后路由是自动生成的,后面具体看例子)
  • plugins插件⽬录 plugins ⽤于组织那些需要在 根vue.js应⽤ 实例化之前需要运⾏的Javascript 插件。
  • static静态⽂件⽬录 static ⽤于存放应⽤的静态⽂件,此类⽂件不会被 Nuxt.js 调⽤ Webpack进⾏构建编译处理。 服务器启动的时候,该⽬录下的⽂件会映射⾄应⽤的根路径 / 下。
  • store:⽤于组织应⽤的 Vuex 状态树⽂件。 Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store ⽬录下创建⼀个 index.js ⽂件可激活这些配置。
  • nuxt.config.js:该⽂件⽤于个性化配置Nuxt应⽤。

好啦,项目结构大概就说这么多了,接下来主要针对Nuxt.js的特点特性来进行一些简单的实践~

路由

1、自动路由配置

  • 基础路由

我们先来看一个非常方便的特性,这是常规vue项目结构没有的,我们都知道我们平时使用vue-cli开发我们在pages里面新建完文件之后还要自己去配置路由,项目大了多少有点麻烦,但是我们的Nuxt.js就推出了自动路由配置

简单点说,就是只要我们在pages里面新建文件之后,路由结构就能自动配置好,没错,你没听错,就是自动配置好!(是不是感觉体验感贼好)那我们就来试试

我们在pages里面新建两个.vue文件:

  • pages/admin.vue 商品管理⻚
  • pages/login.vue 登录⻚

然后我们什么都不做,直接在浏览器当中输入URL:http://localhost:3000/admin 我们看图:神奇吧,什么都没配置,路由就自动已经配置好了,是不是感觉很香!

自动生成路由admin.png 那这个路由是在哪里自己配置的呢?哎,我们来到文件结构当中,所有的路由都是在这里进行了自动配置:

自动配置路由文件.png

  • 动态路由

那我们可能会想,简单的路由是可以自动生成,那假如说我们是动态路由呢?路由后面携带参数怎么做呢?

很显然,Nuxt是考虑到的,在我们需要使用动态路由的时候,Nuxt规定:在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线(_)作为前缀的 Vue 文件 或 目录

举个例子:

动态路由.png

动态路由添加结果.png

动态路由结果.png 我们来看这三张图,我们按照规则添加了文件夹,结果在路由表当中也是自动添加了我们想要的动态路由,是不是同样很方便呢?

  • 嵌套路由

那我们又会说,如果我们项目中是嵌套路由呢?那又该怎么做呢?

同样Nuxt.js也有考虑到,规则是:创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件

我们同样也来举一个🌰:

嵌套路由.png

嵌套路由表.png

嵌套路由结果显示.png 我们看这个,同样也会比较方便的自动生成嵌套路由的问题,不得不说,这种开发体验感真的是挺好的,而且按照一定的规则来命名文件名称也有利于我们来管理文件结构

2、导航 我们来添加路由导航,我们在layouts/default.vue文件里面添加代码:

<template>
  <div>
    <nav>
      <nuxt-link to="/">首页</nuxt-link>
      <nuxt-link to="/admin">管理</nuxt-link>
      <nuxt-link to="/cart">购物车</nuxt-link>
    </nav>
    <Nuxt />
  </div>
</template>

是不是感觉很眼熟?没错就是和传统vue开发的router-link差不多,但是这里我们由于是使用SSR的模式,所以我们还是选择nuxt-link做导航

导航.png PS:layout里面的default.vue就是默认的全局使用的布局组件,我们看上图就可以看到其实就是对全局的一个包裹容器

3、自定义路由 我们学习了在Nuxt中怎么实现动态路由,那我们想一想那我们怎么来实现自定义路由呢?换句话说就是假如我现在有个页面我想用我自定义的路由名称怎么做呢?(这还用想嘛,改文件名字,ennnn,没错的确可以,但是我们需要用较为高端的方式)

我们找到nuxt.config.js,我们在里面加入代码:

自定义路由配置.png 然后这时候我们就可以使用我们自定义的路由配置了(虽然好像并没有什么太大的用)

视图

我们来看下Nuxt中关于布局部分的内容,我们先来看一张图,这张图展示了Nuxt.js 如何为指定的路由配置数据和视图

视图.png 1、默认布局 默认布局其实上面我们已经提到过了,就是刚开始创建项目的时候layouts/default.vue

2、自定义布局 同样我们也能自定义布局,比如我们现在想创建空⽩布局⻚⾯ layouts/blank.vue ,⽤于login.vue

那首先我们先在layouts里面新建一个blank.vue

<template>
  <div>
    <h1>hello, welcome to login</h1>
    <Nuxt />
  </div>
</template>

然后我们在pages/login.vue里面使用

<template>
  <div>
    登录页面
  </div>
</template>
<script>
  export default {
    layout: 'blank'
  }
</script>

我们来看结果:

自定义布局.png 3、自定义错误页面 我们日常开发的过程中经常有着自定义错误页面的需求,那在Nuxt里面这个就很方便来做,新建layouts/error.vue:

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">⻚⾯不存在</h1>
    <h1 v-else>应⽤发⽣错误异常</h1>
    <p>{{ error }}</p>
    <nuxt-link to="/">⾸ ⻚</nuxt-link>
  </div>
</template>
<script>
export default {
  props: ["error"],
  layout: "empty"
};
</script>

我们来看下结果:

自定义错误页面.png 4、页面 ⻚⾯组件就是 Vue 组件,只不过 Nuxt.js 为这些组件添加了⼀些特殊的配置项,比如给⾸⻚添加标题和meta等,index.vue:

export default {
  head() {
    return {
      title: "课程列表",
      // vue-meta利⽤hid确定要更新meta
      meta: [{ name: "description", hid: "description", content: "set pagemeta" }],
      link: [{ rel: "favicon", href: "favicon.ico" }],
    }
  }
}

我们来看下结果:

自定义页面配置.png Nuxt.js为页面提供的自定义特殊配置,具体可以查看官网:

为页面提供的特殊配置.png

异步数据获取

asyncData ⽅法使得我们可以在设置组件数据之前异步获取或处理数据。 PS:看了上一篇的会对这个方法熟悉

接下来我们就通过一个例子来实践一下:

1、安装依赖,我们以koa为例编写服务端接口:

npm i koa-router koa-bodyparser -S

2、编写服务端脚本

const Koa = require('koa');
const app = new Koa();
const bodyparser = require("koa-bodyparser");
const router = require("koa-router")({ prefix: "/api" });

const goods = [
  { id: 1, text: "iphone12", price: 1000 },
  { id: 2, text: "guangying", price: 1000 }
];

// 配置路由
// 获取产品列表
router.get("/goods", ctx => {
  ctx.body = {
    ok: 1,
    goods
  };
});

// 解析post数据并注册路由
app.use(bodyparser());
// 注册路由
app.use(router.routes());
app.listen(3030, () => console.log('api服务已启动'))

3、整合Axios模块 先安装依赖:

npm install @nuxtjs/axios -S

然后在nuxt.config.js里面配置:

modules: [
  '@nuxtjs/axios',
],
axios: {
  proxy: true
},
proxy: {
  "/api": "http://localhost:3030"
},

4、测试代码,index.vue:

export default {
  async asyncData({ $axios, error }) {
    const { ok, goods } = await $axios.$get("/api/goods");
    if (ok) {
      return { goods };
    }
    // 错误处理
    error({ statusCode: 400, message: "数据查询失败" });
  }
}

我们启动server服务器,然后看下我们测试的结果:

asyncData异步数据.png

状态管理Vuex

应⽤根⽬录下如果存在 store ⽬录,Nuxt.js将启⽤vuex状态树。定义各状态树时具名导出state, mutations, getters, actions即可

我们以用户登陆为例,先新建store/user.js:

export const state = () => ({
  token: ""
});
export const mutations = {
  init(state, token) {
    state.token = token;
  }
};
export const getters = {
  isLogin(state) {
    return !!state.token;
  }
};
export const actions = {
  login({ commit, getters }, u) {
    return this.$axios.$post("/api/login", u).then(({ token }) => {
      if (token) {
        commit("init", token);
      }
      return getters.isLogin;
    });
  }
};

然后我们在pages/login.vue中写入登录逻辑:

<template>
  <div>
    <h2>⽤户登录</h2>
    <el-input v-model="user.username"></el-input>
    <el-input type="password" v-model="user.password"></el-input>
    <el-button @click="onLogin">登录</el-button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      user: {
        username: "",
        password: ""
      }
    };
  },
  methods: {
    onLogin() {
      this.$store.dispatch("user/login", this.user).then(ok => {
        if (ok) {
          const redirect = this.$route.query.redirect || "/";
          this.$router.push(redirect);
        }
      });
    }
  }
};
</script>

然后这时候我们的登录逻辑就完成了!

中间件

中间件会在⼀个⻚⾯或⼀组⻚⾯渲染之前运⾏我们定义的函数,常⽤于权限控制、校验等任务。

比如我们在进入admin管理页面的时候就可以使用中间件,判断此时是否登录,如果没有则重定向到登录页面,我们先来编写中间件,创建middleware/auth.js:

export default function({ route, redirect, store }) {
  // 上下⽂中通过store访问vuex中的全局状态
  // 通过vuex中令牌存在与否判断是否登录
  if (!store.state.user.token) {
    redirect("/login?redirect=" + route.path);
  }
}

写完之后我们剩下来要做的就是注册中间件,这里有两方式:

  • 全局注册中间件,这种方式就是会直接作用在全局范围内,对每一个页面的访问都会有用,这种方式就直接在nuxt.config.js里面配置:
router: {
    middleware: ['auth'],
  },
  • 局部注册中间件,这种方式就是按需注册,哪个页面需要就在哪个页面里面注册使用就行,注册方式就是如在admin.vue中:
export default {
  middleware: ['auth']
}

插件

Nuxt.js会在运⾏应⽤之前执⾏插件函数,需要引⼊或设置Vue插件、⾃定义模块和第三⽅模块时特别有⽤

我们可以来写一个简单的插件,添加请求拦截器附加token,创建plugins/interceptor.js:

export default function({ $axios, store }) {
  //onRequest是@nuxtjs/axios模块提供的帮助方法
  $axios.onRequest(config => {
    // 附加令牌
    if (store.state.user.token) {
      config.headers.Authorization = "Bearer " + store.state.user.token;
    }
    return config;
  });
}

同样我们也需要注册插件,在nuxt.config.js中:

plugins: ["@/plugins/interceptor"]

这样我们每次在会有用到网络请求的时候都会使用到这个插件

nuxtServerInit

通过在store的根模块中定义 nuxtServerInit ⽅法,Nuxt.js 调⽤它的时候会将⻚⾯的上下⽂对象作为第2个参数传给它。当我们想将服务端的⼀些数据传到客户端时,这个⽅法⾮常好⽤。

  • nuxtServerInit只能写在store/index.js
  • nuxtServerInit仅在服务端执⾏

比如在这个例子里面我们要做到登录状态初始化,我们就可以在store/index.js:

export const actions = {
  nuxtServerInit({ commit }, { app }) {
    // 注意这个app是服务端实例,在我们这个例子中就是koa实例
    const token = app.$cookies.get("token");
    if (token) {
      commit("user/init", token);
    }
  }
};

但是在运行之前我们需要先安装一个依赖:

npm i -S cookie-universal-nuxt

然后在nuxt.config.js中注册:

modules: ['@nuxtjs/axios', '@nuxtjs/proxy', "cookie-universal-nuxt"],

开发过程中会涉及的几大特性基本完结!现在你再回过头去看那个nuxt的渲染流程图,是不是会明白许多了呢?

发布部署

1、服务端渲染应用部署:先进⾏编译构建,然后再启动 Nuxt 服务

npm run build
npm start

打包生成的内容位置见图:

服务端渲染应用部署.png 2、静态应用部署:Nuxt.js 可依据路由配置将应⽤静态化,使得我们可以将应⽤部署⾄任何⼀个静态站点主机服务商。

npm run generate
  • 执行命令之前注意渲染和接⼝服务器都需要处于启动状态
  • ⽣成内容在dist中(根目录的,和上一种方式的不一样)

这种方式的好处就是他会在打包过程中将所有的页面都会打包处理成静态html页面,这样其实在我们打开URL的时候我们其实访问就是静态页面,我们可以想到那这样不用每次都要渲染了,那我们访问时候的效率将会是一个巨大的提升!

OK,Vue SSR最后一篇文章也就结束啦~

这一篇我们主要学习了Vue SSR开箱即用的方案Nuxt.js,我们分析了Nuxt的基本核心内容,其余具体还有更多的点还需要我们以后慢慢去挖掘,好啦,Vue SSR系列基本上就已经结束了,经过三篇文章下来,相信我们应该对SSR的相关的东西都有了一些了解了吧~

文末

欢迎关注「前端光影」公众号,公众号都是以系统专题模块的形式来展示的,这样看起来就会比较方便,系统,让我们一起持续学习各种前端知识,加油!