Nuxt2 构建 SSR 商城——统一管理 api 模块

721 阅读1分钟

接上文《用 NuxtJS 构建 SSR 商城 实战笔记——axios 配置与跨域的实现》继续我们的搬砖之旅。有不足之处或是任何意见建议,欢迎各位大佬不吝斧正。本篇主要介绍基于 NuxtJS 构建的 vue 后端渲染(SSR)项目中关于 api 模块的统一管理~

创建 api 模块

在项目中,如果在哪请求接口就把请求写在哪儿,一是会增添重复代码,二是不好管理维护,所以一般都会把 api 写在一起集中管理。因为 nuxt 中的目录结构都是约定式的,所以我们在 assets 目录下创建 api 目录,并在该目录下新建 index.js 作为 api 模块入口文件,导出 modules 对象, key 为 modules 目录下各个文件名,value 为对应的函数:

// assets\api\index.js
// require.context 为 webpack 的方法
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // 将例如 ./passport.js 这样的路径名替换为 passport
  const moduleName = modulePath.replace(/^\.\/(.*)\.js$/, '$1')
  // 导入模块
  const value = modulesFiles(modulePath)
  // default 是 value 的属性,为定义在 modules 里的 js 文件对外暴露的值
  modules[moduleName] = value.default || value
  return modules
}, {})

export default modules // 导出 modules 对象, key 为 modules 目录下各个文件名,value 为对应的函数

说明:

  • require.context:webpack 的方法,可以创建我们自己的上下文 context,一般用于批量引入模块。
    1. 第一个参数 ./modules: 指明要查找的文件路径
    2. 第二个参数 true:代表要查找子目录,false 则为不查找子目录
    3. 第三个参数是个正则:指明要匹配文件的文件名格式,这里就是查找所有 js 文件
  • keysrequire.context() 的返回值的属性,其值为匹配成功的模块的名字组成的数组,比如目录结构为

image (3).png
则得到的 modulesFiles.keys() 的值如下:

['./passport.js', './setting.js' ]
  • const value = modulesFiles(modulePath),打印输出 value,可以得到如下结果,可知 value.default 的值为一个方法,其实就是下面将要定义在 modules 目录下的文件(setting.js)的导出的值(axios => ({})):
Object [Module] { default: [Function] }

在 api 目录下新建 modules 目录,用于存放不同的 api 文件,比如 setting.js:

// axios 是在 plugins/api.js 里传入的
export default axios => ({
  settingByDomain(domain) {
    return axios({
      url: `/api/web/setting/by-domain/${domain}`,
      method: 'get'
    })
  }
})

注入 Vue 实例、服务器端 context 和 Vuex

在 plugins 目录下新建 api.js,使用 inject 暴露 api。我们希望在整个应用程序中都能使用这些 api,因此,需要将它们注入到 Vue 实例(客户端),context(服务器端)和 store(Vuex)。按照惯例,新增的属性或方法名使用 $ 作为前缀:

// plugins/api.js   
import apis from '@/assets/api/index'
// inject 用于将 api 注入到服务端 context, Vue 实例, Vuex 中
export default ({ $axios }, inject) => {
  const apiObject = {}
  for (const i in apis) {
    // 将 $axios 传给定义在 api/modules 下的 js 文件所默认导出的函数
    apiObject[i] = apis[i]($axios)
  }

  // 系统会自动将 $ 添加到方法名 api 的前面
  inject('api', apiObject)
}

说明: inject 方法是 plugin 导出函数的第二个参数,系统会自动将 $ 添加到方法名的前面。所以使用时是调用 $api

配置 nuxt.config.js

plugins 属性配置的所有插件会在 Nuxt.js 应用初始化之前被加载导入

plugins: ['@/plugins/api']  

使用

可以在 context, Vue 实例或者 Vuex 的 actionsmutation 方法中使用:

context 中

可以直接通过解构赋值得到,一般用在服务器端的一些钩子:

// store/index.js 
export const actions = {
  async nuxtServerInit(store, { $api }) {
    try {
      const res = await $api.setting.settingByDomain(domain)
      // ...
    } catch (error) {
      console.log(error)
    }
  }
}

Vue 实例中

通过 this,也就是 vue 实例,可以直接拿到:

methods: { 
  handle() { 
    this.$api.test.appUser().then(res => { console.log(res) }) 
  } 
}

Vuex 中

也是通过 this 拿到,不过这里的 this 指向的是 vuex 实例 store:

export const actions = { 
  login({ commit }, userInfo) { 
    return new Promise((resolve, reject) => { 
      // ... 
      this.$api.passport.login(clientSetting)
        .then(res => { console.log(res) })
        .catch(error => { reject(error) }) 
    }) 
  } 
}

注意:一般 mutation 中的函数都会写成箭头函数的形式,但如果想要使 this 指向 vuex 实例,就需要使用非箭头函数。

感谢.gif
点赞.png