从零开始——React基础入门(虚拟DOM、JSX语法、组件化开发、生命周期、单项数据流)

React最初来自Facebook内部的广告系统项目,项目实施过程中前端开发遇到了巨大挑战,代码变得越来越臃肿且混乱不堪,难以维护。他们又对市场的现有的前端mvc框架都不满意。于是他们决定抛开很多所谓的“最佳实践”,重新思考前端界面的构建方式,就决定自己写一套框架来解决这些问题,然后就有了React。

后来发现React框架在开发中很好用,于是FB投入了更多的人力去开发这套框架,最后在2013年5月宣布开源。

由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能引领未来用户界面开发的主流框架。

React 这么火,那么它到底有什么牛逼的地方?

特点及优势

1.虚拟dom (开发时候不需要在页面中写任何dom元素)

2.jsx语法(写页面时候使用javascript xml格式的语法)

3.组件化开发(react最核心的思想是将页面中任何一个区域或者元素都看成是一个组件 Component)

4.单向数据流(组件和后端之间的数据是单向的,从后端流动到react组件中)

5.组件生命周期(任何一个组件在dom中都具有一个完整的声明周期,组件初始化的时候开始,组件被移除的时候消失,从而保证性能的优越)

虚拟DOM

虚拟DOM则是在DOM的基础上建立了一个抽象层,我们对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。

虚拟DOM会使得App只关心数据和组件的执行结果,中间产生的DOM操作不需要App干预,而且通过虚拟DOM来生成DOM,会有一项非常可观收益——-性能。

React会在内存中维护一个虚拟DOM树,当我们对这个树进行读或写的时候,实际上是对虚拟DOM进行的。当数据变化时,然后React会自动更新虚拟DOM,然后拿新的虚拟DOM和旧的虚拟DOM进行对比,找到有变更的部分,得出一个Patch,然后将这个Patch放到一个队列里,最终批量更新这些Patch到DOM中。

缺陷——首次渲染大量DOM时因为多了一层虚拟DOM的计算,会比innerHTML插入方式慢,所以使用时尽量不要一次性渲染大量DOM。

JSX语法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
</head>
<body>
    <div id="root"></div>
    <!-- bebel是工具,把es6转换成es5 -->
    <script type="text/babel">
            ReactDOM.render(<h1>React</h1>,document.getElementById("root"))
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
</head>
<body>
    <div id="root"></div>
    <!-- bebel是工具,把es6转换成es5 -->
    <script type="text/babel">
        var arr = ["张三","李四","王五"];
        ReactDOM.render(<div>{
            arr.map(function(ele,index){
                return <p>{ele}</p>
        })
        }</div>,document.getElementById("root"))
    </script>
</body>
</html>

JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。

组件

模块化中的模块一般指的是为了实现某些功能的代码片段,模块化主要实现了代码分工,分模块完成,强调的是功能;

组件是组成页面的部件,内部封装了组件的结构、样式、行为,更多的考虑代码的复用性,页面结构的实现

