豪翔天下

Change My World by Program

0%

“懂得很多道理,却仍然过不好这一生”,这是我唯一一次独自去看的电影《平凡之路》的经典台词,这句话从那时起就一直激励着我去思考: 为什么很多人懂得很多道理,却仍然过不好这一生呢。当然,这句话本身也算是一个道理吧。

我们这一辈子,直接听来的道理最多的肯定是来自父母。从小到大,他们孜孜不倦地给我们讲着那么多道理。小时候觉得他们很聪明、很伟大,懂得好多好多,日子也过得很不错;现在我依然觉得他们很伟大,但是,并不聪明,他们所说的那些道理,在劝慰别人的时候,我也可以信手拈来,毕竟我也是一个“饱读诗书”的大学生。爸每次喝醉酒都会和我聊很多很多,我喜欢听他聊国家大事,喜欢他聊过去的事情,但是最不喜欢他聊那些大道理。在他心里,自己一直是一个失败者,这一辈子没挣到什么大钱,二十几岁结婚开始就一直贷款过日子,三十几岁才把家里房子修好,四十几岁才买车,无论是吃饭还是喝酒都比不过自己的朋友。随着年龄的增长,有了自己的价值观,我和他也总是在很多问题上争吵。不过,即使是我现在独自生活了,在我眼里也还是有好多好多的问题,只有他才是我最好的导师。比如前几天,在买车的这个问题上,我肯定是要先问问他这位三十几年的老司机啦。我执意要买一辆合资的二手车,他却说二手车完全不值那个钱,还说了一大堆的道理。当然和以前一样,每次我以理科生的思维跟他讲道理就会跟他吵起来,因为他总会平白无故地说我什么都不懂。我问他他到底知道多少买这种车被坑的,能不能举例说说各种可能的情况,当然,我只得到一阵谩骂,每次一他这么骂我,我就知道他其实并不懂了。他们这一代人,喜欢用经验从事,并且喜欢用理所当然的经验办事。和他吵并不是因为叛逆,只是想,难道,所谓的人生经验不应该是亲身经历过才会有的吗,“连世界都没观过,哪儿来的世界观”呢。我没经历过,我最亲近的人你也没经历过,为什么你就觉得这些道理是对的呢?

道理这些东西,谁都懂,但并不是谁都能去做,去做的也并不是谁都能做到,很多人只当它们是废话,但是却总是爱拿这些废话去教育别人。这就是人性的弱点。知道得很多,但是知而不行,知行不一,不也相当于完全不懂吗。用现在的说法,就是懒,执行力差,看到一个道理,等几天就忘了;有一个梦想,却一直拖到忘记。真实的世界,没过好这一生的人才会有很多道理。王健林的道理很简单,先有一个小目标;马云的道理很简单,想做就做,永不放弃;比尔盖茨的道理很简单,有梦想,就用一辈子去实现。但是,每个胖子都有很多减肥方法,每个体弱多病的人都有很多方法健身,每个loser都有一万种成功的方法。我自己的人生我还是想要自己去走,更多的道理我也想走过才能理解得深刻,请放心!

很多次,我都不知道该怎么写这么一篇文章,到底该怎么写才不至于把自己搞成一个不懂事儿的孩子。其实,上大学以后就开始怀念以前老爸老妈吵我的样子,现在我们几乎不吵架、很和谐,但是再也回不去了。偶尔和爸妈吵一下,其实蛮开心的,虽然可能会让他们血压升高,但是他们平淡的后半生,没准也想回味回味从前骂我的样子呢。

相比于强大的wireshark来说,Charles的功能算是十分局限了,因为Charles只能用于http/https的抓包。但是术业有专攻,Charles的体验比wiresharek好了好多倍。之所以最近要用到Charles,是因为wireshark的https抓包比较鸡肋,设置复杂,不同的电脑可能抓取不到想要的结果。所以我选择了Charles,当然是试用版啦,能试用30天,试用期以后能正常使用,不过每次试用不超过半小时,超过后不保存就退出了,有点儿恶心。WireShark的原理是监听网卡,而Charles的原理则是非常简单的设置代理。其主要的特点有:

  • 抓取https和http,外观展示类似postman,十分详细并且结构化
  • 能改变请求的内容
  • 能改变响应的内容
  • 能模拟弱网环境
  • 能做压力测试

Charles的基本设置

  1. 点击菜单Proxy -> Proxy Setting,设置Http代理
    Http代理设置
  2. 系统代理设置
  3. 安装Charles的证书: Help -> SSL Proxying -> Install Charles Root Certificate,安装证书,并完全信任该证书
    Mac 钥匙串管理
  4. 虽然设置了ssl证书,但是默认并没有对每个请求开启https的抓取,还需要针对单独的请求进行选择,在请求上面右键选择SSL Proxy: Enabled, 注意这里我不知道为什么,我在mac上右键的时候必须把键盘按下去才行

Charles抓取移动端设备iPhone过程

  1. Mac上开启Wifi热点,或者连接到统一局域网

  2. 手机连接Mac的Wifi,在wifi详情的最下面设置http代理,代理地址即使路由地址,也即mac的地址

  3. 选择Help -> SSL Proxying -> Instanll Charles Root Certificate on a Mobile Device or Remote Browse。在移动端上面安装,下载后需要再设置APP里面搜索描述文件就能找到已下载的描述文件了然后点击安装即可

  4. 这样和mac端一样进行抓取。抓取示例:

TroubleShooting
  • Charles的网络出现不可描述的问题: 关闭系统的其他代理,例如vpn和ss。
  • 想要解开所有的HTTP/HTTPS请求?不可能的。:移动端不像web端,能够看到所有的源码。如果对post的data进行加密,即使是HTTP也不能解开。当然移动端也有apk包反编译的工具,但并不是每次都能成功,所以和web端爬虫更相似的一种万能方法是模拟真机操作。我的想法是使用安卓模拟器,然后在上面进行点击操作,这一点我正在试验。
相关文章

Charles 从入门到精通

推荐一个动漫网站JA日本动漫交流平台,没有这个网站,我也不会想着去找这个好用的工具(看动漫看电影还是支持正版,不过由于某些众所周知的原因,我们有时候没有选择)。该网站是通过google drive进行传播,你可以将文件保存到自己的google drive里面去,然后使用rclone工具进行下载,在没有使用代理的情况下,速度也还是客观的,各方面压制垃圾百度云。百度云上几百G的文件我同步下来花了两周,还是用的aria2,不用aria2的话,基本没有速度。

rclone支持Google Drive, Amazon Drive, S3, Dropbox, Backblaze B2, One Drive, Swift, Hubic, Cloudfiles, Google Cloud Storage, Yandex Files,不仅支持主流的几个云盘,而且支持几个主流的对象存储平台。软件本身支持跨平台的安装使用。

rclone安装

直接参考官方文档,包括了Linux,MacOS以及源码安装方式

rclone配置

通过rclone config命令可以进行交互式的配置,参见官方文档

代理设置

只需要设置环境变量HTTP_PROXY或者HTTPS_PROXY或者NO_PROXY即可,例如export HTTP_PROXY=127.0.0.1:8118

rclone使用

假设上一步添加配置时候取的名字叫googlegoogle drive里面有目录叫test

1
2
3
4
5
6
7
8
9
10
# 通用参数
-P 打印进度

rclone ls google:test # 列出远程目录下的所有文件,斜杠/表示列出所有文件
rclone lsd google:/ # 列出远端所有文件夹
rclone copy -P google:Google相册 相册 将远程目录下的所有文件同步到本地
rclone size google:Google相册
rclone sync -v google:test ./ # 将远程目录下的所有文件同步到本地
rclone mount google:test /path/本地目录 # 将远程目录直接挂载到本地,这一步就和很多的同步盘差不多了。我有个猜想是这个能拿来备份timemachine。远程的文件会显示在挂载的目录里面,但是不会下载,只有使用的时候才会下载,使用起来方便,并且节约本地资源。需要注意的是,这还只是个实验功能,可能会不稳定。我使用起来唯一的不爽是不能选择下载下来。
rcloen copy google: google1 # 直接两个网盘之间对拷文件,并且不会经过本地,真的是太方便了。

万万没想到,数据在这个世界中的地位从很早很早以前就那么重要了。作者所说的“用理工科的思维理解世界”,主要就是在讲数据。真实世界中有很多的规律或者是定律,在人类发现他们的时候,其实早就存在,只是科学家们用大量的数据来总结与归纳出来的。

说到弱联系,这也是第一次在书中见到的一个概念,不过和以前的二度人脉,三度人脉相似。但是以前只知道这种关系在社会上办事情可能会对自己有很多的帮助,从没想过对自己的提高也会有那么大的影响。这就像微博和微信的区别,在微信里面基本都是认识的朋友,人生观价值观以及所处的环境都比较接近,然而弱联系给我们的帮助则更大,认识不同的人,接触不同的社交圈子,向不同的人学习,经常修正自己的观点,弱联系比强联系更有用。我发现我总是迈不出去那一步,只和自己熟悉的人联系,和不熟悉的人实在不知道怎么开始,如果我能在与人交流上刻意练习一万小时呢。

另外,本书是我今年第三次看的有提到“一万小时天才理论”的书籍,并且同样地,提出了一个观点,一万小时并不是瞎忙乎一万小时,而是真的刻意地去练习才能有所收获。如果不是可以联系,写百万行代码也拯救不了一个砌砖的程序员。

语录

人做判断的时候有两种机制:一种是“科学家机制”,先有证据再下结论;一种是“律师机制”,先有了结论再去找证据。

给观众想要的东西,比给观众事实更能赚钱。观众想要什么呢?娱乐和确认。观众需要你的新闻能用娱乐的方式确认他们已有的观念。

要主动刻意地消费,吸收有可能修正我们观念的新信息,而不是吸收对我们现有观念的肯定(Consume deliberately. Take in information over affirmation.)。

问题的关键是随机分布不等于均匀分布。人们往往认为,如果是随机的,那就应该是均匀的,殊不知这一点仅在样本总数非常大的时候才有效。

你可以在所有的时间里欺骗一部分人,也可以在一段时间内欺骗所有的人,但你不可能在所有的时间欺骗所有的人。”

凡事都有目的,是普通人思维区别于科学思维的根本之一。

你要敢于做一些社会上通常认为不应该做的事。你不是去适应这个社会,而是让这个社会去适应你。他们追求取胜,他们根本不追求别人的喜欢。

真正提升我们水平的不是文化,不是艺术,不是哲学,不是制度,不是自虐,而是刻意练习。

真正的练习不是为了完成运动量,练习的精髓是要持续地做自己做不好的事。

心理学家把人的知识和技能分为层层嵌套的三个圆形区域:最内一层是“舒适区”,是我们已经熟练掌握的各种技能;最外一层是“恐慌区”,是我们暂时无法学会的技能,二者中间则是“学习区”。

在舒适区做事,叫生活;在学习区做事,才叫练习。

如果你的一切思维都围绕着“这么做是对的”进行,就不会再去从别的角度看这个项目。

所以,一个人爱好什么,喜欢干什么,能死心塌地地在什么方向上刻意练习,基本上是天生的。

找不到,未必是这个人不行,更大的可能性是整个环境都不行。

想象力和知识是天敌.人在获得知识的过程中,想象力会消失。因为知识符合逻辑,而想象力无章可循。

从读书的角度看,世界上有两种人。 一种人读书是为了掌握技能,通过各类考试,或者纯粹是为了娱乐。另一种人读书却是为了提升自己的内力。这两种人最初的“智力”水平未必有多大差别,但是假以时日,他们的“智慧”水平将会有天壤之别。只有后一种人,才配被称为“读书人”。

谚语说,If it works, don’t fix it! 只有落后者,光脚不怕穿鞋的,反而可以冒这个险。冒险至少还有赢的机会,不冒险就输定了。 这就是为什么本文不说创新是落后者的“权利”,而说,创新是落后者的“特权”

人生面临着一个风险悖论。如果你一辈子谨慎小心,干什么事情都谋定而后动,你的生活再差也差不到哪去;而如果你勇于承担风险、大胆尝试,你可能会特别失败,但也可能特别成功。那么平均而言,我们到底应该更冒险一点好,还是更谨慎一点好呢? 根据2011年发表在《自然》上的一篇论文[4],答案是冒险更好。生活中有自信和不自信的人,还有一种过度自信的人,他们过高估计了自己的能力,尝试去干一些比他们水平高的人都不敢干的事情,而这种人却往往能够侥幸成功。而且平均而言,他们比能正确评估自己能力的人更成功。

结果发现越是成功的企业家,其无名指相对食指就越长,那些最成功企业家的无名指要比食指长10%,甚至20%!

所以,决定一个人喜不喜欢竞争的重要因素之一,是睾酮水平。

研究者认为生理因素大约只能解释40%到60%的竞争力,后天教育和文化传统仍然有作用。

哪怕这个问题是全新的,在大多数情况下我们也能用旧的知识解决它。掌握科学知识的人有凭借理论推导就能破解世界的力量。

我非常钦佩研究者们做这个研究的勇气。他们既没有受到“主流科学”的影响,也没有受到宗教的影响,他们既不相信有神论也不相信无神论,他们只看证据。

所以这个研究的一个重大意义就是告诉人们:哪怕你关心的是“灵魂转世”这样的问题,你唯一正确的判断办法仍然是科学方法。

科学家不从绝学出发,而选择从证据出发的根本原因不仅仅是科学尚未达到找到绝学的程度(物理学家仍未找到统一理论),更是因为就算有绝学也无法解决所有问题。

科学放弃了从一套最基本的哲学出发推导所有结论的尝试,改为在每一个领域内就事论事地搜集事实。

好的科学除了能证明因果关系之外,还必须有一个机制,得能解释为什么会有这种现象。

但探索未知最基本的科学方法是证据,然后谋求建立因果关系,然后是提出机制。

谨以此文标题纪念王小波。他曾经在这个标题下讲述过类似的道理。可惜大多数人只记住了他文章的结论和价值取向,而没有学会他使用的方法。

