2天用vue3.0實現《掘金 - 2020年度人氣創做者榜單》網站

初看到掘金 - 2020年度人氣創做者榜單這個網站,感受總體界面效果給我一種清爽的感受,因而花了點時間琢磨如何實現。目前實現的功能有:列表展現,搜索,無限加載(與原網站有些區別,加了loading效果),活動介紹,tab切換。經過這些,我對vue3.0的composition api有了必定的認知,下面讓咱們來看看吧!javascript

ps:我的認爲原網站應該是使用react.js寫的前端

直接請求該網站的數據接口,應該是會報跨域問題的。因而我想了一個辦法,就是經過node.js來爬取數據。下面來看看代碼:vue

node後端爬取數據

代碼以下: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

web前端

技術點:vue3.0,typescript,vue-cli4.0,axios,lessios

首先分析一下頁面,主要分爲首頁和活動介紹頁。其中HeaderFooter組件做爲一個公共組件,這是毋庸置疑的。固然,這兩個組件的代碼也比較簡單,能夠不作分析。以下: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>

複製代碼

固然後續代碼我就不一一展現了,我主要總結一下所用到的技術知識點。

首先是vuexvue2熟練使用的話,其實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封裝一下,固然代碼還有些粗糙,由於實現的有些匆忙,後續會作優化。

最後,附上部分效果圖:

相關文章
相關標籤/搜索