Nuxt使用

370 阅读5分钟

理解SSR

CSR vs SSR

  先来看看传统的web开发

传统的web开发(前后端不分离时代)都属于服务端渲染,因为整个页面所有的内容都是在服务端生成的。

  • 好处:这些页面在服务端就渲染完成了,所以当用户看见内容的时候是立刻就展示在页面中的,对SEO比较友好。
  • 缺点:服务端的压力会比较大(数据库查询,html模板解析等等)

CSR(SPA单页应用)

页面的框架不会在变了,只会不停地刷新不同的数据。

可以看到这种方式打开的页面,初始状态下,只是一个具有id=app的一个空页面。

  • 缺点1:首屏渲染等待时间长,必须得等js加载完毕,并且执行完毕,才能渲染出首屏。
  • 缺点2:seo不友好,爬虫只能拿到一个div,认为页面是空的,不利于seo

SSR(服务端渲染)

  为了解决这两个问题,出现了SSR解决方案,后端渲染出完整的首屏dom结构返回,前端拿到内容后放上首屏;后续的页面操作,再用单页的路由跳转和渲染,称之为服务端渲染(server side render)。

  • 缺点1:学习难度较高
  • 缺点2:第三方库有时候会有问题(有些生命周期只是在前端跑的)
  • 缺点3:增加服务器压力

Nuxt框架

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

优点:

  • nuxt不仅仅用于服务端渲染,也可以用于SPA应用开发。
  • 利用nuxt提供的基础项目结构、路由生成、中间件、插件等等,可大幅提高开发效率。
  • nuxt可用于网站静态化。
# 脚手架命令
npx create-nuxt-app@2.9.2 xxx(项目名称)

通过npx命令,npm5+版本自带到命令,这里注意要指定create-nuxt-app版本,目前最新版本是3.2.0,新的版本不自动搭建服务端文件了,这是我们需要指定对应版本的create-nuxt-app脚手架创建,才有server文件夹。

路由

路由生成
pages目录中所有 *.vue 文件自动生成应用的路由配置,新建:

  • pages/admin.vue 商品管理页
  • pages/login.vue 登录页
    查看 .nuxt/router.js 验证了路由生成

默认布局

查看 layouts/default.vue

<template>
	<nuxt />
</template>

自定义布局

创建空白布局页面 layouts/blank.vue,用于login.vue

<template>
	<div>
    	<nuxt />
    </div>
</template>

自定义错误页面

创建 layouts/error.vue

<template>
  <div>
    <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: 'blank'
};
</script>

页面

页面组件就是Vue组件,只不过Nuxt.js为这些组件添加了一些特殊的配置项。
给首页添加标题和meta等,index.vue

head() {
  return {
    title: '课程列表',
    meta: [{ name: 'dylan', hid: 'haha', content: 'set page meta' }],
    link: [{ rel: 'favicon', href: 'favicon.ico' }]
  }
},

异步数据获取

asyncData 方法使得我们可以在设置组件数据之前异步获取或处理数据

接口准备:

  • 安装@nuxt/axios模块:npm i @nuxtjs/axios -S
    配置:nuxt.config.js
modules: [
    '@nuxtjs/axios',
],
axios: {
    proxy: true
},
proxy: {
    "/api": "http://localhost:8080"
},
  • 安装依赖:npm i koa-router koa-bodyparser -S
  • 创建接口文件,setver/api.js
const Koa = require('koa');
const app = new Koa();
// 处理post请求参数
const bodyparser = require('koa-bodyparser');
// 路由配置
const router = require('koa-router')({ prefix: '/api' });

// 设置cookie加密密钥
app.keys = ["some secret", "another secret"];

const goods = [
  { id: 1, text: "web全栈架构师1", price: 10000 },
  { id: 2, text: "web全栈架构师2", price: 10000 },
]

// 获取商品列表接口  /api/goods
router.get('/goods', ctx => {
  ctx.body = {
    ok: 1,
    goods
  }
})

router.get('/detail', ctx => {
  ctx.body = {
    ok: 1,
    data: goods.find(good => good.id == ctx.query.id)
  }
})

