惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

博客一周年记 P13020 [GESP202506 八级] 遍历计数 题解 Moonlark(Nonebot2+Python)命令式聊天机器人插件开发记录 使用Github Actions定制Github个人主页 P3435 [POI 2006] OKR-Periods of Words 题解 Qt Troubleshoot(三) Qt Troubleshoot(二) Qt Troubleshoot (一) 使用Qt实现一个简易时钟应用 自己动手写三维引擎(一) 自己动手写二维物理引擎(一) 域名迁移到blog.qyadbr.top 自己动手写二维物理引擎(零) Markdown与外挂标签语法测试 Hexo博客0氪建站记录(下) Hexo博客0氪建站记录(中) Hexo博客0氪建站记录(上)
埃氮幂の命名空间
2026-02-14 · via

为什么迁移?

自从用Hexo建站以来,由于每次更新文章都要推仓库,等Vercel那边构建个几十秒,有时候网络环境不好,这就变成一项巨大的工程,看着群友纷纷(?好像也就几个吧)玩起了Next.js,我也有了去研究它的念头,正好可以掌握一个新的技术栈

但是没有使用HTML CSS JS许久并且没有用过React 和TS的我在这个项目面前还是太菜了。。。勉强做了一个导航栏就不知道怎么继续了。只好借鉴来Aria大佬的代码(多谢Aria),调一下颜色之类的,所以这个新博客和Aria的博客非常的像(可以说就是导航栏的位置和整个博客的配色不同)。

Aria之前已经写过一篇迁移小记了

前往以下网站,不保证安全性哦喵~新篇章:Next.js迁移小记Ariasaka

我记录一下改动的部分,其他的可以看Aria那篇。

修改

TimeLine外挂标签

我以前的Hexo站有个时间戳(站点历史)页,当时是用的butterfly主题带的时间线标签,然而我发现Aria没有做这个标签,于是我得自己动手了。

ts代码:

TS
"use client";
import { ReactElement } from "react";

export default function TimeLineTag({
  children,
  time,
  color,
}: {
  children: ReactElement[] | ReactElement;
  time: string;
  color?: string;
}) {
  
  return (
    <div className={`etag-timeline${color?" "+color:""}`}>
      <div className="timeline-item">
        <div className="timeline-item-title">
          <div className="item-circle">
            <p>{time}</p>
          </div>
        </div>
        <div className="timeline-item-content">
          {children}
        </div>
      </div>
    </div>
  );
}

css:

CSS
.etag-timeline {
    margin: 0 0 0px 10px;
    padding: 14px 20px 5px;
    border-left: 2px solid var(--timeline-color,#425aef)
}

.etag-timeline.blue {
    --timeline-color: #428bca;
    --timeline-bg: rgba(66,139,202, 0.2)
}

.etag-timeline.pink {
    --timeline-color: #ff69b4;
    --timeline-bg: rgba(255,105,180, 0.2)
}

.etag-timeline.red {
    --timeline-color: #f00;
    --timeline-bg: rgba(255,0,0, 0.2)
}

.etag-timeline.purple {
    --timeline-color: #6f42c1;
    --timeline-bg: rgba(111,66,193, 0.2)
}

.etag-timeline.orange {
    --timeline-color: #ff8c00;
    --timeline-bg: rgba(255,140,0, 0.2)
}

.etag-timeline.green {
    --timeline-color: #5cb85c;
    --timeline-bg: rgba(92,184,92, 0.2)
}

.etag-timeline .timeline-item {
    margin: 0 0 15px
}

.etag-timeline .timeline-item:hover .item-circle:before {
    border: 3px solid var(--admi-theme);
}


.etag-timeline .timeline-item .timeline-item-title {
    position: relative
}



.etag-timeline .timeline-item .item-circle:before {
    position: absolute;
    top: 50%;
    left: -27px;
    width: 6px;
    height: 6px;
    border: 3px solid var(--timeline-color,#425aef);
    border-radius: 50%;
    background: var(--admi-fontcolor);
    content: '';
    -webkit-transition: all .3s;
    -moz-transition: all .3s;
    -o-transition: all .3s;
    -ms-transition: all .3s;
    transition: all .3s;
    -webkit-transform: translate(0,-50%);
    -moz-transform: translate(0,-50%);
    -o-transform: translate(0,-50%);
    -ms-transform: translate(0,-50%); 
    transform: translate(0,-50%)
}

.etag-timeline .timeline-item .item-circle>p {
    margin: 0 0 8px;
    font-weight: 500
}

.etag-timeline .timeline-item .timeline-item-content {
    position: relative;
    padding: 12px 15px;
    border-radius: .5rem;
    border: 0.5px solid var(--admi-fontcolor);
    font-size: .93em
}

.etag-timeline .timeline-item .timeline-item-content>p {
    margin-top: 0;
}

.etag-timeline .timeline-item .timeline-item-content>:last-child {
    margin-bottom: 0
}

.etag-timeline+.timeline {
    margin-top: -20px
}

效果见时间戳

网址导航&时间戳

如你所见,网址导航这个就是复制的友链,时间戳后端和面板复制说说,前端用自己做的Timeline。

flink-check.jsonflink-circle.json

这两个都是ts生成的文件,分别给友链延时检测系统和朋友圈(Friend-Circle-Lite)提供友链信息

首页公告、友链公告、关于页等

这一堆Aria都是直接写了个mdx存在前端,但是我想尽量减少推仓库,于是多开了几个Vditor支持了在后端修改。不过发现Vditor有个问题就是不能在同一个页面显示多个Vditor,于是我只好加了按钮切换。

颜色模式

Aria的前端颜色模式是跟随浏览器的,想换必须改浏览器的设置,我想还是改成手动的比较好。但是这就涉及到一个组件向另一个隔了好几层的组件传递信息。Aria说可以用Context。

createContext()建一个Context:

TS
export declare interface Settings{
    colorMode:"light" | "dark"
}
export const defaultSettings:Settings={
    colorMode:"light"
};
export const SettingsContext = createContext({
    settings: defaultSettings,
    setSettings: (settings: Settings) => {}
});

然后在根布局中使用它:

TS
import { defaultSettings,getSettings,SettingsContext } from "@/utils/settings";
//其他引入

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  // 其他内容
  return (
    <html lang="zh-cn" data-theme={settings.colorMode}>
      //其他内容
      <body>
        <style>{`#out-aplayer{position:fixed;left:10000px}`}</style>
        <SettingsContext.Provider value={{settings,setSettings}}>
          //需要用到value属性内容的组件都放里面
        </SettingsContext.Provider>
      </body>
    </html>
  );
}

使用Context的值:

TS
const {settings,setSettings}=useContext(SettingsContext);

音乐

这个本来准备也支持后端改的(可以把不同平台的歌放一起组成一个歌单)但后来发现网易和酷狗都用不了只有QQ音乐可以用于是就省去这个了,直接在QQ音乐那边编辑歌单。

(不知道为什么被这一点工作硬控了半天)

组件

注意文件后缀是jsx

JS
"use client";
import { useEffect } from "react";

import '@/styles/APlayer.css';




export default function AplayerBase({
    data
}
) {
    useEffect(() => {
        const Aplayer=require("aplayer/dist/APlayer.min");
        window.aplayer = data?
            (new Aplayer({
                container: document.getElementById("aplayer"),
                fixed: false,
                autoplay: false,
                lrcType: 3,
                listMaxHeight: 20,
                audio: data.map((item,index)=>
                    ({
                        name: item.name || item.title || 'Audio name',
                        artist: item.artist || item.author || 'Audio artist',
                        url: item.url,
                        cover: item.cover || item.pic,
                        lrc: item.lrc || item.lyric || '',
                        type: item.type || 'auto',
                    })
                )
            }))
        :undefined;
    },[data]);
    return (
        <>
        </>
    );

}

放入根layout中

TS
"use client";
//...
import AplayerBase from "@/components/thirdpartyjs/Aplayer";
import { MusicInfo } from "@/interfaces/music";
import { getPlayListInfo } from "@/utils/metingbase"; 

process.env.TZ = "Asia/Shanghai";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const [settings,setSettings]=useState<Settings>(defaultSettings);
  const [musicData,setMusicData]=useState<MusicInfo[]>();
  const playerRef=useRef<HTMLElement>();
  //...
  useEffect(()=>{(async()=>{
    setMusicData(await getPlayListInfo("tencent","9307834870"));
  })()},[])
  return (
    <html lang="zh-cn" data-theme={settings.colorMode}>
      //...
      <body>
        <style>{`#out-aplayer{position:fixed;left:10000px}`}</style>
        <SettingsContext.Provider value={{settings,setSettings}}>
          //...
          <div id="out-aplayer">
          <div id="aplayer"></div>
          </div>
          <AplayerBase data={musicData}/>
        </SettingsContext.Provider>
      </body>
    </html>
  );
}

获取音乐信息

刚开始用的是Meting暴露的一些函数,但是发现网易那边本地测试可以获取到但是部署上去之后就获取不到了,去看了MetingJS发现用的是作者Meto的api,于是后来改成直接获取歌单的时候也用它了

TS
export async function getPlayListInfo(platform: string,id: string):Promise<MusicInfo[]>{
    try{
        let result: MusicInfo[]=[{
            type: "error",
            url: ""
        }];
        const url="https://api.i-meto.com/meting/api?server="+platform+"&type=playlist&id="+id+"&r=0.122";
        await fetch(url,{next: { revalidate: 7200, tags: ["music"] }}).then(async(res) => res.ok ? (await res.json()): {})
        .then((data)=>{result=data;});
        return result;
    }catch(e: any){
        return [{
            type: "error",
            url: e
        }];
    }
}

页面切换调整Aplayer位置

打开音乐厅的时候要把隐藏的aplayer显示出来

音乐厅里放一个id为musicpage-aplayer<div>

然后再根layout里建一个ref并加入

TS
useEffect(() => {
    setInterval(()=>{
      if(window.location.href.includes("/music")){
        const div1 = document.getElementById('aplayer');
        const div2 = document.getElementById('musicpage-aplayer');
        if(div1&&div2&&!(div1?.parentElement==div2)){
          playerRef.current=div1;
          div2.appendChild(div1)
        }
      }
      else{
        const div1 = document.getElementById('out-aplayer');
        if(div1&&playerRef.current&&(!(playerRef.current?.parentElement==div1))){
          div1.appendChild(playerRef.current);
        }
      }
    },1000);
  }, []);

就好了

一些收获

useState()useRef()的区别

之前尝试把HTML的元素装进state里发现不管怎样那个state就是undefined后来改成用ref就发现元素被装进去了。说明一般ref就用来存储HTML元素,state存其他的(数字、字符串之类)

window as any

后来觉得我尝试用context搞aplayer,Aria告诉我一个不太优雅但是很简单的方法,给window这个全局变量直接加属性,直接实现组件之间信息互通,不用一层一层套ContextProvider了。(解决了为什么Aria的前端里有一堆(window as any).xxx的困惑)