nuxt.js相关

nuxt.js相关

      • Nuxt
      • 生命周期
      • 路由
        • 基础路由
        • 动态路由
      • 异步数据-asyncData方法
        • 返回 Promise
        • 使用 async或await
        • 使用 回调函数
        • 返回 对象
        • 数据的展示
        • 上下文对象
          • 使用 req/res(request/response) 对象
        • 错误处理
      • Vuex状态树
        • 使用状态树
        • 普通方式
        • 模块文件
        • fetch 方法
        • nuxtServerInit 方法
      • 创建nuxt项目(实战项目)
        • 兼容es6语法(import)
          • babel-node依赖包
        • 兼容sass语法
        • 入口文件
        • 登录注册功能(服务端)
          • 配置文件
          • 创建mongoose模型user
        • 城市服务相关接口
          • 获取定位接口
          • nuxtServerInit方法跟vuex
          • 获取菜单接口

Nuxt

Nuxt官方文档

简单说,Nuxt就是基于Vue的一个应用框架,采用服务端渲染,让你的SPA应用(Vue)也可以拥有SEO

生命周期

Vue生命周期全都跑在客户端(浏览器),而Nuxt生命周期有些在服务端(Node),客户端,甚至两边都在:

生命周期流程图,红框内的是Nuxt的生命周期(运行在服务端)黄框内同时运行在服务端&&客户端上绿框内则运行在客户端
nuxt.js相关_第1张图片

路由

Nuxt.js-路由文档
Nuxt.js 依据 pages 目录结构自动生成vue-router 模块的路由配置。
要在页面之间使用路由,建议使用 标签。

<template>
  <nuxt-link to="/">首页</nuxt-link>
</template>

基础路由

假设 pages 的目录结构如下:

pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue

那么,Nuxt.js 自动生成的路由配置如下:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'user',
      path: '/user',
      component: 'pages/user/index.vue'
    },
    {
      name: 'user-one',
      path: '/user/one',
      component: 'pages/user/one.vue'
    }
  ]
}

动态路由

在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录

以下目录结构:

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

Nuxt.js 生成对应的路由配置表为:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },
    {
      name: 'slug-comments',
      path: '/:slug/comments',
      component: 'pages/_slug/comments.vue'
    }
  ]
}

你会发现名称为users-id的路由路径带有 :id? 参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。

路由参数校验、嵌套路由、动态嵌套路由等等可以直接看官方文档,Nuxt.js-路由文档

异步数据-asyncData方法

asyncData方法

Nuxt.js 扩展了 Vue.js,增加了一个叫asyncData 的方法,使得我们可以在设置组件的数据之前能异步获取或处理数据。

asyncData方法会在组件(限于页面组件每次加载之前调用。它可以在服务端或路由更新之前被调用。
在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据
Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件

注意:由于asyncData方法是在组件初始化前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。

Nuxt.js 提供了几种不同的方法来使用 asyncData 方法,你可以选择自己熟悉的一种来用:

如果项目中直接使用了node_modules中的axios,并且使用axios.interceptors添加拦截器对请求或响应数据进行了处理,确保使用 axios.create创建实例后再使用。否则多次刷新页面请求服务器,服务端渲染会重复添加拦截器,导致数据处理错误。

import axios from 'axios'
const myaxios = axios.create({
  // ...
})
myaxios.interceptors.response.use(function (response) {
  return response.data
}, function (error) {
  // ...
})

返回 Promise

export default {
  asyncData ({ params }) {
    return axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        return { title: res.data.title }
      })
  }
}

使用 async或await

export default {
  async asyncData ({ params }) {
    const { data } = await axios.get(`https://my-api/posts/${params.id}`)
    return { title: data.title }
  }
}

使用 回调函数

export default {
  asyncData ({ params }, callback) {
    axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        callback(null, { title: res.data.title })
      })
  }
}

返回 对象

如果组件的数据不需要异步获取或处理,可以直接返回指定的字面对象作为组件的数据。

export default {
  data () {
    return { foo: 'bar' }
  }
}

数据的展示

asyncData 方法返回的数据在融合 data 方法返回的数据后,一并返回给模板进行展示,如:

<template>
  <h1>{{ title }}</h1>
</template>

上下文对象

可通过 API context 来了解该对象的所有属性和方法。

使用 req/res(request/response) 对象

在服务器端调用asyncData时,您可以访问用户请求的req和res对象。

