When I first saw the site, I found the overall interface so refreshing that I spent some time figuring out how to do it. The current functions are: list display, search, unlimited loading (with the original site some differences, with loading effect), activity introduction, TAB switch. From this, I have a certain understanding of vue3.0’s Composition API. Let’s take a look!
Ps: I think the original site should be written using React. Js
Direct requests to the site’s data interface should be reported cross-domain issues. So I came up with the idea of using Node.js to crawl the data. Here’s the code:
Back-end node crawls data
The code is as follows:
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.'Please pass in parameters! '));
} 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) {
* indicates that any domain name is allowed to cross domains
res.header("Access-Control-Allow-Origin"."*");
// Allowed header types
res.header("Access-Control-Allow-Headers"."content-type");
// Cross-domain allowed request mode
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}! `))
Copy the code
These are just the three main interfaces, such as the list interface, the INFO interface, and the User interface. Of course, the login function is not written, nugget should be through cookie technology to determine whether the user is logged in, when opened from nugget, jump to the website, will store the user’s login information to the browser cookie. As shown in the figure below:
The realization of this function can be known, source code will not be realized. Then get the cookie from the site and pass the parameter to the user interface to get the login information.
The above code idea is also very simple, is to build a local server, and then crawl the site’s three main interfaces, mainly using the superagent library to crawl. Refer to the SuperAgent documentation for apis. The next step is to allow cross-domain Settings, using the Node framework Express. There are no technical difficulties.
Web front end
Technical points: vue3.0, typescript, vue – cli4.0 axios, less
First analysis of the page, mainly divided into home page and activity introduction page. There is no doubt that the Header and Footer components are a common component. Of course, the code for these two components is relatively simple and can be left unanalyzed. As follows:
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>
Copy the code
Obviously, the Header component looks at the CSS layout, which is, well, a copy of everything (the same goes for all layouts, nothing to say), but it’s a copy (PS: HOPE the Nuggets tech team doesn’t mind).
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 nuggets"}, {text: "About us".url: "https://juejin.cn/about"}, {text: "Business License".url: "https://juejin.cn/license"}, {text: "User Agreement".url: "https://juejin.cn/terms"}, {text: ICP 18012699-3.url: "https://beian.miit.gov.cn/"}, {text: "Jinggong.com Case Preparation No. 11010802026719".url:
"http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802026719"}, {text: Copyright 2008 Beijing Beibi Information Technology Co., LTD. All Rights Reserved.],},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>
Copy the code
This component is not very difficult, it just puts the navigation data together.
The activity introduction page is also relatively simple, with a single TAB component and the rest of the layout being images.
<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="Image loading"
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>
Copy the code
Of course, I will not show the following code, I mainly summarize the technical knowledge points used.
The first is vuex. If you are skilled in vuE2, the grammar of vuE3 is not very different.
import { useStore } from "vuex";
// store.state
// store.dispath(method name, data)
Copy the code
If the child component wants to pass the event to the parent component, it needs to pass the MITT plug-in. For example, the code of the search component is as follows:
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('Please enter your favorite author name! ');
// Pass to the parent component
emitter.emit('on-search',state.keyword);
}
return{... refState, onSearch }; }};Copy the code
The rest are vue3.0 syntax, such as watch listening and so on, more source code here.
PS: I don’t know if the mining authorities will stop the service of the relevant data interface when the time comes, so next, I might consider writing static data and encapsulating axios. Of course, the code is still a little rough, because the implementation is a little hasty, and I will optimize it later.
Finally, attached are some renderings: