Name: Password: Sign in
Javascript 中的文章

本文在 署名-非商业性使用-相同方式共享 3.0 版权协议下发布, 转载请注明出自 kyleslight.net

React Native Practice


准备

  • Node : go to https://nodejs.org/en/
  • watchman : brew install watchman
  • flow : brew install flow
  • React Native Cli : npm install -g react-native-cli
  • NVM :
    • git clone https://github.com/creationix/nvm
    • cd nvm
    • source nvm.sh

创建项目

  • 初始化 : react-native init <projectname>
  • 项目入口 :open <yourproject>/ios/yourproject.xcodeproj
  • 运行
  • 修改 index.ios.js 然后 command + R 就能重新加载

了解 React

JSXTransformer.js 可以将 JSX 转译成 Javascript 和 HTML,我们可以把这样的工作从线上转移到线下。

  • react 工具 :npm install -g react-tools
<html>
    <head>
    </head>
    <body>
        <div id="example"></div>
        <script src="lib/react.min.js"></script>
        <script src="lib/JSXTransformer.js"></script>
        <script type="text/jsx">
            var Hello = React.createClass({
                render: function () {
                    return <h1>Hello {this.props.name}</h1>;
                }
            });
            React.render(<Hello name = "React" />, document.getElementById('example'));
        </script>
    </body>
</html>
  • 更改 this.state 更新视图
var Timer = React.createClass({
    getInitialState : function () {
        return {
            second: 0
        };
    },
    kick : function () {
        this.setState({second : this.state.second + 1});
    },
    componentDidMount : function () {
        this.interval = setInterval(this.kick, 1000);
    },
    componentWillUnmount : function () {
        clearInterval(this.interval);
    },
    render : function () {
        return <h1>Time Goes : {this.state.second} s</h1>
    }
});

React.render(<Timer />, document.getElementById('example'));
  • 加入事件动态更新
var ShowEditor = React.createClass({
                getInitialState : function () {
                    return {
                        value : "Kyles the Great!"
                    };
                },
                getChanged : function () {
                    this.setState({
                        value : React.findDOMNode(this.refs.textarea).value
                    });
                },
                render : function () {
                    return (
                        <div>
                            <h3>Input</h3>
                            <textarea
                                style = {{height: 100, width: 200}}
                                onChange={this.getChanged} 
                                ref="textarea" 
                                defaultValue = {this.state.value} />
                            <h3>Output</h3>
                            <p>{this.state.value}</p>
                        </div>
                    );
                }
            });
            React.render(<ShowEditor />, document.getElementById('example'));

React Native The First Time

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';
import React, {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  Image // add image class
} from 'react-native';

class MyInitial extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          欢迎来到 Kyles the Great 的世界
        </Text>
        <Image  style = {styles.myImage}
          source = {{uri : 'http://kyleslight.net/static/image/ky.png'}} >
        </Image>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#05A5D1',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  myImage: {
    width: 50,
    height: 50, 
    resizeMode: Image.resizeMode.contain
  }
});

AppRegistry.registerComponent('MyInitial', () => MyInitial);

效果如下图:

FlexBox

flexbox 是 W3C 的布局规范,目前主流的手机浏览器都支持这种规范,参见 http://caniuse.com/#feat=flexbox

伸缩容器有如下属性:

.flex-container {
    display: flex | inline-flex; /* 声明 flex 属性 */
    flex-direction: row(auto) | row-reverse | column | column-reverse; /* 主轴伸展方向 */
    flex-wrap: nowrap (auto) | wrap | wrap-reverse; /* 不换行;换行;换行垂直方向导置 */
    flex-flow: row wrap; /* 上面两者的缩写 */
    justify-content: flex-start(auto) | flex-end | center | space-between | space-around; /* 在主轴方向的对齐方式 */
    align-items: flex-start | flex-end | center | baseline | stretch (auto); /* 在交叉轴的对齐方式 */
    align-content: flex-start | flex-end | center | space-between | space-around | stretch(auto) /* 出现换行后在交叉轴的对齐方式 */

}

伸缩项目有如下属性:

