iOS RN 0.45以上版本所需的第三方编译库(boost等)
2017-07-30 by 晴明

第三方依赖库百度盘链接: http://pan.baidu.com/s/1kVDUAZ9

RN iOS 0.45以上版本开始需要依赖一些第三方编译库,这些库在国内下载都非常困难(一般的翻墙工具都很难下载)
未来RN不同版本可能依赖不同版本的第三方编译库,具体所需库和版本请查看ios-install-third-party.sh文件,注意先把左上角的branch切换到对应的版本
0_1501422473793_branch.png

然后在底部查看所需的依赖库名字和版本
0_1501422599981_version.png

然后去网盘里下载↓

第三方依赖库百度盘链接: http://pan.baidu.com/s/1kVDUAZ9

下下来后请放置到

~/.rncache

目录

比如你可以打开终端,输入

# 井号表示注释,不要复制井号开头的句子

# 进入~目录,即用户目录
cd ~   

# 创建目录,如果已经存在就不用创建了
mkdir .rncache

# 复制文件
cp ~/Downloads/boost_1_63_0.tar.gz ~/.rncache/ 

全部复制完成后,就可以开始init新的RN项目

[阅读全文]

用 jpush-react-native 插件快速集成推送功能(Android 篇)
2017-02-27 by KenChoi

概述

jpush-react-native 是极光推送官方开发的 React Native 版本插件,可以快速集成推送功能(Github 项目中包含了 example,文中提到的 demo 来源于此)。现在最新版本的 JPush SDK 分离了 JPush 及 JCore,让开发者可以分开集成 JMessage 及 JPush(以前 JMessage 包含了 JPush)。下面就来具体说一下如何快速集成以及使用 jpush-react-native 插件。

安装

npm install jpush-react-native --save
npm install jcore-react-native --save ## jpush-react-native 1.4.2 版本以后需要同时安装 jcore-react-native

配置

配置包括两个步骤,自动配置和手动操作。

一、自动配置部分(以下命令均在你的 React Native Project 目录下运行,自动配置后仍需手动配置一部分)

  • 执行脚本
npm run configureJPush <yourAppKey> <yourModuleName>
//module name 指的是你 Android 项目中的模块名字(对 iOS 没有影响,不填写的话默认值为 app,会影响到查找 AndroidManifest 问题,
//如果没找到 AndroidManifest,则需要手动修改,参考下面的 AndroidManifest 配置相关说明)
//举个例子:
npm run configureJPush d4ee2375846bc30fa51334f5 app
  • Link 项目
//执行自动配置脚本后再执行 link 操作
react-native link

二、手动操作部分 (3个步骤)

  • 第一步:修改 app 下的 build.gradle 配置:

your react native project/android/app/build.gradle

android {
    defaultConfig {
        applicationId "yourApplicationId"
        ...
        manifestPlaceholders = [
                JPUSH_APPKEY: "yourAppKey", //在此替换你的APPKey
                APP_CHANNEL: "developer-default"    //应用渠道号
        ]
    }
}
...
dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile project(':jpush-react-native')  // 添加 jpush 依赖
    compile project(':jcore-react-native')  // 添加 jcore 依赖
    compile "com.facebook.react:react-native:+"  // From node_modules
}

将此处的 yourApplicationId 替换为你的项目的包名;yourAppKey 替换成你在官网上申请的应用的 AppKey。

  • 第二步:检查是否导入以下配置项:

i. 检查一下 dependencies 中有没有添加 jpush-react-native 及 jcore-react-native 这两个依赖。

your react native project/android/app/build.gradle

...
dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile project(':jpush-react-native')  // 添加 jpush 依赖
    compile project(':jcore-react-native')  // 添加 jcore 依赖
    compile "com.facebook.react:react-native:+"  // From node_modules
}

ii. 检查 android 项目下的 settings.gradle 配置有没有包含以下内容:

settings.gradle

include ':app', ':jpush-react-native', ':jcore-react-native'
project(':jpush-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jpush-react-native/android')
project(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android')

iii. 检查一下 app 下的 AndroidManifest 配置,有没有增加 <meta-data> 部分。

your react native project/android/app/AndroidManifest.xml

    <application
        ...
        <!-- Required . Enable it you can get statistics data with channel -->
        <meta-data android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}"/>
        <meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}"/>

    </application>

iiii. 现在重新 sync 一下项目(点击 Android Studio sync 按钮),应该能看到 jpush-react-native 以及 jcore-react-native 作为 android Library 项目导进来了。

  • 第三步:加入 JPushPackage (找到 app 下的 MainApplication.java):

app/src.../MainApplication.java

    private boolean SHUTDOWN_TOAST = false;
    private boolean SHUTDOWN_LOG = false;

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

        @Override
        protected boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }


        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    //加入 JPushPackage
                    new JPushPackage(SHUTDOWN_TOAST, SHUTDOWN_LOG)
            );
        }
    };

上面 JPushPackage 的两个参数是 bool 类型的,第一个参数设置为 true 表示关闭 toast 提示,第二个设置为 true 表示关闭日志打印,建议在 debug 版本中打开。然后在 MainActivity 中加入一些初始化代码即可:

app/src.../MainActivity.java

public class MainActivity extends ReactActivity {    
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        JPushInterface.init(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        JPushInterface.onPause(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        JPushInterface.onResume(this);
    }
}

这样就完成了所有的配置。接下来就可以在 JS 中调用插件提供的 API 了。

使用

收到推送

添加了此事件后,在收到推送时将会触发此事件。

需要注意的是,v1.6.6 版本以后,增加了 notifyJSDidLoad 方法,在监听所有相关事件之前要调用此方法,否则不会收到点击通知事件

example/react-native-android/push_activity.js

...
import JPushModule from 'jpush-react-native';
...
export default class PushActivity extends React.Component {
    componentDidMount() {
        // 在收到点击事件之前调用此接口
        JPushModule.notifyJSDidLoad((resultCode) => {
			if (resultCode === 0) {
			}
		});
        JPushModule.addReceiveNotificationListener((map) => {
            console.log("alertContent: " + map.alertContent);
            console.log("extras: " + map.extras);
            // var extra = JSON.parse(map.extras);
            // console.log(extra.key + ": " + extra.value);
        });
}
点击通知

在用户点击通知后,将会触发此事件。

...
componentDidMount() {
    JPushModule.addReceiveOpenNotificationListener((map) => {
            console.log("Opening notification!");
            console.log("map.extra: " + map.key);
        });
}

高级应用

点击通知跳转到指定界面,需要在 Native 中声明一个 Activity,如 demo 中的 SecondActivity,而 SecondActivity 的界面仍然用 JS 来渲染。只需要改动一下 SecondActivity,让它继承自 ReactActivity 即可:

example/android/app/src.../SecondActivity.java

public class SecondActivity extends ReactActivity {

    @Override
    protected String getMainComponentName() {
        return "SecondActivity";
    }

}

声明完成后,需要在 AndroidManifest 中声明一下(必要的时候可以声明 Activity 的启动模式,以更好地适配自己的需求)。

AndroidManifest.xml

...
<application>
    ...
    <activity android:name=".SecondActivity"/>
</application>

然后使用 SecondActivity 中返回的字符串 “SecondActivity” 注册一个 Component 即可:

example/react-native-android/second.js

import React from 'react';
import ReactNative from 'react-native';

const {
  AppRegistry,
  Text,
} = ReactNative;


export default class second extends React.Component { 
    constructor(props) { 
        super(props); 
    } 