在组件化开发中,一个模块可能需要多个组件(比如评价的模块,可能需要列表的组件、按钮的组件、用户信息组件…),但是有些模块也可以跟组件没有关系,只是实现一些功能。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        .header{
            position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
        }
        .footer{
             position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
             list-style: none;
        }
        .footer li{
            float: left;width: 25%;text-align: center;
        }
        .content{
            padding-top: 45px;padding-bottom: 45px;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <!-- bebel是工具,把es6转换成es5 -->
    <!-- 组件都是用面向对象构建的,组件经常会被复用,用面向对象写可以做到共用,组件名称要大写 -->
    <!-- 不能用class,class在js中是保留字符 -->
    <script type="text/babel">
    var Header = React.createClass({
        render:function(){
            return <div className="header">网站头部</div>
        }
    })
    var Footer = React.createClass({
        render:function(){
            return <ul className="footer">
                <li>首页</li>
                <li>列表</li>
                <li>购物车</li>
                <li>我的</li>
            </ul>
        }
    })
    var Content = React.createClass({
        render:function(){
            return <div className="content">内容</div>
        }
    })
    var IndexPage = React.createClass({
        render:function(){
            return <div className="page">
                <Header/>
                <Footer/>
                <Content/>
            </div>
        }
    })
    ReactDOM.render(<IndexPage/>,document.getElementById("root"))
    </script>
    <!-- 组件里面render的内容,就是当组件被调用显示的内容 -->
</body>
</html>
单项数据流

props(properties 特性)是在调用时候被调用者设置的,只设置一次,一般没有额外变化,可以把任意类型的数据传递给组件,尽可能的吧props当做数据源,不要在组件内部设置props。

组件传参:
1.this.props.children
2.this.props.xxx

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        .header{
            position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
        }
        .footer{
             position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
             list-style: none;
        }
        .footer li{
            float: left;width: 25%;text-align: center;
        }
        .content{
            padding-top: 45px;padding-bottom: 45px;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <!-- bebel是工具,把es6转换成es5 -->
    <!-- 组件都是用面向对象构建的,组件经常会被复用,用面向对象写可以做到共用,组件名称要大写 -->
    <!-- 不能用class,class在js中是保留字符 -->
    <script type="text/babel">
    var Header = React.createClass({
        render:function(){
            return <div className="header">{this.props.title}</div>
        }
    })
    var Footer = React.createClass({
        render:function(){
            return <ul className="footer">
                <li>首页</li>
                <li>列表</li>
                <li>购物车</li>
                <li>我的</li>
            </ul>
        }
    })
    var Content = React.createClass({
        render:function(){
            return <div className="content">{this.props.children}</div>
        }
    })
    var List = React.createClass({
        render:function(){
            return <ul>
                <li>商品1</li>
                <li>商品2</li>
                <li>商品3</li>
            </ul>
        }
    })
    var IndexPage = React.createClass({
        render:function(){
            return <div className="page">
                <Header title="首页" />
                <Footer/>
                <Content>
                    <List/>
                </Content>
            </div>
        }
    })
    ReactDOM.render(<IndexPage/>,document.getElementById("root"))
    </script>
    <!-- 组件里面render的内容,就是当组件被调用显示的内容 -->
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        .header{
            position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
        }
        .footer{
             position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
             list-style: none;
        }
        .footer li{
            float: left;width: 25%;text-align: center;
        }
        .content{
            padding-top: 45px;padding-bottom: 45px;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <!-- bebel是工具,把es6转换成es5 -->
    <!-- 组件都是用面向对象构建的,组件经常会被复用,用面向对象写可以做到共用,组件名称要大写 -->
    <!-- 不能用class,class在js中是保留字符 -->
    <script type="text/babel">
    var Header = React.createClass({
        render:function(){
            return <div className="header">{this.props.title}</div>
        }
    })
    var Footer = React.createClass({
        render:function(){
            return <ul className="footer">
                <li>首页</li>
                <li>列表</li>
                <li>购物车</li>
                <li>我的</li>
            </ul>
        }
    })
    var Content = React.createClass({
        render:function(){
            return <div className="content">{this.props.children}</div>
        }
    })
    var List = React.createClass({
        render:function(){
            return <ul>
                {this.props.listData.map((ele,index)=>{
                    return <li>商品{ele}</li>
                })}
            </ul>
        }
    })
    var IndexPage = React.createClass({
        render:function(){
            return <div className="page">
                <Header title="首页" />
                <Footer/>
                <Content>
                    <List listData={["1","2","3"]}/>
                </Content>
            </div>
        }
    })
    ReactDOM.render(<IndexPage/>,document.getElementById("root"))
    </script>
    <!-- 组件里面render的内容,就是当组件被调用显示的内容 -->
</body>
</html>
组件的生命周期

随着该组件的props(数据)或者state(状态)发生改变,它的DOM表现也将有相应的变化,一个组件就是一个状态机:对于特定的输入,它总会返回一致的输出。 React为每个组件提供了生命周期钩子函数去响应不同的时刻,组件的生命周期分为三个部分:(1)实例化;(2)存在期;(3)销毁&清理期。

钩子函数类似有回调函数(callback),但他是在方法内容一开始调用的,而callback是在事件结束调用的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 核心 -->
    <script src="build/react.min.js"></script>
    <!-- 渲染dom -->
    <script src="build/react-dom.min.js"></script>
    <!-- 把jsx、es6转换成js、es5 -->
    <script src="build/browser.min.js"></script>
</head>
<body>
    <div id="root"></div>
</body>
<script type="text/babel">
    //getInitialState给组件设置默认的状态
    //setState是React方法来修改state,视图可以自动更新
    var Switch = React.createClass({
        getInitialState:function(){
            return{
                show:false
            }
            console.log("设置初始状态")
        },
        changeShow:function(){
            this.setState({
                show:!this.state.show
            })
        },
        componentWillMount:function(){
            console.log("即将添加")
        },
        render:function(){
            return (
                <div id="warp" ref="warp">
                    <button onClick={this.changeShow} ref="btn">切换</button>
                    <div style={{display:this.state.show?'block':'none'}}>虚拟DOM,组件化开发,生命周期,jsx语法,单项数据流</div>
                </div> 
            )
        },
        componentDidMount:function(){
            //获取真实dom,请求数据
            //this.refs或取到的是设置了ref属性的dom集合
            console.log("添加完成")
            console.log(this.refs)
        }
    })
    ReactDOM.render(<Switch/>,document.getElementById("root"))
</script>
</html>

实例化阶段(在组件调用之前的——创建阶段)

getDefaultProps方法发生在创建组件类的时候,即调用React.createClass的时候。这个阶段只会触发一个getDefaultProps方法,给this.props作为该组件的默认属性。

props属性是一个对象,是组件用来接收外面传来的参数的组件内部是不允许修改自己的props属性的,只能通过父组件来修改。在getDefaultProps方法中,是可以设定props默认值的。

getInitialState 初始化组件的state的值,其返回值会赋值给组件的this.state属性。对于组件的每个实例来说,这个方法的调用次数有且只有一次。

componentWillMount 此方法会在完成首次渲染之前被调用。这也是在render方法调用前可以修改组件state的最后一次机会。

render 生成页面需要的虚拟DOM结构,用来表示组件的输出。render方法需要满足:(1)只能通过this.props和this.state访问数据;(2)可以返回null、false或者任何React组件;(3)只能出现一个顶级组件;(4)必需纯净,意味着不能改变组件的状态或者修改DOM的输出。

componentDidMount 该方法发生在render方法成功调用并且真实的DOM已经被渲染之后,在该函数内部可以通过this.getDOMNode()来获取当前组件的节点。然后就可以像Web开发中的那样操作里面的DOM元素了。

存在期

用户改如果变了组件的state,或者要展示的数据发生改变,这时候需要重新渲染组件。

componentWillReceiveProps 在任意时刻,组件的props都可以通过父辈组件来更改。当组件接收到新的props(这里不同于state)时,会触发该函数,我们同时也获得更改props对象及更新state的机会。

shouldComponentUpdate 该方法用来拦截新的props和state,然后开发者可以根据自己设定逻辑,做出要不要更新render的决定,让它更快(当props 发生改变,或者调用了setState方法,调用了setState 方法哪怕state没有改变也会触发)

componentWillUpdate 与componentWillMount方法类似,组件上会接收到新的props或者state渲染之前,调用该方法。但是不可以在该方法中更新state和props。

render 生成页面需要的虚拟DOM结构,并返回该结构。

componentDidUpdate 与componentDidMount类似,更新已经渲染好的DOM。

demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <!-- 核心 -->
  <script src="build/react.min.js"></script>
  <!-- 渲染dom -->
  <script src="build/react-dom.min.js"></script>
  <!-- 把jsx、es6转换成js、es5 -->
  <script src="build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
  var ProductList = React.createClass({
    getDefaultProps:function () {
      //设置默认数据
      return {
        listData:[]
      }
    },
    fnClick:function (index) {
      alert(index)
    },
    render:function () {
      return (
        <ul>
          {
            this.props.listData.map(function (ele,index) {
              return <li key={index} onClick={()=>this.fnClick(index)}>{ele}</li>
            }.bind(this))
          }
        </ul>
      )
    }
  });
  var IndexPage = React.createClass({
    render:function () {
      return (
        <div>
          <header>头部</header>
          <ProductList listData={[1,2,3,4]}/>
          <footer>底部</footer>
        </div>
      )
    }
  });
  ReactDOM.render(<IndexPage/>,document.getElementById("root"));
</script>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容