抓数据
一、首先抓取主页列表数据:
- 研究一下主页的接口,需要传入经纬度和分页数据
- 通过fetch获取到数据后,重新定义一个对象,对应到数据库的设计结构
- 根据地址抓,利用writeFile取其中的图片到本地
- 将数据插入数据库
二、抓取列表项对应的详情页,包括餐馆介绍、菜单、购物车
流程同上,需要抓取菜单数据和食物数据
koa搭建后台
用到的插件们
const koa = require('koa');
const static = require('koa-static');//用于静态页面
const Router = require('koa-router');
const db = require('./libs/mysql');//mysql的操作集合
const pathlib = require('path');//文件路径
const config = require('./config');//所有配置信息(数据库配置)
const betterBody = require('koa-better-body');//用于form表单
const convert = require('koa-convert')//betterbody需要搭配convert使用
const session = require('koa-session')
const tokenSession = require('./libs/token-session')
betterBody
在获取post请求时,ctx中的request参数无法直接获取,需要通过betterBody中间件进行转换。betterBody是generator格式,通过koa-convert可以转换为promise使用。
支持text, buffer, json, json patch, json api, csp-report, multipart, form and urlencoded bodies
参数储存在这里:
console.log(this.request.files)
console.log(this.request.fields)
1. 首先创建一个server,listen
const server = new koa();
server.listen(PORT);
2. form用到的中间件
注意,第二步和第三步的处理要放在router之前。
server.use(convert(betterBody()));
3. 对请求的处理和过滤,token处理
server.use(async (ctx, next) => {
ctx.session = tokenSession();
ctx.set({'Access-Control-Allow-Origin': '*'});
ctx.set({'Access-Control-Allow-Headers': '*'});
let token = ctx.request.headers['x-token'];
await next();
});
4. Router
let router = new Router();
router.use('/api/', require('./routers/api.router'));
server.use(router.routes());
5. 静态页面处理
server.use(static(pathlib.resolve(__dirname, 'www/')));
api.router接口定义:
restaurant/:page/:size(get)分页获取餐馆的数据
restaurant/:id(get)获取某个餐馆的信息
cart/:item_id/:count (post)购物车物品的增加和更新
cart/:item_id(delete)购物车物品的删除
image/:id(get)获取图片
token(get)获取token
user/(post)注册
user/:username/:password(get)登录
verifyCode(get)获取验证码
Vue框架
下载 Vue-cli
vue init webpack 创建项目
一共涉及三个页面:主页(餐馆列表)、餐馆详情页、登录页
先去书写router
使用的Vue组件:
Vuex:组件间的状态管理
Vue.use(Vuex);
const store = new Vuex.Store({
strict: true,
state: {
token: null,
cur_user: null
},
mutations: {
updateToken (state, arg) {
if( !arg || arg.length != 32) {
console.log(arg, '格式不对!');
} else {
state.token = arg;
}
},
updateUser (state, arg) {
state.cur_user = arg;
}
},
actions: {
updateToken ({commit}, arg) {
commit('updateToken', arg);
},
updateUser ({commit}, arg) {
commit('updateUser', arg);
}
},
getters: {
},
modules: {
user: User
}
})
Axios
Vue.prototype.axios = axios.create({
baseURL: 'http://localhost:8090/api/',
timeout: 1000
});
Router
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'index',
component: Index
},
{
path: '/detail/:id/:menu',
name: 'detail',
component: Detail
},
{
path: '/login/:type',
name: 'login',
component: Login
}
]
})
涉及的Components
这里不详细描述,在实际操作的过程中,根据需要拆分。
遇到的问题
1. 初次加载列表页可能会报错,因为数据的还未完成获取,可以先使用v-if进行判断,有值再进行渲染
2. router的几种操作方法:
-
router.replace()
直接替换当前页,无法回退 -
router.push();
向路由里添加一个路径。可以回退到这个页面 -
router.back();
返回上一级页面(push进来的路由可以通过这里访问) -
router.go(-3);
指定返回上三级页面
3. 子组件的渲染交给子组件自己进行,无需从主组件进行数据传递,传对应的id即可
4. 选项卡的编写
//在data中储存一个数组
titles: [{
title: '点餐',
type: 'menu'
},{
title: '评价',
type: 'rate'
},{
title: '商家',
type: 'info'
}],
curTab: 'menu'
}
//在html中v-for循环数组显示。点击显示的时候通过type进行判断。
5. filter
需要对返回的数据进行处理的,可以使用filter进行处理
<span>{{Math.round(restaurant.distance/60)|cn_time}}</span>
time_filter (time) {
time = Number(time);
return time<60?`${time}分钟`:`${time/60}小时${time%60}分钟`
}
6. 子组件接收父组件的属性传值:
props: [ 'restaurant' ]
7. 获取页面地址的参数:let {id} = this.$route.params;
8. token
//组件初始化的时候,先为用户生成一个token
async created () {
if (localStorage.token) {
this.$store.dispatch('updateToken', localStorage.token);
} else {
let token = (await this.axios.get('token')).data;
localStorage.token = token;
}
}
//token是储存在全局变量里进行传递的:
this.$store.state.token
//登录注册时候放在header里
if (this.$store.state.token) {
headers['x-token'] = this.$store.state.token;
}
9. button disabled
<button type="submit" :disabled="btnDisabled"></button>
Vue中disabled的属性也可以通过data里的参数来控制。btnDisabled为true或false
10. loading…
可以利用mutation显示loading状态
mutations: {
startLoading (state) {
state.loading = true
},
endLoading (state) {
state.loading = false
},
}
actions: {
async loadOnePageData ({state, commit}, arg) {
commit('startLoading')
commit('startLoadingMore')
await sleep(3)
let res = await (await fetch(`http://localhost:8082/list?page=${state.cur_page}`)).json()
commit('endLoading')
commit('endLoadingMore')
},
},
11. getters用法
getters:对state 里面的数据二次处理,组件引用的时候可以直接去引用处理后的数据
getters: {
list_data (state) {
if (state.cur_page === 0) {
store.dispatch('loadOnePageData')
}
return state.article_list
}
}
this.$store.getters.list_data
mapState、mapGetters
import { mapState } from 'vuex'
computed: {
count(){
return this.$store.state.count;
},
//还可以换种写法:
...mapState({
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
//在这种双方名字都一样的情况下,可以直接一步到位
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
Getters就更简单了
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
doneCount: 'doneTodosCount'//起个名字
// ...
])
}
}