    render() { 
      return (
        <Text> Welcome ! </Text>   
      );
    } 
}

AppRegistry.registerComponent('SecondActivity', () => second);

最后监听点击通知事件,并完成跳转:

JPushModule.addReceiveOpenNotificationListener((map) => {
	console.log("Opening notification!");
	console.log("map.extra: " + map.extras);
	JPushModule.jumpToPushActivity("SecondActivity");
});

这样就完成了用户点击通知后的自定义跳转界面。

接收自定义消息

在用户收到自定义消息后触发。

example/react-native-android/push_activity.js

 ...
    componentDidMount() {
        JPushModule.addReceiveCustomMsgListener((map) => {
            this.setState({
                pushMsg: map.message
            });
            console.log("extras: " + map.extras);
        });
...

得到 RegistrationId

用户注册成功后(一般在用户启动应用后),如果订阅了这个事件,将会收到这个 registrationId。

...
    componentDidMount() {
        JPushModule.addGetRegistrationIdListener((registrationId) => {
            console.log("Device register succeed, registrationId " + registrationId);
        });
    }
清除所有通知

建议在用户退出前台后调用。

    ...
    componentWillUnmount() {
        console.log("Will clear all notifications");
        JPushModule.clearAllNotifications();
    }
设置标签

example/react-native-android/set_activity.js

    ...
    setTag() {
		if (this.state.tag !== undefined) {
			/*
			* 请注意这个接口要传一个数组过去,这里只是个简单的示范
			*/		
			JPushModule.setTags(["VIP", "NOTVIP"], () => {
				console.log("Set tag succeed");
			}, () => {
				console.log("Set tag failed");
			});
		}
	}
设置别名
    ...
    setAlias() {
		if (this.state.alias !== undefined) {
			JPushModule.setAlias(this.state.alias, () => {
				console.log("Set alias succeed");
			}, () => {
				console.log("Set alias failed");
			});
		}
	}

以上就是插件提供的主要接口的示例。总的来说,配置和使用都比较简单,适合开发者快速集成推送功能。

[阅读全文]

安卓Back键的处理·基本+高级篇
2016-03-01 by tdzl2003

基本用法

根据文档,安卓back键的处理主要就是一个事件监听:

BackAndroid.addEventListener('hardwareBackPress', this.onBackPressed);
BackAndroid.removeEventListener('hardwareBackPress', this.onBackPressed);

starter-kit里,我们在App这一级别,实现了按back键回退导航栈的功能:

class App extends React.Component {
  componentWillMount() {
    if (Platform.OS === 'android') {
      BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid);
    }
  }
  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackAndroid.removeEventListener('hardwareBackPress', this.onBackAndroid);
    }
  }
  onBackAndroid = () => {
    const nav = this.navigator;
    const routers = nav.getCurrentRoutes();
    if (routers.length > 1) {
      nav.pop();
      return true;
    }
    return false;
  };
  ……
}
[阅读全文]

设置React Native自定义字体
2016-01-11 by magicismight

最近在给公司开发一个内嵌在原生APP里面的RN项目
产品老大说RN项目内的字体需要和外部原生的字体保持一致,然后就踩了这个坑
对自定义字体的设置官方给出的文档很少,几乎没有
倒腾了一上午终于搞定了,给大家分享一下

iOS的问题比较少,直接使用客户端同事给出的字体名称就可以直接使用

{
    fontFamily: 'customFont'
}

这个字体名称是他们在外层客户端里面已经打包好了的,我可以直接通过名称调用

自己打包字体教程如下

adding-custom-fonts-to-react-native

比较棘手的是安卓的字体使用,他们的字体是放在assets目录下的
我尝试用名称和路径去调用最终都以失败告终,找遍了google,stackoverflow以及官方的issues都没有提及相关的字体定义方法
后来在源代码里面终于找到了一条信息

/**
 * Class responsible to load and cache Typeface objects. It will first try to load typefaces inside
 * the assets/fonts folder and if it doesn't find the right Typeface in that folder will fall back
 * on the best matching system Typeface The supported custom fonts extensions are .ttf and .otf. For
 * each font family the bold, italic and bold_italic variants are supported. Given a "family" font
 * family the files in the assets/fonts folder need to be family.ttf(.otf) family_bold.ttf(.otf)
 * family_italic.ttf(.otf) and family_bold_italic.ttf(.otf)
 */

原来要把字体文件放在assets/fonts文件夹下才能生效
他会根据fontFamily自动在assets/fonts文件夹下尝试匹配字体文件
如下的代码,程序会尝试在assets/fonts 目录下查找customFont.ttf文件,如果没找到会使用默认的字体

{
    fontFamily: 'customFont'
}

试了一下,成功了!

自定义字体引用的问题解决了,另外一个比较棘手的问题是之前的项目里面Text的样式有很多,而且分布在不同的地方
不可能给每个样式都设置一个fontFamily,而且ios和安卓的字体名称都不一样,一个一个的加那样太蠢了
最开始想的是给Text再包一层,但是一秒钟之后也把这方案pass了
因为
import {Text} from 'components'
这类代码老是觉得有点怪怪的,而且这样代码修改量也很大,每个引用了Text的组件都要改一下
后面又尝试修改了Text的源代码,虽然可以实现,但是也觉得不太恰当,后续每次rn升级都得改一遍,比较麻烦
后来灵光一闪,何不把Text.prototype.render改一下呢,给每个Text都添加一个字体样式

    // _.wrap是lodash的一个函数,用来包裹传入的函数,然后返回一个新的函数
    Text.prototype.render = _.wrap(Text.prototype.render, function (func, ...args) {
        let originText = func.apply(this, args);
        return cloneElement(originText, {
            style: [
                originText.props.style,
                styles.defaultFontFamily
            ]
        });
    });

这样果然成功了,其他的的代码一行不用动!

[阅读全文]

在Windows下搭建React Native Android开发环境
2015-11-20 by tdzl2003

推荐新手们先看一下环境搭建的视频教程

[阅读全文]

在React Native中使用ART
2016-01-28 by magicismight

前半个月捣腾了一下React Native ART
现在手上闲下来了和大家分享一下React Native中的ART使用心得

React Native ART 究竟是什么?

所谓ART,是一个在React中绘制矢量图形的JS类库。这个类库抽象了一系统的通用接口,统一了SVG,canvas,VML这类矢量图形在React中的书写格式。你可以通过ART将SVG,canvas,VML的矢量图形拿到React中使用,也可以把ART反转回去。(虽然有很多局限性,后面会讲到)
React Native ART 是react-art在React Native中的移植版,接口几乎完全一致,
React Native中的ART很早之前就已经开源了iOS版,最近又在0.18.0中开源了Android版本
因为缺少官方文档,一直不为人所知。

为什么要在React Native中使用 ART?

我在前端页面切片的时候比较喜欢用SVG,有条件我都尽量让设计师出AI的设计稿或者把图标导成SVG的
因为SVG和图片相比,优点太多太多:可以代码复用,可以无损放大,通过合理导出的svg代码比同等压缩过的图片文件大小要小很多,而且svg代码还可以通过gzip压缩
最近在做一个原生内嵌的React Native项目,设计师是按照原生APP的标准把每个图标切了3个尺寸给我,感谢React Native中强大的打包工具,可以很方便的使用这写图标。所以项目开始也没想着要用SVG(当时android的ART还没开源,这也是一个重要因素)。
在整个项目完成之后,看了一下,所有图片在经过压缩之后加起来有200多K,大小还可以接受。
但是在项目后期优化过程中,发现一个使用图片图标非常致命的问题:

<Image />元素在iOS下不是同步渲染的,即使是使用的本地图片资源!
更过分的是具有相同source的多个Image也不能在同一帧里面渲染出来的,如下的5个图标,在iOS下它们不会同时被渲染出来,这样造成的影响是在打开一个列表的时候,看见这些图标一个接一个无序的跳出来,这个效果如果在iphone4s,5s下看相当的明显,很恶心,完全不能忍受!想过各种方法也没办法绕过去。
0_1453948316450_upload-02d66234-742f-4272-ac8e-11b87cfbee64

首屏的如下几个图标以及返回按钮和logo,还有TabBar里面的图标在iOS下都是在界面出现的若干帧之后再无序的跳出来,这样的效果很不好,一点都没有原生APP的感觉,有一种hybird的既视感。
0_1453945350760_icons.png
0_1453946171360_upload-3df0c88a-2f35-4e7a-a076-e822afed6325

后来等到了0.18.0,Android的ART对外开源了,我想到为什么不用SVG图标尝试一下呢?
然后让设计师把这个图标:
0_1453949196676_upload-f3a4ad35-8bf5-4f67-b74e-5caa5415057f
重新切成SVG给我,然后再把SVG转成ART。
在iOS下完美渲染!所有图标和界面文字一起出现,不管是放5个还是100个。而且对界面的整体渲染速度也没有任何能感知到的影响。
在安卓下也是同样的完美,而且生成的ART代码要比图片小了很多。

<Surface
    width={50}
    height={50}
>
    <Group scale={0.23}>
        <Shape
            fill={`${this.props.active ? '#ff5942' : '#ccc'}`}
            d={`M46.31,0H3.69C1.63,0,0,1.63,0,3.69v42.63C0,48.37,1.63,50,3.69,50h42.63c2.05,0,3.69-1.63,3.69-3.69V3.69
C50,1.63,48.37,0,46.31,0z M44.5,22.92l-7.76,7.65l1.8,10.61c0.18,1.07-0.29,2.14-1.21,2.77c-0.51,0.34-1.1,0.52-1.69,0.52
c-0.49,0-0.98-0.12-1.42-0.35l-9.23-4.75l-9.23,4.75c-0.44,0.24-0.94,0.35-1.42,0.35c-0.6,0-1.19-0.17-1.69-0.52
c-0.92-0.63-1.38-1.69-1.21-2.77l1.8-10.61l-7.76-7.65c-0.77-0.76-1.04-1.86-0.69-2.87c0.35-1.01,1.25-1.73,2.34-1.9l10.6-1.55
l4.6-9.43c0.49-1,1.52-1.62,2.66-1.62c1.15,0,2.18,0.64,2.66,1.62l4.6,9.43l10.59,1.55c1.09,0.16,1.99,0.9,2.34,1.9
C45.53,21.06,45.27,22.16,44.5,22.92z`}
        />
    </Group>
</Surface>

这里面的路径代码是通过PS CC直接导出的,没有做任何优化,如果再优化一番可以再缩小一半。SVG代码优化后面再做介绍。
下面正式进入正题

如何在React Native中使用 ART?

安装 ART

安装ART很简单
安卓里面根本不用安装,0.18.0里面自带,直接在js引入既可

import React, {ART} from 'react-native';
const {Surface, Group, Shape} = ART;

iOS里面的ART是可选的,你需要手动导入xcodeproj文件,以及加入静态链接库
但是也很简单:教程

使用ART

请大家原谅我,其实我是标题党,我不会在这里详细的讲解ART的用法,而是讲一个基于ART的SVG库:react-native-art-svg

为什么我不在这里讲ART呢?原因如下
上面那段代码是ART代码和设计师给我svg代码其实有一些差异,还要把SVG转换一次,很不友好,因为ART的元素和SVG元素名称和用法都不太一样
在SVG代码里面,画布元素是<svg>而ART里面是<Surface>
绘制路径的元素在SVG里面是<path>,而ART里面的Path又是另外一个东西,在ART里面需要<Shape>
这都不是最麻烦的地方,一些基础的SVG图形<circle>、<rect>、<polygon>...等等的元素ART都是不支持的。
前端切图,不管是网页还是React Native的界面,设计稿和图标都是设计师给我们的(部分连设计都一起做了的大神除外)
而设计师给我们的矢量图标要么是AI原图要么是SVG,但是最终到我们手里,导出来必定是SVG。
所以ART虽然通过抽象接口来统一了SVG,canvas,VML在React中的使用,但是ART提供的接口还是相当不友好的。然后我就萌发了一个想法,为什么不再把ART封装一层,把语法封装成和SVG的语法呢?
然后react-native-art-svg就诞生了,支持SVG所有常用的元素和属性,svg代码拿过来几乎可以直接使用(后面会添加直接导入svg代码的功能)

预览图如下:
art-svg

安装很简单,因为是纯JavaScript的类库,直接一行搞定
npm i react-native-art-svg --save

通过如下代码在js中引入

import Svg, {
    Circle,
        Ellipse,
        G,
        LinearGradient,
        RadialGradient,
        Line,
        Path,
        Polygon,
        Polyline,
        Rect,
        Symbol,
        Text,
        Use,
        Defs,
        Stop
} from 'react-native-art-svg';

上面的ART代码等同于如下的代码

<Svg
    width={50}
    height={50}
>
    <G scale={0.23}>
        <Path
            fill={`${this.props.active ? '#ff5942' : '#ccc'}`}
            d={`M46.31,0H3.69C1.63,0,0,1.63,0,3.69v42.63C0,48.37,1.63,50,3.69,50h42.63c2.05,0,3.69-1.63,3.69-3.69V3.69
C50,1.63,48.37,0,46.31,0z M44.5,22.92l-7.76,7.65l1.8,10.61c0.18,1.07-0.29,2.14-1.21,2.77c-0.51,0.34-1.1,0.52-1.69,0.52
c-0.49,0-0.98-0.12-1.42-0.35l-9.23-4.75l-9.23,4.75c-0.44,0.24-0.94,0.35-1.42,0.35c-0.6,0-1.19-0.17-1.69-0.52
c-0.92-0.63-1.38-1.69-1.21-2.77l1.8-10.61l-7.76-7.65c-0.77-0.76-1.04-1.86-0.69-2.87c0.35-1.01,1.25-1.73,2.34-1.9l10.6-1.55
l4.6-9.43c0.49-1,1.52-1.62,2.66-1.62c1.15,0,2.18,0.64,2.66,1.62l4.6,9.43l10.59,1.55c1.09,0.16,1.99,0.9,2.34,1.9
C45.53,21.06,45.27,22.16,44.5,22.92z`}
        />
    </G>
</Svg>

这段代码从SVG转过来可省心多了,只需要改一下元素的大小写,把元素名称改成大写字母开头的既可,属性名称改成驼峰式的命名方式。

通用属性列表:

属性名称 默认值 描述
fill '#000' 内部填充规则(填充颜色,填充渐变图形)
fillOpacity 1 填充的透明度
stroke 'none' 描边颜色
strokeWidth 1 描边的宽度
strokeOpacity 1 描边的透明度
strokeLinecap 'square' 描边线段端点显示方式
strokeLinejoin 'miter' 描边线段连接处的显示方式
strokeDasharray [] 描边线段断点显示规则
x 0 当前图形x轴偏移量
y 0 当前图形y轴偏移量
rotate 0 当前图形旋转值
scale 1 当前图形的缩放值
origin 0, 0 变形原点(x,y,rotate,scale的变形原点坐标)
originX 0 变形原点x轴坐标
originY 0 变形原点y轴坐标

可以看到,几乎所有SVG的常用属性都可以支持
不支持fillRule,因为官方认为fillRule不是SVG,canvas和VML公用的属性,他们不对这类独有的属性进行支持
暂不支持clipPath,因为ART官方还未完成

