最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 2天用vue3.0实现《掘金 - 2021年度人气创作者榜单》网站

    正文概述 掘金(夕水)   2021-01-29   474

    初看到掘金 - 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存储用户相关登录信息。如下图所示:

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

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

    这一个功能的实现思路知道即可,源码不会实现。然后在该网站去获取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="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')"
                    
                    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监听等等,更多源码在这里。

    最后,附上部分效果图:

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

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

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

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

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

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

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

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

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


    起源地下载网 » 2天用vue3.0实现《掘金 - 2021年度人气创作者榜单》网站

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元