react native 入门

287 阅读6分钟

目录

0. 概述

1. React

2. React Native

3. 总结

React 和 React Native

是什么:

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。 React 不是一个 MVC 框架,仅仅是视图(V)层的库。两年后,Facebook开源了React Native。

使用JavaScript和React编写原生移动应用

React Native使你只使用JavaScript也能编写原生移动应用。 它在设计原理上和React一致,通过声明式的组件机制来搭建丰富多彩的用户界面。

React Native应用是真正的移动应用

React Native产出的并不是“网页应用”, 或者说“HTML5应用”,又或者“混合应用”。 最终产品是一个真正的移动应用,从使用感受上和用Objective-C或Java编写的应用相比几乎是无法区分的。 React Native所使用的基础UI组件和原生应用完全一致。 你要做的就是把这些基础组件使用JavaScript和React的方式组合起来。

Learn once, write anywhere(也就是一次学习,多次开发,而不是一次开发跨平台运行)

组件化开发(基于React的概念)

为什么学:

关于前端技术调查

星星数
使用Javascript写出近乎原生的流畅,实在是让人惊喜~

React

holle world

// 有删减,引入React,React Native,Child组件
export default class Father extends Component {
    constructor(props){
        this.state = {
            textFather: 'init'
        }
    }
    changeText(){
        this.setState({ textFather: 'hello world' })
    }
    render() {
        return (
            <View style={styles.container}>
                <Child text={this.state.textFather}></Child>
                <TouchableOpacity onPress={this.changeText.bind(this)}>
                    <Text style={styles.button}>修改Text</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

export default class Child extends Component {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.text}>
                    child:{this.props.text}
                </Text>
            </View>
        );
    }
}

View = function(Date)

虚拟DOM(Virtual DOM)

  1. 用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中
目前,在浏览器中存在四种形态的对象
超轻量 Object.create(nulll)
轻量 一般的对象 {}
重量 带有访问器属性的对象, avalon或vue的VM对象
超重量 各种节点或window对象

有了虚拟DOM,我们是使用够轻量的对象代替超重对象作为直接操作主体,
减少对超重对象的操作
  1. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
Diff 和 Batching

Diff:
// 旧
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

// 新
<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

在没有key属性时执行过程:
React将改变每一个子删除重新创建,而非保持 <li>Duke</li><li>Villanova</li> 不变

// 旧
<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

// 新
<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>
执行过程:
现在 React 知道带有key '2014' 的元素是新的,对于 '2015' 和 '2016' 仅仅移动位置即可

Batching: 把所有的DOM操作搜集起来,一次性提交给真实的DOM. 
  1. 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了

JSX

...
render() {
    return (
        <View style={styles.container}>
            <Text style={styles.text}>
                child:{this.props.text}
            </Text>
        </View>
    );
}
...
  1. JSX 的语法需要通过 babel-preset-react 编译后, 才能被解析执行, 点击查看

JSX转译

<div classname="container">
    <p>hello</p>
    {
        someCondition? <p style={{color: red}}>what?</p> : null
    }
    <p>world</p>
</div>


==>

React.createElement(
    "div",
    { classname: "container" },
    React.createElement(
        "p",
        null,
        "hello"
    ),
    someCondition ? React.createElement(
        "p",
        { style: { color: red } },
        "what?"
    ) : null,
    React.createElement(
        "p",
        null,
        "world"
    )
);
  1. JSX 是Javascript
  2. 在 JSX 中只能使用表达式,但是不能出现语句
<View>
{ list.map(item => { <Text>{item.name}</Text> }) }
</View>
  1. 在 JSX 中注释语法:{/* 中间是注释的内容 */}
  2. React 和 React Native 的 JSX 写法有些许的不同, 以及 JSX 和 HTML 也有些不同

优点:可组合型,压缩,提高抽象程度,简洁

组件

View = function(Date)

Father.js