支持的元素列表:

Svg

Svg为所有矢量图形的画布元素,所有Svg内的矢量图形都会绘制在Svg内部
并且Svg支持viewbox和preserveAspectRatio属性(关于这两个属性的详解)

<Svg
    height="100"
    width="100"
>
    <Rect x="0" y="0" width="100" height="100" fill="black" />
    <Circle cx="50" cy="50" r="30" fill="yellow" />
    <Circle cx="40" cy="40" r="4" fill="black" />
    <Circle cx="60" cy="40" r="4" fill="black" />
    <Path d="M 40 60 A 10 10 0 0 0 60 60" stroke="black" />
</Svg>;

上面的代码会绘制如下的图形
Svg
其中height和width定义了Svg元素的宽高

注意
在react-native-art-svg中,所有元素的数字类型的属性都可以直接用字符串表示,如:
下面两段属性声明都可以正常使用,这样写起来很方便
height="100"
height={100}

矩形:Rect

<Rect>元素可以在画布上绘制一个矩形,矩形位置和大小通过x,y,width,height属性共同定义

<Svg
    width="200"
    height="60"
>
    <Rect
        x="25"
        y="5"
        width="150"
        height="50"
        fill="rgb(0,0,255)"
        strokeWidth="3"
        stroke="rgb(0,0,0)"
    />
</Svg>

Svg

上面的代码绘制了一个左上角位于画布25,5,宽150,高50的矩形
并且使用蓝色(rgb(0,0,255))填充(fill)内部背景
用黑色(rgb(0,0,0))进行描边(stroke),描边宽度(strokeWidth)为3

圆型:Circle

<Circle>元素可以在画布上绘制一个圆形,圆型需要cx,cy,r三个属性,分别对应圆形x轴坐标,y轴坐标以及半径

<Svg
    height="100"
    width="110"
>
    <Circle
        cx="50"
        cy="50"
        r="50"
        fill="pink"
    />
</Svg>

Svg

椭圆:Ellipse

<Ellipse>元素可以在画布上绘制一个椭圆,椭圆和圆形类似,只是把圆形的r属性替换成了分别对应水平半径和垂直半径的rx,ry属性,rx == ry的椭圆是一个圆形

<Svg
    height="100"
    width="110"
>
    <Ellipse
        cx="55"
        cy="55"
        rx="50"
        ry="30"
        stroke="purple"
        strokeWidth="2"
        fill="yellow"
    />
</Svg>

直线:Line

<Line>元素可以在画布上画一条从x1,y1到x2,y2的直线

<Svg
    height="100"
    width="100"
>
    <Line
        x1="0"
        y1="0"
        x2="100"
        y2="100"
        stroke="red"
        strokeWidth="2"
    />
</Svg>

Svg

多边形:Polygon

<Polygon>元素可以在画布上画一个由多个点收尾相接组合而成的多边形,每个点的坐标由空格分开定义在points属性中

<Svg
    height="100"
    width="100"
>
    <Polyline
        points="10,10 20,12 30,20 40,60 60,70 95,90"
        fill="none"
        stroke="black"
        strokeWidth="3"
    />
</Svg>

Svg

多边线:Polyline

<Polyline>元素和多边形类型,只是多边形的最后一个点和第一个点是相连的,组成的是一个闭合的图形,多边线的最后一个点和第一个点没有连接

<Svg
    height="100"
    width="100"
>
    <Polyline
        points="10,10 20,12 30,20 40,60 60,70 95,90"
        fill="none"
        stroke="black"
        strokeWidth="3"
    />
</Svg>

Svg

路径:Path

<Path>的d属性内定义了一系列的路径坐标以及绘制规则命令,上面的所有图形都可以通过Path绘制而成

  • M = 把绘制点移动到某个位置
  • L = 从当前绘制点画一条直线到某个坐标
  • H = 从当前绘制点沿着x轴水平画线
  • V = 从当前绘制点沿着y轴垂直画线
  • C = 从当前绘制点画一条曲线到某个坐标
  • S = 从当前绘制点画一条平滑的曲线到某个坐标
  • Q = 从当前绘制点画一条贝赛尔曲线到某个坐标
  • T = 从当前绘制点画一条平滑的贝赛尔曲线到某个坐标
  • A = 从当前绘制点画一条椭圆曲线到某个点
  • Z = 闭合当前路径

注意: 上面所有的命令都可以使用小写字母,大写字母的命令是使用的坐标是绝对值,小写字母的命令的坐标是相对值

<Svg
    height="100"
    width="100"
>
    <Path
        d="M25 10 L98 65 L70 25 L16 77 L11 30 L0 4 L90 50 L50 10 L11 22 L77 95 L20 25"
        fill="none"
        stroke="red"
    />
</Svg>

Svg

文字:Text

<Text>元素可以在画布上绘制文字图形

<Svg
    height="60"
    width="200"
>
    <Text
        fill="none"
        stroke="purple"
        fontSize="20"
        fontWeight="bold"
        x="100"
        y="20"
        textAnchor="center"
    >STROKED TEXT</Text>
</Svg

Svg

SVG进阶元素使用

分组:G

<G>元素可以把它内部的子元素组合成一个分组,G元素内所有的子元素都将继承除(id,变形属性之外的所有属性)

下面两段代是相同的

<Svg
    height="60"
    width="200"
>
    <G width="100" height="30" fill="blue" x="0">
        <Rect y="0" />
        <Rect y="30" />
    </G>
</Svg>
<Svg
    height="60"
    width="200"
>
    <G>
        <Rect width="100" height="30" fill="blue" x="0" y="0" />
        <Rect width="100" height="30" fill="blue" x="0" y="0" />
    </G>
</Svg>

G元素的变形属性不会被继承

<Svg
    height="100"
    width="200"
>
    <G
        rotate="50"
        origin="100, 50"
    >
        <Line
            x1="60"
            y1="10"
            x2="140"
            y2="10"
            stroke="#060"
        />

        <Rect
            x="60"
            y="20"
            height="50"
            width="80"
            stroke="#060"
            fill="#060"
        />

        <Text
            x="100"
            y="75"
            stroke="#600"
            fill="#600"
            textAnchor="center"
        >
            Text grouped with shapes</Text>
    </G>
</Svg>

Svg

定义:Defs

<Defs>元素内的所有图形都不会被绘制在画布上,<Defs>元素用于定义一些代码复用相关的元素,下面使用其他元素解释<Defs>元素的用法

复用:Use

使用<Use>元素可以进行代码复用,可以将<Defs>元素中定义的元素复制一份绘制在当前<Use>元素的位置

<Svg
    height="100"
    width="300"
>
    <Defs>
        <G id="shape">
            <Circle cx="50" cy="50" r="50" />
            <Rect x="50" y="50" width="50" height="50" />
            <Circle cx="50" cy="50" r="5" fill="blue" />
        </G>
    </Defs>
    <Use href="#shape" x="20" y="0"/>
    <Use href="#shape" x="170"y="0" />
</Svg>

Svg

*注意:*Use元素使用href指定复用对象(SVG中是使用的xlink:href=),属性值为#+需要复用元素的id,也可以复用<Defs>外面具有id属性的元素,Use元素上的属性将覆盖被复用元素的属性

标记:Symbol

使用<Symbol>元素可以更快捷的定义元素,使用<Defs>元素定义可复用元素时,每个子元素对应一个可复用元素,如果需要复用多个元素组成的图形时通常使用<Symbol>元素进行定义,<Symbol>自身也不会被绘制在画布上,而且可以给Symbol指定viewbox和preserveAspectRatio属性,更为方便的对图形进行缩放