你不能说“我看到这个现象,而你们解释的不对,所以它一定是个新东西”。全世界的实验室中可能每天都会产生一些看上去不太对的实验结果,它们中的大多数是……不对的。

科学是成年人玩的东西。我认为抱着谦卑的情绪去“仰望科学”是个错误的态度,正确的视角应该像下棋一样,是俯视。

以前年少无知的我总觉得软件架构是非常虚的东西,随着工作经验的逐渐增加,慢慢地意识到,一个没有经过合理设计的软件,完全是在给自己埋坑。当我们开始觉得软件迭代困难,埋怨bug越来越多的时候,多想想为什么,有可能就是我们没有采用合理的软件架构模式。在阅读完《软件架构模式》以后,特整理书摘在此。

软件架构模式

分层架构

概念
  1. 基本上算是我们最常见的架构了,例如MVC等。
  2. 建议在Model之上增加DAO或者MANAGER层,统一数据库读写
缺点
  1. 容易掉入污水池反模式(architecture sinkhole anti-pattern),请求流只是简单地穿过层次,中间不做任何处理。
  2. 数据必须经过每一层传递才能最终得到结果,性能不高,难以扩展。

事件驱动架构

概念
  1. 该模式是一种主流的异步分发事件架构模式,常用于设计高度可拓展的应用。异步性能非常高,解耦程度高,伸缩性抢。
  2. 包含两种拓扑结构:
    中介(mediator)拓扑结构: 通常在需要在事件内使用一个核心中介分配、协调多个步骤间的关系、执行顺序时使用
    代理(brorker)拓扑结构: 在想要不通过一个核心中介将多个事件串联在一起时使用。思想就是将对事件流的处理转换为对事件链的业务功能处理。类似于常用的消息中间件的拓扑结构
  3. github的webhooks也算是一种事件的触发
缺点
  1. 远程操作功能的可用性
  2. 缺少权限控制
  3. 代理或中介处理事件失败时的错误处理和重试逻辑
  4. 事务的粒度划分难抉择

微内核架构

概念
  1. 一个微内核,提供良好的插件模式。例如常用的IDE就是一个微内核,其上面可以搭配各种插件,这种模式,常用语软件而不是web。灵活性高,也易于部署。
  2. 能够被嵌入或者作为另一种架构的一部分
缺点
  1. 伸缩性不高而且不易于开发,内核必须做到足够的强大。

微服务架构

概念
  1. 是一个分布式的架构,意味着架构中的所有组件之间是完全解耦的,并通过某种远程访问协议(JMS/AMQP,REST,SOAP,RMI等)进行访问。
  2. 三种拓扑结构:
    基于REST API的拓扑结构: 适用于网站通过某些API对外提供小型的、自包含的服务。
    基于REST的应用拓扑结构: 与REST API不同,它通过传统的基于web或胖客户端业务应用来接收客户端请求,而不是通过一个简单的API层。
    集中式消息拓扑结构:使用一个轻量级的集中式消息处理(ActiveMQ/HornetQ等)
  3. 其实就是相当于把以前的一整个大应用,分割成独立的模块,使得维护性、可用性和分布式性增强。
缺点
  1. 服务间通信,可能导致组件之间产生耦合,但可以通过共享数据库解决。例如,若一个服务组件处理网络订单而需要用户信息时,它可以去数据库检索必要的数据,而不是调用客户服务组件的功能。
  2. 为了保持服务组件独立和部署分离,为服务架构模式实现中会存在一小部分由重复的业务逻辑而造成的冗余。
  3. 和分层模式差不多,依然可能存在调用多个微服务引发的性能问题。

基于空间的架构

概念
  1. 对于用户数量不可预测且数量级经常变化的情况同样适用,在架构级别来解决这个伸缩性问题通常是比增加服务器数量或者提高缓存技术更好的解决办法。
  2. 旨在减少限制应用伸缩的因素。高伸缩性是通过去除中心数据库的限制,并使用从内存中复制的数据框架来获得的。保存在内存的应用数据被复制给所有运行的进程。进程可以动态的随着用户数量增减而启动或结束,以此来解决伸缩性问题。这样因为没有了中心数据库,数据库瓶颈就此解决,此后可以近乎无限制的扩展了。
  3. 根据我的理解,就相当于去除中间服务器,可以将关键数据放在redis,然后复制多份。
缺点
  1. 测试性低而且开发困难
扩展阅读

如何少写PHP”烂”代码: 更好的MVC分层实践

你的项目应该如何正确分层

应该和很多人一样,拿起这本书的时候就想看看这本魔法书里到底有什么黑魔法,会不会有很多奇淫技巧,很多强大的收纳工具。当然,最后证明,这里面什么都没有。它反而像一本心灵治愈类的书籍,从精神的高度来教我们整理房间、整理人生。当然,最后的最后,依然没有治疗我的拖延症,不过倒是让我对自己所拥有的物品们产生了一种敬畏感。

去年年底买了人生的第一套房,住进来已经一个多月了。刚帮进来的时候,父母帮我收拾了一大部分的东西。基本上只有客厅的一些不知道怎么归类的杂物没有整理以外,其他的东西都已经各有所处了。秉着每天收拾一点点的原则,一个多月以来,终于使得每个房间都如最开始的时候那样杂乱了。衣服分散在衣柜、床上、沙发,洗衣机上面;电视柜下面交换机、无线路由器、电视盒子、nas主机,各种线缆杂乱纷争;原本不知道怎么分类的杂物也随着不停的网购变得越来越多。就这样的状态,我都已经不害臊地请了好几次朋友们来我新家里做客了。

看了此书,最大的感悟就是终于明白了那么一句话:一屋不扫,何以扫天下。不是什么特别大的道理,也不是叫你要干出一番大事业,只是,当你不知道如何整理自己最亲近的这些东西的时候,可能你还缺少了一份对生活应有的积极态度。当你能学着扔掉大多数你买错了并且你心里也清楚确实买错了的东西的时候,就不会被太多的欲望占据了你的生活,你的生活本来就应该简简单单的。我觉得在今后的生活中,至少我还需要作如下的改变:

  • 我知道自己总是舍不得丢掉以前那些能带给我回忆但是却真心没用的东西,所以,我打算还是扔掉他们,但是在每次扔掉之前,拍一张照片,保留一份电子的照片和打印出来的照片就可以了。
  • 尽量一次整理完,而不是每次只整理一点点,每次只整理一点点永远整理不完。
  • 少依赖收纳神器,家里的收纳已经够用了。
语录

如果一个人不是发自内心地想要做一件事,那么,他是无法改变自己的人生的

其实每样物品各有各的作用,并非所有衣服都是因为要被完全穿坏才来到你的身边

厨房好用与否,关键不在于收纳,而是清理的容易成粗。

我们的目标是争取在理想的房间里过上理想的生活,而穿着没有新动感的家居服待在房间里实在太可惜了。

所谓整理,就是整理每一件物品的“过去”。

不管多么凌乱的房间,整理都只是一种物理性的作业。东西毕竟不是无穷无尽的,只要能留下让自己心动的东西,给它们固定位置,整理工作就一定会结束。

