ReactJS入门(一)—— 初步认识React
React刚开始红的时候,由于对其不甚了解,觉得JSX的写法略非主流,故一直没打算将其应用在项目上,随着身边大神们的科普,才后知后觉是个好东西。
好在哪里呢?个人拙见,有俩点:
1. 虚拟DOM —— 在DOM树的状态需要发生变化时,虚拟DOM机制会将同一Event loop前后的DOM树进行对比(自然通过一系列高效的算法),如果俩个DOM树存在不一样的地方,那么React仅仅会针对这些不一样的区域(DOM diff)来进行响应的DOM修改,从而实现最高效的DOM操作和渲染。
如下图,我们修改了DOM树上一些节点(或UI组件)对应绑定的state(不知道state是什么没关系,后续我们会提及),React会即刻将其标记为“脏状态”,在一个Event loop结束时(即使过程中你对某个组件的state进行了多次修改),React会计算得出DOM树上需要修改的地方(“脏”了的地方,如下图红点)及其最终的状态,并仅仅针对这些地方进行一次性的重新渲染。
于是好处显而易见,并非每修改一次组件的state,就会重新渲染一次,而是在Event loop结束后做一次“秋后算账”,减少冗余的DOM操作。另外React只针对需要修改的地方来做新的渲染,而非重新渲染整个DOM树,自然效率很是不错。
2. 组件可嵌套,而且,可以模版化嘛 —— 其实在React里提及的“组件”,常规是一些可封装起来、复用的UI模块,说的接地气了可以理解为“带有细粒度UI功能的部分DOM区域”。然后我们可以把这些组件层层嵌套起来使用(当然这样组件间会存在依赖关系)。
至于模块化,类似于ejs那样可以作为独立的模块被引用到页面上来复用,不过咧,它可以直接把UI组件当作脚本模块那样来使用,咱完全可以配合CommonJS、AMD、CMD等规范来require需要的组件模块,并处理好它们的依赖关系(是不是碉堡了)。
基于上述的俩点,自然的也打算投入React怀抱了。不过在这之前得先理清俩点事情:
1. React是一个纯View层,不擅长于和动态数据打交道(哎哟咱也不谈Flux了,Flux的概念其实也不完善),因此它不同于,也替代不了常规的MV*框架;
2. React很擅长于处理组件化的页面,在页面上搭组件的形式有点像搭乐高一样,因此用上React的项目需求常规为界面组件化。另外React只支持到IE8+,就天朝的情况,是否使用React还是得稍微斟酌一番。
唠嗑了这么多,下面开始进入React的入门课程。本章提及的代码都可以在我的Github上下载到。
JSX
JSX是React编写组件的一种语法规范,可以看为是js的扩展,它支持将HTML和JS混写在一起,最终会通过React提供的 JSXTransformer.js 来将其编译为常规的js,方便浏览器解析。我们来个最简单的例子:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>最简单的jsx</title>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a">123</div>
- <script type="text/jsx">
- var name = 'VaJoy';
- React.render(<h1>{name}</h1>, document.getElementById('a'));
- </script>
- </body>
- </html>
页面上我们引入了 react.js 和 JSXTransformer.js 俩个重要的脚本,前者自然是react的核心,后者则是将JSX语法转为js语法。
实际上使用 JSXTransformer.js 在客户端来解析JSX语法是一件冗余、耗性能的事情,我们可以在上线项目之前,事先使用诸如 gulp-react 的工具先把所有的JSX均转换为js文件再引入到页面中。
当然如果你掌握了react的JS写法(当然相比JSX的学习成本要高不少),你可以直接在页面上书写相应的 原生js 即可,来个对比:
- //使用JSX
- React.render(
- <div>
- <div>
- <div>content</div>
- </div>
- </div>,
- document.getElementById('example')
- );
- //不使用JSX
- React.render(
- React.createElement('div', null,
- React.createElement('div', null,
- React.createElement('div', null, 'content')
- )
- ),
- document.getElementById('example')
回到开头第一段代码段,我们将页面的JSX直接写在 <script type="text/jsx"> 中,注意 type 类型要标明为 "text/jsx" 。
然后我们定义了个变量name,并使用了React最基础和最常用的一个方法 React.render() 来渲染DOM:
- var name = 'VaJoy';
- React.render(<h1>{name}</h1>, document.getElementById('a'));
React.render() 支持两个参数(其实还有第三个可选的参数,作为渲染后的回调),第一个参数为模板的渲染内容(HTML形式),第二个参数表示要插入这段模板的DOM节点(DOM node)。
这里要提及一个知识点 —— 在JSX中,遇到 HTML 标签(以 < 开头),将用 HTML 规则解析;遇到代码块(以 { 开头),则用 JavaScript 规则解析。所以我们在<h1>中嵌入变量 name 时是以花括号的形式 {name} 来实现的。
至于执行结果,相信大家很容易猜出来:
即往div里插入了(其实应该说是彻底替换为)JSX中的模板内容(<h1>元素)。
鉴于JSX支持HTML跟JS的混写,故其灵活性很高,我们试着把变量换为数组:
- var arr = ['HELLO', "here is", 123, "VaJoy`s blog"];
- React.render(
- <ul>{
- arr.filter(function(v){
- return typeof v === 'string'
- }).map(function(v){
- return <li> {v} </li>
- })
- }</ul>,
- document.getElementById('a')
- );
结果如下:
组件
开头已经提及React是用于组件化开发的,组件可看为是其最小组成单位,那么什么是组件(component)呢?我们看看下方这张图:
这是一个移动端上的UI界面,用于查询员工列表和某员工的具体信息。那么我们按页面上各功能来细分,以左侧的搜索主页界面而言,可以把它细分为SearchBar、EmployeeList、EmployeeListItem等UI组件,其中EmployeeList组件嵌套了多个EmployeeList组件,而这众多组件的搭配组成了整个Hompage界面。
常规我们用React开发的页面可以看为一个大组件,它由多个小组件搭建而成。
在JSX中,我们可以通过 React.createClass() 方法来创建一个组件(注意组件的名称必须为大写字母开头),并以命名标签的形式(<MyClassName />)来引用:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>组件</title>
- <style>
- .nameStyle{
- color: dodgerblue;
- }
- </style>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a"></div>
- <script type="text/jsx">
- var name = 'VaJoy';
- var Demo = React.createClass({ //随便建了个叫Demo的组件
- render: function() { //每个组件都需要有自己的render方法
- var a = "Hello,";
- return (
- <p className="nameStyle" >{a,name}</p> //当然你可以写为{a}{name}
- );
- }
- });
- React.render(
- <Demo />, //引入组件Demo
- document.getElementById('a')
- );
- </script>
- </body>
- </html>
注意每个组件都需要有一个render方法,用于输出组件内容。另外组件DOM元素上的 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
执行效果如下:
可以看到,如果在一个容器中引用了多个变量,React会对应每个文本节点来自动生成对应span标签(React防止XSS的机制,因此要注意规避span的样式污染!)
再来看个组件嵌套的示例,其实都很简单:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>组件嵌套</title>
- <style>
- .nameStyle{
- color: dodgerblue;
- }
- </style>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a"></div>
- <script type="text/jsx">
- var name = 'VaJoy',
- Demo = React.createClass({
- render: function() {
- var a = "Hello,";
- return <span className="nameStyle" >{a,name}</span>;
- }
- }),
- DemoWrap = React.createClass({
- render: function() {
- return <ul>{
- [1,2,3].map(function(v){
- return <li style={{fontSize:'20px'}}>{v} <Demo /></li> //嵌套组件Demo
- })
- }</ul>;
- }
- });
- React.render(
- <DemoWrap />, //引入组件DemoWrap
- document.getElementById('a')
- );
- </script>
- </body>
- </html>
注意在JSX里,给DOM设置 style 必须写成 {{ marginRight : '10px', fontSize : '18px' }} 的映射形式(预防XSS),否则会报错
执行结果(注意连空格都生成了一个span):
组件的“数据”
我们在前头提及了,React只是一个纯view层,并不涉及model。不过但对于React组件而言,它有两种特殊的data —— Props 和 States。其中的 States 有点类似于各MV*框架中的model部分,可以称为React的状态机制,用于与用户进行交互。
1. Props
props表示组件自身的属性,也可以用于在嵌套的内外层组件中传递信息(常规是由父层组件传递给子层组件)。要注意它是不变的、也不应该尝试去改变的。我们来个简单的示例:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>组件Props</title>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a"></div>
- <script type="text/jsx">
- var Component1 = React.createClass({
- render: function() {
- return <p> {this.props.abc, this.props.name} </p>;
- }
- });
- var Component2 = React.createClass({
- render: function() {
- return (
- <div className="commentList" onClick={this.handleClick}>
- <Component1 abc="你好!" name="张三" />
- <Component1 abc="Hi!" name="李四" />
- </div>
- );
- },
- handleClick: function (e) {
- console.log(this.props.name, e.target);
- }
- });
- React.render(
- <Component2 name="我是Component2的name哦!" />,
- document.getElementById('a')
- );
- </script>
- </body>
- </html>
这里我们注册了俩个组件类 Component1 和 Component2 ,其中 Component2 内嵌了 Component1 。
我们先看 Component1 的定义:
- var Component1 = React.createClass({
- render: function() {
- return <p> {this.props.abc, this.props.name} </p>;
- }
- });
它返回了一个p标签,其中的内容是 this.props.abc 和 this.props.name,这里的 this 指的是组件 Component1 本身,因此比如 this.props.name 获取到的便是组件 Component1 自身的属性 name 的值(abc 和 name 的值我们都在 Component2 中进行了定义)。
我们接着看 Component2:
- var Component2 = React.createClass({
- render: function() {
- return (
- <div className="commentList" onClick={this.handleClick}>
- <Component1 abc="你好!" name="张三" />
- <Component1 abc="Hi!" name="李四" />
- </div>
- );
- },
- handleClick: function (e) {
- console.log(this.props.name, e.target);
- }
- });
在这里我们除了嵌入组件 Component1 并定义了它们的属性值 abc 和 name ,也使用了React的事件机制——我们用 onClick 方法来触发点击事件,其对应的 this.handleClick 是我们自定义的一个方法,用于输出 Component2 的 name 属性和当前被点击的目标元素。
最后我们用 React.render 方法渲染组件 Component2 ,并定义了它的 name 属性:
- React.render(
- <Component2 name="我是Component2的name哦!" />,
- document.getElementById('a')
- );
执行结果如下:
这里有点要留意的,无论是 props 组件属性,或者是 onClick 事件,都是 React 内部的东西,我们在HTML上是看不到它们的:
顺便提个Props的语法糖 —— 我们可以通过 “...this.props” 来将父层组件绑定的全部属性都直接写到子组件中:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>test</title>
- <style>
- .a{margin-right:30px;}
- </style>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <script type="text/jsx">
- var CheckLink = React.createClass({
- render: function() {
- // 这样会把 CheckList 所有的 props 复制到 <a>
- return <a {...this.props}>{this.props.children}</a>;
- }
- });
- var arr = ['第一个连接', '第二个连接'];
- React.render(
- <div>
- <CheckLink href="/a.html" name="abc" className="a">{arr[0]}</CheckLink>
- <CheckLink href="/b.html" name="efg" className="a">{arr[1]}</CheckLink>
- </div>
- ,document.body
- );
- </script>
- </body>
- </html>
执行结果:
Props也并非都是直接写在组件标签上的属性,有一个例外 —— props.children,它表示当前组件的所有子节点(它们常规是在外部的组件赋予的):
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>props.children</title>
- <style>
- p:active{ color: deeppink; }
- </style>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a"></div>
- <script type="text/jsx">
- var Component1 = React.createClass({
- render: function() {
- return <li>{this.props.children}</li>
- }
- });
- var Component2 = React.createClass({
- list: ['张三', '李四', '王五'],
- render: function() {
- return (
- <ul>
- {
- this.list.map(function(item){
- return <Component1>{item}</Component1>
- })
- }
- </ul>
- );
- }
- });
- React.render(
- <Component2 />,
- document.getElementById('a')
- );
- </script>
- </body>
- </html>
留意第27行的代码 return <Component1>{item}</Component1> ,其中的{item}会作为组件Component1的子节点(即 props.children)来处理。
执行如下:
2. State
State 是组件的状态,它是可变的,因此常用于跟用户的交互。
不同于Props,我们需要在组件中使用 getInitialState() 方法来初始化组件的State,并使用 this.setState() 来修改State的值:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>state</title>
- <style>
- p:active{ color: deeppink; }
- </style>
- <script src="react.js"></script>
- <script src="JSXTransformer.js"></script>
- </head>
- <body>
- <div id="a"></div>
- <script type="text/jsx">
- var Component = React.createClass({
- getInitialState: function() {
- return {
- isClick: !1
- }
- },
- render: function() {
- var text = this.state.isClick ? 'clicked!' : 'none click';
- return <div>
- <input type="checkbox" onChange={this.clickCb} />{ text }
- </div>;
- },
- clickCb: function(){
- this.setState({
- isClick : !this.state.isClick //点击checkbox时改变state.isClick
- })
- }
- });
- React.render(
- <Component />,
- document.getElementById('a')
- );
- </script>
- </body>
- </html>
我们先通过 getInitialState() 方法初始化了组件Component的State对象,里面有个 isClick 的属性,默认值为 false。
接着在组件的 render 方法里定义了一个 text 变量,其值会根据 state.isClick 的变化而变化:
var text = this.state.isClick ? 'clicked!' : 'none click';
另外给checkbox控件绑定了React的 onChange 事件,checkbox的勾选状态改变时触发组件的 clickCb 自定义回调事件,它是这样的:
- clickCb: function(){
- this.setState({
- isClick : !this.state.isClick //点击checkbox时改变state.isClick
- })
- }
如注释,它会通过 this.setState 方法来改变 state.isClick 的值,进而依赖于 state.isClick 的变量 text 的值也会间接变化:
本系列的第一章就暂时讲到这里,也算是一个最基础的入门吧。
共勉~
ReactJS入门(一)—— 初步认识React的更多相关文章
- 一看就懂的ReactJs入门教程-精华版
现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...
- ReactJS入门指南
ReactJS入门指南 本文旨在介绍ReactJS的基本知识,并一步步详细介绍React的基本概念和使用方法等,以及相应的Demo.本文在很大程度上参考了React官方文档和官方指南.如果你英语还不错 ...
- ReactJs入门教程
现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...
- ReactJS入门学习二
ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...
- ReactJS入门学习一
ReactJS入门学习一 阅读目录 React是什么? React如何制作组件? 理解组件属性props 理解页面中如何渲染数据的 理解从服务器端获取数据及理解state的 回到顶部 React是什么 ...
- 一看就懂的ReactJs入门教程(精华版)
一看就懂的ReactJs入门教程(精华版) 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和 ...
- [转]ReactJS入门教程
Refference From:http://www.cocoachina.com/webapp/20150721/12692.html 现在最热门的前端框架有AngularJS.React.Boot ...
- ReactJS入门二
ReactJS入门学习二 ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事 ...
- ReactJs入门思路
ReactJs入门思路小指南 原文 http://segmentfault.com/blog/fakefish/1190000002449277 React是怎么搞的? React中,把一切东西都看 ...
- ReactJs入门教程-精华版
原文地址:https://www.cnblogs.com/Leo_wl/p/4489197.html阅读目录 ReactJs入门教程-精华版 回到目录 ReactJs入门教程-精华版 现在最热门的前端 ...
随机推荐
- git使用入门
添加文件到git仓库 git add readme.txt git commit -m "write a readme file" 查询工作区状态 git status 查询修改内 ...
- RSA算法学习
package com.test.rsa; /* * 为了选择公钥和私钥,Bob必须执行如下步骤: * 1)选择两个大素数p和q.那么p和q应该多大呢?该值越大,RSA越难于破解,但是执行加密和解密所 ...
- C语言的一些小知识
注:本文讨论的"C语言"为GNU C,而非ANSI C 标准库 语法 switch语句中的case关键词可以放在任何地方 switch (a) { case 1:; if (b== ...
- 创建Unity3D的MacOS Plugin的正确姿势
http://www.tedlindstrom.se/how-to-link-dylibs-into-unity3d/ I was roaming around the net looking for ...
- postgresql数据库
新建一个虚拟机,在本地跑程序,连虚拟机数据库报错: no pg_hba.conf entry for host "192.168.1.4" Google下,发现是要修改postgr ...
- 图解HTTP
1.返回结果的HTTP状态码 a. 2xx 成功: 200 ok 204 No Content 206 Partial Content b. 3XX重定向:301 Moved Permanently ...
- Operation not allowed after ResultSet closed--操作mysql数据库
一个stmt多个rs进行操作.那么从stmt得到的rs1,必须马上操作此rs1后,才能去得到另外的rs2,再对rs2操作.不能互相交替使用,会引起rs已经关闭错误——Operation not all ...
- php正则逆向引用与子模式分析
先看一个例子: <?php $string = 'April 15, 2003'; $pattern = '/(\w+) (\d+), (\d+)/i'; $replacement = '${1 ...
- bzoj 4330: JSOI2012 爱之项链
听说这题不公开.. 那就不贴题意了 首先要用burnside引理求出戒指的种数,那么对于一个顺时针旋转$k$个位置的置换就相当于连上一条$(i,(i+k)%R)$的边,每个环颜色必须相同 环的个数为$ ...
- webpack 使用教程--实时刷新测试
学习webpack,基本始终是围绕: 1.如何安装webpack 2.如何使用webpack 3.如何使用loader 4.如何使用开发服务器 可能我们会在如何使用开发服务器的时候,遇到诸如调试的相关 ...