React基础篇学习
到今天为止, 使用react已经一年了, 现在整理一下入门时的一些重要知识, 帮助想要学习react的同学们理解某些内容。
React 元素
React 元素,它是 React 中最小基本单位,我们可以使用 JSX 语法轻松地创建一个 React 元素:
const element = <div className="demo">Hello Everyone!</div>
React 元素是虚拟DOM元素,并不是真实的 DOM 元素,它仅仅是javascript的 普通对象
,所以也没办法直接调用 DOM 原生的 API。
如果访问 element.style
会得到 undefined
.
上面的 JSX 转译后的对象大概是这样的:
{
"type": "div",
"key": null,
"ref": null,
"props": {"
children": "Hello Everyone!",
className: "demo"
},
"_owner": null,
"_store": {}
}
只有在这个元素渲染被完成后,才能通过选择器的方式获取它对应的 DOM 元素。不过要尽量避免 DOM 操作,即便要进行 DOM 操作,也应该使用 React 提供的接口ref
和findDOMNode()
。
除了使用JSX
语法,我们还可以使用React.createElement()
和React.cloneElement()
来构建 React 元素。
React.createElement()
JSX 语法就是用React.createElement()
来构建 React
元素的。它接受三个参数,第一个参数可以是一个标签名。如div
、span
,或者 ReactClass
组件名。第二个参数为传入的属性。第三个以及之后的参数,皆作为组件的子组件。
React.createElement(
type,
[props],
[...children]
)
** React.cloneElement() **
该方法与上面的方法相似, 不同的是它传入的第一个参数是React
元素, 而不是html
标签名或组件, 新添加的属性会并入原有的属性, 传入到返回的新元素中,原来的子元素会被替换掉. 该方法第一个参数若是小写的话会被当作html标签
React 组件
在React中, 组件是用于分离关注点的, 而不是被当作模版或处理显示逻辑的, 组件是一个函数, 这点与元素不同, 元素是个js对象, typeof 得到的是object, 而组件typeof出来的是function.
React中有3种构建组件的方式. React.createClass
, ES6 class
, 无状态函数
.
React.createClass()
var Greeting = React.createClass({
render: function() {
return <h1>Hello, {this.ptops.name}</h1>;
}
});
ES6 class
实现仍是调用 React.createClass()
, 其生命周期和自动绑定方式与 React.createClass()
略有不同.
无状态函数
无状态函数是使用函数构建的无状态组件,无状态组件传入props和context两个参数,它没有state,除了render(),没有其它生命周期方法。
function Greeting (props) {
return <h1>Hello, {props.name}</h1>;
}
React.createClass()
和 ES6 class
构建的组件的数据结构是类,无状态组件
数据结构是函数,它们在 React 中被视为是一样的。
元素与组件的区别
组件是用来生成元素的, 元素是组件实例化的描述。元素数据结构是普通对象,而组件数据结构是类或纯函数。除此之外,还有几点区别要注意:
** this.props.children **
在 JSX 中,被元素嵌套的元素会以属性 children 的方式传入该元素的组件。当仅嵌套一个元素时,children 是一个 React 元素,当嵌套多个元素时,children 是一个 React 元素的数组。可以直接把 children 写入 JSX 中,但如果要给它们传入新属性,就要用到React.cloneElement()来构建新的元素。
render() {
let Child = this.props.children;
return <div><Child tip={'some-value'} /></div>
}
这种写法是行不通的, 因为 this.props.children
是一个React元素, 并不是组件, 正确的方法是:
render() {
var Child = this.props.children;
return <div>{React.cloneElement(Child, {tip: 'this is right'})}</div>;
}
** 自定义组件 **
以属性传入组件的是React元素, 并非React组件
<Nav sub={<SubNav />} />
<Nav sub={SubNav}>
我们可以认为 SubNav
是React组件, <SubNav />
是React元素.
Render
一旦组件的
this.state
或者this.props
的值发生变化, 当前的组件将会被重新绘制, 即render
。 这里重新绘制并不是重建,this.getInitialState
,this.componentWillMount
和this.componentDidMount
不会被在此调用。通过变更
this.setState
引起重绘是当前组件引起重绘的主要手段,还有一个手段是this.forceUpdate()
。而通过为子组件设定新的 props 的方式,是父组件 引起子组件重绘的主要方法。检测 this.state 或者 this.props 的不同是通过“深度比较”来完成的,也就是说,只要整个对象树的任何一个“叶子”的值变化,就将触发“重绘”。所以你需要密切注意每一个 Component 的 state 和 props 到底绑定了多大范围的数据,避免不必要的“重绘”。
你可以通过重载
this.shouldComponentUpdate
替代React
的深度比较算法。但是,只有当你真的理得清楚的时候才使用这种方法。
Component Mount
组件在第一次被绘制前被
Mount
,之前会执行this.getInitialState
、this.componentWillMount
等一系列的类成员初始化方法。但是, 重绘 不会引发
Re-mount
(你在 父组件 为 子组件 设定新的props
,只会引起 子组件 的Re-render
,不会引起 子组件 的Re-mount
(重新创建))。React 仅在两种情况下会
Re-mount
组件。一种是通过React.render
重新绘制了整个 组件树; 还有一种就是 父组件 为 子组件 设定了key property
,且当key property
变化了的时候,老的 子组件 被卸载 并销毁, 而新的子组件被重新创建、初始化和Mount
。
jsx是什么
它是一种JavaScript语法扩展,在React中可以方便地用来描述UI。
JSX基本语法规则:
JSX本身就和XML语法类似,可以定义属性以及子元素。唯一特殊的是可以用大括号来加入JavaScript表达式。遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。对于相邻的两个同级元素必须要有一个外层标签来包裹, 一个jsx元素总是被一个最外层的标签包裹, jsx标签是严格封闭的;
// 这样写是不正确的
let ele = (<span>hello</span><span>world</span>);
// 这样写是可以的
let ele = [<div><span>hello</span><span>world</span></div>];
let ele = [<span>hello</span>, <span>world</span>]; // 此时的ele并不是react元素
jsx的使用方法
我们在使用尖括号来创建jsx元素, 如 <div />
其实是调用了 React.createElement()
方法, 也正因为这样在有 jsx 出现的地方, 都需要引入 React
,
<Div />
, <div />
, <shuai />
- 如果是大写字母开头的将会被解析成变量, 该变量必须是字符串或者是个
function
不然会报错, 如果是字符串, 而且开头也大写了, 将会被解析成对应的小写的html标签, 该标签可以不是html
的标准标签; - 若是表达式将会报错, 这一点与
React.createElement()
有所不同, 后者可以接受一个表达式做为其type
, - 如果是小写字母开头的将会解析成标签, 尽管html5中没有这个标签也会被解析出来, 这也是为什么自定义的组件名必须要大写字母开头;
let obj = {
a: 'div',
b: 'Div',
c: 'Hello',
d: function(){return <div />;}
};
let d = function() {return <div />};
let Div = function() {return <div />};
window.element = <obj['a'] />; // error, 标签名不能是js表达式
window.element = <(obj.a) />; // error, 标签名不能是js表达式
window.element = <obj.b />; // right, <div></div>
window.element = <obj.d />; // <div></div>
window.element = <d />; // <d></d>
window.element = <Div />; // <div></div>
jsx中的if-else
// 在jsx标签属性中使用if:
<div id={if (condition) { 'msg' }}>Hello World!</div>
// babel编译之后:
React.createElement(
"div",
{id: if (true) { 'msg' }},
"Hello World!"
)
// 在jsx标签之间使用if
<div>{if(true) 'Hello World!'}</div>
// babel编译之后:
React.createElement(
"div",
null,
if(true) 'Hello World!'
);
不要使用 && || 给属性赋值
jsx中使用js
- 在jsx中使用变量: 这个情况很常见;
- jsx中使用函数, 一般在标签之间使用函数, 而且必须执行完返回一个
jsx元素
或者 原始类型的值, 或者返回一个(合法的jsx元素, 原始类型的值)的集合, [ , 'Right']是合法的; - jsx中使用数组, 最常见的就是map, 在jsx中遇到数组会自动展开,
React.createElement()
在处理是第三个参数是数组还是元素。
jsx注释
let ele = (
<div>
//<span>comment text</span>
<span>some text</span>
</div>
);
上述代码并不会报错, 但是却达不到注释的目的, 将会会在浏览器中显示 //
和两个 span
, 原因 是React.createElement()
正确的姿势是用 {}
将注释部分包裹起来.
let ele = (
<div>
{/*<span>comment text</span>*/}
<span>some text</span>
</div>
);
注意行间注释不要使用 {}
let ele = (
<div>
<span
clasName="demo"
/*
* 行间注释, 不能加{}, 否则会报语法错误, props里不能干巴巴的放个大括号进去
***/
>
some text
</span>
</div>
);
JSX小结:
JSX的特点:
- 允许使用熟悉的语法定义HTML元素树
- 更加语义化,允许自定义标签及组件
- 更加直观,标签处理方式,更加可读
- 抽象了
ReactElement
的创建过程 - 关注点分离, 模块化,JSX以干净且简洁的方式保证了组件中的标签与所有业务逻辑的互相分离
- 原生的js
组件的生命周期
一个React组件就是一个状态机, 随着该组件的 props
或者 state
发生改变, 它的DOM表现也会有相应的变化, React为每个组件提供了生命周期钩子函数去相应不同的时刻, 创建, 存在, 销毁. (以es5写法为例说明, es6略有不同)
实例化
一个实例初次被创建时的生命周期方法与其他各个后续实例被创建时所调用的方法略有不同. 首次创建一个组件类时下列方法被依次调用:
- getDefaultProps
- getInitialState
- componentWillMount
- render
- componentDidMount
对于该组件的所有后续创建的实例, getDefaultProps
不再被调用
- getInitialState
- componentWillMount
- render
- componentDidMount
** getDefaultProps **
在es5的写法中这个方法只会被调用一次, 不管有多少个组件实例都是这样, 为未指定props的实例设置默认属性.
任何复杂的值都在实例中共享, 并没有发生拷贝和克隆
getInitialState
对于每个实例来讲该方法只调用一次, 这一点与 getDefaultProps
不一样, 在这里可以访问到 this.props
const Hello = React.createClass({
getDefaultProps() {
return {
num: value
};
},
getInitialState() {
return {
num: this.props.num * 10
};
}
});
componentWillMount
该方法在实例被渲染之前调用, 可以在 render
之前最后一次修改组件的 state
render
创建一个虚拟DOM, 用来表示组件的输出, 对于一个组件来讲, render
是必需的一个方法, 一般满足以下几点:
- 只能通过
this.props
和this.state
访问数据 - 可以返回
null
、false
或者任何的React组件 - 只能出现一个顶级组件,不能返回一组元素
- 保持纯净,意味着不能改变组件的状态或者修改 DOM 的输出
componentDidMount
在 render
成功调用并且真实的DOM已经被渲染后, 可以在 componentDidMount
内部使用 ReactDOM.findDOMNode(this)
访问到它.
存在期
这个周期内, 组件已经可以和用户进行交互了, 用户改变了组件或者整个应用的 state
会有新的 state
流入组件树
componentWillReceiveProps
用此函数可以作为react 在 prop
传入之后, render() 渲染之前更新 state
的机会。老的 props
可以通过 this.props
获取到。在该函数中调用 this.setState()
将不会引起第二次 render
。
componentWillReceiveProps:function(nextProps){
this.setState({
isVisible: true
});
}
shouldComponentUpdate
boolean shouldComponentUpdate(object nextProps, object nextState)
在接收到新的 props
或者 state
,将要渲染之前调用。该方法在初始化渲染的时候不会调用,在使用 forceUpdate
方法的时候也不会。
如果确定新的 props
和 state
不会导致组件更新,则此处应该 返回 false。
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.someVlaue !== this.props.someVlaue;
}
如果 shouldComponentUpdate
返回 false
,则 render()
将不会执行,直到下一次 state
改变。另外,componentWillUpdate
和 componentDidUpdate
也不会被调用。
默认情况下,shouldComponentUpdate 总会返回true,建议使用React15.3版本及以后版本添加的 PureComponent
作为父级继承, 必要时你可以覆盖 shouldComponentUpdate
方法,实现新老 props
和 state
的比对逻辑。使用 shouldComponentUpdate
可以提升应用的性能。
componentWillUpdate
componentWillUpdate(object nextProps, object nextState)
在接收到新的props 或者 state 之前立刻调用, 使用该方法做一些更新之前的准备工作。在初始化渲染的时候该方法不会被调用。 如果需要更改 state
的话可以在 componentWillReceiveProps
周期中进行操作 state
。
componentDidUpdate
componentDidUpdate(object prevProps, object prevState)
在组件的更新已经同步到 DOM 中之后立刻被调用。该方法不会在初始化渲染的时候调用。使用该方法可以在组件更新之后操作DOM 元素。
销毁期
componentWillUnmount
componentWillUnmount()
在组件从DOM 中移除的时候立刻被调用。在该方法中执行任何必要的清理,比如无效的定时器,或者清除在 componentDidMount
中创建的 DOM 元素。
React基础篇学习的更多相关文章
- Linux随笔-鸟哥Linux基础篇学习总结(全)
Linux随笔-鸟哥Linux基础篇学习总结(全) 修改Linux系统语系:LANG-en_US,如果我们想让系统默认的语系变成英文的话我们可以修改系统配置文件:/etc/sysconfig/i18n ...
- android基础篇学习心得
android技术中,线程.进程.JNI.IPC和各个小框架结构是基本功.在跟随高焕堂老师的android程序猿到架构师之路系列视频中 学习完基础篇之后,颇有些心得,记录下来. android开发就是 ...
- React进阶篇学习
继续上一次基础篇, 分享一些关于React的进阶技术 React 进阶部分 ** context ** ** setState vs forceUpdate ** ** Mixins ** ** HO ...
- 2-STM32+W5500+GPRS物联网开发基础篇-基础篇学习的内容
https://www.cnblogs.com/yangfengwu/p/10936553.html 这次的基础篇为公开篇,将公开所有基础篇的资料和源码 现在说一下基础篇准备公开的内容:(大部分哈,要 ...
- React基础语法学习
React主要有如下3个特点: 作为UI(Just the UI) 虚拟DOM(Virtual DOM):这是亮点 是React最重要的一个特性 放进内存 最小更新的视图,差异部分更新 diff算法 ...
- react 基础篇 #2 create-react-app
1. 介绍 在开发react应用时,应该没有人用传统的方法引入react的源文件(js),然后在html编辑吧. 大家都是用webpack + es6来结合react开发前端应用. 这个时候,我们可以 ...
- Python基础篇学习感悟:学如不及,犹恐失之
从2019年3月底开始学习Python,4月12日在CSDN发表第一篇博文,时至今日已有4个月零12天. 4个多月的学习,老猿从一个Python小白成长到今天,可以说对Python这门语言已经略知一二 ...
- [react 基础篇]——React.createClass()方法同时创建多个组件类
react 组件 React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件.React.createClass 方法就用于生成一个组件类 一个组 ...
- React基础篇 - 02.JSX 简介
JSX 简介 请观察下面的变量声明: const element = <h1>Hello, world!</h1>; 这种看起来可能有些奇怪的标签语法既不是字符串也不是HTML ...
随机推荐
- jquery getScript动态加载JS方法改进详解
有许多朋友需要使用getScript方法动态加载JS,本文将详细介绍此功能的实现方法 $.getScript(url,callback) 这个方法是jquery自身提供的一个用于动态加载js的方法.当 ...
- 手动升级 Confluence 6 - 升级 Confluence
4. 备份 备份你的数据库,并且确定的你的数据库备份已经被正确的创建了.如果你的数据库不支持在线备份的话,你需要首先停止 Confluence 的运行. 备份你的 Confluence 安装目录(in ...
- Springboot入门实战, 使用@Value
今天开始最简单的Springboot应用 entity.Book package com.draymonder.amor.entity; import java.util.List; import o ...
- MySql中根据一列状态值查询状态的个数
最近搞报表的项目,要写数据库sql语句,根据状态值查询状态的个数,这个开始难为到我了,不过已经有解决办法了. 在数据库表中有一个字段是状态(zt),这里面有1-7这7个状态,现在查询每个状态的数量,并 ...
- jar 在windows 启动服务,卸载服务,停止端口
参考:https://www.cnblogs.com/zhuchunlei/p/9469569.html 1,启动服务 install.bat @echo off SET JAVA_HOME=&qu ...
- jsPDF生成pdf文件和中文编码
jsPDF的简单使用以及中文编码问题的解决 文中js通过CDN引入,若是为了加载时间最好下载至本地. jsPDF的使用 jsPDF简介 jsPDF 是一个基于 HTML5 的客户端解决方案,用于在客户 ...
- C++入门经典-例3.4-根据成绩划分等级
1:代码如下: // 3.4.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using ...
- [NLP] 语义网络与知识图谱入门(一)
语义网络与知识图谱入门(一) RDF/XML 本体:一种形式化的对于共享概念体系明确而又详细的说明.就是指一种抽象的模型,可以用来描述对象类型.属性以及关系类型所构成的世界. RDF/XML主要讲的就 ...
- 读取PC版微信数据库(电脑版微信数据库)内容
原始网址 https://www.cnblogs.com/Charltsing/p/WeChatPCdb.html 1.PC版微信的密钥是32位byte,不同于安卓版(7位字符串) 2.通过OD或 ...
- 使用docker 部署python 项目
使用python 开发了一个restfu api程序,使用docker镜像部署.主要有如下步骤,简单记录以供以后参考. 1. 创建DockerFile文件 创建一个DockerFile文件,文件名为D ...