真正的必需品肯定是能让自己幸福的东西,所以,应该积极地把必需品当做“心动物品”来对待。

这个忙碌的五月,继续拜读了王小波的书,《一只特立独行的猪》。先说一下看书的心境。这个月完成了人生的一件大事,搬进了新家,自己的房子,自己的家。从此以后,在城市里有了自己的一个方格子,有了自己的一片小天地了,开始了自己梦想中的生活。每天七点起床,用kindle看半个多小时的书,然后起床洗漱,再然后做早餐,然后收拾一下,出门上班。生活中有些事情每天我们都得重复,但是谁又能说就一定很无聊呢。每天看不同类型的书,每天做不同口味的菜,每天有没有一天的惊喜和意外,多么有趣的生活。

王小波是一个有趣的人,“活在世上,无非想要明白些道理,遇见些有趣的事”,王小波人如此,做事如此,写书也如此。从小到大,看够了语文课本上那群大师的严肃文学作品,现在开始迷上王小波这种类型的作品了。当然,绝不仅仅是有趣,我们和他生活的时代完全不同,要谈感同身受的有趣,那肯定是不对的,有趣的是,他的作品确实能让我们感到有趣。没有对现实的阿谀奉承,没有随大流,而是真正的以一个平凡人的角度,看待问题,看待这个世界,我觉得这是文学永恒不变的主题,源于生活,高于生活。

有时候我想问问王小波,比尔盖茨的紧身衣现在还没发明出来,但肯定在研发中,知道AlphaGo吗,你对人工智能怎么看?不过今天的文学可能和你那个时候相比变样了,网络文学的兴起,传统文学的没落,当然也出想过许多红极一时的书,但是已经很久没有出现过那种能够流传千古的著作了,你要不再来挥墨作文?虽然不是每个人都读过你的书,但相比于以前,现在这个年代可能是离你梦想最近的时代。人人可以追求自由,人人都是特立独行,人人都可以无视生活的设置,肆意地追逐心中所想。你说,这是文明的进步还是退化呢?

至于我的生活能不能一直有趣下去呢?还是自己来书写答案吧!

语录:

井底之蛙也拥有一片天空

只按名声来理解文学,就会不知道什么是坏,什么是好。

同性恋研究给我们以这样的启示:倘若生活中存在着完全不能解释的事,那很可能是因为有我们所不知道的事实,而不知道的原因却是我们并不真正想知道。比如我们以前不知道同性恋的存在,是因为我们是异性恋;我们不知道农民为什么非生很多孩子不可,是因为我们是城里人。

这世界上有很多书都是这样的:内容无可挑剔,只是很没有意思。

假如你想听听电脑,我可以说,现在在中关村花二百五十块钱可以买到八兆内存条,便宜死了……

我原是学理科的,学理科的不承认有牢不可破的囚笼,更不信有摆不脱的噩梦;人生唯一的不幸就是自己的无能。

我总觉的文学的使命就是制止整个社会变得无趣……

人们知道得越多,明辨是非就越困难

学生是穷人中最趾高气扬的一种:虽然穷,但前程远大。

我总觉得,大多数人在受到重视之后,行为就会好。

我时常回到童年,用一片童心来思考问题,很多烦恼的问题就变得易解。

基础概念

**特别注意: **

  • React-Native是基于React实现的,更多语法可以参考React 开发手册
  • 如果是自己开发新产品,那么希望每次都把各个基础组件升级到最新稳定版。

React Native开发的优点

  • 拥有系统级别的通知或提醒
  • 可以访问本地通讯录、相册等资源
  • 可以针对不同的平台提供不同的体验

React Native采用的是ES2015(即ES6)的语法标准,模板上使用了自己的JSX语法(在代码中嵌入结构标记)。

环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 初始化项目
npm uninstall -g react-native-cli # 官方说不要用这个来初始化了,并且得卸载了,否则可能出现奇怪的问题
npx react-native init testProject --verbose # 新建项目目录,并初始化项目。命令会执行很久,且--verbose像没用似的,像卡死了一样
npx react-native init testProject --version 0.68.2 --verbose # 创建指定版本的项目
npx react-native init testProject --template react-native-template-typescript --verbose # 创建一个typescript的项目
npx react-native init testProject --template "react-native-template-typescript@6.10.*" --verbose # 创建一个typescript的项目,指定版本

## 运行项目
cd testProject
npx react-native start
npx pod-install
npx react-native run-ios # 第一次启动会很慢。等模拟器运行起来后可以直接Cmd+R刷新应用,Cmd+D打开调试菜单
npx react-native run-ios --simulator='iPhone 13 Pro Max' # 指定云行的模拟器的名称
npx react-native run-android # 安卓开发最好安装上android studio,这不仅会帮你安装java、jdk,而且还能直接管理安卓模拟器,把android studio配置好了以后,android的开发环境也好了

npm install --save react-native@X.Y # 直接指定版本号的更新升级,手动升级更爽。我不喜欢用react-native-git-upgrade来升级,需要注意的是,升级以后一定要顺便升级一下命令行工具react-native-cli,否则会可能会出现不预期的错误

prop&&state

两者可以说是大同小异,在大多数情况下,两者没什么很大的差别。两者的改变的时候,渲染的地方都会重新渲染。

prop: 一个组件的设置参数,可以理解为初始化参数或者对象的静态变量,并且可以在父组件中设置,在子组件中不可改变,但是可以一直往下传递至子子孙孙。

state: 更像是对象的一些变量,并且确实是经常改变的,只是父子之间不能传递。

1
2
3
4
// 动态设置某个状态值
this.setState({
results: value,
});

布局

不用css,但是类似css。所有的组件都有style属性。样式名是将默认的css的命名更改为了驼峰命名。一般使用StyleSheet.create在组件外面集中定义组件的样式。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 指定固定的高度和宽度用width和height,React Native中的尺寸都是无单位的。
<View style={{width: 50, height: '50%', backgroundColor: 'powderblue'}} />

// 弹性的高度和宽度用flex。flex为1的时候表示撑满所有的剩余空间,如果多个并列子组件一起使用,则他们会平分空间,并且值越大所占比例就越大。例如
<View style={{flex: 2, backgroundColor: 'skyblue'}} />

<View style={[styles.css1, styles.css2]} /> // 包含多个样式

// 这样还能直接看出来层级关系。例如<Text style={styles.red}>test</Text>
const styles = StyleSheet.create({
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});

Flexbox布局

规定某个组件的子元素的布局。flex的值就类似于栅栏布局中的row宽度,一个2一个1,那么画面总共可以分成三份这种,如果直接flex:1,那么就表示直接占据整个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 父视图属性
<View style={{
flex: 1,
flexDirection: 'row', // 规定布局方向,默认是column垂直方向布局,row表示水平方向布局
flexWrap:'wrap', // 默认为nowrap,表示子元素是否允许多行排列
justifyContent: 'flex-start', // 规定子元素沿着主轴的排列方式。可选项有flex-start、center、flex-end、space-around以及space-between
alignItems: 'stretch', //规定子元素沿着次轴(与主元素垂直的轴)的排列方式。可选项有flex-start、center、flex-end、stretch
}}>