<Svg
    height="150"
    width="110"
>
    <Symbol id="symbol" viewbox="0 0 150 110" width="100" height="50">
        <Circle cx="50" cy="50" r="40" strokeWidth="8" stroke="red" fill="red"/>
        <Circle cx="90" cy="60" r="40" strokeWidth="8" stroke="green" fill="white"/>
    </Symbol>

    <Use
        href="#symbol"
        x="0"
        y="0"
    />
    <Use
        href="#symbol"
        x="0"
        y="50"
        width="75"
        height="38"
    />
    <Use
        href="#symbol"
        x="0"
        y="100"
        width="50"
        height="25"
    />
</Svg>

Svg

渐变填充

LinearGradient:线性渐变

<LinearGradient>可以定义一个线性渐变的填充规则,改元素必须定义在<Defs>元素内。
线性渐变可以被定义为水平渐变、垂直渐变和带角度的渐变
通过调整x1,y1设置渐变开始点,x2,y2设置渐变结束点
使用<Stop>元素定义渐变的变色点

<Svg
    height="150"
    width="300"
>
    <Defs>
        <LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0">
            <Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" />
            <Stop offset="1" stopColor="red" stopOpacity="1" />
        </LinearGradient>
    </Defs>
    <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>

注意:<LinearGradient>和<RadialGradient>的x1,y2,x2,y2属性均可以使用百分比
下面<LinearGradient>绘制的结果等同于上面代码的绘制结果

<LinearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
    <Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" />
    <Stop offset="100%" stopColor="red" stopOpacity="1" />
</LinearGradient>

Svg

RadialGradient:径向渐变

<RadialGradient>可以定义一个线性渐变的填充规则,和<LinearGradient>一样改元素必须定义在<Defs>元素内。
cx,cu,rx,ry属性定义了最外层渐变椭圆的位置坐标和大小,fx和fy属性定义了最内层渐变椭圆的位置坐标

<Svg
    height="150"
    width="300"
>
    <Defs>
        <RadialGradient id="grad" cx="150" cy="75" rx="85" ry="55" fx="150" fy="75">
            <Stop
                offset="0"
                stopColor="#ff0"
                stopOpacity="1"
            />
            <Stop
                offset="1"
                stopColor="#83a"
                stopOpacity="1"
            />
        </RadialGradient>
    </Defs>
    <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>

Svg

总结

好了,差不多就这些了。
上面包含了大多数react-native-art-svg类库的用法,想看更具体的例子请fork之后运行Example下面的示例工程。

让我们再回到在文章开头的那个项目中去,通过几番折腾,终于把项目中的所有图片都换成了矢量图形,在iOS下再也没有出现图片闪动或图片不能和界面同时渲染的问题了。
而且SVG代码再经过一番优化之后,把200多K的图片变成了30多K的矢量代码。完美!

我再顺带讲一下SVG代码优化:

  1. 优先使用基础图形!
    优先使用<Rect><Circle><Line><Polygon><Polyline><Ellipse>之类的基础图形
    能够使用这些元素绘制的图形就尽量使用这些元素来绘制,因为同样一个图形使用<Path>命令生成的代码要比其他基础元素生成的代码多很多倍
  2. 让设计师在画图的时候全部使用局中描边,因为只有局中描边导出的SVG代码会保留原始设计稿中的图形信息,用外层描边或内侧描边生成的SVG代码都是通过<path>元素绘制的,这样就会使得SVG代码成倍的增加。
[阅读全文]

React/React Native 的ES5 ES6写法对照表
2015-11-20 by tdzl2003

很多React/React Native的初学者都被ES6的问题迷惑:各路大神都建议我们直接学习ES6的语法(class Foo extends React.Component),然而网上搜到的很多教程和例子都是ES5版本的,所以很多人在学习的时候连照猫画虎都不知道怎么做。今天在此整理了一些ES5和ES6的写法对照表,希望大家以后读到ES5的代码,也能通过对照,在ES6下实现相同的功能。

[阅读全文]

使用TypeScript编写react-native(高效版)
2017-08-28 by tdzl2003

TypeScript作为JavaScript的一个富类型扩展语言,深受代码风格严谨的前端开发者欢迎。但在react-native下,因为packager的配置困难,使用TypeScript一直是个麻烦的选择。网上的大部分方案,甚至微软的官方方案都是启动两个进程,一个进程将typescript编译成javascript,另一个进程则是RN默认的packager。

实际上自从RN的packager独立并改名为metro之后,也多了很多配置的可能性。因此我们也能更高效的在React Native工程中使用TypeScript了。现在我们来尝试进行这样的配置。

初始化RN工程

如果你已经有一个RN工程了,可以跳过这一步。

按照RN中文网的入门文档安装所需的软件,然后初始化项目:

react-native init MyProject
cd MyProject

安装TypeScript相关依赖

yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript

配置tsconfig.json

您可以从您之前的TS项目中复制这个文件,也可以使用tsc初始化(具体参考TypeScript的教程)。注意务必设置"jsx":"react"
注意多余的注释可能会不兼容,需要移除。

{
  "compilerOptions": {
    "importHelpers": true,
    "target": "es2015",
    "jsx": "react",
    "noEmit": true,
    "moduleResolution": "node",
  },
  "exclude": [
    "node_modules",
  ],
}

在项目目录中新建或编辑rn-cli.config.js,使用支持typescript的transfomer

module.exports = {
  getTransformModulePath() {
    return require.resolve('react-native-typescript-transformer');
  },
  getSourceExts() {
    return ['ts', 'tsx'];
  }
}

最后修改你的源代码

入口index.jsApp.js的文件名请不要改变,你可以新建src文件夹,在其中建立index.tsx,然后把App.js的内容改成

import './src';

然后在index.tsx里重新编写你的入口代码。

注意:引用react的方式必须是这样:

import * as React from 'react';
import { Component } from 'react';

而初始代码中的

import React, { Component } from 'react';

会导致错误。

自由的TS吧,少年

接下来你可以在工程中自由的使用ts/tsx来编写TypeScript代码啦。

[阅读全文]

2017最新发布到App Store上架流程—详细图文
2017-03-16 by qtb000

0_1495769315415_11.png

如果你是新手,将在这里清晰发布到App Store整个上架流程,还有相应的流程解决方案。
0_1495769243898_间隔.png
如果你上架过iOS APP,这里会了解到有更快捷的上架过程。
0_1495769243898_间隔.png
上架iOS最基本需要一个付费的开发者账号,还没有的话申请一个或者借用。
0_1495769243898_间隔.png
如果只是安装到自己测试,现在有个新技术用普通的苹果id就能申请ios证书进行真机调试了。
0_1495769243898_间隔.png
免开发者账号申请ios证书真机调试介绍
0_1495769243898_间隔.png

ios上架通常也还需要一台Mac电脑,不过这里介绍到不用Mac在Windows系统中直接发布iOS APP到App Store。

0_1495769243898_间隔.png
如果没有Mac,也无所谓。
0_1495769243898_间隔.png
申请开发者账号介绍
0_1495769243898_间隔.png
iOS APP上架需要创建一个APP ID、申请iOS发布证书、再用iOS证书打包IPA,然后在上传IPA到App Store
0_1495769243898_间隔.png

上架过程分七个步骤,按步骤一步步来,清晰了步骤就跟简单了,欢迎交流!

0_1495769243898_间隔.png

1、创建APP身份证(App IDs)

2、申请发布证书

3、申请发布描述文件

4、使用iOS证书编译打包IPA

5、在iTunes Connect创建App

6、Windows下上传IPA到App Store

7、上传好IPA回到iTunes Connect填写APP信息并提交审核