export default {
  async asyncData ({ req, res }) {
    // 请检查您是否在服务器端
    // 使用 req 和 res
    if (process.server) {
      return { host: req.headers.host }
    }

    return {}
  }
}

错误处理

Nuxt.js 在上下文对象context中提供了一个 error(params) 方法,你可以通过调用该方法来显示错误信息页面。params.statusCode 可用于指定服务端返回的请求状态码。

以返回 Promise 的方式举个例子:

export default {
  asyncData ({ params, error }) {
    return axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        return { title: res.data.title }
      })
      .catch((e) => {
        error({ statusCode: 404, message: 'Post not found' })
      })
  }
}

如果你使用 回调函数 的方式, 你可以将错误的信息对象直接传给该回调函数, Nuxt.js 内部会自动调用 error 方法:

export default {
  asyncData ({ params }, callback) {
    axios.get(`https://my-api/posts/${params.id}`)
      .then((res) => {
        callback(null, { title: res.data.title })
      })
      .catch((e) => {
        callback({ statusCode: 404, message: 'Post not found' })
      })
  }
}

Vuex状态树

Vuex状态树

对于每个大项目来说,使用状态树 (store) 管理状态 (state) 十分有必要。这就是为什么 Nuxt.js 内核实现了 Vuex。

使用状态树

Nuxt.js 会尝试找到应用根目录下的 store 目录,如果该目录存在,它将做以下的事情:

  • 引用 vuex 模块
  • 将 vuex 模块 加到 vendors 构建配置中去
  • 设置 Vue 根实例的 store 配置项

Nuxt.js 支持两种使用 store 的方式,你可以择一使用:

  • 模块方式: store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块 (当然,index 是根模块)
  • Classic(不建议使用): store/index.js返回创建Vuex.Store实例的方法。.

无论使用那种模式,您的state的值应该始终是function,为了避免返回引用类型,会导致多个实例相互影响。

普通方式

Nuxt.js允许您拥有一个 store 目录,其中包含与模块对应的每个文件。

首先,只需将状态导出为 函数,将变量和操作作为 store/index.js 中的对象导出:

export const state = () => ({
  counter: 0
})

export const mutations = {
  increment (state) {
    state.counter++
  }
}

然后,您可以拥有 store/todos.js 文件:

export const state = () => ({
  list: []
})

export const mutations = {
  add (state, text) {
    state.list.push({
      text,
      done: false
    })
  },
  remove (state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle (state, todo) {
    todo.done = !todo.done
  }
}

Vuex将如下创建

new Vuex.Store({
  state: () => ({
    counter: 0
  }),
  mutations: {
    increment (state) {
      state.counter++
    }
  },
  modules: {
    todos: {
      namespaced: true,
      state: () => ({
        list: []
      }),
      mutations: {
        add (state, { text }) {
          state.list.push({
            text,
            done: false
          })
        },
        remove (state, { todo }) {
          state.list.splice(state.list.indexOf(todo), 1)
        },
        toggle (state, { todo }) {
          todo.done = !todo.done
        }
      }
    }
  }
})

在您的 pages/todos.vue 中,使用 todos 模块:

<template>
  <ul>
    <li v-for="todo in todos">
      <input type="checkbox" :checked="todo.done" @change="toggle(todo)">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
    </li>
    <li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  computed: {
    todos () {
      return this.$store.state.todos.list
    }
  },
  methods: {
    addTodo (e) {
      this.$store.commit('todos/add', e.target.value)
      e.target.value = ''
    },
    ...mapMutations({
      toggle: 'todos/toggle'
    })
  }
}
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>

模块方法也适用于顶级定义,而无需在 store 目录中实现子目录
示例:您创建文件 store/state.js 并添加以下内容

export default () => ({
  counter: 0
})

相应的可以在文件夹中添加 store/mutations.js

export default {
  increment (state) {
    state.counter++
  }
}

模块文件

您可以将模块文件分解为单独的文件:state.js,actions.js,mutations.js和getters.js。如果您使用index.js来维护state,getters,actions和mutations,同时具有单个单独的操作文件,那么仍然可以正确识别该文件。

注意:在使用拆分文件模块时,必须记住使用箭头函数功能, this 在词法上可用。词法范围this意味着它总是指向引用箭头函数的所有者。如果未包含箭头函数,那么this将是未定义的(undefined)。解决方案是使用 “normal” 功能,该功能会将this指向自己的作用域,因此可以使用。

