【Vue】Nuxt最佳实践之常用库封装(一)

前言

有关同构渲染SSR相关的基础知识,不再赘述,小伙伴们可自行补充,传送门: Vue.js 服务器端渲染指南

本文主要介绍了基于Vue官方脚手架: create-nuxt-app,如何实现基础的项目搭建过程,相关代码已开源至git,戳这里 BugBoomNuxt 抱走,看都看了,点个star呗~

概览

全文大概3000字,阅读需要15分钟,配合专栏文章食用更佳哦:)

  1. 服务端框架 - Koa
  2. UI框架 - Vant
  3. HTTP请求 - nuxt@axios
  4. 常用工具类
  5. 部署管理
  6. 团队管理

服务器端框架

Express VS Koa,别问我为啥选这两货,我手指前端大海的方向,哪条腿大抱哪家就完事了,萌新可怜弱小又无助,对比分析可参考某乎,Express和koa各有啥优缺点?,Koa官方说明-来自全世界最大的同性交友平台

博主选择Koa,因为No Callback! No Callback! No Callback! 重要的事情说三遍!,处理异步的async await用起来太爽啦,比generator yield好用多了,PS:用了koa做了几个项目之后,才感觉出Nodejs的神奇

咳咳,回归正题,选择这两货在创建项目时即可,app文件会生成在server/index.js下
【Vue】Nuxt最佳实践之常用库封装(一)_第1张图片
【Vue】Nuxt最佳实践之常用库封装(一)_第2张图片

UI框架

本项目以H5为主,会接入微信SDK,所以对于UI框架的要求是轻巧,耐抗,加可扩展,之前有详细对比过iview,vux,vant

/ 生态 颜值 可扩展
iview 2019年10发布4.0,团队 22.9k 主题、国际化、按需、后台管理
vux 最近更新去年,个人 16.6K we-ui同胞,内置wxsdk、h5&&wx
vant 持续更新,有赞前端团队 12.2K 主题、国际化、按需、h5

在这里安利一波 tailwindcss,简单点就是高度定制化的Bootstrap,让开发者拥有更多的控制权,可以少写80%的css,Nuxt官网开源代码就是采用的Tailwind CSS

Tailwind CSS 是一个高度可定制的基础层 CSS 框架,它为您提供了构建定制化设计所需的所有构建块,而无需重新覆盖任何内建于框架中的设计风格。

博主选择vant + Tailwind CSS,因为之前做过小程序,用过zan-ui,有知道的小伙伴可以举个爪 -。-,

前方高能预警,干货到了~~~~

npm i less less-loader -D

安装less扩展,建议安装2.x, 3.x上的less有较严重的兼容性问题,配合nuxt使用

npm i @nuxtjs/style-resources vant -S

注入到nuxt.config.js,修改主题色可参考官方说明,坑位预留: 按需导入组件

modules: [
  '@nuxtjs/style-resources'
],
styleResources: {
  less: '@/assets/**/*.less'
},
build: {
  loaders: {
    less: {
        javascriptEnabled: true  // 兼容less 3.x写法
        , modifyVars: {          // 定制 vant 主题文件
          hack: `true; @import "${path.join(
            __dirname,
            './assets/css/theme.less'
          )}";`
        }
    }
  }
}

配置vant-ui插件,并注入到nuxt全局,目录 plugins/vant-ui.js

import Vue from 'vue'
import { 
  Button, Swipe, SwipeItem, Image, Lazyload 
} from 'vant';

const Vant = {
  Button, Swipe, SwipeItem, Image, Lazyload
}

import 'vant/lib/index.less' // 替代方案 babel-plugin-import

Object.entries(Vant).forEach(item => {
  const [key, value] = item
  Vue.use(value)
})

// plugins: [ '@/plugins/vant-ui' ] write code for nuxt.config.js
// 思考?添加ssr属性 { src: String, ssr: Boolean }

Tip: 配置tailwind可参考源代码和官方说明

HTTP请求

官方推荐axios,咳咳@nuxtjs/axios,但是留有一点疑问,$axios对象无法对外抛出,既无法在普通模块js中引用该对象0.0,全家桶直接撸起~

npm i @nuxtjs/axios js-cookie -S
npm i @nuxtjs/proxy mockjs axios-mock-adapter -D

对应模块信息待博主慢慢道来

  • @nuxtjs/proxy 本地代理,解决跨域, 以微信开发为例
axios: {
  proxy: true,
  // prefix: '/api-wx', // baseURL
  credentials: true
},
proxy: { 
  '/api-wx/': {
	   target: 'https://api.weixin.qq.com/', // 代理地址
	   changeOrigin: true,
	   pathRewrite: {
	     '^/api-wx/': ''
	   }
   }
}
  • @nuxtjs/axios http请求基本配置, plugins/axios.js
import { Notify } from 'vant';
import Cookies from 'js-cookie'

const ERRORS = new Map([
  [500, '服务器异常...'],
  [404, '未找到对应资源'],
  [401, '未鉴权']
])

