
























自从用Hexo建站以来,由于每次更新文章都要推仓库,等Vercel那边构建个几十秒,有时候网络环境不好,这就变成一项巨大的工程,看着群友纷纷(?好像也就几个吧)玩起了Next.js,我也有了去研究它的念头,正好可以掌握一个新的技术栈
但是没有使用HTML CSS JS许久并且没有用过React 和TS的我在这个项目面前还是太菜了。。。勉强做了一个导航栏就不知道怎么继续了。只好借鉴来Aria大佬的代码(多谢Aria),调一下颜色之类的,所以这个新博客和Aria的博客非常的像(可以说就是导航栏的位置和整个博客的配色不同)。
Aria之前已经写过一篇迁移小记了
前往以下网站,不保证安全性哦喵~新篇章:Next.js迁移小记Ariasaka
我记录一下改动的部分,其他的可以看Aria那篇。
我以前的Hexo站有个时间戳(站点历史)页,当时是用的butterfly主题带的时间线标签,然而我发现Aria没有做这个标签,于是我得自己动手了。
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:
.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.json 和 flink-circle.json这两个都是ts生成的文件,分别给友链延时检测系统和朋友圈(Friend-Circle-Lite)提供友链信息
这一堆Aria都是直接写了个mdx存在前端,但是我想尽量减少推仓库,于是多开了几个Vditor支持了在后端修改。不过发现Vditor有个问题就是不能在同一个页面显示多个Vditor,于是我只好加了按钮切换。
Aria的前端颜色模式是跟随浏览器的,想换必须改浏览器的设置,我想还是改成手动的比较好。但是这就涉及到一个组件向另一个隔了好几层的组件传递信息。Aria说可以用Context。
用 createContext()建一个Context:
export declare interface Settings{
colorMode:"light" | "dark"
}
export const defaultSettings:Settings={
colorMode:"light"
};
export const SettingsContext = createContext({
settings: defaultSettings,
setSettings: (settings: Settings) => {}
});然后在根布局中使用它:
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的值:
const {settings,setSettings}=useContext(SettingsContext);这个本来准备也支持后端改的(可以把不同平台的歌放一起组成一个歌单)但后来发现网易和酷狗都用不了只有QQ音乐可以用于是就省去这个了,直接在QQ音乐那边编辑歌单。
(不知道为什么被这一点工作硬控了半天)
注意文件后缀是jsx
"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 (
<>
</>
);
}"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,于是后来改成直接获取歌单的时候也用它了
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显示出来
音乐厅里放一个id为musicpage-aplayer的<div>
然后再根layout里建一个ref并加入
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的困惑)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。