且听疯吟 在此记录扯淡的青春

前几个星期,在 npm install 的时候,不小心打错了包名,结果居然安装成功了……当时还感慨 JS 社区真是太可怕了=-=

当然这个可怕并非贬义。写过一段的 .NET 和 Erlang 之后才知道,拥有一个如此流行和活跃的社区是件多么幸福的事 😂

但是打开一个项目看的时候我是懵逼的=-= 大大小小的依赖加起来好几百个,加起来近百兆了 😂

归根到底,还是语言本身很多奇奇怪怪的行为和标准库的弱鸡,导致很多人懒得去写一个能满足任何情况并保证不出问题的「简单」功能,更别说给它加上测试

left-pad 的事情出来之后,其实我是挺佩服这个社区的。这个体系运转了这么久才因为商标的关系,而不是个人利益出现了一点问题。这依靠的是许多开发者的自律,真的挺不容易。

兹瓷不兹瓷 left-pad 存在于 npm,俨然已经成为一个新的异端之争。比如,还有人跑到我纯粹是自言自语的微博下面,就之前我感慨 JS 社区太可怕的那条微博没头没脑地把我批判一番 😂

我觉得没必要上升到那么高的高度,和兹瓷不兹瓷 one-line module 无关,单纯和 left-pad 有关
也没有人会去建立一个标准来说多少行的代码可以作为 package,这确实挺傻
我想很多人包括我自己还是承认 one-line module 带来的好处的
你甚至也可以找到一大堆理论和一堆名人名言来支持它
但是,这个世界不是运行在理想模型中的

我需要 React 这样的 package,它给我带来的好处很大,而且我不可能自己去实现它,所以就算它只有一行,我依然会用
而且它也拥有一个好爹,我也不需要去担心稳定的问题

但是这不代表我愿意去在自己项目中使用 left-pad 这样的,随时可以根据个人意愿 unpublish,并且也没有重要到需要花很大精力去替代的 package

层层依赖之下,谁会去在意最下层那个 left-pad 写得好不好,稳定不稳定?
你觉得 React Native 项目的维护者们,检查过每个依赖么?看过 left-pad 的代码么?
还是不要过于高估社区维护的可靠性了,真的
三天两头出问题的 OpenSSL 前车之鉴还在那里呢

当然,肯定有人说开源了之后,大家可以改进啊,可以提 Pull Request 啊。
别闹。
我们就拿 left-pad 来说。来,我们看看,在 unpublish 之前,有几个 Pull Request?1 个。
大部分都是这次事件之后才提出来的,有没有想到如出一辙的 OpenSSL?

我也不觉得给 npm 加上 Review 会是个好想法。
至于 npm 这种中心化的方式好不好,我只能说,大多数情况下,它解决了问题。
但是现在我们可以讨论更好的解决方式了。

归根到底,用不用 left-pad ,只是一个选择问题。
至少,我可以选择风险还是效率。

编译、测试、打包、部署这一系列的操作实在是太麻烦而且容易出错漏,能自动化的东西我们就没必要手动去点。端着咖啡悠哉地等着叮的一声,安装包出现在面前,这才是我们想要的
花了点时间,把正在进行的 React Native 项目的自动部署完善了一下,实现了通过 Travis CI 自动编译测试,并打包成 ipa 发布到 FTP 的整个流程
现在只要 Push 到 Github 上,等到 Travis CI 运行完成,就直接可以拿到 ipa 包安装测试了

准备工作:
首先你需要一个 Github 账户
Travis CI 连接 Gtihub 后,会自动检查根目录下带有 .travis.yml 的项目
关于 Travis CI 的功能和文档,请参考 https://docs.travis-ci.com/

这里给出一个 React Native 项目的 yml 文件示例:

language: objective-c
osx_image: xcode7.1
xcode_project: ios/MyApp.xcodeproj
xcode_scheme: MyApp
env:
  matrix:
    - SPEC=spec1

before_install:
    - ./scripts/decrypt_key.sh
    - ./scripts/add_key.sh
    - brew update

install:
    - brew reinstall node flow watchman xctool
    - npm install -g react-native-cli

branches:
  only:
    - master

script:
    - ./scripts/release.sh

首先设置项目编译环境:

包括 language 和 要使用的编译镜像 osx_image,并指定项目文件和编译的 scheme

证书加密:

由于 iOS 打包过程需要一些证书密钥,这些是无法公开 Push 到 Github 的
虽然 Travis CI 没有提供 security file 的功能,但是提供了一个 security 的环境变量的功能
因此,我们可以通过在本地加密证书然后上传,线上编译时再解开来实现这一目的

比如:

  • Encrypt
# encrypt_key.sh
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/MyAppdev.mobileprovision -out scripts/MyAppdev.mobileprovision.enc -a
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/dist.cer -out scripts/dist.cer.enc -a
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/dist.p12 -out scripts/dist.p12.enc -a
  • Decrypt