0_1495769243898_间隔.png

一、创建唯一标示符App IDs
0_1495769243898_间隔.png

APP IDs在后面创建发布文件,创建APP时都要用到。

首先打开开发者中心https://developer.apple.com/cn/,进入证书页面。

0_1495769243898_间隔.png
1.1点击证书、ID及配件文件,进入设置。
0_1495769243898_间隔.png
0_1495769165257_1.1.png

1.2选择App IDs –>点击+创建一个新的App ID
0_1495769243898_间隔.png

其中有两项需要你自己填:

第一项Name,用来描述你的App ID,这个随便填,没有什么限制,最好是项目名称,这样方便自己辨识(不允许中文)

第二项Bundle ID (App ID Suffix),这是你App ID的后缀,需要仔细填写。用来标示我们的 app,使它有一个固定的身份,和你的程序直接相关。填写 Explicit App ID 的格式为:com.company.appName,一定照着格式写,写个方便记的,后面很多地方要用到。

第三项App Services,默认会选择2项,不能修改,其它根据自己需要的服务选择上,然后点击Continue确认,下一步。

1_1495769156479_1.3.png 0_1495769156479_1.2.png
0_1495769243898_间隔.png
检查下没有错的话直接点击Register后点击Done完成App ID的创建。
0_1495769243898_间隔.png
0_1495769142657_1.4.png

二、申请发布证书
0_1495769243898_间隔.png
iOS证书申请要用到一个工具Appuploader,在win系统中辅助快速申请iOS证书,和后面上传IPA到App Store都要用到。
0_1495769243898_间隔.png
先安装好 Appuploader安装教程
0_1495769243898_间隔.png
2.1打开程序,输入苹果开发者中心账号,登录。
0_1495769243898_间隔.png
0_1495769125681_2.1.png
0_1495769243898_间隔.png
2.2选择Certification
0_1495769243898_间隔.png
0_1495769115558_2.2.png
0_1495769243898_间隔.png
2.3点击+ADD,这里有开发证书等等创建选项,这里选第三项发布证书。
0_1495769243898_间隔.png
0_1495769102737_2.3.png
0_1495769243898_间隔.png
2.4输入,证书名称(随意) 邮箱(任何邮箱都行) 密码(后面打包编译时用到)然后点击ok
0_1495769243898_间隔.png
0_1495769091400_2.4.png
0_1495769243898_间隔.png
2.5此时生成了好了一个发布证书,点击p12 File,下载证书文件,保存到电脑。
0_1495769243898_间隔.png
0_1495769079731_2.5.png

这样发布证书就创建好了。
0_1495769243898_间隔.png

三、申请发布描述文件
0_1495769243898_间隔.png

3.1回到软件点击Profiles
0_1495769243898_间隔.png
0_1495769068038_3.1.png
0_1495769243898_间隔.png
3.2点击+ADD,这里有开发描述文件等等选项,这里选发布描述文件。
0_1495769243898_间隔.png
0_1495769049038_3.2.png
0_1495769243898_间隔.png
3.3选择APP IDs,之前在开发者中心创建的,这里会自动出现。
0_1495769243898_间隔.png
0_1495769041573_3.3.png
0_1495769243898_间隔.png

3.4勾选刚创建的发布证书关联好,输入name,点击ok
0_1495769243898_间隔.png

0_1495769033401_3.4.png
0_1495769243898_间隔.png

3.5此时生成了发布描述文件,点击 Download下载到电脑保存

0_1495769025141_3.5.png
0_1495769243898_间隔.png

四、上传证书编译打包iOS

0_1495769243898_间隔.png

楼主做了几个开发工具的打包教程、可以参考下

xcode打包教程

HBuilder打包教程

APICloud打包教程

phonegap打包教程

0_1495769243898_间隔.png
这里以APPcan平台为例

0_1495769243898_间隔.png
4.1、选择完工的APP,点击应用打包,选择证书管理,iOS发布证书,上传之前申请的发布证书和申请时设置的密码及发布描述文件

点击检验,APP ids将自动出现,然后保存。

0_1495769243898_间隔.png
0_1495769009930_5.1.png
0_1495769243898_间隔.png
4.2选择云端打包,iOS打包,没有推送证书就不勾选支持推送,选择发布版证书,禁用http请求容易审核通过。
0_1495769243898_间隔.png
设置好版本号,选择appcan服务器,点击生成安装包。
0_1495769243898_间隔.png
1_1495768998277_5.3.png 0_1495768998277_5.2.png
0_1495769243898_间隔.png
4.3安装包生成成功后,下载保存到电脑,这个ipa包就可以上传到App Store了
0_1495769243898_间隔.png
0_1495768978829_5.4.png
0_1495769243898_间隔.png
五、在iTunes Connect创建App
0_1495769243898_间隔.png

5.1回到软件,点击ItunerConnect,进入iTunes Connect进入创建APP。
0_1495769243898_间隔.png

0_1495768963283_4.1.png
0_1495769243898_间隔.png

5.2选择我的APP点击左上角+号选择新建APP,输入你的应用名称,语言,套装ID,之前在开发者中心创建的对应APP IDs

(与创建iOS证书所选的appid要一致,这样用iOS证书打包的IPA,上传就能关联到了)

sku不能写中文,点击创建。
0_1495769243898_间隔.png
0_1495767942877_4.1.png

0_1495769243898_间隔.png
5.3这里提示要用到Xcode或者Application loader提交IPA,等下用Appuploader直接在Windows环境下上传ipa,不用Mac了。

现在APP各项信息都没填写,等下把IPA上传成功了再填写。

这里构建版本旁边还没有出现+号,后面上传了IPA就会出现,等下会用到。
0_1495769243898_间隔.png

0_1495768860246_4.5.png
0_1495769243898_间隔.png

六、Windows环境下上传IPA到App Store

0_1495769243898_间隔.png
6.1打开Appuploader程序,点击Upload.

0_1495768843604_6.1.png
0_1495769243898_间隔.png
6.2选择刚生成的iap包
0_1495769243898_间隔.png
0_1495768836696_6.2.png
0_1495769243898_间隔.png
6.3Appuploader将自动上传你的IPA,当出现以下提示时,说明上传成功,点击deail可以查看APP信息。
0_1495769243898_间隔.png
0_1495768829313_6.3.png
0_1495769243898_间隔.png
七、回到iTunes Connect提交审核

0_1495769243898_间隔.png
7.1上传好了IPA,然后回到iTunes Connect,进入填写信息的页面,下边有一个构建版本的选项,之前这旁边没有+号的,如果上传成功了,过几分钟旁边会出现一个加号按钮,点击一下+然后会出现你刚上传的APP,点击完成即可。
0_1495769243898_间隔.png
注意:如果一段时间后没有出现+号,可能ipa不符合要求,会有个反馈邮件过来,根据反馈的问题修改后重新上传。
0_1495769243898_间隔.png
0_1495768819259_7.1.jpg
0_1495769243898_间隔.png
7.2然后设置好APP相关的信息,截图、类别,价格、销售服务等。
0_1495769243898_间隔.png
0_1495767824373_7.1.jpg
0_1495769243898_间隔.png
7.2设置好相应的APP信息后,点击提交以供审核。
0_1495769243898_间隔.png
0_1495768804184_7.2.png
0_1495769243898_间隔.png
7.3提交审核回到我的APP查看会提示正在等待审核,审核要几天时间,常登陆看看审核情况

如果变成可供销售,恭喜你~上架成功了,如果显示被拒绝,点击查看问题,根据反馈修改再重新上传。
0_1495769243898_间隔.png
0_1495768797229_7.3.png

[阅读全文]

