本项目代码已开源,具体见:
前端工程:vue3-ts-blog-frontend
后端工程:express-blog-backend
小程序源码:blog-weapp
数据库初始化脚本:关注公众号程序员白彬,回复关键字“博客数据库脚本”,即可获取。
前言
我是在 2019 年启动这个全栈博客项目的,当时自己的前端技术其实也不怎么样,但是我脑子里就一个想法,必须掌握一点点全栈技术,了解整个系统全栈是怎么运行起来的。于是,我就决定自己动手做一个全栈的博客,当时也还从没在公司做过小程序项目,就想着也做个小程序版的博客练练手。
我学习一个新东西不太喜欢一来就上框架,我会先开始学一点最原始的基础,有了基础后,再换框架做,这样效率高,同时遇到框架问题也不至于由于没有了解过底层基础而无从下手去解决。由于当时我对小程序技术处于一个一无所知的阶段,我决定先用微信小程序原生语法做一遍。
所以可能大家看到的小程序源码水平不是很高,不过没关系,我自认为用它来入门小程序还是没多大问题的。
小程序包含的内容
小程序基本是仿照 PC 端的内容去做的,主要还是由首页的文章瀑布流、文章详情、分类、留言板、我的等几个页面组成。
目前微信对未认证的个人小程序限制了被搜索能力,而认证是需要收保护费的,确实挺符合他们家做事风格的。
我的博客小程序被限制搜索能力了,大家可以扫这个小程序码浏览下。
内心OS:访问量不大,懒得交保护费
下面就挑几个我认为比较重要的点展开聊聊。
怎么运行项目?
要了解项目,首先要给它运行起来。
我们 clone 代码后,先照常 npm install 安装依赖。
接着我们打开微信开发者工具,导入项目后,先构建 npm,这是微信小程序中用到 npm 必须执行的一个步骤,这一步执行后会生成一个 miniprogram_npm 目录。
此时需要刷新编译一下,接着就能看到界面效果了。
自定义 tabbar
我们知道,移动端中页面通常分为 tabbar 页面和非 tabbar 页面。tabbar 页面通常用来承载核心页面,切换 tabbar 页面不是以页面栈的形式交互,来回切换不会销毁 tabbar 页面。而非 tabbar 页面是通过页面栈来维护的,页面返回时会销毁当前页面。
当你的微信小程序需要 tabbar 页面时,可以通过 app.json 的 tabBar 配置项管理。
tabBar 配置对样式的支持度很低,很难达到完全自定义的效果。因此也衍生出了自定义 tabBar 的需求。
微信小程序官方在基础库 2.5.0 以上版本支持了自定义 tabBar。
可能是由于时间错开了,当时没有用到自定义 tabBar,转而是用组件的形式承载了几个核心页面。
也就是说,目前看到的我的博客小程序其实并不是用 tabBar 页面来组织首页、分类、留言、我的等几个页面,这几个页面是 index 页面中的几个组件。
当然,我不是很推荐用这种方式实现自定义底部 tabBar 样式,虽然看起来视觉效果也一样,但是肯定不如官方推荐的
custom-tab-bar 组件。
授权开放信息
博客小程序需要拿到用户的开放信息,主要是头像和昵称,用于评论留言功能。
微信对用户信息这块卡得一直很死,从最初可以结合 open-type=“getUserInfo” 和 wx.getUserInfo API 做到只需要用户授权一次后续就能静默取得用户信息,到后来只能用 open-data 组件展示开放信息,再到现在,昵称和头像都拿不到了,必须引导用户填写,你就说服不服吧。
本小程序就是采用的 API 调用用户信息,判断用户是否授权过 userInfo,如果没有授权过,通过 open-type=“getUserInfo” 的 button 组件引导用户授权;
如果授权过,就可以调用 wx.getUserInfo 直接取用户信息。
而现在,如果你不进行代码调整,通过上述方式拿到的头像就是灰色默认头像了,而昵称则是“微信用户”这种没有什么用的信息。
更牛的是,现在获取手机号授权也要收费了,一个字,绝!
接口封装
看过博客 PC 端前端源码的应该知道,API 接口可以封装到类里面,然后对外输出一个单例。
调用实例的方法就相当于调用后端 API。
那么小程序端能不能这样封装呢?也可以的,不过我换了一种形式,不是用类封装,而是用函数封装。
函数有函数的好处,类有类的好处。
函数的好处在于它对构建工具是友好的,如果一个函数是纯函数,虽然它被定义了,但是没有被引用过,那么在编译阶段可以通过 Tree Shaking 去掉它。
而 Class 实例是一个对象,是一个整体,不能摇树去掉其中的某个方法。但是 Class 有面向对象的优点,调用起来很简单,能免去 import 多个函数的繁琐。
大家可以两种方式都体验一下,根据实际情况再去选择。
前端封装 API 调用的核心还是通过 Promise 封装平台提供的 Request 能力,封装好了之后,对外暴露 Class 实例或者函数,这样可以做到不管是在 Web、还是小程序、甚至桌面应用、跨端 APP,对于业务代码来说,调用体验一模一样,我在公司内部项目中就做了这样的事情。
不同平台提供的 Request 能力不一样,
- Web 端底层是 XMLHttpRequest 或者 fetch,接着可能被 axios 之类的库又封装了一遍,那么我们的封装就是基于 axios 去包装即可。
- 小程序,以微信小程序为例,它提供的 Request API 是 wx.request,直接对它进行封装即可,不必使用第三方库再进行封装。
- uniapp,提供的是 uni.request,封装它就行。
封装的范式是什么样呢?简单给个例子:
const request = () => {
return new Promise((resolve, reject) => {
底层request库请求()
如果 success, resolve()
如果 fail, reject()
})
}
当然,如果用到 axios 这样的库,我们通常在其拦截器中处理。
复杂的封装还会在其中考虑并发,鉴权,业务错误码判定,无感刷新 token,甚至微前端通讯等等。
不过这些对业务调用方来说都是无感知的,内核多复杂都没关系,上层调用要简单化!
markdown 渲染
我们的文章内容是用 markdown 存储的,在小程序上的文章详情页中应该怎么渲染出来呢?
小程序没有 innerHTML 能力,这意味着 Web 生态的 markdown 引擎在小程序端都不奏效了。小程序要实现 markdown 渲染,应该怎么做呢?用 rich-text 组件行不行呢?理论上可以,但是 rich-text 组件内会屏蔽所有事件,这意味着如果你希望很难与其中节点进行交互。
如果你希望支持更多的交互能力,就需要将 markdown 解析成结构化数据(本质上是节点树的结构),再遍历这个结构化数据,将其渲染成微信小程序的各种组件(比如 view, text, image, video 等等)。有几个库可以参考,虽然都不算完美,但是提供了一种实现思路。
- towxml
- wemark
CI 的实现
小程序发布版本流程比较繁琐,其中还需要在微信开发者工具中手动上传代码到微信公众平台。
那么有没有办法把这个过程自动化呢?
嗯,可以的,至少通过脚本上传代码到微信公众平台是可以实现的,官方提供了 miniprogram-ci 来做这件事情。
使用前需要使用小程序管理员身份访问"微信公众平台-开发-开发设置"后下载代码上传密钥,并配置 IP 白名单,才能进行上传、预览操作。
具体使用方式可以参考博客源码ci.js。
你完全可以考虑把这个脚本集成到 CI/CD 流程中。
上传完代码后,还有提交版本审核环节,这一步是不在 miniprogram-ci 支持范围内的。
如果确实有这方面需要,可以考虑用 Puppeteer 或者抓包分析实现,这里就不做延申了。
个人感受
写小程序代码和写 html, css, js 三件套本质上没多大区别,各家小程序的使用语法其实跟 Vue Options API 风格也挺像的。小程序最大的不同是:它是双线程的,渲染层和逻辑层是分开的,渲染层基于定制化的 WebView,逻辑层也是一个沙箱环境,很多 API (DOM/BOM相关)都被屏蔽了,由于小程序是双线程模型,这使得很多 API 操作都是异步的,比如你要获取一个元素节点的属性状态,这就是异步的,需要线程间通信。理解了这些,上手小程序开发就不是很困难。
- 开源地址:vue3-ts-blog-frontend
- 专栏导航:Vue3+TS+Node打造个人博客(总览篇)