这个项目是为了学习周边技术而自娱自乐做的,nuxt实现服务端渲染(基于vue部分引入ts),koa做后台服务,mongo做数据库存储,前端涉及页面十余个,后台数据库表9个,图片存储用七牛云(有个问题:测试域名30天后回收,持续使用需要自己绑定域名)。
进展
计划实现功能(已实现6/16)
买家:
- 登陆 注册
- 添加购物车
- 支付
- 退货 (optional)
- 收藏
- 已经买过给评论
- 搜索
- 修改个人信息(上传照片)(七牛云)
- 用户行为埋点 (optional, 查看/收藏/购物车/支付)
卖家:
- 登陆 注册
- 开店 & 修改店铺信息
- 上传宝贝(上传照片)
- 管理宝贝
- 发货
- 管理评论/回复评论
- 查看交易图表
目录
前端1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28├─assets
├─components
├─layouts
├─middleware
├─note
├─pages
│ ├─cart
│ ├─payment
│ ├─product
│ ├─seller
│ │ ├─account
│ │ ├─login
│ │ └─management
│ │ ├─delivery
│ │ ├─product
│ │ ├─shop
│ │ └─transaction
│ ├─store
│ └─user
│ ├─account
│ └─login
├─plugins
├─public
│ └─header
├─server
├─service
├─static
└─store
后端1
2
3
4
5
6
7
8
9
10
11
12├─bin
├─config
├─controller
├─db
├─model
├─public
│ ├─images
│ ├─javascripts
│ └─stylesheets
├─routes
└─views
└─app.js
nuxt
官网阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过
nuxt的原理?
个人理解: 打包后生成2份bundle, 一份是server bundle, 一份是client bundle, 前者是服务端在请求返回前将请求好的数据渲染到静态html,后者是客户端运行的有挂载vue的文档。客户端拿到服务端返回的HTML字符串后,会去“激活”这些静态HTML,使其变成由Vue动态管理的DOM,以便响应后续数据的变化。
客户端渲染的时候只能拿到beforeCreate / created的生命周期钩子
与ssr 相关api
asyncData用于在服务端获得数据写入data, fetch用于服务端获取数据写入state, actions中nuxtServerInit可以获得服务端信息写入state(如用户已登录)
plugins
plugins 属性配置的所有插件会在 Nuxt.js 应用初始化之前被加载导入。
使用场景:
- 使用 Vue 插件 (插件文件引入vue,然后Vue.use(xx))
- 注入vue 实例,修改vue 原型链
- 注入context,配置路由处理和axios。
如1
2
3
4
5
6
7// plugins/router.js
export default ({ app }) => {
app.router.afterEach((to, from) => {
// todo 增加埋点
console.log('to path', to.path)
})
}
1 | // plugin/axios.js |
1 | // nuxt.config.ts |
本地跨域配置
1 | // nuxt.ts |
坑
- 非页面组件(如header),不能使用服务端异步数据,官网说 Nuxt.js 仅仅扩展增强了页面组件的 data 方法,使得其可以支持异步数据处理。方法有2:用mounted或者页面组件调用api用pros传递给子组件,后者的问题是因为正在加载其他组件的数据可能异步数据不太可读(详见官网)。
koa
轻量,使用了集成权限认证中间件使用koa-passport。
koa洋葱模型原理
个人理解是利用了栈的后进先出,比如中间件a中调用了next方法,这个时候下一个中间件b被压入调用栈,b执行完后b被弹出,
又回到a的,a继续执行。所以如果有顺次3个中间件abc,ab都调用了next,那么具体执行的调用栈顺序就是abcba.
koa中具体实现的过程是这样的,具体实现用到了递归有个middleware的数组存放存放所有的中间件,当我们调用app.use方法就会向这个数组里面
插入一个中间件。koa中有个compose方法实现中间件的串行调用,内部使用了闭包保存了调用中间件的索引,内部定义一个
diapatch方法,可以理解dipatch(i)执行第i个中间件,这里设计很巧妙的地方是执行第i个中间件的时候可以传参,中间件的第二个参数next被传入了dipatch(i+1),所以当执行某个中间件的时候,这个中间件内部调用了next方法,这个时候下个中间件就会被压入调用栈。用Promise包裹中间件,方便await调用。
源码地址1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function compose (middleware) {
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
mongo
本地安装moongo, 将mongo设置为本地服务1
2
3
4// 管理员运行cmd
mongod --dbpath G:\mongo\data\db --logpath G:\mongo\log\mongodb.log --install --serviceName MongoDB // 注册服务
net start MongoDB // 开启服务
net stop MongoDB // 关闭服务
安装Robt3T 是mongo的图形客户端工具