一个组件,有自己的结构,有自己的逻辑,有自己的样式,会依赖一些资源,会依赖某些其他组件。比如日常写一个组件,比较常规的方式:

  • 通过前端模板引擎定义结构
  • JS文件中写自己的逻辑
  • CSS中写组件的样式
  • 通过RequireJS、SeaJS这样的库来解决模块之间的相互依赖

那么在React中是什么样子呢?

结构和逻辑

在React的世界里,结构和逻辑交由JSX文件组织,React将模板内嵌到逻辑内部,实现了一个JS代码和HTML混合的JSX。

结构

在JSX文件中,可以直接通过React.createClass来定义组件:

var CustomComponent = React.creatClass({
render: function(){
return (<div className="custom-component"></div>);
}
});

通过这种方式可以很方便的定义一个组件,组件的结构定义在render函数中,但这并不是简单的模板引擎,我们可以通过js方便、直观的操控组件结构,比如我想给组件增加几个节点:

var CustomComponent = React.creatClass({
render: function(){
var $nodes = ['h','e','l','l','o'].map(function(str){
return (<span>{str}</span>);
});
return (<div className="custom-component">{$nodes}</div>);
}
});

通过这种方式,React使得组件拥有灵活的结构。那么React又是如何处理逻辑的呢?

逻辑

写过前端组件的人都知道,组件通常首先需要相应自身DOM事件,做一些处理。必要时候还需要暴露一些外部接口,那么React组件要怎么做到这两点呢?

事件响应

比如我有个按钮组件,点击之后需要做一些处理逻辑,那么React组件大致上长这样:

var ButtonComponent = React.createClass({
render: function(){
return (<button>屠龙宝刀,点击就送</button>);
}
});

点击按钮应当触发相应地逻辑,一种比较直观的方式就是给button绑定一个onclick事件,里面就是需要执行的逻辑了:

function getDragonKillingSword() {
//送宝刀
}
var ButtonComponent = React.createClass({
render: function(){
return (<button onclick="getDragonKillingSword()">屠龙宝刀,点击就送</button>);
}
});

但事实上getDragonKillingSword()的逻辑属于组件内部行为,显然应当包装在组件内部,于是在React中就可以这么写:

var ButtonComponent = React.createClass({
getDragonKillingSword: function(){
//送宝刀
},
render: function(){
return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
}
});

这样就实现内部事件的响应了,那如果需要暴露接口怎么办呢?

暴露接口

事实上现在getDragonKillingSword已经是一个接口了,如果有一个父组件,想要调用这个接口怎么办呢?

父组件大概长这样:

var ImDaddyComponent = React.createClass({
render: function(){
return (
<div>
//其他组件
<ButtonComponent />
//其他组件
</div>
);
}
});

那么如果想手动调用组件的方法,首先在ButtonComponent上设置一个ref=""属性来标记一下,比如这里把子组件设置成<ButtonComponent ref="getSwordButton"/>,那么在父组件的逻辑里,就可以在父组件自己的方法中通过这种方式来调用接口方法:

this.refs.getSwordButton.getDragonKillingSword();

看起来屌屌哒~那么问题又来了,父组件希望自己能够按钮点击时调用的方法,那该怎么办呢?

配置参数

父组件可以直接将需要执行的函数传递给子组件:

<ButtonComponent clickCallback={this.getSwordButtonClickCallback}/>

然后在子组件中调用父组件方法:

var ButtonComponent = React.createClass({
render: function(){
return (<button onClick={this.props.clickCallback}>屠龙宝刀,点击就送</button>);
}
});

子组件通过this.props能够获取在父组件创建子组件时传入的任何参数,因此this.props也常被当做配置参数来使用

屠龙宝刀每个人只能领取一把,按钮点击一下就应该灰掉,应当在子组件中增加一个是否点击过的状态,这又应当处理呢?

组件状态

在React中,每个组件都有自己的状态,可以在自身的方法中通过this.state取到,而初始状态则通过getInitialState()方法来定义,比如这个屠龙宝刀按钮组件,它的初始状态应该是没有点击过,所以getInitialState方法里面应当定义初始状态clicked: false。而在点击执行的方法中,应当修改这个状态值为click: true

var ButtonComponent = React.createClass({
getInitialState: function(){
//确定初始状态
return {
clicked: false
};
},
getDragonKillingSword: function(){
//送宝刀 //修改点击状态
this.setState({
clicked: true
});
},
render: function(){
return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
}
});

这样点击状态的维护就完成了,那么render函数中也应当根据状态来维护节点的样式,比如这里将按钮设置为disabled,那么render函数就要添加相应的判断逻辑:

render: function(){
var clicked = this.state.clicked;
if(clicked)
return (<button disabled="disabled" onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
else
return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
}

小节

这里简单介绍了通过JSX来管理组件的结构和逻辑,事实上React给组件还定义了很多方法,以及组件自身的生命周期,这些都使得组件的逻辑处理更加强大

资源加载

CSS文件定义了组件的样式,现在的模块加载器通常都能够加载CSS文件,如果不能一般也提供了相应的插件。事实上CSS、图片可以看做是一种资源,因为加载过来后一般不需要做什么处理。

React对这一方面并没有做特别的处理,虽然它提供了Inline Style的方式把CSS写在JSX里面,但估计没有多少人会去尝试,毕竟现在CSS样式已经不再只是简单的CSS文件了,通常都会去用Less、Sass等预处理,然后再用像postcss、myth、autoprefixer、cssmin等等后处理。资源加载一般也就简单粗暴地使用模块加载器完成了

组件依赖

组件依赖的处理一般分为两个部分:组件加载和组件使用

组件加载

React没有提供相关的组件加载方法,依旧需要通过<script>标签引入,或者使用模块加载器加载组件的JSX和资源文件。

组件使用

如果细心,就会发现其实之前已经有使用的例子了,要想在一个组件中使用另外一个组件,比如在ParentComponent中使用ChildComponent,就只需要在ParentComponentrender()方法中写上<ChildComponent />就行了,必要的时候还可以传些参数。

疑问

到这里就会发现一个问题,React除了只处理了结构和逻辑,资源也不管,依赖也不管。是的,React将近两万行代码,连个模块加载器都没有提供,更与Angularjs,jQuery等不同的是,他还不带啥脚手架...没有Ajax库,没有Promise库,要啥啥没有...

虚拟DOM

那它为啥这么大?因为它实现了一个虚拟DOM(Virtual DOM)。虚拟DOM是干什么的?这就要从浏览器本身讲起

如我们所知,在浏览器渲染网页的过程中,加载到HTML文档后,会将文档解析并构建DOM树,然后将其与解析CSS生成的CSSOM树一起结合产生爱的结晶——RenderObject树,然后将RenderObject树渲染成页面(当然中间可能会有一些优化,比如RenderLayer树)。这些过程都存在与渲染引擎之中,渲染引擎在浏览器中是于JavaScript引擎(JavaScriptCore也好V8也好)分离开的,但为了方便JS操作DOM结构,渲染引擎会暴露一些接口供JavaScript调用。由于这两块相互分离,通信是需要付出代价的,因此JavaScript调用DOM提供的接口性能不咋地。各种性能优化的最佳实践也都在尽可能的减少DOM操作次数。

而虚拟DOM干了什么?它直接用JavaScript实现了DOM树(大致上)。组件的HTML结构并不会直接生成DOM,而是映射生成虚拟的JavaScript DOM结构,React又通过在这个虚拟DOM上实现了一个 diff 算法找出最小变更,再把这些变更写入实际的DOM中。这个虚拟DOM以JS结构的形式存在,计算性能会比较好,而且由于减少了实际DOM操作次数,性能会有较大提升

道理我都懂,可是为什么我们没有模块加载器?

所以就需要Webpack了

本文只转载了原文的说说react部分,至于webpack部分跟我所用法不一样,所以砍掉了,下面提供原文链接。

原文链接:https://segmentfault.com/a/1190000002767365

说说React的更多相关文章

  1. react组件的生命周期

    写在前面: 阅读了多遍文章之后,自己总结了一个.一遍加强记忆,和日后回顾. 一.实例化(初始化) var Button = React.createClass({ getInitialState: f ...

  2. 十分钟介绍mobx与react

    原文地址:https://mobxjs.github.io/mobx/getting-started.html 写在前面:本人英语水平有限,主要是写给自己看的,若有哪位同学看到了有问题的地方,请为我指 ...

  3. RxJS + Redux + React = Amazing!(译一)

    今天,我将Youtube上的<RxJS + Redux + React = Amazing!>翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: https:/ ...

  4. React 入门教程

    React 起源于Facebook内部项目,是一个用来构建用户界面的 javascript 库,相当于MVC架构中的V层框架,与市面上其他框架不同的是,React 把每一个组件当成了一个状态机,组件内 ...

  5. 通往全栈工程师的捷径 —— react

    腾讯Bugly特约作者: 左明 首先,我们来看看 React 在世界范围的热度趋势,下图是关键词“房价”和 “React” 在 Google Trends 上的搜索量对比,蓝色的是 React,红色的 ...

  6. 2017-1-5 天气雨 React 学习笔记

    官方example 中basic-click-counter <script type="text/babel"> var Counter = React.create ...

  7. RxJS + Redux + React = Amazing!(译二)

    今天,我将Youtube上的<RxJS + Redux + React = Amazing!>的后半部分翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: ht ...

  8. React在开发中的常用结构以及功能详解

    一.React什么算法,什么虚拟DOM,什么核心内容网上一大堆,请自行google. 但是能把算法说清楚,虚拟DOM说清楚的聊聊无几.对开发又没卵用,还不如来点干货看看咋用. 二.结构如下: impo ...

  9. React的使用与JSX的转换

    前置技能:Chrome浏览器   一.拿糖:React的使用 React v0.14 RC 发布,主要更新项目: 两个包: React 和 React DOM DOM node refs 无状态的功能 ...

  10. Vue.js 2.0 和 React、Augular等其他框架的全方位对比

    引言 这个页面无疑是最难编写的,但也是非常重要的.或许你遇到了一些问题并且先前用其他的框架解决了.来这里的目的是看看Vue是否有更好的解决方案.那么你就来对了. 客观来说,作为核心团队成员,显然我们会 ...

随机推荐

  1. Nuget很慢,我们该怎么办

    在VS中给项目添加程序已经采用NuGet 十分方便 不过很多时候速度很慢,一直显示“正在检索信息” 其实直接使用程序包管理控制台,速度就会好很多 如果命令不太会写,安装包名不是确认,可以先登录 htt ...

  2. 关于WPF中文件夹浏览对话框的方式

    文件夹浏览时dialogresult要写全引用路径 string path=null; FolderBrowserDialog fbd = new FolderBrowserDialog(); fbd ...

  3. iOS/Android网络消息推送的实现两种方法

    移动时代,用户为王,而每个APP拥有的活跃用户量(Active Users),决定了其价值. 消息推送成为了不可或缺的活跃唤起工具. 目前消息推送有如下两种途径: 1.iOS传统方式: 通过Apple ...

  4. 线程安全、数据同步之 synchronized 与 Lock

    本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要 ...

  5. RapidFloatingActionButton框架正式出炉

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4474748.html RapidFloatingActionB ...

  6. TFS中向源代码方案中添加文件

    一些情况下,不能使用VS提供的菜单直接将文件添加到源代码项目,例如该文件是使用TT生成的,或者依赖于其它文件     此时可以在此文件的父级依赖文件上右击,即可添加未受托管的文件     另一种方法, ...

  7. 在SQL中使用CLR提供基本函数对二进制数据进行解析与构造

      二进制数据包的解析一般是借助C#等语言,在通讯程序中解析后形成字段,再统一单笔或者批量(表类型参数)提交至数据库,在通讯程序中,存在BINARY到struct再到table的转换. 现借助CLR提 ...

  8. 使用sencha cmd 一键生成你的应用程序代码

    一键生成你的应用程序代码: ------------------------------------------------------------ 我们的出发点就是使用命令来产生一个应用程序,执行以 ...

  9. jar 命令 打包装class文件的文件夹

    由于将spring源代码导入到eclipse后,缺少jar包,     所以从maven仓库中下载spring发布的spring-core  jar包. 为了方便理解目录结构,使用tree命令: tr ...

  10. ios app响应background,foreground 事件实现

    1 通过AppDelegate 实现 App进入后台事件方法 - (void)applicationDidEnterBackground:(UIApplication *)application AP ...