.flex-item {
    order: integer; /* 排序;值越小越前 */
    flex-grow: 0(auto) | 1 | 2; /* 占领剩余空间 */
    flex-shrink: 1(auto) | x; /* 默认为1,空间不足缩小到其他项目的 1/x */
    flex-basis: auto | length; /* 剩余空间按比例伸缩 */
    flex: 0 1 auto (auto) | 0 0 auto (none); /* 上述三个属性缩写 */
    align-self: auto(stretch) | flex-start | flex-end | center | baseline;
}

React Native 的 flexbox

React Native 支持以下 flexbox 属性:

alignItems | alignSelf | flex | flexDirection | flexWrap | justifyContent

属性用法同上一节,只是使用了 camelCase

Flexbox Simulates Box Model in Web

.marginBox {
    position: relative;
    padding-left: 7px;
    padding-right: 7px;
}
.box {
    display: flex;
    height: 400px;
    width: 400px;
    flex-direction: column;
    flex: 1;
    justify-content: space-between;
    position: relative;
    color: #FDC72F;
    line-height: 1em;
}
.label {
    position: absolute;
    top: 0;
    left: 0;
    padding:0 3px 3px 0;
    background: #FDC72F;
    color: white;
}
.borderBox {
    display: flex;
    justify-content: space-between;
    flex: 1;
}
.top, .bottom {
    width: 100%;
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #6AC5AC;
}
.right, .left {
    width: 50px;
    display: flex;
    justify-content: space-around;
    align-items: center;
    background: #6AC5AC;
}
<h1>Box Model</h1>
<span class="marginBox">
    <span class="box">
        <span class="label">Margin</span>
        <span class="top">Top</span>
        <span class="borderBox">
            <span class="left">Left</span>
            <span class="right">Right</span>
        </span>
        <span class="bottom">Bottom</span>
    </span>
</span>

React Native Flexbox simlulates Box Model

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';
import React, {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View
} from 'react-native';

class MyInitial extends Component {
  render() {
    return (
      <View style={[BoxStyles.marginBox]} ref="lab1">
        <View style={[BoxStyles.box, BoxStyles.height400, BoxStyles.width400]}>
          <View style={[BoxStyles.topBottom, BoxStyles.height50, BoxStyles.bgred]}>
            <Text style={BoxStyles.yellow}>Top</Text>
          </View>
          <View style={[BoxStyles.borderBox]}>
            <View style={[BoxStyles.leftRight, BoxStyles.bgred]}>
              <Text style={BoxStyles.yellow}>Left</Text>
            </View>
            <View style={[BoxStyles.leftRight, BoxStyles.bgred]}>
              <Text style={BoxStyles.yellow}>Right</Text>
            </View>
          </View>
          <View style={[BoxStyles.topBottom, BoxStyles.height50, BoxStyles.bgred]}>
            <Text style={BoxStyles.yellow}>Bottom</Text>
          </View>
          <View style={[BoxStyles.label]}>
            <Text style={BoxStyles.white}>Margin</Text>
          </View>
        </View>
      </View>
    );
  }
}

const BoxStyles = StyleSheet.create({
  bgred: {
    backgroundColor: "#6AC5AC",
  },
  height50: {
    height: 50
  },
  height400: {
    height: 400
  },
  width400: {
    width: 400
  },
  box: {
    flexDirection: 'column',
    flex: 1,
    position: 'relative'
  },
  label: {
    position: 'absolute',
    top: 0,
    left: 0,
    paddingTop: 0,
    paddingRight: 3,
    paddingBottom: 3,
    paddingLeft: 0,
    backgroundColor: '#FDC72F'
  },
  topBottom: {
    justifyContent: 'center',
    alignItems: 'center'
  },
  leftRight: {
    width: 50,
    justifyContent: 'space-around',
    alignItems: 'center'
  },
  yellow: {
    color: '#FDC72F',
    fontWeight: '900'
  },
  white: {
    color: 'white',
    fontWeight: '900'
  },
  marginBox: {
    position: 'absolute',
    top: 100,
    paddingLeft: 7, 
    paddingRight: 7
  },
  borderBox: {
    flex: 1,
    justifyContent: 'space-between',
    flexDirection: 'row'
  }
});

AppRegistry.registerComponent('MyInitial', () => MyInitial);

JSX