// 子视图属性
<View style={{
alignSelf: 'auto', // 定义了flex容器内被选中项目的对齐方式可选auto, flex-start, flex-end, center, stretch
}}>

// flexGrow与flex有些类似,但是flex会使子元素的空间大小限定在父元素空间范围内,而flexGrow会使子元素起码维持其本身大小,再根据父元素是否有剩余空间进行空间分配。

居中问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 图标悬浮与图片的正中间,两者均居中对齐
<View style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<Icon name="microphone" size={70} style={{
position: 'absolute',
zIndex: 1,
justifyContent: 'center',
alignItems: 'center'
}}/>
<Image source={require('../img/test.png')} style={{
width: 250,
height: 250,
alignItems: 'center',
justifyContent:'center',
}}
/>
</View>

定位问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 获取屏幕尺寸
import Dimensions from 'Dimensions';
Dimensions.get('window');

// 获取元素的位置: https://stackoverflow.com/questions/30096038/react-native-getting-the-position-of-an-element
class MyComponent extends React.Component {
render() {
return <View ref={view => { this.myComponent = view; }} />
}
componentDidMount() {
// Print component dimensions to console
this.myComponent.measure( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
}
}

组件

Animated动画

第三方库里面那些酷炫的效果均是通过动画来实现的

1
2
3
4
5
6
7
8
9
10
11
12
const top = useRef(new Animated.Value(100)).current;	// 将一个属性变为可以执行动画的属性

<Animated.View>
<View style={{top}}></View>
</Animated.View>

top.setValue(1000); // 当改变值的时候用setValue来执行,就能让改变变得平滑
Animated.timing(top, { // 也可以自定义执行时间
toValue: 1000,
duration: 500, // 默认500
delay: 100, // 默认为0
}).start()

Button基础按钮

这个组件的样式是固定的,如果需要自定义,那么高级的按钮参考Touchable系列

1
2
3
4
<Button
onPress={() => this._func()}
title="按钮标题必填"
/>

Image

图片组件,如果我们在同一个目录里面同时包含a.png/a@2x.png,a@3x.png那么react native就能通过屏幕的分辨率自动选择不同尺寸的图片,并且在代码里面仅需要require(./img/check.png)就行了。

Navigation文档,Navigation已经单独成为一个模块,强烈建议不再使用老的导航器,导航器对比,在这里有其更详细的文档。在0.44版本移除了Navigator,该模块被移动到react-native-custom-components现在也仅用于兼容老版本。使用前得先安装npm install --save react-navigation。有如下三种类型的导航器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 看官网的意思就是要安装这些东西
npm install --save @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack

# ios需要执行
npx pod-install ios

# android需要再MainActivity中添加一个方法
import android.os.Bundle;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}

# 然后需要全局使用NavigationContainer包裹app,在app.js中
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
return (
<NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
);
}

StackNavigator

类似于普通的Navigator,体现在屏幕上方的导航栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// StackNavigator用于创建多页面应用。其中每一个都是一个Component
import React from 'react';
import { View, Text } from 'react-native';
import { StackNavigator } from 'react-navigation';

class HomeScreen extends React.Component {
static navigationOptions = ({navigation}) => {return ()}; // 可以使用return的方法,这样可以在上面写一些逻辑
static navigationOptions = ({navigation}) => ({
title: '头部标题',
headerStyle: {
backgroundColor: '#ffffff', // 设置头部样式
},
headerTintColor: '#fff',
headerTitleStyle: { // 设置头部字体样式
fontWeight: 'bold'
},
headerRight: ( // 设置header bar的左右按钮
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
// header头中直接进行页面跳转
headerRight: (<Button onPress={() => navigation.navigate('Setting')} title={'设置'} />),

});

componentWillMount() {} // render之前执行,并且永远只执行一次
render() {} // 渲染页面
componentDidMount() {} // 组件加载完成后执行,在render之后,已经有了DOM结构,不要把其他逻辑写在render,以防阻塞UI
componentWillReceiveProps() {} // 组件接收到一个新的prop时执行,这个方法在初始化render时不会被调用
shouldComponentUpdate() {} // 返回一个布尔值
componentWillUpdate() {} // 在组件接收到新的props或者state但还没有render时执行,初始化时不会执行
componentDidUpdate() {} // 组件更新完成后

render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
}

// 可以在App.js中声明所有的页面,默认放在第一个的为首页
export default StackNavigator({
Home: {
screen: HomeScreen,
},
});

// 组件之间跳转方式
this.props.navigation.push('Home'); // 跳转至新的场景,并且将场景入栈
this.props.navigation.navigate('Home', {param1: '...'}) // 将新路由推送到堆栈导航器,如果它不在堆栈中,那么跳转到该页面
this.props.navigation.goBack()

TabNavigator

类似于ios的TabBarController,屏幕下方的标签栏

DrawerNavigator

侧边弹出的抽屉效果

SafeAreaView

  • 使用该组件包裹可以自动实现异形屏的padding,也不用考虑android还是iOS
1
2
import { SafeAreaView } from 'react-native'	// 如果不工作,就使用下面的方式
import { SafeAreaView } from 'react-native-safe-area-context'

ScrollView滚动

可以在该组件下面添加任意组件,能轻松实现几个组件的共同滑动

1
2
3
4
5
6
7
8
9
const scrollViewRef = useRef<ScrollView>(null);

<ScrollView
ref={scrollViewRef}
scrollEnabled={false} // 禁用滚动
></ScrollView>

scrollViewRef.scrollToEnd() // 滑动到底部
scrollViewRef.scrollTo({x: 0, y: 100, animated: true}) // 滑动到指定位置

StatusBar状态栏

Text

  • 默认情况下,系统字体的大小会直接影响到APP里面的显示,我们需要防止这种情况,防止用户把字体调得太大,可以在app.tsx中全局设置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import {Text, TextInput} from 'react-native';

    if (Text.defaultProps == null) {
    Text.defaultProps = {};
    Text.defaultProps.allowFontScaling = false;
    }
    if (TextInput.defaultProps == null) {
    TextInput.defaultProps = {};
    TextInput.defaultProps.allowFontScaling = false;
    }
1
2
3
4
<Text 
numberOfLines={2} // 最多显示几行,多的会被隐藏
ellipsizeMode={'tail'} // 多的显示省略号
/>

TextInput输入框

TextInput默认宽度与父节点相同。如果想要其在没有文字的时候也能占据宽度,可以设置flex:1并且父View也得设置flex:1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<TextInput
style={{
height: 40,
alignSelf: 'center', // 输入框文字居中
alignItem: 'center',
textAlign: 'center', // 这个才是输入框里面的文字居中
}}
autoCapitalize="none" // 禁用自动大写
textContentType="oneTimeCode" // 禁用自动填充,不显示键盘上面的password选项
returnKeyType="next" // 定义keyboard键盘右下角的字体或样式,可选next、done、go、join、search、send等
onSubmitEditing={() => {
nextInputRef.current.focus(); // 如果上面的是next,并不会自动跳转,而是需要使用下一个input的ref来进行focus操作
Keyboard.dismiss(); // 隐藏键盘
}}
onChangeText={(text) => this.setState({text})}
clearTextOnFocus={true}
keyboardType="numeric" // 仅允许数字
placeholder='请输入' // 默认是灰色的
value={this.state.text}
/>

Touchable*系列

