⭐️基礎連結導航⭐️
伺服器 → ☁️ 阿里雲活動地址
看樣例 → 🐟 摸魚小網站地址
學程式碼 → 💻 原始碼庫地址
一、前言
大家好呀,我是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);
}
}
}