前言
最近开发了一款 基于大模型的小游戏
《我在同福》,整体的技术架构如下:
- 前端:React + Taro
- 后端:Spring系列 + MyBatis
- 数据:MySQL
- 其他:Linux(Rocky)、NGINX
本文记录一些在开发过程中的"小事"。
这些"小事"应该说都比较冷门,不会是热点问题。但并不是不重要。
MySQL数据目录
背景
数据盘挂在/data
目录下。
基于合理分配利用磁盘的考虑,考虑把MySQL的数据目录也修改到数据盘下。
安装MySQL
这次用的是Rocky这个发行版的Linux,采用的是dnf 安装
,整体过程和CentOS中的yum 安装
没有太大区别。
安装命令如下:
- 更新软件包
1
dnf upgrade --refresh -y
- 从Appstream中安装
1
dnf -y install mysql mysql-server
- 查看MySQL版本。示例代码:运行结果:
1
mysql --version
1
mysql Ver 8.0.36 for Linux on x86_64 (Source distribution)
修改数据目录
取消SELINUX
在修改数据目录之前,我们需要取消SELINUX。
这是一个坑,否则之后修改数据目录,会报各种权限错误。
关于"取消SELINUX",可以参考《ClickHouse应用实践:1.基本操作》中"CentOS取消SELINUX",在Rocky中,没有任何区别。
修改目录
修改配置文件/etc/my.cnf
。如果/etc/my.cnf
的内容如下,则修改/etc/my.cnf.d
的内容。
1 | # |
在本文修改的是/etc/my.cnf.d/mysql-server.cnf
1 | [mysqld] |
datadir=/data/mysql
,指定了MySQL数据文件存放的目录,所有的数据库文件、表定义文件等都将存放在这个目录下。socket=/data/mysql/mysql.sock
,定义了MySQL服务器与客户端之间进行本地通信时使用的套接字文件的路径。log-error=/var/log/mysql/mysqld.log
:指定了MySQL错误日志的文件位置。pid-file=/run/mysqld/mysqld.pid
:设置了MySQL服务器进程ID(PID)文件的位置。
在本文,我们不但修改了datadir
,还修改了socket
。
最后,注意权限问题!示例代码:
1 | chown -R mysql:mysql /data/mysql/ |
连接MySQL
修改完成后,启动MySQL,连接。
1 | mysql -uroot -p -S/data/mysql/mysql.sock |
解释说明:
- 如果使用
-S
参数,通过指定实例下的socket文件来登录到指定的实例。 - 如果使用
-h
参数,则使用"TCP/IP"的方式,这时候不能是"localhost",因为"localhost"会使用默认的socket文件登录。
NGINX相关
SSL
FreeSSL
通过FreeSSL,申请证书。
官网:https://freessl.cn
在本文,我们选择浏览器生成
和文件验证
。
之后会得到我们需要设置的文件内容。
文件验证
–with-http_ssl_module
在服务器上安装NGINX,安装方法可以参考《NGINX入门与提高:1.安装、基础操作和配置》。
需要注意的是,如果采用编译安装,需要在安装的时候配置SSL模块。示例代码:
1 | ./configure --with-http_ssl_module |
- 注意参数
--with-http_ssl_module
。
修改server块中的location
我们添加或修改server
块中的location
指令如下:
1 | server { |
- 正如配置文件,我们需要新建
/root/temp/5D3505A7F4CDF9AB39B863519463CA3F.txt
,内容是证书发行方提供的验证:1
2
306967BE234FEB6DEF786A60075EE80F005F04ED0AC56A453E3D24FA5F568FB4F
trust-provider.com
cmcdt3xs4rp8we
配置SSL证书
验证通过后,FreeSSL会给我们发放证书。
配置SSL证书的方法,可以参考《一种基于nginx的反向代理策略》,本文不赘述。
反向代理SpringBoot
最后,我们还需要反向代理到一个SpringBoot应用。
1 | server { |
解释说明:
proxy_pass http://localhost:8080/
,目标地址(SpringBoot)。proxy_set_header
:设置转发请求的头部信息,确保SpringBoot应用能够获取正确的客户端信息。
小程序相关
跳转
背景
《我在同福》有点卡充值功能,因为个人小程序不允许有支付功能,所以考虑采取点卡的方式。
可以通过淘口令的方式,将购买点卡的地址给客户。如果能直接跳转到其他小程序就更好。
微信官方和Taro,都提供了跳转到其他小程序的方法,具体参加官方的文档:
- 微信官方:https://developers.weixin.qq.com/miniprogram/dev/api/navigate/wx.navigateToMiniProgram.html
- Taro:https://docs.taro.zone/docs/apis/navigate/navigateToMiniProgram/
获取任意小程序的AppID和页面路径
两个参数
小程序跳转中,两个关键参数是:
AppID
,标明需要跳转到哪一个小程序。path
,目标页面的路径。
AppID
AppID可以通过小程序的资料页获取:
path
目标页面的路径需要有一个微信公众号。
步骤如下:
- 新建图文
- 插入小程序
- 点击去搜索
- 输入小程序的AppID
- 获取更多页面路径
- 输入自己的微信号,点击开启。
- 手机微信打开目标小程序的目标页面,复制页面路径。
最后,需要注意的是虽然我们复制的地址形如:
1 | pages/personal/index.html?userId=【XXX】 |
但是,我们在代码中填写的参数如下,没有.html
后缀。
1 | Taro.navigateToMiniProgram({ |
useEffect
原因
在React中,useEffect
钩子用于执行副作用操作,如数据获取、订阅或手动更改DOM等。
当组件首次渲染或者依赖项发生变化时,useEffect
中定义的函数会被执行。
对于微信小程序(特别是使用Taro框架开发的小程序),页面的生命周期与React组件的生命周期有所不同。
在微信小程序中,页面的显示和隐藏并不会导致页面组件的卸载和重新挂载。
这意味着当你从A页面跳转到B页面,再从B页面返回A页面时,A页面实际上并没有被销毁和重建,而是被隐藏和重新显示了。
因此,A页面的组件实例仍然存在,其useEffect
钩子中的清理函数(如果有)可能会执行,但是由于页面没有重新渲染,useEffect
钩子的初始化部分不会再次执行。
解决方案
使用onShow
生命周期方法。
- 在Taro中,我们可以利用页面的
onShow
生命周期方法来模拟useEffect
的行为。onShow
方法会在页面每次显示时调用,包括首次加载和从其他页面返回时。你可以在onShow
方法中调用你需要在页面显示时执行的逻辑。 - 在
onShow
方法修改一个useState
变量,而这个变量作为useEffect
的第二个参数。
不校验域名
默认小程序只能调用合法域名(我们自己配置)的接口。
这样在开发过程会很麻烦。
我们可以设置,不校验域名。
在微信开发者工具,依次点击:
详情
本地设置
不校验合法域名...
调整样式
可以直接在微信开发者工具上,调整样式。
安卓手机的BUG
现象
在部分安卓手机上,会有如下的BUG。
输入框展示不全。
探索
复现过程
- 进入"开始页",或"新开页"。
- 输入"自我介绍"(或输入之后删除,或不输入只点了一下输入框)
- 点击"确认"之后。
估计有50%的概率,会出现上述现象。
不会复现的情况
- 通过"继续"页,进入聊天框。
- 进入"开始页",或"新开页"。不输入自我介绍,直接点"确认"。
试图解决
强制重新渲染
怀疑是"聊天页"的页面渲染问题,尝试各种修改聊天页,没有解决问题。
甚至强制React重新渲染,还是没有解决问题。
React自动渲染机制
React依靠虚拟DOM来实现组件的自动渲染。当状态(state)或属性(props)发生变化时,React会自动检测这些变化,并重新渲染组件以反映最新的状态。
通常情况下,React的自动渲染机制能够很好地工作,但在某些情况下,可能需要手动强制组件重新渲染。
强制更新函数组件
类组件可以通过调用this.forceUpdate()
方法来强制重新渲染。
函数组件没有this.forceUpdate()
方法,但可以通过创建一个空的状态变量并在需要时更新它来达到类似的效果。
下文的例子展示了如何使用useState
和useCallback
钩子来创建一个强制更新函数:
1 | const [, updateState] = React.useState(); |
- 调用
forceUpdate
函数,即可完成强制重新渲染。
注意
强制重新渲染组件并不是最佳实践,应当尽量避免,因为过度使用会导致性能问题。
只有在必要时,例如React未能自动更新组件的情况下,才考虑使用这种方法。在尝试强制重新渲染之前,应仔细检查代码,确保状态和属性的更新是正确的。
解决
最后,发现,这是一个"坑"。
在进入"聊天页"之前,输入法需要不能处于弹出状态,否则会有一定的机率影响页面渲染。
- 复现的情况都是输入法都处于弹出状态,直接进入聊天页。
- 没复现的情况,包括
进入"开始页",或"新开页"。不输入自我介绍,直接点"确认"
,这种情况下输入法都没有处于弹出状态。
所以,解决问题的方法是:在"进入聊天页"之前 收起输入法。