  • hitSlop属性可以让可以点击的区域比实际的要大,非常适合在移动端的点击操作

  • 包括了触摸的相关事件(触摸、点击、长按、反馈等):

    • onPressIn: 触摸开始

    • onPressOut: 触摸离开

    • onPress: 单击事件

    • onLongPress: 长按事件

TouchableHighlight

触摸点击高亮效果。点击的时候,不透明度会降低,同时会看到变暗或者变量。只支持一个子节点,如果要多个子视图组件,可以用View进行包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
<TouchableHighlight onPress={this._onPressButton.bind(this)} underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableHighlight</Text>
</View>
</TouchableHighlight>

// 可点击的图片
<TouchableHighlight onPress={this._onPressButton}>
<Image
style={styles.button}
source={require('./myButton.png')}
/>
</TouchableHighlight>

TouchableNativeFeedback

仅限android。

TouchableOpacity

透明度变化。

TouchableWithoutFeedback

不带反馈效果的。

API

Share分享功能

1
2
3
4
5
6
import { Share } from 'react-native';
Share.share({ // 官方文档说android用message、ios用url,但经过我的测试最好都用url,因为分享到不同的app,获取的字段并不相同
title: url,
message: url,
url: url,
})

JSX语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 使用循环
<View>
{this.state.voices.map((voice, index) => {
return (
<Text key={`voice-${voice.id}`}>
{voice.text}
</Text>
)
})}
</View>

// 定义模板(自定义标签)
const InfoText = ({ text }) => ( // 其中text是模板的参数
<View style={styles.container}>
<Text style={styles.infoText}>{text}</Text>
</View>
)
<InfoText text="haofly"/> // 使用模板

样式stylesheet

  • 官方建议不要将stylesheet放在render函数中
  • 最好不同的组件使用不同的名称,不要全都用styles命名
  • 原生不支持scss那样的嵌套语法,好像也没有啥好用的嵌套方式,就是感觉原生就是不支持什么复杂的样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const page = StyleSheet.create({
container: {
flex: 1,
padding: 24,
alignItems: "center"
}
})

const typography = StyleSheet.create({
header: {
color: "#61dafb",
fontSize: 30,
marginBottom: 36
}
})

网络请求

React Native使用的网络请求是Fetch API,但是,统治js的http请求库明显是axios,所以我还是喜欢用axios,另外,网络请求天生就应该是异步的,这两个库都是不支持同步的。

1
2
3
// 安装npm install --save axios
import axios from 'axios';
axios.get('...').then((response)=>(console.log(response.data))); // 得到响应结果,不用像fetch那样responseJson了

Debug

  • 如果是真机,可以通过摇一摇弹出debug菜单,但是基本上没啥用,最有用的可能就是Chrome里面调试了,至少能看到打印出来的object的详情

  • LogBoxrelease/production中是自动禁用的

常用插件推荐

Awesome React Native

  • 包含很多的react native的插件扩展

customauth-react-native-sdk

  • torus sdk

  • 如果运行不起来可以试试它项目里面的example,虽然文档少了,但是那个example还是更新的挺及时的,照着看有没有遗漏的,我在1.0.1版本上发现有这些需要额外配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    // ios/Podfile,具体行数参考example中的配置
    use_modular_headers
    pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec', :modular_headers => false
    installer.pods_project.build_configurations.each do |config|
    # config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
    end

    installer.pods_project.targets.each do |target|
    if target.name == "web3.swift"
    target.build_configurations.each do |config|
    config.build_settings["SWIFT_INCLUDE_PATHS[sdk=iphonesimulator*]"] = "$(inherited) $(PODS_CONFIGURATION_BUILD_DIR)/BigInt $(PODS_CONFIGURATION_BUILD_DIR)/GenericJSON $(PODS_TARGET_SRCROOT)/web3swift/lib/**"
    config.build_settings["SWIFT_INCLUDE_PATHS[sdk=iphoneos*]"] = "$(inherited) $(PODS_CONFIGURATION_BUILD_DIR)/BigInt $(PODS_CONFIGURATION_BUILD_DIR)/GenericJSON $(PODS_TARGET_SRCROOT)/web3swift/lib/**"
    end
    end
    end

    // AppDelegate.m,我最开始就是点了登录后没反应,后来发现是它根本没有监听openURL
    - (BOOL)application:(UIApplication *)app
    openURL:(NSURL *)url
    options:(NSDictionary<NSString *, id> *)options {

    NSString *myString = url.absoluteString;

    NSLog(@"String to handle : %@ ", myString);
    if (@available(iOS 10.0, *)) {
    [RNCustomAuthSdk handle:myString];
    } else {
    // Fallback on earlier versions
    }

    // Your additional URL handling (if any) goes here.
    return NO;
    }


    // ios/xxx/Info.plist,添加url scheme
    <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>torusapp</string>
    </array>
    </dict>

react-native-geolocation-service

  • 谷歌定位插件,能够获取当前的定位

  • 如果出现获取不到地理位置,经常提示timed out并且time out设置为很大依然报错,可以参考这个issueLocation request timed out most of the time,下载谷歌地图然后定位一下,再重新安装一下应用试试

  • 如果出现Location settings are not satisfied: 根据我的尝试,可能是因为国内或者说是因为小米手机的问题,ios和android得不同的设置才行:

    1
    2
    3
    4
    5
    Geolocation.getCurrentPosition(
    (position) => {console.log(position)},
    (error) => {console.log(error)},
    Platform.OS === 'ios' ? { enableHighAccuracy: true, timeout: 25000, maximumAge: 20000 } : { enableHighAccuracy: false, maximumAge: 20000, forceRequestLocation: true, forceLocationManager: true, distanceFilter: 250, accuracy: { android: 'balanced', ios: 'threeKilometers' } }
    );

react-native-async-storage

  • 能够用来持久化mobx等的状态,在应用退出后不会清空
  • React-native iOS, Async storage error: "Invalid key - must be at least one character. Key: 出现这个错误是因为在getItem/setItem的时候key的值为空,需要修改一下,注意如果key的值修改后可能需要重新build才能生效

react-native-bottom-sheet

  • 一个比较好用的底部弹出功能,drawer,抽屉
  • snapPoints: 定义弹出的区域的高度,这之外的地方不能点击
  • enablePanDownToClose: 向下滑自动关闭
  • 如果是多个sheet叠加显示,好像DOM后面的就是最上层

react-native-dotenv

react-native-config

  • react-native-configreact-native-dotenv更通用,不用为每个环境变量声明typescript,并且它支持不同的环境使用不同的环境变量

  • 使用.env文件来加载环境变量

  • 需要注意的是,它是有缓存的,如果变量更改了记得参考文档清理cache

