项目到一段落,先来记录一下,本文以前端新手的角度记录React、TypeScript、Taro相关技术的开发体验以及遇到的问题和解决方法。
之前总说要学React(这篇博客:代码使我头疼之React初学习),这次项目需要做H5前端+小程序,我终于能用上React了~
使用React的开发框架之前就听过京东的Taro,所以就这个了,直接开码。
不错,感觉比Vue的模板写法自由很多,我看Taro文档的例子都是class组件,但一开始「前端带师」就推荐我用function组件,现在我全都是用function组件,react就该这么写,真香~
因为之前写了一段时间的Flutter,所以react对我来说很亲切,至少可以无缝上手声明式UI的写法。
不过我感觉React的生态太大,更新太快了,有点碎片化,很多第三方库官方文档都跟不上更新速度(批评一下mobx,害人不浅)
话说一开始我看了某位知乎大V的那本React和Redux的书,应该是我太菜的原因,感觉不是很容易理解,果然技术厉害的大佬不一定教书也厉害吗~
第一次用TypeScript,不过作为日常用C#写后端的人,又处处是熟悉的感觉~
反正比JS好用一万倍就是了,类型提示真是太棒了
目前用得不深,后续有什么相关的我再写写博客记录一下。
框架是个好框架,不过文档方面感觉还不是很完善,有些地方写得不是很清楚,感觉很多文档都是复制了微信小程序的文档来的,对于没开发过微信小程序且没读过微信官方文档的人来说,不是很友好。(不过官方文档还是要看,不看的话遇到很多问题都查不到的)
然后Taro官方提供了一个UI库,叫TaroUI,用的话还是能用的,就是更新太慢了,它的github项目主页 显示上次更新时间还是去年(2021年)6月份,到现在近一年时间没动过了。最新稳定版本还在2.x,而Taro框架已经更新到3版本了。
因为我用的Taro框架是3.x版本,所以只能硬着头皮上TaroUI 3的beta版本,导致遇到了一些奇奇怪怪的问题,头大。
除了TaroUI这个界面库,Taro官方还提供另一个叫NutUI 的库,不过是基于Vue的,我这个项目没法用,这个库就更新挺勤快的,github上最近更新还是6小时前,Star也有4.2k,比TaroUI的3.9k多。~~(看来React在京东不受待见呀)~。
我还看到有一个叫Taroify 的UI库,看起来好像不错,更新也很勤快,不过GitHub Stars只有300多,不敢用~ 下次来试试看
用的同时我还参考了这些项目/代码/文档:
接下来进入正题,总体说说遇到的一些问题/坑,以及解决方案。
Taro封装了路由相关的方法,我是做完了项目有时间去翻一下微信小程序文档 才发现这玩意跟小程序的路由特别像。
PS:我讨厌这种路由设计,不知道小程序这样是哪个人才想出来的,页面多了的话不太好维护啊~
在app.config.ts
文件里把路由配置好,类似这样:
export default defineAppConfig({ pages: [ 'pages/index/index', 'pages/info/place', 'pages/supply/index', 'pages/user/login', ], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: 'WeChat', navigationBarTextStyle: 'black' }, })
然后要跳转的地方就用Taro.navigateTo({url: 'pages/user/login'})
就行了
这里有个很坑的地方!Taro的热更新不完善,添加了新页面后热更新是不生效的,必须yarn dev:h5
重启才能看到效果,一开始我被坑得嗷嗷叫~
这个问题在我之前的博客:Django + Taro 前后端分离项目实现企业微信登录 有提到,Taro本身提供了useRouter()
来给我们读取地址里的参数
比如上面那个路由跳转的地方我们加上了参数:Taro.navigateTo({url: 'pages/user/login?title=hello'})
那我们在pages/user/login
页面里要获取参数就是这样
import {useEffect} from "react" import {useRouter} from "@tarojs/taro" export default function () { const router = useRouter() useEffect(() => { console.log(router.params.title) }, []) }
但当在读取微信登录服务器回调参数的时候,就不行,就取不出来,得自己拿完整链接window.location.href
去匹配。详见我这篇博客:Django + Taro 前后端分离项目实现企业微信登录
这看起来不是什么大问题,不过也导致了一个小bug,就是我在使用微信登录后,注销登录的时候不会清除地址里的code,这样没关闭页面的情况下,再次使用微信登录,那个code还是旧的,就直接报错了~
说实话我不知道这是哪里的问题
只有一个页面出现了这个问题,在最后一个输入框按回车,表现是form提交,但其实也没提交,并且页面变成重新刷新了
百思不得其解
我只好在最后面再加了一个隐藏的input
<AtInput name='hide' onChange={() => { }} disabled={true} border={false} style={{display: 'none'}}/>
Taro框架自带了Taro.request
可以用来请求,不过我用的时候很奇怪一直提示跨域,因为前期时间很赶,我就没去深入,直接换成我之前vue项目封装好的axios,果然还是axios好用~
(不过之后做成小程序的话,应该还是得重构一下,据说小程序不支持formdata)
感谢「前端带师coppy」提供的代码~
import {useState} from 'react' export default function useYourState<T extends {}>(state: T): [T, (state: Partial<T>) => void] { const [_state, _setState] = useState(state); return [ _state, (state: Partial<T>) => { _setState((_state) => { return { ..._state, ...state }; }); } ]; }
这样就不需要每次setState都需要加...state
了
使用前:
import {useState} from 'react' export const LoginPage = observer(() => { const [state, setState] = useState({ username: '', password: '', }) setState({ ...state, username: '', password: '' }) }
使用后:
import useYourState from "@/utils/coppy_state"; export const LoginPage = observer(() => { const [state, setState] = useYourState({ username: '', password: '', }) setState({ username: '', password: '' }) }
生产力获得了提高~
没去用大名鼎鼎的redux,转而使用比较简单的mobx
但是找到的例子文档都不太行(举例,官方中文文档:https://cn.mobx.js.org/)
最终还是寻求「前端带师」的帮助,搞定了
makeAutoObservable
mobx-react-lite
,别用Taro官网那个4.8版本,太老了没用不需要全局provider包装了,直接用全局变量,当然也可以用React Context
store定义
import {makeAutoObservable} from "mobx"; import {User} from "@/models/user"; import * as auth from '@/utils/auth' export class UserStore { isLogin = false user: User | null = null token = '' constructor() { makeAutoObservable(this) } login(user: User, token: string) { this.user = user this.token = token this.isLogin = true // 保存登录数据到本地 auth.login(token, user.username) } logout() { this.isLogin = false this.user = null this.token = '' auth.logout() } } export const myUserStore = new UserStore()
组件使用
import {View} from "@tarojs/components" import {AtButton} from "taro-ui"; import {observer} from "mobx-react-lite"; import {myUserStore} from "@/store/user"; import Taro from "@tarojs/taro"; import {useEffect} from "react"; export const UserPage = observer(() => { useEffect(() => { if (!myUserStore.isLogin) { Taro.redirectTo({url: '/pages/user/login'}) } }, []) return ( <View className='py-3 px-2'> <View className='at-article__h1'>用户中心</View> <View className='at-article__h3 mt-1'>用户名:{myUserStore.user?.first_name}</View> <AtButton className='mt-3' onClick={logout}>退出登录</AtButton> </View> ) function logout() { myUserStore.logout() Taro.reLaunch({url: '/pages/index/index'}) } }) export default UserPage
使用这个库:https://github.com/typestack/class-transformer
model定义,这个定义可以用JSON来生成,有很多在线工具,比如:https://apihelper.jccore.cn/jsontool
export class User { username: string first_name: string last_name: string email: string date_joined: string }
注意项目主页上的文档也是过期了的,plainToClass
方法已过期,得用这个方法:plainToInstance
import {plainToInstance} from "class-transformer"; const user = plainToInstance(User, res.data.user) myUserStore.login(user, res.data.token)