共计 5915 个字符,预计需要花费 15 分钟才能阅读完成。
初看到掘金 – 2020 年度人气创作者榜单这个网站,感觉整体界面成果给我一种清新的感觉,于是花了点工夫推敲如何实现。目前实现的性能有: 列表展现,搜寻,有限加载(与原网站有些区别,加了 loading 成果),流动介绍,tab 切换。通过这些,我对 vue3.0 的 composition api 有了肯定的认知,上面让咱们来看看吧!
ps: 集体认为原网站应该是应用 react.js 写的
间接申请该网站的数据接口,应该是会报跨域问题的。于是我想了一个方法,就是通过 node.js
来爬取数据。上面来看看代码:
node 后端爬取数据
代码如下:
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
存储用户相干登录信息。如下图所示:
这一个性能的实现思路晓得即可,源码不会实现。而后在该网站去获取 cookie
并传递参数给 user
接口既能够获取登录相干信息。
以上代码思路也很简略,就是通过搭建一个本地服务器,而后爬取该网站的三个次要的接口,次要应用了 superagent
这个库来进行爬取。相干 API 能够参考 superagent 文档。而后就是容许跨域的设置,用了 node
框架express
。没什么技术难点。
web 前端
技术点:vue3.0,typescript,vue-cli4.0,axios,less
首先剖析一下页面,次要分为首页和流动介绍页。其中 Header
和Footer
组件作为一个公共组件,这是毋庸置疑的。当然,这两个组件的代码也比较简单,能够不做剖析。如下:
Header
<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 封装一下, 当然代码还有些毛糙,因为实现的有些匆忙,后续会做优化。
最初,附上局部效果图: