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: