《花100塊做個摸魚小網站! 》第十篇—響應式佈局適配PC端和移動端

sum墨發表於2024-11-25

⭐️基礎連結導航⭐️

伺服器 → ☁️ 阿里雲活動地址

看樣例 → 🐟 摸魚小網站地址

學程式碼 → 💻 原始碼庫地址

一、前言

大家好呀,我是summo,小網站一直有個問題,就是PC端的樣式和移動端的樣式是兩套,並且不能根據螢幕的大小進行動態化佈局,如果PC端螢幕非常小就是這樣:

如果移動端螢幕非常大就是這樣:

總之,這樣的效果不是我想要的,我想要是下面這樣的:

這個功能我本來以為會很麻煩,實際上還好,已經發布上線了,程式碼也提交到Gitee了,下面介紹一下我是怎麼實現這個響應式佈局功能的。

二、響應式佈局

響應式佈局是同一頁面在不同的螢幕上有不同的佈局,即只需要一套程式碼使頁面適應不同的螢幕。這個聽起來很牛逼,但是小網站想要實現這個功能卻很容易,因為小網站的前端是基於ElementUI實現的,而ElementUI本身就支援響應式佈局。

也就是說我們只要利用ElementUI的這個特性就可以實現響應式佈局。

這是小網站的main區域修改後的程式碼

<el-main>
  <el-row :gutter="10">
    <el-col :sm="24" :md="20" :lg="20" :xl="20">
      <el-row :gutter="10">
        <el-col
          v-for="(board, index) in hotBoards"
          :key="index"
          :xs="24"
          :sm="10"
          :md="8"
          :lg="6"
          :xl="6">
                <hot-search-board
                  :title="board.title"
                  :icon="board.icon"
                  :fetch-url="board.fetchUrl"
                  :type="board.type"
                /> 
          </el-col>
        </el-row>
      </el-col>

      <el-col :xs="0" :sm="4" :md="4" :lg="4" :xl="4">
            <visitor-log />
            <holiday-calendar />
            <word-cloud />
            <poetry />
            <camera-player />
      </el-col>
   </el-row>
</el-main>

從程式碼上可以看到,在內容區域和側邊欄區域設定了響應式佈局屬性,正常情況下兩塊區域的比例分配是20:4,但由於側邊欄在頁面寬度很小時會出現樣式混亂的問題,所以可以透過設定:xs="0"直接隱藏。
第二部分就是內容區域裡面的熱搜元件,由於每行展示的熱搜元件個數需要根據頁面寬度來動態調整,所以這裡也是使用了響應式佈局屬性,核心邏輯就是隨著頁面寬度的減少,單個熱搜元件的佔比增加,最終增加到24,佔滿整個螢幕。

三、PC端和移動端樣式替換

本來我以為這個功能會很複雜,但實際上這個反而是最簡單的,我先上程式碼:

<template>
  <div v-if="!isSmallScreen">
    <pc-app />
  </div>
  <div v-else>
    <mp-app />
  </div>
</template>
<script>
import PcApp from "@/PCApp.vue";
import MpApp from "@/MpApp.vue";
export default {
  components: { PcApp, MpApp },
  data() {
    return {
      // 用於標識是否為小螢幕
      isSmallScreen: false,
    };
  },
  created() {
    // 根據初始螢幕寬度設定isSmallScreen的值
    this.checkScreenSize();
  },
  methods: {
    checkScreenSize() {
      const screenWidth = window.innerWidth;
      this.isSmallScreen = screenWidth < 768;
    },
  },
  mounted() {
    // 監聽視窗大小變化事件
    window.addEventListener("resize", this.checkScreenSize);
  },
  beforeDestroy() {
    // 移除視窗大小變化事件監聽器
    window.removeEventListener("resize", this.checkScreenSize);
  },
};
</script>
<style scope></style>

核心邏輯就是,我將PC端和移動端分為了兩個元件,然後利用isSmallScreen屬性來判斷當前螢幕是否需要進行PC端和移動端切換,非常簡單。

四、小結一下

小網站的功能一直都不是很全,很多都只有最初始的版本,最佳化的點很多,但我前面確實對前端技術不是怎麼熟悉就沒有做。不過最近好好重新學了一下,感覺自己前端功力大漲,後續我會嘗試增加一些新功能,讓小網站的功能更加豐富。

番外:虎撲戀愛區熱搜爬蟲

1. 爬蟲方案評估