fetch 方法

fetch 方法会在渲染页面前被调用,作用是填充状态树 (store) 数据,与 asyncData 方法类似,不同的是fetch不会设置组件的数据

fetch-API文档

nuxtServerInit 方法

如果在状态树中指定了 nuxtServerInit 方法,Nuxt.js 调用它的时候会将页面的上下文对象作为第2个参数传给它(服务端调用时才会生效)。当我们想将服务端的一些数据传到客户端时,这个方法是灰常好用的。

举个例子,假设我们服务端的会话状态树里可以通过 req.session.user 来访问当前登录的用户。将该登录用户信息传给客户端的状态树,我们只需更新 store/index.js 如下:

actions: {
  nuxtServerInit ({ commit }, { req }) {
    if (req.session.user) {
      commit('user', req.session.user)
    }
  }
}

如果你使用_状态树模块化_的模式,只有主模块(即 store/index.js)适用设置该方法(其他模块设置了也不会被调用)。

nuxtServerInit 方法接收的上下文对象和 fetch 的一样,但不包括 context.redirect() 和 context.error()。

注意:异步nuxtServerInit操作必须返回Promise来通知nuxt服务器等待它们。

actions: {
  async nuxtServerInit({ dispatch }) {
    await dispatch('core/load')
  }
}

创建nuxt项目(实战项目)

使用nuxt官网提供的脚手架

// nuxt-learn 是项目名
npx create-nuxt-app nuxt-learn

nuxt.js相关_第2张图片
nuxt.js相关_第3张图片
项目目录
nuxt.js相关_第4张图片

兼容es6语法(import)

初始化的项目使用了nodemon监听和热刷新脚本,仅支持require语法,不支持es6的imoort语法

使用nodemon可以监听文件修改,然后让服务器自行重启。

// package.json
 "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js",
    "generate": "nuxt generate"
  },
// 正常
const Koa = require('koa')

// 改为import  报错
import Koa from 'koa'

nuxt.js相关_第5张图片

babel-node依赖包

使用 babel-node可以在 node 端自行编译并运行es6 甚至 es7。安装方法如下:

npm i @babel/core @babel/cli @babel/preset-env @babel/node -D 
// 或者使用 yarn

注意:我这里是局部安装的,全局安装的方法请自行看官方文档。

然后我们需要在项目的根目录下面创建 .babelrc 文件

// .babelrc文件
{
  "presets": ["@babel/preset-env"]
}

最后修改 package.json

// package.json文件 在dev和start后面加上--exec babel-node
  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server --exec babel-node",
    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js --exec babel-node",
    "generate": "nuxt generate"
  },

说明一下为什么要加 --exec这个参数:这个参数是让 nodemon 能运行非 node 程序
比如运行 py 文件nodemon --exec “python -v” ./app.py。在这里因为我们是用 nodemon 运行 babel-node,而不是 server.js,所以需要加 --exec 这个参数。

这样就支持import语法了,可以重新跑一下服务

npm run dev

nuxt.js相关_第6张图片

兼容sass语法

// pages/index.vue
<style lang="scss">
</style>

不处理直接使用会报错

引入两个依赖包sass-loader和node-sass

npm install sass-loader

npm install node-sass
// 如果node-sass安装失败  可以尝试使用淘宝镜像安装

// 如果没有淘宝镜像 可以先安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install node-sass

安装好两个依赖包后,就能兼容sass语法了

入口文件

vue-cli入口文件是app.vue,在nuxt开发当中则是./layout/default.vue

<template>
  <div id="app">
    <!-- 公共头部组件 -->
    <xxx-header></xxx-header>
    <!-- 路由视图,相当于router-view -->
    <nuxt/>
    <!-- 公共底部组件 -->
    <xxx-footer></xxx-footer>
  </div>
</template>

登录注册功能(服务端)

验证码通过qq邮箱发送
QQ邮箱 设置 账户
nuxt.js相关_第7张图片
得到一个授权码,需要写进配置文件里
nuxt.js相关_第8张图片

配置文件

import 引入的包可以通过
npm install 名字 去获取
server/dbs/config.js

export default {
  // mongodb数据库地址 端口号默认 27017   student为数据组名称
  dbs: 'mongodb://127.0.0.1:27017/student',
  // redis 用来存验证码 效率比较高
  redis: {
    get host() {
      return '127.0.0.1'
    },
    get port() {
      // 默认端口号
      return 6379
    }
  },
  // smtp服务  发验证码邮件
  smtp: {
    get host() {
      // 腾讯邮箱
      return 'smtp.qq.com'
    },
    get user() {
      return '[email protected]'
    },
    // 通过qq邮箱设置 得到的授权码
    get pass() {
      return 'ersetxsjtpuxijae'
    },
    get code() {
      return () => {
        // 随机生成四位验证码
        return Math.random().toString(16).slice(2, 6).toUpperCase()
      }
    },
    get expire() {
      // 过期时间 1分钟
      return ()=> {
        return new Date().getTime() + 60 * 60 * 1000
      }
    }
  }
}
创建mongoose模型user

server/dbs/models/users.js

import mongoose from 'mongoose'
const Schema = mongoose.Schema
const UserSchema = new Schema({
  username: {
    type: String,
    // true表示唯一
    unique: true,
    // 必填
    require: true
  },
  password: {
    type: String,
    require: true
  },
  email: {
    type: String,
    require: true
  }
})

export default mongoose.model('User', UserSchema)

server\interface\utils\axios.js
简单封装axios

import axios from 'axios'

const instance = axios.create({
  baseURL: `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`,
  // 超时
  timeout: 1000,
  // 默认头部
  headers: {

  }
})

export default instance

server\interface\utils\passport.js
用来验证用户密码

import passport from 'koa-passport'
import LocalStrategy from 'passport-local'
import UserModel from '../../dbs/models/users'

passport.use(new LocalStrategy(async (username, password, done) => {
  let where = {
    username
  }
  let result = await UserModel.findOne(where)
  if (result != null) {
    if (result.password === password) {
      // 用户输入密码跟数据库密码一致
      return done(null, result)
    } else {
      return done(null, false, '密码错误')
    }
  } else {
    return done(null, false, '用户不存在')
  }
}))

// 用户每次进来 自动通过session验证
// 在每次请求时 会从session中读取用户对象  用户通过验证后serializeUser会将用户数据存在session中
// 序列化
passport.serializeUser((user, done) => {
  done(null, user)
})
// 反序列化
passport.deserializeUser((user, done) => {
  done(null, user)
})

export default passport

server\interface\users.js
users下相关接口实现

import Router from 'koa-router'
import Redis from 'koa-redis'
// nodemailer支持node发邮件
import nodeMailer from 'nodemailer'
import User from '../dbs/models/users'
import Passport from './utils/passport'
import Email from '../dbs/config'
import axios from './utils/axios'

// 路由前缀
let router = new Router({
  prefix: '/users'
})

// 获取redis客户端
let Store = new Redis().client

// 注册接口
router.post('/signup', async(ctx) => {
  const {
    username,
    password,
    email,
    code
  } = ctx.request.body
 
  // 校验验证码 nodemailer发验证码时会存进redis里 这里从redis里拿出对比
  if (code) {
    // Store.hget(`nodemail:${username}`, 'code')  redis是key-value存储 nodemail表示属于哪个模块 username用来匹配    减值code
    const saveCode =await Store.hget(`nodemail:${username}`, 'code')
    // 过期时间
    const  saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
    if (code === saveCode) {
      if (new Date().getTime() - saveExpire > 0) {
        ctx.body = {
          code: -1,
          msg: '验证码已过期,请重新尝试'
        }
        return false
      }
    } else {
      ctx.body = {
        code: -1,
        msgL: '请填写正确的验证码'
      }
    }
  } else {
    ctx.body = {
      code: -1,
      msg:'请填写验证码'
    }
  }

  let user = await User.find({
    username
  })

  if (user.length) {
    ctx.body = {
      code: -1,
      msg: '已被注册'
    }
    return
  }

  // 写库操作
  let nuser = await User.create({
    username,
    password,
    email
  })
  if (nuser) {
    // 写库成功
    let res = await axios.post('/users/signin', {
      username,
      password
    })
    if (res.data && res.data.code ===0) {
      ctx.body = {
        code: 0,
        msg: '注册成功',
        user: res.data.user
      }
    } else {
      ctx.body = {
        code: -1,
        msg: 'error'
      }
    }
  } else {
    ctx.body = {
      code: -1,
      msg: '注册失败'
    }
  }
})

