react

React-Native Native Module In Practise

React-Native 是Facebook 开发的一套移动端跨平台开发的框架,其脱胎于 ReactJS 。React-Native允许你使用JavaScript开发移动端的应用,相对于 PhoneGap React-Native 确实带来了原生应用的性能,如果之前有写过ReactJS,用React-Native开发一个移动端应用是非常容易的。不过虽然React-Native社区一天天壮大,但是仍旧可能需要自己开发一些React-Native的原生模块。

什么是React-Native原生模块

React-Native 模块就是包含原生代码库的React-Native的模块(在Android 里是包含Java代码库,在iOS里是包含Objecive-C/Swift代码库),其工作原理是将原生代码库加载到React-Native的包管理器中,然后用JS通过React-Native的bridge调用原生库里的方法,最后返回结果。

Setup
创建React-Native项目
  1. 创建工作目录mkdir hello-rct
  2. 使用npm init 初始化项目
  3. 使用android sdk自带的cli工具初始化一个android library项目
android create lib-project \  
   --name hellorct \                # Project name
   --target android-23 \            # Android SDK version(target)
   --package com.xeodou.hellorct \  # Package name
   --gradle --gradle-version 1.3+ \ # Use gradle and 2.4 later version
   --path android                   # The project's directory

4.在android/build.gradle最后添加

dependencies {  
    compile 'com.facebook.react:react-native:0.13.+'
}

5.创建Java组件
添加HelloRCTManager.java

/*
* @Author: xeodou
* @Date:   2015
*/
package com.xeodou.hellorct

import com.facebook.react.ReactPackage;  
import com.facebook.react.bridge.JavaScriptModule;  
import com.facebook.react.bridge.NativeModule;  
import com.facebook.react.bridge.ReactApplicationContext;  
import com.facebook.react.uimanager.ViewManager;  
import java.util.ArrayList;  
import java.util.Collections;  
import java.util.List;

public class HelloRCTManager implements ReactPackage {

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }
    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new HelloRCT(reactContext));
        return modules;
    }
}

创建HelloRCT.java

/*
* @Author: xeodou
* @Date:   2015
*/

package com.xeodou.hellorct

import com.facebook.react.bridge.ReactApplicationContext;  
import com.facebook.react.bridge.ReactContextBaseJavaModule;  
import com.facebook.react.bridge.ReactMethod;  
import com.facebook.react.bridge.Callback;

public class HelloRCT extends ReactContextBaseJavaModule {

    public static final String REACT_CLASS = "HelloRCT";

    public HelloRCT(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return REACT_CLASS;         //这里的getName方法是将你的原生模块注册
    }                               //到React-Native的模块包管理器中,然后
                                    //在使用JS使用相同的名字获得该原生模块
    @ReactMethod                    // ReactMethod表明该方法是暴露给JS的
    public void hello(Callback cb) {// 的外部方法,返回一个字符串`Hello RCT`
        cb.invoke("Hello RCT");     // React-Native的调用是异步的
    }                               // 所以这里我们用Callback返回结果
}

6.创建index.android.js

/*
* hello-rct - index.android.js
* Copyright(c) 2015 xeodou <[email protected]>
* MIT Licensed
*/

var { NativeModules } = require('react-native');  
module.exports = NativeModules.HelloRCT;  

7.使用react-native init Example创建一个React-Native的Example应用测试我们的模块

  • 修改Example/settings.gradle
include ':app','hellorct'  
project(':hellorct').projectDir = new File(rootProject.projectDir, '../../android')  
  • 修改Example/app/build.gradle
dependencies {  
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.facebook.react:react-native:0.13.+'
    compile project(':hellorct')  // <- 添加这行
}
  • 修改Example/app/src/main/java/.../MainActivity.java
import com.xeodou.hellorct.HelloRCTManager; //<- import java class

public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {  
     ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .addPackage(new HelloRCTManager())  //<- 注册模块
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        mReactRootView.startReactApplication(mReactInstanceManager, "Example", null);

        setContentView(mReactRootView);
    }
...
}
  • 修改Example/index.android.js
...
var { NativeModules } = require('react-native');  
var HelloRCT = NativeModules.HelloRCT;

