接上文《用 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,一般用于批量引入模块。
- 第一个参数
./modules
: 指明要查找的文件路径 - 第二个参数
true
:代表要查找子目录,false
则为不查找子目录 - 第三个参数是个正则:指明要匹配文件的文件名格式,这里就是查找所有 js 文件
- 第一个参数
- keys:
require.context()
的返回值的属性,其值为匹配成功的模块的名字组成的数组,比如目录结构为
则得到的 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 的 actions
或 mutation
方法中使用:
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 实例,就需要使用非箭头函数。