nuxt.js相關

Nuxt

Nuxt官方文檔

簡單說,Nuxt就是基於Vue的一個應用框架,採用服務端渲染,讓你的SPA應用(Vue)也可以擁有SEO

生命週期

Vue生命週期全都跑在客戶端(瀏覽器),而Nuxt生命週期有些在服務端(Node),客戶端,甚至兩邊都在:

生命週期流程圖,紅框內的是Nuxt的生命週期(運行在服務端)黃框內同時運行在服務端&&客戶端上綠框內則運行在客戶端
在這裏插入圖片描述

路由

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

在這裏插入圖片描述
在這裏插入圖片描述
項目目錄
在這裏插入圖片描述

兼容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'

在這裏插入圖片描述

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

在這裏插入圖片描述

兼容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郵箱 設置 賬戶
在這裏插入圖片描述
得到一個授權碼,需要寫進配置文件裏
在這裏插入圖片描述

配置文件

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

在這裏插入圖片描述
啓動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

謝謝你閱讀到了最後
期待你,點贊、評論、交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章