// 登录接口
router.post('/signin', async (ctx, next) => {
  return Passport.authenticate('local', function(err, user, info, status) {
    if (err) {
      ctx.body = {
        code: -1,
        msg: err
      }
    } else {
      if (user) {
        ctx.body = {
          code: 0,
          msg: '登录成功',
          user
        }
        return ctx.login(user)
      } else {
        // 异常
        ctx.body = {
          code: 1,
          msg: info
        }
      }
    }
  })(ctx, next)
})

// 验证码验证
router.post('/verify', async (ctx, next) => {
  let username = ctx.request.body.username
  const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
  if (saveExpire && new Date().getTime() - saveExpire > 0) {
    ctx.body = {
      code: -1,
      msg: '验证请求过于频繁,1分钟内1次'
    }
    return false
  }

    // 验证邮件相关
    // 发送对象
    let transporter = nodeMailer.createTransport({
      host: Email.smtp.host,
      port: 587,
      // secure: true 则监听405端口 false 为其他端口
      secure: false,
      // 权限校验
      auth: {
        user: Email.smtp.user,
        pass: Email.sntp.pass
      }
    })
    // 接手对象
    let ko = {
      code:Email.smtp.code(),
      expire:Email.smtp.expire(),
      email: ctx.request.body.email,
      user: ctx.request.body.username
    }

    // 邮件显示内容
    let mailOptions = {
      // 标题
      from: `"认证邮件" <${Email.smtp.user}>`,
      to: ko.email,
      // 主题
      subject: '《慕课网高仿美团网全栈实战》注册码',
      html: `您在《慕课网高仿美团网全栈实战》课程中注册,您的邀请码是&{ko.code}`
    }
    // 发送邮件
    await transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        return console.log('error')
      } else {
        // 存到redis哈希表里
        Store.hmset(`nodemail:${ko.user}`, 'code', ko.code, 'expire', ko.expire, 'email', ko.email)
      }
    })
    ctx.body = {
      code: 0,
      msg: '验证码已发送,可能会有延时,有效期1分钟'
    }
})

// 退出接口
router.get('/exit', async (ctx, next) => {
  await ctx.logout()
  // isAuthenticated 这个API由passport包提供
  if (!ctx.isAuthenticated()) {
    // 检查当前是不是登录状态 不是的话 说明退出成功
    ctx.body = {
      code:0
    }
  } else {
    ctx.body = {
      code: -1
    }
  }
})

// 获取用户名
router.get('/getUser', async (ctx) => {
  // isAuthenticated 这个API由passport包提供  user也是登录成功时passport这个包存进session里的
  if (ctx.isAuthenticated()) {
    const {username, email} = ctx.session.passport.user
    ctx.body = {
      user: username,
      email
    }
  } else {
    ctx.body = {
      user: '',
      email: ''
    }
  }
})

// 导出路由
export default router

server\index.js
引入相关包,配置

// const Koa = require('koa')
import Koa from 'koa'
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')

import mongoose from 'mongoose'
// 用来处理post请求相关 没有这个包就无法通过ctx.request.body获取post请求参数
import bodyParser from 'koa-bodyparser'
// 存session cookie相关的 包
import session from 'koa-generic-session'
import Redis from 'koa-redis'
// 解决服务端向客户端发json 格式美化
import json from 'koa-json'
// 数据库相关配置
import dbConfig from '../server/dbs/config'
import passport from './interface/utils/passport'
import users from '../server/interface/users'

const app = new Koa()

// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
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

  app.keys = ['mt', 'keyskeys']
  app.proxy = true
  // 客户端存的是cookie 服务端是session
  app.use(session({
    key: 'mt',
    prefix: 'mt:uid',
    // session 相关存到redis
    store: new Redis()
  }))
  app.use(bodyParser({
    extendTypes: ['json', 'form', 'text']
  }))
  app.use(json())
  // 连接数据库
  mongoose.connect(dbConfig.dbs, {
    useNewUrlParser: true
  })
  // 处理登录相关
  app.use(passport.initialize())
  app.use(passport.session())

  await nuxt.ready()
  // Build in development
  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  }
  // 引入路由
  app.use(users.routes()).use(users.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()

启动redis服务

redis-server

nuxt.js相关_第9张图片
启动mongodb服务

mongo
// 或者   看你的mongo名称
mongod

在这里插入图片描述

跑一下项目服务

npm run dev

在这里插入图片描述
前端调用即可以用

城市服务相关接口

获取定位接口

server\interface\geo.js

import Router from "koa-router";
import axios from "./utils/axios";

// 路由前缀
let router = new Router({
  prefix: "/geo"
});

const sign = 'abcd'

router.get('/getPosition', async (ctx) => {
  let {
    status,
    data: {
      province,
      city
    }
  } = await axios.get(`http://cp-tools.cn/geo/getPosition?sign=${sign}`)
  if (status === 200) {
    ctx.body = {
      province,
      city
    }
  } else {
    ctx.body={
      province: '',
      city: ''
    }
  }
})

export default router

server\index.js
导入路由

import users from '../server/interface/users'
import geo from '../server/interface/geo'

app.use(users.routes()).use(users.allowedMethods())
app.use(geo.routes()).use(geo.allowedMethods())
nuxtServerInit方法跟vuex

通过nuxtServerInit方法实现ssr渲染获取到的定位
store\modults\geo.js

const state = () => {
  position: {}
}

const mutations = {
  setPosition(state, val) {
    console.log('geo mutations val:')
    console.log(val)
    state.position = val
  }
}

const actions = {
  setPosition: ({
    commit
  }, position) => {
    commit('setPosition', position)
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

store\index.js

import Vue from 'vue'
import Vuex from 'vuex'
import geo from './modults/geo'

Vue.use(Vuex)

const store = () => new Vuex.Store({
  modules: {
    geo
  },
  actions: {
    async nuxtServerInit({
      commit
    }, {req, app}) {
      console.log('nuxtServerInit:')
      const {status, data: {province, city}} = await app.$axios.get('/geo/getPosition')
      console.log(province, city)
      // 这里的检查是在客户端做的
      commit('geo/setPosition', status === 200 ? {city, province} : {province:'', city: ''})
    }
  }
})

export default store

前端页面
components\public\header\geo.vue

// 获取到服务端存放在vuex的定位
{{$store.state.geo.position.city}}
获取菜单接口

server\dbs\models\menu.js

import mongoose from 'mongoose'
const Schema = mongoose.Schema
const Menu = new Schema({
  menu: {
    type: Array,
    require: true
  }
})

export default mongoose.model('Menu', Menu)

server\interface\geo.js

import Router from "koa-router";
import axios from "./utils/axios";
import Menu from '../dbs/models/menu'
import Proviece from '../dbs/models/province'

// 路由前缀
let router = new Router({
  prefix: "/geo"
});

const sign = 'abcd'

router.get('/getPosition', async (ctx) => {
  let {
    status,
    data: {
      province,
      city
    }
  } = await axios.get(`http://cp-tools.cn/geo/getPosition?sign=${sign}`)
  if (status === 200) {
    ctx.body = {
      province,
      city
    }
  } else {
    ctx.body={
      province: '',
      city: ''
    }
  }
})

router.get('/getMenu', async (ctx) => {
  // 数据库操作
  const result = await Menu.findOne()
  ctx.body = {
    menu: result.menu || []
  }
  // 线上接口
  // let {status, data: {menu}} = await axios.get(`http://cp-tlls.cn/geo/menu?sign=${sign}`)
  // if (status = 200) {
  //   ctx.body = {
  //     menu: munu
  //   }
  // } else {
  //   ctx.body = {
  //     menu: []
  //   }
  // }
  
})

export default router

store\modults\home.js

const state = () => ({
  menu: []
})

const mutations = {
  setMenu(state, val) {
    state.menu = val
  }
}

const actions = {
  setMenu: ({
    commit
  }, menu) => {
    commit('setMenu', menu)
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

store\index.js

import Vue from 'vue'
import Vuex from 'vuex'
import geo from './modults/geo'
import home from './modults/home'

Vue.use(Vuex)

const store = () => new Vuex.Store({
  modules: {
    geo,
    home
  },
  actions: {
    async nuxtServerInit({
      commit
    }, {req, app}) {
      console.log('nuxtServerInit:')
      // geo
      const {status, data: {province, city}} = await app.$axios.get('/geo/getPosition')
      commit('geo/setPosition', status === 200 ? {city, province} : {province:'', city: ''})
      // home
      const {status:status2,data: {menu}} = await app.$axios.get('/geo/getMenu')
      commit('home/setMenu', status2 === 200 ? menu : [])

    }
  }
})

export default store

前端页面通过

// 获取menu值
this.$store.state.home.menu

谢谢你阅读到了最后
期待你,点赞、评论、交流

你可能感兴趣的:(#,nuxt.js)