【JS】2天用vue3.0实现《掘金 - 年度人气创作者榜单》网站

2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

夕水发布于 40 分钟前

初看到掘金 - 2020年度人气创作者榜单这个网站,感觉整体界面效果给我一种清爽的感觉,于是花了点时间琢磨如何实现。目前实现的功能有:列表展示,搜索,无限加载(与原网站有些区别,加了loading效果),活动介绍,tab切换。通过这些,我对vue3.0的composition api有了一定的认知,下面让我们来看看吧!

直接请求该网站的数据接口,应该是会报跨域问题的。于是我想了一个办法,就是通过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存储用户相关登录信息。如下图所示:

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

这一个功能的实现思路知道即可,源码不会实现。然后在该网站去获取cookie并传递参数给user接口既可以获取登录相关信息。

以上代码思路也很简单,就是通过搭建一个本地服务器,然后爬取该网站的三个主要的接口,主要使用了superagent这个库来进行爬取。相关API可以参考superagent文档。然后就是允许跨域的设置,用了node框架express。没什么技术难点。

web前端

技术点:vue3.0,typescript,vue-cli4.0,axios,less

首先分析一下页面,主要分为首页和活动介绍页。其中HeaderFooter组件作为一个公共组件,这是毋庸置疑的。当然,这两个组件的代码也比较简单,可以不做分析。如下:

<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:希望掘金技术团队不介意吧)。

<template>

<div class="footer">

<ul class="footer-web">

<li v-for="(web, index) in footerWebNavList" :key="web.text + index">

<template v-if="https://segmentfault.com/a/1190000039129377/web.url">

<a :href="https://segmentfault.com/a/1190000039129377/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="https://segmentfault.com/a/1190000039129377/app_item.url">

<a :href="https://segmentfault.com/a/1190000039129377/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="https://segmentfault.com/a/1190000039129377/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监听等等,更多源码在这里。

最后,附上部分效果图:

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

javascript前端vue.js

阅读 39发布于 40 分钟前

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议


eveningwater

每天学习一点点,就可以进步一点点,工作能带来的不仅是技术知识点,还有与人的相处,沟通与交流。这是...

avatar

夕水

问之以是非而观其志,穷之以辞辩而观其变,资之以计谋而观其识,告知以祸难而观其勇,醉之以酒而观其性,临之以利而观其廉,期之以事而观其信。

2.9k 声望

190 粉丝

0 条评论

得票时间

avatar

夕水

问之以是非而观其志,穷之以辞辩而观其变,资之以计谋而观其识,告知以祸难而观其勇,醉之以酒而观其性,临之以利而观其廉,期之以事而观其信。

2.9k 声望

190 粉丝

宣传栏

初看到掘金 - 2020年度人气创作者榜单这个网站,感觉整体界面效果给我一种清爽的感觉,于是花了点时间琢磨如何实现。目前实现的功能有:列表展示,搜索,无限加载(与原网站有些区别,加了loading效果),活动介绍,tab切换。通过这些,我对vue3.0的composition api有了一定的认知,下面让我们来看看吧!

直接请求该网站的数据接口,应该是会报跨域问题的。于是我想了一个办法,就是通过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存储用户相关登录信息。如下图所示:

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

这一个功能的实现思路知道即可,源码不会实现。然后在该网站去获取cookie并传递参数给user接口既可以获取登录相关信息。

以上代码思路也很简单,就是通过搭建一个本地服务器,然后爬取该网站的三个主要的接口,主要使用了superagent这个库来进行爬取。相关API可以参考superagent文档。然后就是允许跨域的设置,用了node框架express。没什么技术难点。

web前端

技术点:vue3.0,typescript,vue-cli4.0,axios,less

首先分析一下页面,主要分为首页和活动介绍页。其中HeaderFooter组件作为一个公共组件,这是毋庸置疑的。当然,这两个组件的代码也比较简单,可以不做分析。如下:

<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:希望掘金技术团队不介意吧)。

<template>

<div class="footer">

<ul class="footer-web">

<li v-for="(web, index) in footerWebNavList" :key="web.text + index">

<template v-if="https://segmentfault.com/a/1190000039129377/web.url">

<a :href="https://segmentfault.com/a/1190000039129377/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="https://segmentfault.com/a/1190000039129377/app_item.url">

<a :href="https://segmentfault.com/a/1190000039129377/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="https://segmentfault.com/a/1190000039129377/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监听等等,更多源码在这里。

最后,附上部分效果图:

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

【JS】2天用vue3.0实现《掘金 - 2020年度人气创作者榜单》网站

以上是 【JS】2天用vue3.0实现《掘金 - 年度人气创作者榜单》网站 的全部内容, 来源链接: utcz.com/a/110414.html

回到顶部