# decrypt_key.sh
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/MyAppdev.mobileprovision.enc -out scripts/MyAppdev.mobileprovision -d -a
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/dist.cer.enc -out scripts/dist.cer -d -a
openssl aes-256-cbc -k ${ENCRYPT_PASS} -in scripts/dist.p12.enc -out scripts/dist.p12 -d -a

注意: 请不要直接上传你的证书和密钥!也不要把密码上传到公开的地方!

导入证书到 Travis 编译环境:

参考下面的步骤:

# add_key.sh

# Create a custom keychain
security create-keychain -p travis ios-build.keychain

# Make the custom keychain default, so xcodebuild will use it for signing
security default-keychain -s ios-build.keychain

# Unlock the keychain
security unlock-keychain -p travis ios-build.keychain

# Set keychain timeout to 1 hour for long builds
# see http://www.egeek.me/2013/02/23/jenkins-and-xcode-user-interaction-is-not-allowed/
security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain

# Add certificates to keychain and allow codesign to access them
security import ./scripts/apple.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/dist.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/dist.p12 -k ~/Library/Keychains/ios-build.keychain -P $KEY_PASS -T /usr/bin/codesign


# Put the provisioning profile in place
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "./scripts/MyAppdev.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/

安装环境

安装编译所需要的环境,比如 xctool、node、npm、react-native 等等

编译和打包

举个栗子:

# release.sh

npm install

react-native bundle --dev false --platform ios \
    --bundle-output "/tmp/main.jsbundle" --entry-file index.ios.js

xctool -project ios/MyApp.xcodeproj -scheme MyApp build -sdk iphoneos \
    configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build

if [[ $? != 0 ]];then
     echo ">> build failed"
     exit 1;
 fi

PROVISIONING_PROFILE="$HOME/Library/MobileDevice/Provisioning Profiles/MyAppdev.mobileprovision"
OUTPUTDIR="$PWD/build/Release-iphoneos"
NAME=Esports_${TRAVIS_COMMIT:0:6}.ipa

xcrun -log -sdk iphoneos PackageApplication "$OUTPUTDIR/MyApp.app" \
    -o "$OUTPUTDIR/$NAME"

if [[ $? != 0 ]];then
     echo ">> package application failed"
     exit 1;
 fi

cd $OUTPUTDIR
curl --ftp-create-dirs -T "$NAME" -u $FTP_USER:$FTP_PASSWORD \
    ftp://$FTP_SERVER/esports/$NAME

echo ">> {$NAME} uploaded!"
echo ">> all done!"

基本上和本地编译运行 React Native 项目类似,不同之处在于使用了 xctool 命令行来编译

  • 首先安装依赖 package
  • 然后 react-native bundle (如果使用 online 的模式,可以忽略这一步)
  • 使用 xctool 编译和测试项目
  • 打包 App 为 ipa 文件
  • FTP 发布(Travis CI 支持多种发布方式,可以参考文档实现)
  • 完成,邮件或 IM 通知(如果你需要)

ref: https://www.objc.io/issues/6-build-tools/travis-ci/