  • 如果使用的是typescript,最好参考文档使用Option 2: specify types manually

react-native-drop-shadow

  • 拖动的时候的阴影

react-native-elements

  • element 的UI套件/UI框架

react-native-fs

  • 文件操作,下载文件,保存文件

react-native-iap

  • 用于google play和apple store的内购组件
  • Android平台能够通过getProducts获取产品列表,但是购买的时候却报错That item is unavailable: 具体原因还未知,在github提交了discussion,但目前没有回复。最后不知道怎么就解决了,尝试过这些方法:
    1. 上传一个signed release到internal testing和closed testing,但是第一次上传审核时间有点久,且审核通过后可能也要等几小时才可以
    2. Google Play Console -> Setup -> API access: 打开了Play Android Developer API,应该和这个无关
    3. License testing得添加设备登录的google账号
    4. App -> Setup -> Advanced settings -> App availability设置为Published
    5. App -> Setup -> Advanced settings -> Managed Google Play设置为Turn on下面的留空就行

react-native-paper

  • material-ui在react-native平台的替代品,同样遵循material design,但是最后不推荐,集成的本来就不多,还不大好用
  • 在使用Menu.Item的时候,如果要自定义menu和整个container的高度,需要设置minHeight和maxHeight才行,不知道为啥container会默认设置为100,源码里没看到哪个地方有设置
  • ActivityIndicator就是一个loading图标,非常好用

react-native-picker

  • 滚动时间或者select选择器

react-native-qr-decode-image-camera

  • 至少从图片里面解析二维码只有这个好用点

react-native-share

  • 弹出原生的分享组件,例如分享airdrop,保存到文件夹

react-native-swipeable-list

  • 触摸滚动组件,但是已经几年没维护了,且可以直接用原生的VirtualizedList替代

react-native-text-input-mask

  • 比如电话号码输入的mask模式
  • 如果出现TypeError: null is not an object (evaluating ‘RNTextInputMask’)in v3.0.0,需要添加这行配置到podfile文件: pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text', :modular_headers => true

swiper

  • 轮播图片
  • 非常强大,各种框架几乎都支持

@testing-library/react-native

  • 测试框架

性能优化

最重要的是将需要变化的状态细化到单独的组件,这样状态变化时就不会影响到其他不需要的地方

Touchable系列组件不能很好的响应

  • 通过requestAnimationFrame,可以让组件的透明度改变效果很快切换回来,而不会卡在那儿
1
2
3
4
requestAnimationFrame(() => {
this.doExpensiveAction();
});
}

开发原生相关问题

在真实设备上调试以及打包到真实设备

在真实设备上调试,只需要在XcodeRun到你自己连接的设备即可,这时候安装在手机上面的,是和电脑上面模拟器出来的一模一样,也能进行调试,但是断开usb后应用不能使用。如果要将应用直接整体打包到设备上面,看看真实使用的效果,可以按照这个教程进行设置https://facebook.github.io/react-native/docs/running-on-device.html,主要就是修改AppDelegate.m中的jsCodeLocation的值,将其改变成如下状态即可。

1
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

APP图标设置

参考Xcode中的图标设置,也只能在xcode中设置,即直接将图标拖如Images.xcassets

原生库

开发者会将很多原生库打包成一些静态库,或者由js直接封装好了的静态库。一般比较好的静态库都能够使用命令自动链接:react-native link 某已安装的具体库名,如果手动链接可以参考文档linking-libraries-ios

TroubleShooting

  • “:CFBundleIdentifier” Does Not Exist: 可能是因为你的代码依赖的是老的react native或者node版本或者xcode版本,可以执行以下命令升级依赖:react nativeupgrade

  • undefined is not an object evaluating React.PropTypes.string: 仍然是版本的问题,新版的已经将React.PropTypes移到单独的库了(prop-types)。需要注意的是React.PropTypes.func更改成了PropTypes.function了,其他的名字没有改,只是位置变了。

  • undefined is not an object(evalauating ‘WeChat.registerApp’): 引入react-native-wechat之后手动去link

  • No bundle url present: 启动的时候报错,有以下几种解决方案:

    • 全部关了以后,看看8081端口是否被占用,然后重新react-native run-ios
    • 上面方法多次尝试不行以后直接删除node_modules目录,重新安装依赖
  • isMounted(…) is deprecated warning: 目前来看,并没有什么解决方案。

  • 闪退: 有如下几种情况

  • _this._registerevents is not a function: 升级的时候没有顺便升级react-native-cli

  • cross-env: command not found: npm install cross-env

  • unable to load script from assets index.android.bundle: 这样做能够解决(来自于Stackoverflow):

    1
    2
    3
    4
    5
    6
    mkdir android/app/src/main/assets
    react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
    react-native run-android

    # 可以将上面的命令放到package.json的scripts中去,这样以后直接npm run android-linux即可
    "android-linux": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res && react-native run-android"
  • **SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.**原因是没有定义android sdk的位置,首先下载android sdk或者安装android studio(会自动下载sdk),最后将地址写在local.properties文件或者直接设置为环境变量ANDROID_HOME

  • **com.android.builder.testing.api.DeviceException: No connected devices! **得去android studio把安卓模拟器打开

  • react-native run-android命令提示Android project not found. Maybe run react-native android first,但是执行react-native android却说命令没找到: 首先看当前目录有没有android文件夹,如果没有,那么使用react-native eject命令生成,如果有,那么就用android studio来运行一次,看看是不是有哪些基础环境没有安装

  • Invalid YGDirection ‘row’ should be one of: (inherit, ltr, rtl): 需要将<Flex direction="row"修改为<Flex flexDirection="row"

  • Print: Entry, ":CFBundleIdentifier", Does Not Exist 解决方法如下

    1
    2
    3
    4
    # 首先关闭XCode
    cd node_modules/react-native/third-party/glog-{X}.{X}.{X}/
    ./configure
    # 然后重新打开xcdoe即可
  • Text strings must be rendered within a component: 首先最基本的,文字必须在text组件里面,但这还是比较容易排查,而不好排查的情况一般是我们在做判断的时候没有使用布尔值,例如

    1
    2
    {icon && {icon}} // 这样会报错
    {!!icon && {icon}} // 将对象转换为布尔值即可
  • 输入框键盘挡住了部分视图: 这时候需要使用KeyboardAvoidingView来包装一下view,该组件可以自动根据键盘的高度,调整自身的height或底部的padding来避免遮挡,有时候也需要再配合ScrollView来使用,注意它可以不需要在整个页面外层包装,可以只包裹住form那部分即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { useHeaderHeight } from '@react-navigation/elements'
    const height = useHeaderHeight()

    <KeyboardAvoidingView
    behavior={Platform.OS == "ios" ? "padding" : "height"}
    style={styles.container}
    keyboardVerticalOffset={height + 47} // 如果发现高度差那么一点可以这样设置
    >
    ...
    </KeyboardAvoidingView>

    <!--如果有时候KeyboardAvoingView不起作用,可以尝试https://www.npmjs.com/package/react-native-keyboard-aware-scroll-view,例如用react-native-google-places-autocomplete的时候-->
    <KeyboardAwareScrollView extraScrollHeight={75}>
    <GooglePlacesAutocomplete />
    </KeyboardAwareScrollView>
  • ARCHS[@]: unbound variable in Xcode 12或者YogaKit.modulemap not found: 需要把Build Settings -> Architectures -> Excluded Architecture设置成这样(来自Stackoverflow):

  • You must have a keystore.properties file in the /android/ folder or set the environments variables: Android目录下新建文件keystore.properities,内容如下即可:

    1
    2
    3
    4
    STORE_FILE=app.keystore
    KEY_ALIAS=app_alias
    STORE_PASSWORD=your_password
    KEY_PASSWORD=your_password
  • Android Studio build签名APK的时候报错index.js not found:可能是因为使用了typescript,文件现在是index.tsx,可以在build.gradle文件中指定entryFile:

    1
    2
    3
    4
    project.ext.react = [
    enableHermes: false, // clean and rebuild if changing
    entryFile: "index.tsx" // 指定为tsx文件
    ]
  • Android Studio报错:ERROR: Could not find method compile() for arguments: 可能是依赖的包在调用老的java的api,找到错误日志中的文件,将compile 'xxx'修改为implementation 'xxx'

  • Android Studio真机测试报错: Unable to load script. Make sure you’re either running a Metro server (run ‘react-natvie start’) or that your bundle ‘index.android.bundle’ is packaged correctly for release. 如果metro没打开就start,如果打开了,可以尝试执行这个命令: adb reverse tcp:8081 tcp:8081

  • **Could not resolve project :react-native-camera.**这是个已经没有维护的库了,参考doc,在android/app/build.gradle中添加missingDimensionStrategy 'react-native-camera', 'general'即可

  • xcrun: error: SDK “iphoneos” cannot be located: 尝试执行sudo xcode-select --switch /Applications/Xcode.app

  • Command PhaseScriptExecution failed with a nonzero exit code: 如果无法查看错误详情,可以尝试运行Archive,可能会显示错误详情,可能就是下面这个问题,node路径没有找到

  • React-Native env: node: No such file or directory: 尝试执行sudo ln -s "$(which node)" /usr/local/bin/node

  • **No simulator found with name “iPhone 13”**运行时可以指定模拟器的名称: yarn ios --simulator="iPhone 14"

    扩展阅读
  • 浅谈前端移动开发(Ionic与React Native)

  • 30天React Native学习

