有关同构渲染SSR相关的基础知识,不再赘述,小伙伴们可自行补充,传送门: Vue.js 服务器端渲染指南
本文主要介绍了基于Vue官方脚手架: create-nuxt-app,如何实现基础的项目搭建过程,相关代码已开源至git,戳这里 BugBoomNuxt 抱走,看都看了,点个star呗~
全文大概3000字,阅读需要15分钟,配合专栏文章食用更佳哦:)
Express VS Koa,别问我为啥选这两货,我手指前端大海的方向,哪条腿大抱哪家就完事了,萌新可怜弱小又无助,对比分析可参考某乎,Express和koa各有啥优缺点?,Koa官方说明-来自全世界最大的同性交友平台
博主选择Koa,因为No Callback! No Callback! No Callback! 重要的事情说三遍!,处理异步的async await用起来太爽啦,比generator yield好用多了,PS:用了koa做了几个项目之后,才感觉出Nodejs的神奇
咳咳,回归正题,选择这两货在创建项目时即可,app文件会生成在server/index.js下
本项目以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可参考源代码和官方说明
官方推荐axios,咳咳@nuxtjs/axios,但是留有一点疑问,$axios对象无法对外抛出,既无法在普通模块js中引用该对象0.0,全家桶直接撸起~
npm i @nuxtjs/axios js-cookie -S
npm i @nuxtjs/proxy mockjs axios-mock-adapter -D
对应模块信息待博主慢慢道来
axios: {
proxy: true,
// prefix: '/api-wx', // baseURL
credentials: true
},
proxy: {
'/api-wx/': {
target: 'https://api.weixin.qq.com/', // 代理地址
changeOrigin: true,
pathRewrite: {
'^/api-wx/': ''
}
}
}
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 模拟数据实现及构建
为什么会考虑用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,姑娘,我与春风携过客,你携秋水揽星河好吗 = =
原创不易,赞赏博主喝杯咖啡呗~