虎撲戀愛區一直都是大家最愛看的熱搜板塊,今天我講一下這個熱搜資料是怎麼獲取到的。這個板塊的核心介面是https://bbs.hupu.com/love-hot,返回的是HTML頁面,所以又得用上我們的老朋友 Jsoup了,核心程式碼在第二段。

2. 網頁解析程式碼

HupuHotSearchJob.java

package com.summo.sbmy.job.hupu;

import com.summo.sbmy.common.model.dto.HotSearchDetailDTO;
import com.summo.sbmy.dao.entity.SbmyHotSearchDO;
import com.summo.sbmy.service.SbmyHotSearchService;
import com.summo.sbmy.service.convert.HotSearchConvert;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.summo.sbmy.cache.hotSearch.HotSearchCacheManager.CACHE_MAP;
import static com.summo.sbmy.common.enums.HotSearchEnum.HUPU;

/**
 * @author summo
 * @version HupuHotSearchJob.java, 1.0.0
 * @description 虎撲熱搜Java爬蟲程式碼
 * @date 2024年08月09
 */
@Component
@Slf4j
public class HupuHotSearchJob {

    @Autowired
    private SbmyHotSearchService sbmyHotSearchService;

    @XxlJob("hupuHotSearchJob")
    public ReturnT<String> hotSearch(String param) throws IOException {
        log.info("虎撲熱搜爬蟲任務開始");
        try {
            String url = "https://bbs.hupu.com/love-hot";
            List<SbmyHotSearchDO> sbmyHotSearchDOList = new ArrayList<>();
            Document doc = Jsoup.connect(url).get();
            //元素列表
            Elements elements = doc.select(".p-title");
            for (int i = 0; i < elements.size(); i++) {
                SbmyHotSearchDO sbmyHotSearchDO = SbmyHotSearchDO.builder().hotSearchResource(HUPU.getCode()).build();
                //設定文章標題
                sbmyHotSearchDO.setHotSearchTitle(elements.get(i).text().trim());
                //設定虎撲三方ID
                sbmyHotSearchDO.setHotSearchId(getHashId(HUPU.getCode() + sbmyHotSearchDO.getHotSearchTitle()));
                //設定文章連線
                sbmyHotSearchDO.setHotSearchUrl("https://bbs.hupu.com/" + doc.select(".p-title").get(i).attr("href"));
                //設定熱搜熱度
                sbmyHotSearchDO.setHotSearchHeat(doc.select(".post-datum").get(i).text().split("/")[1].trim());
                //設定熱搜作者
                sbmyHotSearchDO.setHotSearchAuthor(doc.select(".post-auth").get(i).text());
                //按順序排名
                sbmyHotSearchDOList.add(sbmyHotSearchDO);
            }
            AtomicInteger count = new AtomicInteger(1);
            sbmyHotSearchDOList = sbmyHotSearchDOList.stream().sorted(Comparator.comparingInt((SbmyHotSearchDO hotSearch) -> Integer.parseInt(hotSearch.getHotSearchHeat())).reversed()).map(sbmyHotSearchDO -> {
                sbmyHotSearchDO.setHotSearchOrder(count.getAndIncrement());
                return sbmyHotSearchDO;
            }).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(sbmyHotSearchDOList)) {
                return ReturnT.SUCCESS;
            }
            //資料加到快取中
            CACHE_MAP.put(HUPU.getCode(), HotSearchDetailDTO.builder()
                    //熱搜資料
                    .hotSearchDTOList(sbmyHotSearchDOList.stream().map(HotSearchConvert::toDTOWhenQuery).collect(Collectors.toList()))
                    //更新時間
                    .updateTime(Calendar.getInstance().getTime()).build());
            //資料持久化
            sbmyHotSearchService.saveCache2DB(sbmyHotSearchDOList);
            log.info("虎撲熱搜爬蟲任務結束");
        } catch (IOException e) {
            log.error("獲取虎撲資料異常", e);
        }
        return ReturnT.SUCCESS;
    }

    /**
     * 根據文章標題獲取一個唯一ID
     *
     * @param title 文章標題
     * @return 唯一ID
     */
    private String getHashId(String title) {
        long seed = title.hashCode();
        Random rnd = new Random(seed);
        return new UUID(rnd.nextLong(), rnd.nextLong()).toString();
    }


    @PostConstruct
    public void init() {
        // 啟動執行爬蟲一次
        try {
            hotSearch(null);
        } catch (IOException e) {
            log.error("啟動爬蟲指令碼失敗",e);
        }
    }
}

相關文章