splash是一个轻量级的可执行脚本的server,并且提供了友好的API,而且能直接用docker进行部署,使用起来十分方便,对于那种必须通过js才能找到内容的爬虫来说简直是如虎添翼。

安装splash

最简单的方式,使用docker安装

1
2
docker pull scrapinghub/splash
docker run --name splash -p 5023:5023 -p 8050:8050 -p 8051:8051 -d scrapinghub/splash # 即可完成部署

其中,5023表示http8050表示https8051则是telnet

splash的配置

配置文件在/app/splash/defaults.py,如果是使用docker,那么直接修改该文件即可

1
2
# 在docker内部直接修改该文件/app/splash/defaults.py
PLUGINS_ENABLED = False # 是否开启flash的执行,默认是关闭了的

splash的API

splash的API是根据其HTTP的url来定义的,前缀都是docker的域名+ip。

render.html

例如http://127.0.0.1:8050/render.html?timeout=600&wait=10&proxy=socks5://192.168.0.6:1086&url=http://haofly.net

1
2
3
4
5
# 参数说明
url: 要请求的url
timeout: js执行超时时间,默认是30s
wait: 等待js执行完成的时间,默认是0,最大是10
proxy: 设置代理

render.har

获取所有的har数据,即请求该地址的时候该网页所有的请求以及headerbody信息。

好久没有认真的看雨了。

今晚下班回家,雨很大,即使撑着我的淘宝爆款超大雨伞也被打湿了裤腿,2017年第一次有了大雨滂沱的感觉,但是却突然发现对雨,我早已没有当初的那种感觉了。

犹记得那年我们都还很年少,闲庭屋外,邀几个小伙伴对雨而坐,看着公路上的汽车飞驰而过,溅起的水花是那样的干净澄澈。那时候,我们喜欢称这种雨为毛毛雨,因为雨很大很清澈的缘故,雨滴从几万米高空倾斜而下,与地面撞击后又反弹一定高度,大概十厘米高吧,看起来就像很多很多的白毛在地上跳跃。那时候天上是有真正的乌云的。下雨前,乌云密布,随着雨渐渐地减少,乌云也慢慢散去,最后云开雾散,要么天朗气清,要么彩虹悬挂,而绝不会像现在这样,无论雨来还是雨走,天空都是灰蒙蒙的一片,仿佛对地下的人们毫无感情。

小时候,我家后面有另一户人家,而我的窗外正好能看见他们相对较矮的老式瓦片屋檐。每次下雨或者有了小孩子特有心事的时候,就会在晚上关上灯,静静地听。其实,有心事的时候,你是听不进去雨的,因为雨滴声不是时钟那种滴答滴答的声音,而是滴滴滴滴……但是因为雨持续不断地跌落在瓦上,心扉仿佛也一直被雨滴敲打着,使你也无法安静地想自己的事情,只能任由那雨继续敲打我的心扉。就这样,我不开心门,她就不停息。然后,渐渐地进入了儿时的梦想。

可是,雨总会停,我们也终于长大了。

重庆的春天其实很短的,一般都是春夏秋冬随机播放几十天后,便匆匆地迎来了漫长的夏季。可是今年,重庆的雨仿佛特别多,还赖着不走,我也才有了这番心境。现在的心是静不下来的。雨天,路过的车飞啸而过,而你却只能慢慢走回家,那种感觉,真希望自己也能尽早有辆车,以后应该可以不用再淋雨了吧。每次下雨都很烦躁,这雨一直下,什么时候是个头呀,盼望着雨一停就出去玩儿,当然,我心里明明知道,即使不下雨我也只会宅在家里。雨不是原来的雨,人还是原来的人儿吗?

推荐小时候特别喜欢的一首歌《三月里的小雨》,现在听来,歌词其实挺有韵味的。

三月里的小雨,淅沥沥沥 沥沥 淅沥沥沥下个不停,
山谷里的小溪,哗啦啦啦 啦啦 哗啦啦啦流不停,
小雨为谁飘,小溪为谁流,带著满怀的凄清。
三月里的小雨,淅沥沥沥 沥沥 淅沥沥沥下个不停,
山谷里的小溪,哗啦啦啦 啦啦 哗啦啦啦流不停,
小雨陪伴我,小溪听我诉,可知我满怀的寂寞。
请问小溪,谁带我追寻,追寻那一颗爱我的心。

三月里的小雨,淅沥沥沥 沥沥 淅沥沥沥下个不停,
山谷里的小溪,哗啦啦啦 啦啦 哗啦啦啦流不停,
小雨陪伴我,小溪听我诉,可知我满怀的寂寞。
请问小溪,谁带我追寻,追寻那一颗爱我的心。
追寻那一颗爱我的心
追寻那一颗爱我的心