• React + TypeScript + Taro前端开发小结


    前言

    项目到一段落,先来记录一下,本文以前端新手的角度记录React、TypeScript、Taro相关技术的开发体验以及遇到的问题和解决方法。

    之前总说要学React(这篇博客:代码使我头疼之React初学习),这次项目需要做H5前端+小程序,我终于能用上React了~

    使用React的开发框架之前就听过京东的Taro,所以就这个了,直接开码。

    关于React

    不错,感觉比Vue的模板写法自由很多,我看Taro文档的例子都是class组件,但一开始「前端带师」就推荐我用function组件,现在我全都是用function组件,react就该这么写,真香~

    因为之前写了一段时间的Flutter,所以react对我来说很亲切,至少可以无缝上手声明式UI的写法。

    不过我感觉React的生态太大,更新太快了,有点碎片化,很多第三方库官方文档都跟不上更新速度(批评一下mobx,害人不浅)

    话说一开始我看了某位知乎大V的那本React和Redux的书,应该是我太菜的原因,感觉不是很容易理解,果然技术厉害的大佬不一定教书也厉害吗~

    参考资料

    关于TypeScript

    第一次用TypeScript,不过作为日常用C#写后端的人,又处处是熟悉的感觉~

    反正比JS好用一万倍就是了,类型提示真是太棒了

    目前用得不深,后续有什么相关的我再写写博客记录一下。

    参考资料

    Taro框架使用感受

    框架是个好框架,不过文档方面感觉还不是很完善,有些地方写得不是很清楚,感觉很多文档都是复制了微信小程序的文档来的,对于没开发过微信小程序且没读过微信官方文档的人来说,不是很友好。(不过官方文档还是要看,不看的话遇到很多问题都查不到的)

    然后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 前后端分离项目实现企业微信登录

    Taro.relaunch不会清除URL

    这看起来不是什么大问题,不过也导致了一个小bug,就是我在使用微信登录后,注销登录的时候不会清除地址里的code,这样没关闭页面的情况下,再次使用微信登录,那个code还是旧的,就直接报错了~

    TaroUI form的bug

    说实话我不知道这是哪里的问题

    只有一个页面出现了这个问题,在最后一个输入框按回车,表现是form提交,但其实也没提交,并且页面变成重新刷新了

    百思不得其解

    我只好在最后面再加了一个隐藏的input

    <AtInput
        name='hide'
        onChange={() => {
        }}
        disabled={true}
        border={false}
        style={{display: 'none'}}/>
    

    网络请求封装

    Taro框架自带了 Taro.request 可以用来请求,不过我用的时候很奇怪一直提示跨域,因为前期时间很赶,我就没去深入,直接换成我之前vue项目封装好的axios,果然还是axios好用~

    (不过之后做成小程序的话,应该还是得重构一下,据说小程序不支持formdata)

    封装useState

    感谢「前端带师 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/)

    最终还是寻求「前端带师」的帮助,搞定了

    坑点:

    • store现在没法用装饰器了,用这个makeAutoObservable
    • 不需要全局provider
    • Taro官网和例子可以说是史上最坑,千万别被骗了,地址:https://taro-docs.jd.com/taro/docs/mobx/
    • 请用最新版的mobx和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
    

    参考资料

    JSON反序列化class

    使用这个库: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)
    

    __EOF__

  • 本文作者: 程序设计实验室
  • 本文链接: https://www.cnblogs.com/deali/p/16145924.html
  • 关于博主: 公众号:程序设计实验室,欢迎交流~
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    医院院长修电脑记
    LeetCode-组合总和 III(C++)
    【学习日志】2022.11.10 C++11常量表达式、用户定义字面量、原生字符串字面值、类的改进、Leetcode疼迅优选50提T1
    RabbitMQ之死信队列解读
    十二、流程控制-循环
    基于Fomantic UI Web构建 个人导航站点网站源码 网站技术导航源码
    C++项目:【负载均衡式在线OJ】
    【01】区块链技术概述
    谈谈数字化转型的几个关键问题
    【设计模式】观察者模式
  • 原文地址:https://www.cnblogs.com/deali/p/16145924.html