// /api/login
router.post('/login', ctx => {
  // 获取用户名和密码
  const user = ctx.request.body;
  if(user.username === 'jerry' && user.password === '123') {
    // 将token存入cookie
    const token = 'a mock token';
    // 令牌存入cookie
    ctx.cookies.set('token', token);
    ctx.body = { ok: 1, token };
  } else {
    ctx.body = { ok: 0 };
  }
})

// 解析post数据并注册路由
app.use(bodyparser());
app.use(router.routes());

app.listen(8080, () => console.log('api服务已启动'))

修改pages/index.vue文件

// 前后端都会执行,时间点在beforeCreate之前
// 传递一个上下文
// 里面不能使用this,因为这时组件还不存在
async asyncData({$axios, error, redirect, store, app}) {
  try {
    const { ok, goods } = await $axios.$get('/api/goods');
    if(ok) {
      // 这里返回的对象,最终会和data中的对象融合(这里对象的优先级更高)
      return { goods }
    }
    // 错误处理
    error({statusCode: 400, message: '数据查询失败'})
  } catch (error) {
    error(error)
  }
},

保存后可以看到接口中的数据成功渲染出来了。
打开控制台,可以看到没有请求 /api/goods,说明服务器渲染成功!
接下来进行路由切换,进入admin路由,再回到首页,会发现goods接口又请求了。
说明其实只是首页的渲染,其实还是一个单页应用。

中间件

中间件会在一个页面或一组页面渲染之前运行我们定义的函数,常用于权限控制、校验等任务。
例如:管理员页面保护,创建 middleware/auth.js

export default function({ route, redirect, sotre }) {
  // 上下文通过store访问vuex中的全局状态
  // 判断是否登录,如果没有token,则重定向到login
  if(!sotre.state.user.token) {
    redirect('/login?redirect=' + route.path)
  }
}

注册中间件 admin.vue

export default {
  middleware: ['auth']
};

全局注册:将会对所有页面起作用,nuxt.config.js

router: {
	middleware: ["auth"]
}

Vuex 状态管理

应用根目录下如果存在store目录,Nuxt.js将会启用vuex状态树。定义各状态树时具名导出state,mutations,getters,actions即可。
例如:用户登录及登录状态保存,创建store/user.js

// 具名导出vuex的选项即可
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;
    })
  }
}

修改login页面

<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 {
  layout: 'blank',
  data() {
    return {
      user: {
        username: '',
        password: ''
      }
    }
  },
  methods: {
    onLogin() {
      this.$store.dispatch('user/login', this.user).then(ok => {
        console.log(ok);
        if(ok) {
          const redirect = this.$route.query.redirect || '/';
          this.$router.push(redirect)
        }
      })
    }
  }
};
</script>

在登录后,就可以进admin页面了。
此时刷新页面,会发现页面的cookie还在,但登录态消失了,这是因为vuex是浏览器的缓存,需要服务器在首次获取cookie时,将其写入vuex

跨平台的cookie写法

npm i cookie-universal-nuxt -S

安装完后,修改nuxt.config.js

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

在store文件夹中添加index.js文件

export const actions = {
  // 该action只能出现在index
  // 且只能在服务端执行一次
  // 参数2是nuxt上下文
  nuxtServerInit({commit}, {app}) {
    // 1、获取cookie
    const token = app.$cookies.get('token')
    // 2、写入user模块中
    if(token) {
      commit('user/init', token);
    }
  }
}

插件

Nuxt.js会在运行应用之前执行插件函数(只会在初始化时执行一次),需要引入或设置Vue插件、自定义模块和第三方模块时特别有用。

例:添加请求拦截器附加token,创建plugins/interceptor.js

export default function ({ $axios, store }) {
  // 这是模块提供的帮助方法,可以拦截每一次请求
  $axios.onRequest(config => {
    if(store.state.user.token) {
      config.headers.Authorization = 'Bearer ' + store.state.user.token;
    }
    return config;
  })
}

注册插件,nuxt.config.js

plugins: ["@/plugins/interceptor"]

发布部署

服务端渲染应用部署

// 打包
npm run build
// 启动服务
npm start

生成内容在.nuxt/dist中

静态应用部署

Nuxt.js可依据路由配置将应用静态化,使得我们可以将应用部署至任何一个静态站点主机服务商。

npm run generate

注意渲染和接口服务器都需要处于启动状态
生成内容在dist中