听晴明老师从头讲React Native
2017-04-28 by 三十

0_1493305849286_banner-01.png

死活都要赶在五一前更新个状态/傲娇脸!
五一大家都干嘛呢,虽然是休假但也要保持学习呀。

我们3月份的时候已经发布了晴明老师精心录播的系列课程,前两天我们又拓宽了发布渠道,同学们现在可以通过腾讯课程和网易云课堂两个平台,看到我们的这个课程。点击下方图片开始选择一个平台学习吧 >>

0_1493302453385_腾讯课堂链接.png

0_1493302442976_网易云课堂链接.png

[阅读全文]

2017年4月22日React Native中文网在NJSD2017大会上的分享
2017-04-24 by 晴明

主题:React Native的傻瓜式优化

时间:2017年4月22日

地点:南京国际博览会议中心

视频:待主办方公开

PPT:https://pan.baidu.com/s/1hsywyOW

[阅读全文]

[实时动态]3.8日苹果警告邮件事件
2017-03-08 by tdzl2003

目前的反馈

目前较多反馈同时使用了RN 和 crash 收集统计插件的开发者更多的收到了此邮件。

大部分RN开发者表示暂未收到此邮件。

今天有多个使用RN开发的应用通过审核,包括使用了热更新功能的,暂未接到反馈有因为使用RN而被拒审的案例。

目前有未收到邮件的应用发布了热更新,暂未受到警告。

该做什么

如果您没收到对应的警告邮件,那可以先安心吃瓜,不必过于警惕。

如果您已经收到了对应的邮件,请首先排查第三方库,尤其是涉及统计分析、crash收集、以及性能分析的插件。

暂时避免或减少涉及功能变更的热更新。

禁止通过热更新彻底改变应用功能和应用目的。这是明确违反苹果开发者协议的。

[阅读全文]

新手理解Navigator的教程
2015-11-20 by mozillo

注意!从RN 0.43版本开始,官方将停止维护Navigator,建议大家迁移到新的react-navigation库(文档地址需翻墙)。新的导航库无论从性能还是易用性上都要大大好于老的Navigator!

下面是一个简单的例子,用Navigator来跳转页面,页面之间传递参数 (代码是ES6语法写的):

		
	import React from 'react';
    import {
		View,
		Navigator
	} from 'react-native';
	import FirstPageComponent from './FirstPageComponent';
	
	export default class SampleComponent extends React.Component {
		render() {
			let defaultName = 'FirstPageComponent';
			let defaultComponent = FirstPageComponent;
			return (
	        <Navigator
	          initialRoute={{ name: defaultName, component: defaultComponent }}
	          configureScene={(route) => {
	            return Navigator.SceneConfigs.VerticalDownSwipeJump;
	          }}
	          renderScene={(route, navigator) => {
	            let Component = route.component;
	            return <Component {...route.params} navigator={navigator} />
	          }} />
			);
		}
	} 
[阅读全文]

React Native 0.32以下版本Xcode8报错解决办法
2016-09-20 by tdzl2003

RCTSRWebSocket.m报错

Ignoring return value of function declared with warn_unused_result attribute

这个报错在此文件中有两处,代码

SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key);

修改为

(void)SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key);

前面加上(void)

RCTScrollView.m 报错

Use of undeclared identifier '_refreshControl'; did you mean 'refreshControl'?

@implementation RCTCustomScrollView
{
  __weak UIView *_dockedHeaderView;
  RCTRefreshControl *_refreshControl;  // 加入此行
}
[阅读全文]

1月22日直播:天地之灵——案例开讲Mobx
2017-01-15 by tdzl2003

我们不要用我们搞不懂的技术框架

是不是在15年听老前辈们洗脑说redux是唯一的选择,结果到最后换来的却是从入门到放弃?快来入坑mobx吧~
本次直播,天地之灵老师会就两个框架的优缺点进行一定程度的对比,帮助您在项目中更好的选择合适的框架。

可能是2016年国内用mobx做过最多不同项目的人

天地之灵所带领的团队,在2016年初就逐步停用了redux,经过一段时间的尝试之后(可能部分群友们对我们当时推荐的Async + Subscribe还有印象),我们选定了mobx作为我们下一代的数据框架。
到目前为止,天地之灵的团队已经将mobx应用在了5个不同的网站项目、3个不同的App项目中,面对了管理后台、电商、工具、社交、新闻等多种不同类型的项目,对基于mobx的数据业务层搭建积累了丰富的经验。

不仅仅是数据框架

mobx的模型不仅仅可以用于项目的数据业务层,还可以在视图层发挥很多的价值。
Why? How? 且听天地之灵老师周日分享讲解几个典型场景。

你以为这样就搞懂mobx了?

掌握一个框架,不仅仅是掌握它的使用,我们还要对它的内部运作机理有一定的了解,这样,我们才可以在使用的时候更少的遇到问题。
周日我们会在简单讲解mobx运行原理的基础之上,关注如何避免写出“坑”的代码,以及如何提高代码的性能。让你不仅仅是会用,更能写出健壮的代码。

1月22日周日晚20:00 - 22:00,由天地之灵老师通过案例深入讲解mobx的使用,欢迎大家收看。

直播地址(已结束):斗鱼http://www.douyu.com/tdzl/
优酷录播:http://v.youku.com/v_show/id_XMjQ4NTA4OTUwOA==.html

0_1484624108494_1.22直播群-01.jpg

[阅读全文]

React Native中文网开直播啦!
2017-01-24 by 三十

2017.2.14

各位情人节快乐!😜
情人节没有什么好送大家的,来段视频给大家解解闷呗😆
2017.2.12:「React Native优化技巧分享」

[阅读全文]

React Native安卓端的一些注意事项
2017-01-12 by erhutime

React-native for Android:

标签: react-native

[阅读全文]

新年小目标:晴明老师教你用ReactNative清空购物车!1月15日直播
2017-01-10 by 晴明

直播已结束,感谢大家捧场,欢迎批评指正。

视频:
优酷录播
百度云盘下载

源代码:
https://github.com/sunnylqm/ShopCartDemo

下期直播预告:
http://bbs.reactnative.cn/topic/3337/
咱们下周同一时间(1月22日周日晚八点),由天地之灵老师通过案例深入讲解mobx的使用,欢迎大家同一时间收看。我们在接下来的一周也会对相关的问题进行收集,请大家关注本帖和QQ群的相关通知

[阅读全文]

苹果从17年元旦开始强制要求HTTPS?!官方表示你们误会了
2016-09-20 by 晴明

长期以来流传一则消息,苹果将于17年1月1日收紧ats政策,强制接口使用HTTPS协议。依据据说是6月14日开发者大会上的演讲。

但是苹果真的这么激进吗?苹果开发者论坛里一名官方人员近日对此进行了澄清:

[阅读全文]

ReactNative安卓首屏白屏优化
2016-04-07 by cnsnake11

原文地址

问题描述

公司现有app中部分模块使用reactnative开发,在实施的过程中,rn良好的兼容性,极佳的加载、动画性能,提升了我们的开发、测试效率,提升了用户体验。

但是,在android中,当点击某个rn模块的入口按钮,弹出rn的activity到rn的页面展现出来的过程中,会有很明显的白屏现象,不同的机型不同(cpu好的白屏时间短),大概1s到2s的时间。

注意,只有在真机上才会有此现象,在模拟器上没有此现象完全是秒开。ios上也是秒开,测试的最低版本是ios7,iphone4s。

reactnative版本0.20.0。

jsbundle文件大小717kb。


优化效果

经过了大量的源码阅读,和网上资料查找,最终完美的解决了这个问题,无论什么机型,都可以达到秒开,如图(虽然下图是模拟器的截图,但是真机效果基本一样):