export default class Father extends Component {
    constructor(props){
        this.state = {
            textFather: 'init'
        }
    }
    changeText(){
        this.setState({ textFather: 'hello world' })
    }
    componentWillMount() {
        console.log('Father componentWillMount')
    }
    componentDidMount() {
        console.log('Father componentDidMount')
    }
    componentWillUnmount() {
        console.log('Father componentWillUnmount')
    }
    render() {
        console.log('Father render')
        return (
            <View style={styles.container}>
                <Child text={this.state.textFather}></Child>
                <HeartBeat></HeartBeat>
                <TouchableOpacity onPress={this.changeText.bind(this)}>
                    <Text style={styles.button}>修改Text</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

child.js

export default class Child extends Component {
    componentWillMount() {
        console.log('Child componentWillMount')
    }
    componentDidMount() {
        console.log('Child componentDidMount')
    }
    componentWillReceiveProps(nextProps) {
        console.log('Child componentWillReceiveProps')
        console.log(nextProps)
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log('Child shouldComponentUpdate')
        console.log(nextProps)
        console.log(nextState)
        return true;
    }
    componentWillUpdate(nextProps, nextState) {
        console.log('Child componentWillUpdate')
        console.log(nextProps)
        console.log(nextState)
    }
    componentDidUpdate(prevProps, prevState) {
        console.log('Child componentDidUpdate')
        console.log(prevProps)
        console.log(prevState)
    }
    componentWillUnmount() {
        console.log('Child componentWillUnmount')
    }
    render() {
        console.log('Child render')
        return (
            <View style={styles.container}>
                <Text style={styles.text}>
                    child:{this.props.text}
                </Text>
            </View>
        );
    }
}

heartBeat.js

export default class HeartBeat extends Component {
    componentDidMount() {
        console.log('HeartBeat componentDidMount')
        this.timer = setInterval(() => {
            console.log('HeartBeat check')
        }, 5000);
    }
    componentWillUnmount() {
        console.log('HeartBeat componentWillUnmount')
        this.timer && clearInterval(this.timer);
    }
    render() {
        return null;
    }
}

组件的生命周期

生命周期 调用次数 能否使用 setSate()
componentWillMount() 1
componentDidMount() 1
componentWillReceiveProps(nextProps ) >=0
render() >=1
shouldComponentUpdate(nextProps, nextState) >=0
componentWillUpdate(nextProps, nextState) >=0
componentDidUpdate(prevProps, prevState) >=0
componentWillUnmount() 1
  1. 调用次数不确定的生命周期函数,一定要是一个纯函数,不能有副作用
  2. render的返回值是 element/Array<element>, shouldComponentUpdate返回值为bool
  3. 生命周期函数随RN版本可能变动,服务端渲染和浏览器运行也有区别

组件间传值

father.js

export default class Father extends Component {
    constructor(props){
        super(props);
        this.state = {
            textFather1: 'init1',
            textFather2: 'init2',
            textFather3: 'init3'
        }
    }
    changeText1(){
        this.setState({
            textFather1: 'hello world1'
        })
    }
    changeText2(){
        this._child2.updateText('hello world2');
    }
    changeText3(){
        DeviceEventEmitter.emit('update-text', 'hello world3')
    }
    render() {
        return (
            <View style={styles.container}>
                <Child1 text={this.state.textFather1}></Child1>
                <TouchableOpacity onPress={this.changeText1.bind(this)}>
                    <Text style={styles.button}>change text by props</Text>
                </TouchableOpacity>
                <Child2 text={this.state.textFather2} ref={(element) => {this._child2 = element}}></Child2>
                <TouchableOpacity onPress={this.changeText2.bind(this)}>
                    <Text style={styles.button}>change text by refs</Text>
                </TouchableOpacity>
                <Child3 text={this.state.textFather3} text={this.state.textFather3}></Child3>
                <TouchableOpacity onPress={this.changeText3.bind(this)}>
                    <Text style={styles.button}>change text by DeviceEventEmitter</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

child1.js

export default class Child1 extends Component {
    constructor(props) {
        super(props)
    }
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.text}>
                    child:{this.props.text}
                </Text>
            </View>
        );
    }
}

child2.js

export default class Child2 extends Component {
    constructor(props) {
        super(props)
        this.state = {
            text: this.props.text
        }
    }
    updateText(text) {
        this.setState({
            text
        })
    }
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.text}>
                    child:{this.state.text}
                </Text>
            </View>
        );
    }
}

child3.js

export default class Child3 extends Component {
    constructor(props) {
        super(props)
        this.state = {
            text: this.props.text
        }
    }
    componentDidMount() {
        this.listener = DeviceEventEmitter.addListener('update-text', (text) => {
            this.setState({text: text})
        })
    }
    componentWillUnmount() {
        this.listener.remove();
    }
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.text}>
                    child:{this.state.text}
                </Text>
            </View>
        );
    }
}

React Native

hello world

使用 React Native 命令行工具来创建一个名为"helloworld"的新项目:

react-native init helloworld

提示:你可以使用--version参数(注意是两个杠)创建指定版本的项目。例如react-native init helloworld --version 0.55.4。注意版本号必须精确到两个小数点。今天,2018/08/27,推荐使用0.55.4,虽然新出了0.56和0.57rc,但是还有一些问题,比如windows安装不上。

没说到的东西

  • 布局,flex
  • 性能优化
  • 路由
  • 调试
  • 手势
  • 组件和API

对比优劣分析

VS Hybrid

  • 体验极好
  • 学习成本高,效率较低,跨平台八成代码公用,需要原生支持,SDK升级

VS Weex

  • 不要用Weex

第三方库

  • react-navigator
  • react-native-router-flux

  • realm

  • lottie-react-native

  • react-native-vector-icons

  • react-native-parallax-scroll-view

总结

一切皆组件

组件单一责任

生命周期纯函数

PS.Flutter感觉很好用