我把收藏的ACG壁纸,做成了一个网站
这标题怎么一股奇怪的味道
其实这是一个从七月底就开始着手的项目了,因为中间有点事一直拖到了8月底才完成。
起因
没有起因,纯粹是一时兴起想做个网站练练手罢了。正好收藏了不少图,好东西就要分享出来嘛。
技术实现
由于考虑到成本,这个项目所用的所有资源都将做到最低成本。
数据库部分,MongoDB提供了永久免费的500M数据库,虽然都是最低配,但对我来说完全够用了。
前后端部分,Vercel提供了永久免费的云函数以及前端集成部署,还支持自定义域名,这年头这么良心的平台真是少见了。
架构
整个网站除了数据库之外都是我熟悉的Node.js设计,前端仍然采用Nuxt.js,后端是Express,能够直接与Vercel的云函数API实现对接。
在确定了整体架构之后,一个问题来了。这么多的大图片文件,存哪里?
首先排除了直接存MongoDB,虽然数据库的二进制格式可以直接存文件,但对性能来说是个坎,而且免费的500M空间也远远不够用。
然后,我把目光看向了公共图床。在调查了有国内节点的各个大型免费图床平台后,我发现这些图床大多数都有图片大小限制,大部分是5M到10M。然而我收藏的超清壁纸最大的一张有22M,10M以上的图片文件也比较多,所以最终还是放弃了。
不过,由于网站不仅需要壁纸的原图,通常还需要比较小的图片缩略图,所以我决定把所有图片的缩略图放在这些图床上。
最终,对于大文件,我决定把它们放在OneDrive上,然后使用Graph API获取图片直链,当作图片链接来展示在网站上。不过又要去学GraphAPI就是了≡(▔﹏▔)≡
MongoDB数据库设计
之前从来没接触过MongoDB,所有东西都是现学的。
对于当前的网站架构,数据库所需要的信息应该有:图片的图床链接、图片的文件名等。由于图片大部分来自Pixiv所以储存了p站id信息。又因为前端设计需要,所以又存储了图片的长度和宽度。
后来又为了统计网站访问量,又增加了一个集合来收集访问数据。(本来是可以通过第三方平台实现的,结果发现第三方平台的统计代码被我浏览器插件给屏蔽了🤣)
后端设计
后端的接口目前设计的比较少,大致有下面几个:
- 获取可以分页的图片列表
- 获取一张图片的详情
- 获取一张图片的源文件
后来又应要求加了一个随机图片的接口,下面再介绍。
MongoDB分页查询
前端展示图片需要分页,在经过一番学习后找到了一种比较简单的分页查询方法:
1 | // 指定每页的数量 |
其中的where后紧跟的gte就是MongoDB的查询操作符之一。这里的gte代表大于等于。
Graph API对接
Microsoft Graph是巨硬为自家产品统一设计的接口集。要调用接口先要获取一个Access Token,而Access Token又要通过Refresh Token获取,而Refresh Token又要通过应用ID和密钥来生成(一圈下来头都晕了)
不过,还好我找到了一个可以参考的项目:onedrive-vercel-index
这是一个OneDrive网盘索引程序,我的 Revincx Cloud 目前就在使用这个项目。
核心代码:
1 | const token = await axios.get(`${apiConfig.driveApi}/root${encodePath(authTokenPath)}`, { |
前端设计
前端才是最难的部分,前后累计起来搞了一个多星期。
瀑布流布局
由于自己动手实现瀑布流很复杂,所以我直接用了第三方组件:vue-waterfall
虽然直接有组件可以用,但开发的时候还是踩了不少坑。比如瀑布流的LineGap的动态调整问题,不过最后还是勉强解决了。
下面记录以下折磨我时间最长的一个问题:
由于Nuxt也是SPA单页面应用的概念,所以一旦刷新页面或者打开新的窗口,整个App的状态都会被重置。于是问题就来了:在从瀑布流进入到一张图片的详情页面,然后再返回瀑布流时,瀑布流就会重载,然后就会重新再从后端获取数据,这种体验是极其糟糕的,而且也浪费了服务端资源。
于是,我想到了Vue中的Keep-Alive。
一开始,我在el-main,也就是页面主容器上添加keep-alive,然后发现没有任何作用。一旦返回瀑布流页面,数据还是会重新加载。
后来我换了条思路,在获取到数据后,通过变量储存起来,下次返回瀑布流时直接拿就可以了。
储存到哪呢?Vuex?那个东西差不多都忘完了嘛,算了,先扔到全局里试试。
浏览器端的全局变量,估计就只有Window对象了。于是,我在获取到数据后,把数据都赋值到了window对象上(事实上这么做是很愚蠢的)。
然后却发现,虽然在进入图片详情页面时window变量中的数据还在,但当我按下浏览器返回键时,window对象中的数据消失了。。
好吧,还是得去复习一遍Vuex,虽然好像也不难的样子。
然而在几天之后,我为了提升导航栏组件的复用性,把导航栏放到了layouts/default.vue
中。然后使用<nuxt/>
来加载页面内容。这时我灵机一动,在nuxt组件上加了一个keep-alive
奇迹发生了,当我返回瀑布流页面是,页面没有重载!!
然后我试着获取Vuex中的数据,一切正常!!(可能有点夸张,但我当时真的开心死了)
不过还有个小问题,返回主页面时页面滚动位置被重置了,原因是因为我的布局不是根元素在滚动,是主容器。然后我把滚动位置也存到Vuex里,返回时手动加载,也算是解决了。
图片详情页
由于图片详情页要加载原图,这里我使用了双层的设计,在缩略图上通过absolute定位叠上了原图,然后加一个模糊,在加一个加载动画,逼格不就出来了嘛~
然而理想很丰满,现实….
由于绝对定位脱离了文档流,导致两张图一直无法正常的完全重叠。缩略图使用了相对定位,而且有padding,但这个padding对绝对定位的元素是无效的。
在折腾了一段时间后,终于发现其实用CSS的calc函数就可以解决,看来我真的要去复习CSS了,现在写CSS真的是一头雾水。。
总结
由于部分原因,网站源码暂时不会公开,不过欢迎大家来网站看看。网站内容会持续更新的。
另外网站有一个随机图片的API,不过目前没啥用。
详情可以去壁纸频道看看:https://dd.al/kxG9j
暑假的坑暂时填完了,下次写博客就不知道是什么时候了。