最近了解了下 react 和 react native,感觉看到了一条成为「Full-stack Developer」的捷径啊(雾

React

  • 消息同步

不管是 Web 还是 Native 应用,一个很麻烦的问题就是保持某个 Message 在不同 View 之间的状态同步。
比如,收到一条消息,需要在未读标签显示状态和数字,然后未读列表中插入一条消息记录。
阅读消息后,需要从未读中去掉这条记录,同时在已读中新增一条记录,还不能忘了将未读数字减去。

我们是怎么处理这种情况?
要在接收消息的 handler 中判断各个 View 当前的状态,然后根据对应的状态写不同的处理代码,阅读消息同理。当然高端点的可能自己搞个 Manager 之类的东西去对这些逻辑进行管理。
但是,并没有什么用。一旦业务复杂,处理和 Debug 起来就很麻烦了。看上去未读是 1,点进去发现是空,这种场景想必见得也不少了。

还记得很久之前我们怎么写页面的吗?(多久?大概到 ASP 吧=-=

那时候我们没有这么多烦恼。因为我们是这么干的。不管什么操作,ok,刷新页面,可以保证所有状态都是正确的,so easy!

React + Flux 所做的正和我们很久之前的做法有点像。数据更新了?重新渲染一遍 View 不就好了。
但是将整个 DOM 重新渲染一遍是开销很大的,当然不可能真的这么做。
所以就有了 Virtual DOM。
其实我们对 View 所做的操作大部分都是修改内容,比如修改某个块里面的文字啦,对某个 list 增删改啦。这些都不需要完全重绘。
React 做到了「智能」去更新 DOM,只改变需要改变的地方。(嗯,就是不相信你能写出高效正确的操作 DOM 的代码,所以我们都帮你写好啦~(雾

  • 组件化

通常我们的页面上的控件都是带有不同的状态的,比如选框是否选中等等。而不同情况下状态可能会根据一些参数去变化,这让一般情况下的组件复用变得很麻烦。
而 React 可以做到类似于函数的给定输入参数,输出固定的状态,可以很方便的实现 Web Components,也不会出现组件状态上的冲突。
而组件化的开发带来的效率提升是非常高的。

  • 其他

另外经常看到人说 HTML 嵌在 JS 里面感觉很奇怪是不是模板耦合在代码里面之类的,我倒是觉得这种写法挺简洁优雅的。如果把 JSX 里面的 XML 看作是一个 XML 表示的 Object,这样理解可能会好点吧。当然你也可以使用纯 JS 的方式去写。

好的东西往往是看上去简单实现复杂。
Java 本身虽然不怎么样,但是它可以让不同水平的人写出能够达到标准的代码,依然坚挺不是没有理由的。
所以我多少也有些认同 react 可以让不同水平的前端工程师写出符合效用的代码这个说法了。
算是一点并没有深入研究过前端的人的一点想法吧。

React Native

最近公司同时在做某个应用的 iOS 和 Android 版本,虽然没有参与开发,但是从两边的进程来看,大部分时间其实是把同一份逻辑翻译成 Objective-C 和 Java,但是这也是没办法的事情。
毕竟现有的 Hybrid App 方案表现都不太理想, Native 的优势暂时无法替代。如果可以借鉴 React 的优势和开发效率,同时带来 Native 的体验,excited!
所以 React Native 就顺理成章了。
难得的是 React 没有提「Write once, run anywhere」(JAVA:=-=)
而是 「Learn once, write anywhere」。

目前感觉除了基于 React 本身的那些优势外,React Native 在开发 App 方面也是很爽的

  • 效果

    WebView 总是让我们感觉没有 Native 那么顺畅自然,大概是因为触摸反馈之类的原因吧。React Native 在这方面则没有这个问题,可以非常容易的用到 Native 的触摸和动画效果

  • flex 布局

    熟悉 Web 那一套的话用起来还是很方便的。但是 React Native 的 flex 只是 Web 的 Lite 版,虽然尽力去做 Native 兼容,但是表现能力不如 Web 也不如 Native 是必然的,幸好移动端布局通常没有 Web 那么复杂。

  • 组件化

    React Native 提供了基本组件,通过自己组合,可以做出很多意想不到的效果。加上通过 npm 可以引入其他开源的组件,组合出一个 App 更加容易了。

  • 效率

    从安装 Xcode 到 写出一个新闻列表功能只花了两个小时,虽然之前完全没有接触过 iOS 开发。大公司项目这种提供详细文档和 starterkit 的作风真是太赞。

  • 调试方便

    即时刷新,用调试 Web 的方式调 App 真的很爽

  • 集成到现有 App

    可以将某个 React Native 实现的部分集成到现有 App 中,对已有的业务不产生影响

缺点也有:

  • 平台

    除了一些容易抽象的组件比如 ListView,View,Text 之外,目前很多组件是限于平台的。这点也是可以理解的,毕竟不同平台的 API 千差万别,很难做到通用。所以想要一次开发之后小小修改就可以通吃的可以醒醒了 =-=
    这也导致实际上还是无法绕开去,必须要了解 Native 开发的一些组件和细节。比如在 iOS 中,首先你需要放一个 Tabbar,然后每个 Tabbar Item 里面放一个 Navigator,这和 Native 开发其实是差不多的模式。单纯从做 Web 过来的人,还是需要去学习这些东西才能开始。
    但是比起完整使用 Native 开发,这个学习时间要短得多。

  • 表现能力

    虽然 React Native 的表现能力不如 Native ,但是它并不是为了取代 Native 开发存在的。在需要的领域做得足够好,这就够了。

  • 第三方库

    包括 React 和 React-Native 都有这个问题。事实上由于 Virtual DOM 和 React 革命性的架构方式,以往的第三方库很难做到直接可用了。而 React 的可用第三方库还不够多,当然社区还是挺活跃的,相信这个问题会好转。

  • 成熟度

    随着项目变大和引入的 package 变多,你会发现越来越多的莫名奇妙的出错和 bug,这个时候你需要耐心,放狗去搜。最容易找到解决方案的地方还是 Github 上的 issue list。有很多人会碰到同样的问题,也很可能会有人在下面给出解决的方法或是方向。当然,你也可以 subscribe 这个 issue,等待官方哪天大发慈悲解决掉它。

  • 更新速度

    更新太快有好处也有坏处。当你发现新版本解决了一个问题,迫不及待的想要更新的时候,可能发现:你的 App 挂了;第三方组件挂了;你必须升级一堆 package,并解决可能与之相关的问题。当然啦,大部分情况下还是没有那么蛋疼的。


虽然接触的时间不长,也有碰到一些小坑,但是目前看来,对我这种不会 Native 开发的人来说,React Native 是个非常不错的选择。
也许有时间会写写碰到过的坑和一些学习的建议之类的 :-)

Previous