优化过程

时间分布

一般优化速度问题,首先就是要找到时间分布,然后根据二八原则,先优化耗时最长的部分。

android集成rn都会继承官方提供的ReactActivity

public class MainActivity extends ReactActivity {

然后只在自己的activity中覆盖一些配置项。

在官方的ReactActivity中的onCreate方法中

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
      // Get permission to show redbox in dev builds.
      if (!Settings.canDrawOverlays(this)) {
        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        startActivity(serviceIntent);
        FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
        Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
      }
    }

    mReactInstanceManager = createReactInstanceManager();
    ReactRootView mReactRootView = createRootView();
    mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
    setContentView(mReactRootView);
  }

最慢的就是这两行代码,占了90%以上的时间。

ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());

这两行代码就是把jsbundle文件读入到内存中,并进行执行,然后初始化各个对象。

优化思路---内存换时间

在app启动时候,就将mReactRootView初始化出来,并缓存起来,在用的时候直接setContentView(mReactRootView),达到秒开。

步骤1 缓存rootview管理器

缓存rootview管理器主要用于初始化和缓存rootview对象。

import android.app.Activity;
import android.os.Bundle;
import android.view.ViewParent;

import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;

import java.lang.reflect.Field;

/**
 * 缓存view管理
 */
public class RNCacheViewManager {

    private static ReactRootView mRootView = null;
    private static ReactInstanceManager mManager = null;
    private static AbsRnInfo mRnInfo = null;

    //初始化
    public static void init(Activity act, AbsRnInfo rnInfo) {
        init(act, rnInfo, null);
    }

    public static void init(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {
        if (mManager == null) {
            updateCache(act, rnInfo, launchOptions);
        }
    }

    public static void updateCache(Activity act, AbsRnInfo rnInfo) {
        updateCache(act, rnInfo, null);
    }

    //更新cache,适合于版本升级时候更新cache
    public static void updateCache(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) {
        mRnInfo = rnInfo;
        mManager = createReactInstanceManager(act);
        mRootView = new ReactRootView(act);
        mRootView.startReactApplication(mManager, rnInfo.getMainComponentName(), launchOptions);
    }

//设置模块名称,因为是private,只能通过反射赋值
    public static void setModuleName(String moduleName) {
        try {
            Field field = ReactRootView.class.getDeclaredField("mJSModuleName");
            field.setAccessible(true);
            field.set(getReactRootView(), moduleName);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
    
//设置启动参数,因为是private,只能通过反射赋值
    public static void setLaunchOptions(Bundle launchOptions) {
        try {
            Field field = ReactRootView.class.getDeclaredField("mLaunchOptions");
            field.setAccessible(true);
            field.set(getReactRootView(), launchOptions);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static ReactRootView getReactRootView() {
        if(mRootView==null){
            throw new RuntimeException("缓存view管理器尚未初始化!");
        }
        return mRootView;
    }

    public static ReactInstanceManager getReactInstanceManager() {
        if(mManager==null){
            throw new RuntimeException("缓存view管理器尚未初始化!");
        }
        return mManager;
    }

    public static AbsRnInfo getRnInfo() {
        if(mRnInfo==null){
            throw  new RuntimeException("缓存view管理器尚未初始化!");
        }
        return mRnInfo;
    }

    public static void onDestroy() {
        try {
            ViewParent parent = getReactRootView().getParent();
            if (parent != null)
                ((android.view.ViewGroup) parent).removeView(getReactRootView());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static void clear() {
        try {
            if (mManager != null) {
                mManager.onDestroy();
                mManager = null;
            }
            if (mRootView != null) {
                onDestroy();
                mRootView = null;
            }
            mRnInfo = null;
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private static ReactInstanceManager createReactInstanceManager(Activity act) {

        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
                .setApplication(act.getApplication())
                .setJSMainModuleName(getRnInfo().getJSMainModuleName())
                .setUseDeveloperSupport(getRnInfo().getUseDeveloperSupport())
                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);

        for (ReactPackage reactPackage : getRnInfo().getPackages()) {
            builder.addPackage(reactPackage);
        }

        String jsBundleFile = getRnInfo().getJSBundleFile();

        if (jsBundleFile != null) {
            builder.setJSBundleFile(jsBundleFile);
        } else {
            builder.setBundleAssetName(getRnInfo().getBundleAssetName());
        }
        return builder.build();
    }
}

步骤2 重写ReactActivity

将官方的ReactActivity粘出来,重写2个方法,onCreate和onDestroy,其余代码不动。

onCreate方法中使用缓存rootview管理器来获得rootview对象,而不是重新创建。

这里曾尝试继承ReactActivity,而不是重写这个类,但是子类覆盖onCreate方法时候,必须要调用super.onCreate,否则编译会报错,但是super.onCreate方法会重新创建rootview,所以实在是绕不过去了。

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (RNCacheViewManager.getRnInfo().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
            // Get permission to show redbox in dev builds.
            if (!Settings.canDrawOverlays(this)) {
                Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                startActivity(serviceIntent);
                FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
                Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
            }
        }

        mReactInstanceManager = RNCacheViewManager.getReactInstanceManager();
        ReactRootView mReactRootView = RNCacheViewManager.getReactRootView();
        setContentView(mReactRootView);
    }

onDestroy方法中,不能再调用原有的mReactInstanceManager.destroy()方法了,否则rn初始化出来的对象会被销毁,下次就用不了了。同时,要卸载掉rootview的parent对象,否则下次再setContentView时候回报错。

protected void onDestroy() {
        RNCacheViewManager.onDestroy();
        super.onDestroy();
    }

RNCacheViewManager.onDestroy的方法:

public static void onDestroy() {
        try {
            ViewParent parent = getReactRootView().getParent();
            if (parent != null)
                ((android.view.ViewGroup) parent).removeView(getReactRootView());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

步骤3 在app启动时候初始化缓存rootview管理器

RNCacheViewManager.init((Activity) context, new RnInfo(moduleName, launchOptions));

其中RnInfo如下:

public class RnInfo extends AbsRnInfo {

    private String mModuleName;
    private Bundle mLaunchOptions;

    public RnInfo(String moduleName) {
        this.mModuleName = moduleName;
    }

    public RnInfo(String moduleName, Bundle launchOptions) {
        this.mModuleName = moduleName;
        this.mLaunchOptions = launchOptions;
    }

    @Nullable
    @Override
    public Bundle getLaunchOptions() {
        return mLaunchOptions;
    }

    @Override
    public String getMainComponentName() {
        return mModuleName;
    }

    @Override
    public String getJSMainModuleName() {
        return RNKeys.Default.DEf_JS_MAIN_MODULE_NAME;
    }

    @Nullable
    @Override
    public String getJSBundleFile() {
        return RNManager.getJsBundlePath();
    }

    @Override
    public boolean getUseDeveloperSupport() {
        return true;
    }

    @Override
    public List<ReactPackage> getPackages() {
        return Arrays.asList(
                new MainReactPackage(),
                new BBReactPackage()
        );
    }
}

结语

希望本篇文档能帮助遇到类似问题的小伙伴们。

reactnative虽然不是银弹,但是在目前移动端浏览器兼容性弱爆了的情况下,还是能极大的提升开发测试效率的,性能也是极佳的,看好rn的未来。

#参考文章
http://zhuanlan.zhihu.com/magilu/20587485

http://zhuanlan.zhihu.com/magilu/20259704

https://yq.aliyun.com/articles/3208?spm=5176.100239.yqblog1.39.t2g49u&utm_source=tuicool&utm_medium=referral

[阅读全文]