var Example = React.createClass({  
  getInitialState: function() {
    return {};
  },
  click: function() {
    HelloRCT.hello( msg => {
      this.setState({
        hello: msg
      })
    })
  },

  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Hi, {this.state.hello}
        </Text>
        <TouchableHighlight onPress={this.click}>
            <Text >Press!</Text>
        </TouchableHighlight>
      </View>
    );
  }
});
...

8.cd Example && react-native run-android

Cheers!!

introduce deku

introduce deku

dekusegment.com 公司刚刚发布的开源前端框架,语法和工作原理都和 react 极为相像。但是也有很不多不同点,比如更轻量级,但是只支持现代浏览器, 取消了class的概念。

Components

在 React 中每一个component都是一个class,你可以这样

class Button extends React.Component {  
  render() {
    let {type} = this.props
    return <button type={type}>Hey, i a button!!</button>
  }
}
Button.propTypes = {  
    type: React.PropTypes.string
}

在 deku 没有class的概念,相当于每个文件是一个component,在deku中你创建一个component像这样 Button.js

let propTypes = {  
  type: {
    type: 'string'
  }
}
render(component) {  
  let {props} = component
  let {type} = props
  return <button type={type}>Hey, i am also a button!!</button>
}
export default {propTypes, render}  

从上面的对比中可以看到,deku的component中把component本身作为了render函数的参数,所以在component中不在需要this

Application & Data

React 中如果你需要 render 一个application你只需要React.render(<Button />, document.body); 既可。
但是在deku中你需要

import {tree,render,renderString, element} from 'deku'  
let app = tree(<List items={items}/>)  
render(app, document.body)  

deku 中tree是用来管理所有的component和数据交换,所以就是说你用像react一样在引入另外一个库flux
你可以直接在tree上添加你需要数据

/*app.js*/
let items = []  
let app = tree(<List items={items}/>)  
app.set('removeTodo', function(v) {  
  items = items.filter(i => {
    return i != v
  })
  app.mount(
    <List items={items} />
  )
})

异步获取数据

app.set('updae', function (item, updates) {  
  api.list.update(item, updates)
})

deku的体积只有~10k,但是遗憾的是指支持现代浏览器

我写了个简单的todo list demo,在github上。
了解更多可以去deku 的官方仓库 https://github.com/segmentio/deku

Reactjs 性能优化,系列一

引言

ReactJs是Facebook开发的一个前端库,介绍ReactJs的文章很多,也可以去ReactJs的主页查看官方文档,文档的内容很少,示例也很详细。

Flux是基于的Reactjs的不能算作前端框架的前端框架,Flux并不提供一个完整的前端框架,只是提供一个Reactjs前端的实现,事实上就是推荐一套应用结构,而不提供具体实现。

前文

在结构复杂的ReactJs Application中,其结构往往都是基于树形结构, 类似与:

                         ------------- App Component ---------------  
                         |             |              |            |  
                         |             |              |            |  
                      Message       Project        Other          ...  
                         |             |              |  
                       ------         ------         ------  
                       |    |         |    |         |    |  

在App Component中通常我们会有

在上面的示例代码中,App Component 去监听AppStore的改变,然后在onChange中去改变App Component的state。比如当我执行AppActionCreator.showMessage('Show a Message')的时候,App Component就触发onChange方法改变state,这个时候就会触发render方法,将state的值向下传递,这时候整个应用都会沿着上面的树形结构一级一级向下执行render方法,这个是个不合理的情况,整个App都重新渲染的话,造成性能的消耗,虽然React有virtual DOM 来优化性能,但是这种做法可以在代码层面进行优化。

优化

对于一个Component来说应该只保存自己需要的状态,而且应该管理自己的状态,上面的例子中,App Component接受了Message Component的状态在通过自身的state改变传递给Message Component. 但是Message Component应该管理自身的状态(显示,显示内容)。

在Message Component中实现监听Message Component相关的数据变更,这时当触发AppActionCreator.showMessage('Show a Message') 方法的时候,所有的状态改变只会发生在Message Component内部,也只会传递给他的所有Children Component.

结论

Component是React中的组件,一个Component可以包含另外其他的Component, 但是同样的父级的状态变更也会传递给所有子级。所以降低Component间的耦合性很重要。
上面的Message Component的state改变仍然会传递给其所有子级,下一篇将会介绍如何最小程度减少Component 的render.