Skip to content
This repository has been archived by the owner on Nov 15, 2018. It is now read-only.

Commit

Permalink
refactor(feed): 重构动态首页列表
Browse files Browse the repository at this point in the history
- 使用 vuex.action 代替 api 请求, 数据使用 vuex 进行管理
- 缓存动态列表
- 缓存卡片广告
  • Loading branch information
mutoe committed Aug 21, 2018
1 parent 92cc844 commit 046cdc6
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 87 deletions.
37 changes: 22 additions & 15 deletions src/api/feeds.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import api, { limit } from "./api.js";

/**
* 获取 首页动态列表
* @author jsonleex <[email protected]>
* @param {string} type
* @param {number} limit
* @param {number} after
* @returns {Promise<Array>}
* 定义动态对象
* @typedef {Object} FeedObject
* @property {number} id
* @property {number} user_id
* @property {string} feed_content
* @property {number[]} images
* @property {Object[]} topics
* @property ...others
*/
export function getFeedsByType(type, limit = 15, after) {
const baseUrl = `/feeds?type=${type}&limit=${limit}`;
const url =
after > 0
? type === "hot"
? baseUrl + `&offset=${after}`
: baseUrl + `&after=${after}`
: baseUrl;
return api.get(url);

/**
* 获取动态列表
* @author mutoe <[email protected]>
* @export
* @param {Object} params
* @param {string} params.type
* @param {number} params.limit
* @param {number} params.after
* @param {number} params.offset
* @returns {Promise<FeedObject[]>}
*/
export function getFeeds(params) {
return api.get("/feeds", { params, validateStatus: s => s === 200 });
}

/**
Expand Down
93 changes: 22 additions & 71 deletions src/page/feed/FeedList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
</router-link>
</ul>
</nav>

<jo-load-more
ref="loadmore"
:auto-load="true"
Expand Down Expand Up @@ -74,8 +75,6 @@
import FeedCard from "@/components/FeedCard/FeedCard.vue";
import FeedAdCard from "./components/FeedAdCard.vue";
import BannerAd from "@/components/advertisement/BannerAd.vue";
import * as api from "@/api/feeds.js";
import * as bootApi from "@/api/bootstrappers.js";
import { noop } from "@/util";
const feedTypesMap = ["new", "hot", "follow"];
Expand All @@ -84,39 +83,23 @@ export default {
name: "FeedList",
components: { FeedCard, BannerAd, FeedAdCard },
data() {
return {
pinned: [], // 置顶
adCardList: [],
adIndex: 0,
newFeeds: [],
hotFeeds: [],
followFeeds: []
};
return {};
},
computed: {
feedType() {
return this.$route.query.type;
},
feeds: {
get() {
return this.feedType ? this.$data[`${this.feedType}Feeds`] : [];
},
set(val) {
this.$data[`${this.feedType}Feeds`] = val;
}
feeds() {
return this.$store.getters[`feed/${this.feedType}`];
},
maxId() {
const len = this.feeds.length;
return len ? this.feeds[len - 1].id : 0;
pinned() {
return this.$store.getters["feed/pinned"];
},
/**
* 模拟动态卡片广告列表
* @returns {FeedDetail[]}
*/
feedCardAdsId() {
const adType = this.$store.getters.getAdTypeBySpace("feed:list:analog");
return adType.id;
after() {
const len = this.feeds.length;
if (!len) return 0;
if (this.feedType !== "hot") return this.feeds[len - 1].id; // after
return this.$store.getters[`feed/hot`].length; // offset
}
},
watch: {
Expand All @@ -127,53 +110,21 @@ export default {
}
},
created() {
this.$store.dispatch("feed/getAdvertise");
this.onRefresh(noop);
this.getAdCards();
},
methods: {
onRefresh(callback) {
// TODO: @mutoe [api] refactor there with vuex action
api.getFeedsByType(this.feedType, 15).then(({ data }) => {
const { pinned = [], feeds = [] } = data;
this.pinned = pinned;
callback(feeds.length < 15);
if (this.feedType === "hot") {
// 从广告栈顶取出一条随机插入列表
let rand = ~~(Math.random() * 14) + 1;
rand > feeds.length && (rand = feeds.length);
this.adIndex = 0;
this.adCardList[this.adIndex] &&
feeds.splice(rand, 0, this.adCardList[this.adIndex++]);
}
this.feeds = feeds;
});
},
onLoadMore(callback) {
if (!this.feedType) return;
// 热门动态 修改为 offset
const after =
this.feedType === "hot"
? this.hotFeeds.length - this.adCardList.length
: this.maxId;
api.getFeedsByType(this.feedType, 15, after).then(({ data }) => {
const { pinned = [], feeds = [] } = data;
this.pinned = pinned;
callback(feeds.length < 15);
if (this.feedType === "hot") {
// 从广告栈顶取出一条随机插入列表
let rand = ~~(Math.random() * 9) + 1;
rand > feeds.length && (rand = feeds.length);
this.adCardList[this.adIndex] &&
feeds.splice(rand, 0, this.adCardList[this.adIndex++]);
}
this.feeds = [...this.feeds, ...feeds];
});
async onRefresh(callback) {
const type = this.feedType.replace(/^\S/, s => s.toUpperCase());
const action = `feed/get${type}Feeds`;
const data = await this.$store.dispatch(action, { refresh: true });
callback(data.length < 15);
},
getAdCards() {
// TODO: @mutoe [api] refactor there with vuex action
bootApi.getAdsById(this.feedCardAdsId).then(({ data }) => {
this.adCardList = data.sort((a, b) => a.sort < b.sort);
});
async onLoadMore(callback) {
const type = this.feedType.replace(/^\S/, s => s.toUpperCase());
const action = `feed/get${type}Feeds`;
const data = await this.$store.dispatch(action, { after: this.after });
callback(data.length < 15);
}
}
};
Expand Down
150 changes: 150 additions & 0 deletions src/stores/module/feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import * as api from "@/api/feeds";
import * as bootApi from "@/api/bootstrappers";
import lstore from "@/plugins/lstore/lstore";
import _ from "lodash";

export const TYPES = {
SAVE_FEED_LIST: "SAVE_FEED_LIST",
SAVE_PINNED_LIST: "SAVE_PINNED_LIST",
SAVE_ADVERTISE: "SAVE_ADVERTISE",
POPUP_ADVERTISE: "POPUP_ADVERTISE",
RESET_ADVERTISE: "RESET_ADVERTISE"
};

const state = {
list: {
hot: lstore.getData("FEED_LIST_HOT") || [], // 热门动态列表
new: lstore.getData("FEED_LIST_NEW") || [], // 最新动态
follow: lstore.getData("FEED_LIST_FOLLOW") || [], // 关注列表
pinned: lstore.getData("FEED_LIST_PINNED") || [] // 置顶列表
},

/** 资讯卡片广告 */
advertise: {
type: 0, // 广告类型 ID
list: lstore.getData("ADVERTISEMENT_FEEDS") || [], // 广告列表
index: 0 // 当前插入广告位索引
}
};

const getters = {
pinned(state) {
return state.list.pinned;
},
hot(state) {
const list = state.list.hot;
const chunks = _.chunk(list, 15); // 分片 用于分批插入卡片广告
const result = [];
for (const chunk of chunks) {
// 从广告栈顶取出一条随机插入列表
let index = state.advertise.index;
let rand = ~~(Math.random() * 14) + 1;
rand > chunk.length && (rand = chunk.length);
state.advertise.list[index] &&
chunk.splice(rand, 0, state.advertise.list[state.advertise.index++]);
result.push(...chunk);
}
return result;
},
new(state) {
return state.list.new;
},
follow(state) {
return state.list.follow;
}
};

const mutations = {
[TYPES.SAVE_FEED_LIST](state, payload) {
const { type, data, refresh = false } = payload;
const list = refresh ? data : [...state.list[type], ...data];
state.list[type] = list;
lstore.setData(`FEED_LIST_${type.toUpperCase()}`, list);
},

[TYPES.SAVE_PINNED_LIST](state, payload) {
const { list } = payload;
state.list.pinned = list;
lstore.setData("FEED_LIST_PINNED", list);
},

[TYPES.SAVE_ADVERTISE](state, payload) {
lstore.setData("ADVERTISEMENT_FEEDS", payload);
state.advertise.list = payload;
},

[TYPES.POPUP_ADVERTISE](state) {
state.advertise.index++;
},

[TYPES.RESET_ADVERTISE](state) {
state.advertise.index = 0;
}
};

const actions = {
/**
* 获取最新动态列表
* @author mutoe <[email protected]>
* @param {*} payload
*/
async getNewFeeds({ commit }, payload) {
const { after, refresh = false } = payload;
const { data } = await api.getFeeds({ type: "new", after });
const { feeds = [], pinned = [] } = data;
commit(TYPES.SAVE_PINNED_LIST, { list: pinned });
commit(TYPES.SAVE_FEED_LIST, { type: "new", data: feeds, refresh });
return feeds;
},
/**
* 获取热门动态列表
* @author mutoe <[email protected]>
* @param {*} payload
*/
async getHotFeeds({ commit, state }, payload) {
const { after, refresh = false } = payload;
const { data } = await api.getFeeds({ type: "hot", offset: after });
const { feeds = [], pinned = [] } = data;
commit(TYPES.SAVE_PINNED_LIST, { list: pinned });
commit(TYPES.SAVE_FEED_LIST, { type: "hot", data: feeds, refresh });
if (refresh) {
state.advertise.index = 0;
}
return feeds;
},
/**
* 获取关注动态列表
* @author mutoe <[email protected]>
* @param {*} payload
*/
async getFollowFeeds({ commit }, payload) {
const { after, refresh = false } = payload;
const { data } = await api.getFeeds({ type: "follow", after });
const { feeds = [] } = data;
commit(TYPES.SAVE_FEED_LIST, { type: "follow", data: feeds, refresh });
return feeds;
},

/**
* 获取卡片广告列表
* @author mutoe <[email protected]>
* @returns
*/
async getAdvertise({ state, commit, rootGetters }) {
if (!state.advertise.type) {
const adType = rootGetters.getAdTypeBySpace("feed:list:analog");
state.advertise.type = adType.id;
}
const { data } = await bootApi.getAdsById(state.advertise.type);
commit(TYPES.SAVE_ADVERTISE, data);
return data;
}
};

export default {
namespaced: true,
state,
getters,
mutations,
actions
};
4 changes: 3 additions & 1 deletion src/stores/module/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import currency from "./currency";
import question from "./question";
import news from "./news";
import group from "./group";
import feed from "./feed";

export default {
rank,
Expand All @@ -17,5 +18,6 @@ export default {
currency,
question,
news,
group
group,
feed
};

1 comment on commit 046cdc6

@mutoe
Copy link
Contributor Author

@mutoe mutoe commented on 046cdc6 Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refer from #463

Please sign in to comment.