Name: Password: Sign in
Javascript 中的文章
  • Javascript 的原型继承
  • React Native Practice
  • 本文在 署名-非商业性使用-相同方式共享 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
    

    http://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 号