JSX 是一种允许在 Javascript 中混入 HTML 的语法糖,它可以通过转化得到 Javascript 代码,从而避免直接在 Javascript 中创建组件嵌套结构的繁琐。

引入 JSX 的方式可以像之前写的那样,也可以用外联的方式载入:

// test.jsx
React.render(<h1>Well</h1>, document.getElementById('example'));
<!-- main.html -->
<script type="text/jsx" src="test.jsx"></script>

不过这种方式可能因为异步请求的协议是 file 被浏览器阻止,解决的方法是:

  • 换一个浏览器,例如 Firefox
  • 让静态资源通过服务器加载

使用 JSX 的好处书写标签可以像普通 HTML 标签一样自然:

<h1>Kyles the Great</h1>

可以定义自己的标签:

var MyComponent = React.createClass({
    render: function () {
        return (<h1>Kyles the Great</h1>);
    }
});
React.render(<MyComponent />, document.getElementById('example'));

可以签套标签:

var Author = React.createClass({
    render: function () {
        return (<h1>Kyles the Great</h1>);
    }
});

var MainText = React.createClass({
    render: function () {
        return (<p>This is main text</p>);
    }
});

var Article = React.createClass({
    render: function () {
        return (
            <div>
                <Author />
                <MainText />
            </div>
        );
    }
});
React.render(<Article />, document.getElementById('example'));

需要注意的是 render 返回的最外层只能暴露一个标签。

JSX 转换

解释 JSX

JSX 被解释时会转换成 Javascript 脚本,例如:

<h1>Where is my Kyles!</h1>

会产生一次 React.createElement 调用

React.createElement('h1', null, 'Where is my Kyles!');

React.createElement 接收三个参数

React.createElement(
    string/ReactClass Type, // HTML 标准标签或 ReactClass 自定义标签
    [object props], // 保存有属性值得对象
    [children...] // 子元素
);

调用的结果返回一个 ReactElement 对象

模板

在另一些时候我们会使用 模板

var text = 'Are you OK, Kyles?';

...
<span>{text}</span>
...

这样一种组合会被转换成:

var text = 'Are you OK, Kyles?';

React.createElement('span', null, text);

即相当于模板直接运行 Javascript 表达式并将结果插入其中

属性

普通的 HTML 元素可以通过标签来定义 属性ReactClass 元素也可以,使用这种方式可以直接赋予 props 对象中的属性:

<h1 answer="fine">I\'m very OK</h1>

会转换成:

React.createElement('h1', {answer: 'fine'}, 'I\'m very OK');

(需要注意的是,自定义属性如果不是 HTML 自带的属性或者以 data- 为前缀渲染后并不会出现在 DOM 中)

显示 HTML

确实需要显示 HTML 字符串时可以:

<div>{{_html: '<div>well</div>'}}</div>

事件绑定传参

var testClick = function (gift) {
    console.log('Are you OK, we will give everyone a '+ gift);
}

...
<button onClick={testClick.bind(this, 'band')}>Click Me</button>
...

组件

组件生命周期

组件在生命周期的不同的时机会触发一系列函数:

var LifeTest = React.createClass({
    // creating but not being initialized
    getDefaultProps: function () {
        // return default props
        console.log('getDefaultProps');
        return {};
    },
    // initializing
    getInitialState: function () {
        // return default state value
        console.log('getInitialState');
        return {};
    },
    componentWillMount: function () {
        // called before render
        console.log('componentWillMount');
    },
    render: function () {
        // return a virtual DOM
        console.log('render');
        return (<div>Are you OK, we will give a band to everyone!</div>);
    },
    componentDidMount: function () {
        // called fater render
        console.log('componentDidMount');
    },
    // updating
    componentWillReceiveProps: function () {
        // when props is changed or parent component calls setProps()
        console.log('componentWillReceiveProps');
    },
    shouldComponentUpdate: function () {
        // return a boolean to decide whether it should be updated
        console.log('shouldComponentUpdate');
        return true;
    },
    componentWillUpdate: function () {
        // before update
        console.log('componentWillUpdate');
    },
    componentDidUpdate: function () {
        // after update
        console.log('componentDidUpdate');
    },
    // destroying
    componentWillUnmount: function () {
        // before destoried
        console.log('componentWillUnmount');
    }
});
React.render(<LifeTest />, document.getElementById('lifeCycle'));

Facts:

  • getDefaultProps 用于处理 props 默认值,只要存在 React.createClass 就会被调用,其返回的对象会与父组件指定的 props 对象合并,被赋予为 this.props
  • this.state 改变会被 React 监听,并被主动触发 render 更新 DOM
  • 虚拟 DOM 是一个 JSON 对象,它会被映射成真实的 DOMrenderdiff 算法需要保证状态改变后只修改必要的 DOM

组件间通信

  • 父组件可以指定子组件的 this.props 将消息传递给子组件
  • 父组件可以通过 this.refs 获得子组件

Diff 算法

测试 Diff 算法的效果:

(function() {
    // Observe Object
    var changeNodes = [];
    var observer = new MutationObserver(function (mutations) {
        changeNodes = mutations;
        mutations.forEach(function (mutation) {
            console.log(mutation);
        });
    });
    var options = {
        childList: true,
        attributes: true,
        characterData: true,
        subtree: true,
        attributeOldValue: true,
        characterDataOldValue: true
    };
    observer.observe(document.body, options);

    // Life Cycle
    var Cycle = {
        getDefaultProps: function () {
            console.log(this.name, 'getDefaultProps');
            return {};
        },
        // initializing
        getInitialState: function () {
            console.log(this.name, 'getInitialState');
            return {};
        },
        componentWillMount: function () {
            console.log(this.name, 'componentWillMount');
        },
        componentDidMount: function () {
            console.log(this.name, 'componentDidMount');
        },
        componentWillReceiveProps: function () {
            console.log(this.name, 'componentWillReceiveProps');
        },
        shouldComponentUpdate: function () {
            console.log(this.name, 'shouldComponentUpdate');
            return true;
        },
        componentWillUpdate: function () {
            console.log(this.name, 'componentWillUpdate');
        },
        componentDidUpdate: function () {
            console.log(this.name, 'componentDidUpdate');
        },
        componentWillUnmount: function () {
            console.log(this.name, 'componentWillUnmount');
        }
    };
    var LifeCycle = function (name) {
        var obj = {
            name: name
        }
        for(var attr in Cycle) {
            obj[attr] = Cycle[attr];
        }
        return obj;
    };

    // Components
    var ItemsBox = React.createClass({
        mixins: [LifeCycle('ItemsBox')],
        render: function () {
            console.log(this.name, 'render');
            return (
                <ul>
                    <li>ItemsBox</li>
                    <li>My Children
                        <ul>{this.props.children}</ul>
                    </li>
                </ul>
            );
        }
    });
    var Item1 = React.createClass({
        mixins: [LifeCycle('Item1')],
        render: function () {
            console.log(this.name, 'render');
            return (
                <li>Item1</li>
            );
        }
    });
    var Item2 = React.createClass({
        mixins: [LifeCycle('Item2')],
        render: function () {
            console.log(this.name, 'render');
            return (
                <li>Item2</li>
            );
        }
    });
    var Item3 = React.createClass({
        mixins: [LifeCycle('Item3')],
        render: function () {
            console.log(this.name, 'render');
            return (
                <li>Item3</li>
            );
        }
    });

    // Main Logic
    console.log('-----------first-----------');
    React.render(<ItemsBox><Item1></Item1><Item2></Item2></ItemsBox>, document.getElementById('diff-container'));
    console.log('-----------second----------');
    var testSecond = function () {
        React.render(<ItemsBox><Item1></Item1><Item3></Item3><Item2></Item2></ItemsBox>, document.getElementById('diff-container'));
    }
    setTimeout(testSecond, 1000);
})();
// console log
-----------first-----------
Inline JSX script (4):29 Item1 getInitialState
Inline JSX script (4):33 Item1 componentWillMount
Inline JSX script (4):69 Item1 render
Inline JSX script (4):29 Item2 getInitialState
Inline JSX script (4):33 Item2 componentWillMount
Inline JSX script (4):83 Item2 render
Inline JSX script (4):29 ItemsBox getInitialState
Inline JSX script (4):33 ItemsBox componentWillMount
Inline JSX script (4):92 ItemsBox render
Inline JSX script (4):36 Item2 componentDidMount
Inline JSX script (4):36 ItemsBox componentDidMount
Inline JSX script (4):36 Item1 componentDidMount
Inline JSX script (4):111 -----------second----------
Inline JSX script (4):8 MutationRecord {type: "childList", target: div#diff-container, addedNodes: NodeList[1], removedNodes: NodeList[0], previousSibling: null…}
Inline JSX script (4):8 MutationRecord {type: "childList", target: div#flexbox-with-react-container, addedNodes: NodeList[1], removedNodes: NodeList[0], previousSibling: null…}
Inline JSX script (4):39 Item1 componentWillReceiveProps
Inline JSX script (4):42 Item1 shouldComponentUpdate
Inline JSX script (4):46 Item1 componentWillUpdate
Inline JSX script (4):69 Item1 render
Inline JSX script (4):39 Item2 componentWillReceiveProps
Inline JSX script (4):42 Item2 shouldComponentUpdate
Inline JSX script (4):46 Item2 componentWillUpdate
Inline JSX script (4):83 Item2 render
Inline JSX script (4):52 ItemsBox componentWillUnmount
Inline JSX script (4):29 Item3 getInitialState
Inline JSX script (4):33 Item3 componentWillMount
Inline JSX script (4):101 Item3 render
Inline JSX script (4):29 ItemsBox getInitialState
Inline JSX script (4):33 ItemsBox componentWillMount
Inline JSX script (4):92 ItemsBox render
Inline JSX script (4):49 Item2 componentDidUpdate
Inline JSX script (4):36 Item3 componentDidMount
Inline JSX script (4):36 ItemsBox componentDidMount
Inline JSX script (4):49 Item1 componentDidUpdate
Inline JSX script (4):8 MutationRecord {type: "childList", target: ul, addedNodes: NodeList[0], removedNodes: NodeList[1], previousSibling: li…}
    addedNodes: NodeList[0]
    attributeName: null
    attributeNamespace: null
    nextSibling: null
    oldValue: null
    previousSibling: li
    removedNodes: NodeList[1]
    target: ul
    type: "child
    List"__proto__: Object
Inline JSX script (4):8 MutationRecord {type: "childList", target: ul, addedNodes: NodeList[1], removedNodes: NodeList[0], previousSibling: li…}
    addedNodes: NodeList[1]
    attributeName: null
    attributeNamespace: null
    nextSibling: null
    oldValue: null
    previousSibling: li
    removedNodes: NodeList[0]
    target: ul
    type: "childList"
    __proto__: Object
Inline JSX script (4):8 MutationRecord {type: "childList", target: ul, addedNodes: NodeList[1], removedNodes: NodeList[0], previousSibling: li…}
    addedNodes: NodeList[1]
    attributeName: null
    attributeNamespace: null
    nextSibling: null
    oldValue: null
    previousSibling: li
    removedNodes: NodeList[0]
    target: ul
    type: "childList"
    __proto__: Object

https://calendar.perfplanet.com/2013/diff/

Flexbox 与 React

var Box = React.createClass({
    render: function () {
        var labelClass = 'label';
        var parentClass = 'box';
        var topClass = 'top';
        var leftClass = 'left';
        var rightClass = 'right';
        var bottomClass = 'bottom';
        return (
            <span className={parentClass}>
                <span className={labelClass}>{this.props.boxName}</span>
                <span className={topClass}>Top</span>
                <span className={this.props.childName}>
                    <span className={leftClass}>Left</span>
                    <span className={rightClass}>Right</span>
                </span>
                <span className={bottomClass}>Bottom</span>
            </span>
        );
    }
});
var MarginBox = React.createClass({
    render: function () {
        return (
            <Box className="borderBox" boxName="margin" childName="borderBox"></Box>
        );
    }
});
var BoxContainer = React.createClass({
    render: function () {
        var boxContainerClass = 'marginBox';
        return (
            <span className={boxContainerClass}>
                <MarginBox></MarginBox>
            </span>
        );
    }
});
React.render(<BoxContainer />, document.getElementById('flexbox-with-react-container'));

Copyright (c) 2014-2016 Kyles Light.
Powered by Tornado.
鄂 ICP 备 15003296 号