export default function ({ $axios, redirect, store }) {
  // 基本配置
  $axios.defaults.timeout = 10000

  // 请求回调
  $axios.onRequest((config) => {
    const { baseURL, url, params, data, method } = config
    console.log(baseURL + url + (method === 'get' ? params : data))
    $axios.setHeader('x-auth-token', Cookies.get('token'))
    return config
  })

  // 返回回调
  $axios.onResponse((response) => {
    const { config, data, headers } = response
    const { errcode, errmsg } = data
    if (~config.url.indexOf('/login')) {
      Cookies.set('token', headers['x-auth-token'], { expires: 7 })
    }
    if (errcode && errmsg) {
      Notify(errmsg)
    }
    return response
  })

  // 错误回调
  $axios.onError((error) => {
    const code = parseInt(error.response && error.response.status)
    if (ERRORS.has(code)) {
      Notify('请求失败-' + ERRORS.get(code) + error.response.statusText)
    } else {
      Notify(error.response.statusText)
    }
    return Promise.reject(error.response)
  })
}


// plugins: [ '@/plugins/axios' ] write code for nuxt.config.js

有关mock在vue中的运用,小伙伴们可参考博主的另一篇文章,【Vue】Vue-cli之Mock.js 模拟数据实现及构建

  • mockjs axios-mock-adapter 生成模拟数据,前后端并行开发利器

为什么会考虑用axios-mock-adapter,而不用热门的fetch-mock进行快速构建呢,因为fetch-mock在进行真实http请求时会强制写入路由文件从而引起冲突,axios-mock-adapter则是axios的官方调试器相当于是一套,兼容性更好,综上所述既所得

plugins/mock.js

// // -> Developer mock data
// if (process.env.NODE_ENV === 'development') {
//   require('../mocks/')
// }

import mocks from '../mocks/'
import $$ from '~/utils/index'
import Mock from 'mockjs'

const MockAdapter = require('axios-mock-adapter')
const config = require('../base.config.js').default
const mock = Mock.mock

/**
 * 模拟延时请求
 * @param {any} response 模拟响应数据
 * @param {number} time 延时多少毫秒,省略这个省数将会生成100ms内的一个延时
 */
const delay = (response, time) => {
  return $$.delay(time || Math.random() * 100).then(() => response);
};

// 模拟数据时包装反回数据
// MockAdapter 返回格式要求[200, {}]
const toSuccess = (response, time, code = 200) => {
  if (time) {
    return delay(config.mock.toSuccess(response, code), time);
  } else {
    return config.mock.toSuccess(response, code);
  }
};
const toError = (message, time, code = 200) => {
  if (time) {
    return delay(config.mock.toError(message, code), time)
  } else {
    return config.mock.toError(message, code)
  }
};

// 批量注册路由事件,以插件形式对外抛出
// MockAdapter基本使用可参考官方说明
export default function ({ $axios }) {
  const axiosMock = new MockAdapter($axios);

  Object.values(mocks).forEach(mockFile => {
    let mockAPIs = {}
    if ($$.isFunction(mockFile)) {
      mockAPIs = mockFile({ axiosMock, delay, mock, toSuccess, toError });
    } else if ($$.isObject(mockFile)) {
      mockAPIs = mockFile;
    } else {
      throw new Error('mock file require both Function or Object');
    }

    for (const key in mockAPIs) {
      const method_url = key.split(' ');  // 'api/login get'
      const apiItem = mockAPIs[method_url[0]]
      const method = 'on' + (method_url[1] != 'get' ? 'Post' : 'Get')
      
      if ($$.isFunction(apiItem)) {
        // or reply(apiItem)
        axiosMock[method](method_url[0]).reply(config => {
          const query = config.data ? JSON.parse(config.data) : {}
          return apiItem(config, query)
        })
      } else {
        axiosMock[method](method_url[0]).reply(200, apiItem)
      }
    }
  })
}

mocks/user.js

export default ({axiosMock, delay, mock, toSuccess, toError}) => {
  // 如果现有扩展不满足需求,可以直接使用fetchMock方法
  // axiosMock.mock(/httpbin.org\/post/, {/* response */}, {/* options */});

  return {
    '/api/user/getlogin': (options, user) => {
      if (user.userName === 'admin' && user.password === 'admin') {
        return toSuccess(mock({
          'userName': 'admin',                // 用户名
          'name': '@cname',                   // 中文名称
          'age|1-100': 100,                   // 100以内随机整数
          'birthday': '@date("yyyy-MM-dd")',  // 日期
          'city': '@city(true)',              // 中国城市
          'phone': /^1[385][1-9]\d{8}/,       // 手机号
          'token': '@guid'                    // token
        }), 400);
      } else {
        // 返回延迟400ms的401鉴权错误
        return toError('用户名或密码错误 admin/admin', 400, 401);
      }
    }
  }
}

页面调用,以post请求为例,get请求需要单独携带params参数(objtoparam),{ params: {} }

this.$axios.$post('/api/user/getlogin', {
      userName: 'admin',
      password: 'admin'
}).then(console.log)

暂时先写到这,下一篇文章将会讲述常用工具类封装,部署说明,团队协同规范

PS: hi,姑娘,我与春风携过客,你携秋水揽星河好吗 = =

原创不易,赞赏博主喝杯咖啡呗~

【Vue】Nuxt最佳实践之常用库封装(一)_第3张图片

你可能感兴趣的:(Nuxt,Vue)