初看到掘金 - 2020年度人氣創做者榜單這個網站,感受總體界面效果給我一種清爽的感受,因而花了點時間琢磨如何實現。目前實現的功能有:列表展現,搜索,無限加載(與原網站有些區別,加了loading效果),活動介紹,tab切換。經過這些,我對vue3.0的composition api有了必定的認知,下面讓咱們來看看吧!javascript
ps:我的認爲原網站應該是使用react.js寫的前端
直接請求該網站的數據接口,應該是會報跨域問題的。因而我想了一個辦法,就是經過node.js
來爬取數據。下面來看看代碼:vue
代碼以下:java
const superagent = require('superagent');
const express = require('express');
const app = express();
const port = 8081;
function isObject(value) {
return value && typeof value === 'object';
}
function getApi(url, params,method) {
return new Promise((resolve) => {
if (!isObject(params)) {
return resolve(setResponse(400, null, '請傳入參數!'));
} else {
let paramMethod = method.toLowerCase() === 'post' ? 'send' : 'query';
superagent(method,url)[paramMethod](params).set('X-Agent', 'Juejin/Web').end((err, supRes) => {
if (err) {
return resolve(setResponse(400, null, err));
}
let data = JSON.parse(supRes.text);
resolve(setResponse(data.err_no === 0 ? 200 : data.err_no, data.data, data.err_msg));
});
}
})
}
app.use(express.json());
app.all("*", function (req, res, next) {
//設置容許跨域的域名,*表明容許任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
//容許的header類型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域容許的請求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') {
res.send(200);
} else {
next();
}
});
function setResponse(code, data, message) {
return {
code: code,
data: data,
message: message
}
}
app.post('/info', (req, res) => {
const params = req.body;
getApi('https://api.juejin.cn/list_api/v1/annual/info', params,'post').then(data => {
res.send(JSON.stringify(data));
})
})
app.post('/list', (req, res) => {
const params = req.body;
getApi('https://api.juejin.cn/list_api/v1/annual/list', params,'post').then(data => {
res.send(JSON.stringify(data));
});
})
app.get('/user',(req,res) => {
const params = req.query;
getApi('https://api.juejin.cn/user_api/v1/user/get',params,'get').then(data => {
res.send(JSON.stringify(data));
})
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
複製代碼
以上只是爬了主要的三個接口,如list
接口,info
接口以及user
接口。固然還有登陸功能沒有寫,掘金應該是經過cookie
技術去實現判斷用戶是否登陸的,當從掘金打開,跳往該網站,會向瀏覽器的cookie
存儲用戶相關登陸信息。以下圖所示:node
這一個功能的實現思路知道便可,源碼不會實現。而後在該網站去獲取cookie
並傳遞參數給user
接口既能夠獲取登陸相關信息。python
以上代碼思路也很簡單,就是經過搭建一個本地服務器,而後爬取該網站的三個主要的接口,主要使用了superagent
這個庫來進行爬取。相關API能夠參考superagent文檔。而後就是容許跨域的設置,用了node
框架express
。沒什麼技術難點。react
技術點:vue3.0,typescript,vue-cli4.0,axios,lessios
首先分析一下頁面,主要分爲首頁和活動介紹頁。其中Header
和Footer
組件做爲一個公共組件,這是毋庸置疑的。固然,這兩個組件的代碼也比較簡單,能夠不作分析。以下:git
Headergithub
<template>
<div class="header"> <div class="header-logo"></div> <div class="header-screen"></div> <div class="header-cascade"></div> <div class="header-person"></div> <div class="header-python"></div> <div class="header-vue"></div> <div class="header-react"></div> <div class="header-phone"></div> <div class="header-phone-wolpe"></div> <div class="header-bug"></div> <div class="header-coffee"></div> <div class="header-year"></div> <div class="header-title"></div> </div>
</template>
複製代碼
顯然,Header
組件主要考查CSS
佈局,好吧,雖然能夠說是模仿寫了一遍及局(全部佈局都是同理,沒什麼好說的),但也算是抄襲了(PS:但願掘金技術團隊不介意吧)。
Footer
<template>
<div class="footer"> <ul class="footer-web"> <li v-for="(web, index) in footerWebNavList" :key="web.text + index"> <template v-if="web.url"> <a :href="web.url" target="_blank">{{ web.text }}</a> </template> <template v-else>{{ web.text }}</template> </li> </ul> <div class="footer-app"> <ul class="footer-app-item" v-for="(app, index) in footerAppNavList" :key="app + index" > <li v-for="(app_item, app_index) in app" :key="app_item.text + app_index" > <template v-if="app_item.url"> <a :href="app_item.url" target="_blank">{{ app_item.text }}</a> </template> <template v-else>{{ app_item.text }}</template> </li> </ul> </div> </div>
</template>
<script lang="ts"> import { reactive, toRefs } from "vue"; interface FooterItem { text: string; url?: string; } type FooterList = Array<FooterItem>; export default { setup() { const state = reactive({ footerWebNavList: [ { text: "@2020掘金", }, { text: "關於咱們", url: "https://juejin.cn/about", }, { text: "營業執照", url: "https://juejin.cn/license", }, { text: "用戶協議", url: "https://juejin.cn/terms", }, { text: "京ICP備18012699號-3", url: "https://beian.miit.gov.cn/", }, { text: "京公網案備11010802026719號", url: "http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802026719", }, { text: "北京北比信息技術有限公司版權全部", }, ], footerAppNavList: [] as any[], }); const first: FooterList = state.footerWebNavList.slice(0, 4); const second: FooterList = state.footerWebNavList.slice(4); state.footerAppNavList = [first, second]; return { ...toRefs(state), }; }, }; </script> 複製代碼
這個組件難度也不大,就是把導航數據概括到一塊兒了而已。
活動介紹頁面也比較簡單,就一個tab
組件,而後其它都是圖片佈局。
<template>
<div class="info-container"> <Header /> <div class="pc-info"></div> <div> <div class="home-button-container"> <router-link to="/"> <div class="home-button"></div> </router-link> </div> <div class="info-box"> <div class="info-title"></div> <div class="info-box1"></div> <div class="info-box2"></div> <div class="info-box3"></div> <div class="info-box4"> <div class="info-prizes"> <div class="info-prizes-tab"> <div class="info-prizes-tab1" :style="{ 'z-index': curInfoTab === 0 ? 3 : 1 }" @click="onChangeInfoTab(0)" ></div> <div class="info-prizes-tab2" :style="{ 'z-index': curInfoTab === 1 ? 3 : 1 }" @click="onChangeInfoTab(1)" ></div> </div> <div> <img :src="require('../assets/' + (curInfoTab === 0 ? 'individual' : 'group') + '_prize_web.png')" alt="圖片加載中" style="width: 100%" /> </div> </div> </div> </div> </div> </div>
</template>
<script lang="ts"> import { reactive, toRefs } from "vue"; import Header from "../components/Header.vue"; export default { components: { Header, }, setup() { const state = reactive({ curInfoTab: 0, }); const onChangeInfoTab = (value: number) => { state.curInfoTab = value; }; return { ...toRefs(state), onChangeInfoTab, }; }, }; </script>
複製代碼
固然後續代碼我就不一一展現了,我主要總結一下所用到的技術知識點。
首先是vuex
,vue2
熟練使用的話,其實vue3
語法也差異不大。
import { useStore } from "vuex";
// store.state
// store.dispath(方法名,數據)
複製代碼
主要若是子組件想經過事件傳遞給父組件,則須要經過mitt
插件,譬如搜索組件的代碼實現以下:
import mitt from 'mitt';
export const emitter = mitt();
export default {
setup() {
const state = reactive({
keyword:""
})
const refState = toRefs(state);
const onSearch = () => {
if(!state.keyword)return alert('請輸入你喜歡的做者名!');
//傳遞給父組件
emitter.emit('on-search',state.keyword);
}
return {
...refState,
onSearch
};
},
};
複製代碼
其它的都是vue3.0
的語法了,好比watch
監聽等等,更多源碼在這裏。
PS:不知道到時間了掘金官方會不會中止相關數據接口的服務,因此下一步,我可能會考慮寫靜態數據,而後把axios封裝一下,固然代碼還有些粗糙,由於實現的有些匆忙,後續會作優化。
最後,附上部分效果圖: