React
一、React的简介
1、介绍
-
React 是一个用于构建用户界面的 JAVASCRIPT 库。
-
React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。
-
React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
-
React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它
2、特点
1). 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
2). 灵活 −React可以与已知的库或框架很好地配合。
3). JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
4). 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
3、框架对比
与其他框架的共同点是,都采用虚拟dom,和数据驱动
angularJs | reactJs | vueJs | |
---|---|---|---|
控制器 | √ | - | - |
过滤器 | √ | - | √ |
指令 | √ | - | √ |
模板语法 | √ | - | √ |
服务 | √ | - | - |
组件 | - | √ | √ |
jsx | - | √ | 2.0之后加入 |
二、环境搭建
1、引入文件的方式(CDN)
1、React.js:
React的核心库,解析组件,识别jsx
https://cdn.staticfile.org/react/16.4.0/umd/react.development.js
2、reactDom.js:
处理有dom相关的操作
https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js
3、Babel.js
Babel 可以将 ES6 代码转为 ES5 代码,这样我们就能在目前不支持 ES6 浏览器上执行 React 代码。Babel 内嵌了对 JSX 的支持。通过将 Babel 和 babel-sublime 包(package)一同使用可以让源码的语法渲染上升到一个全新的水平
https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js
2、官方脚手架(模块化)
yarn global add create-react-app | npm install create-react-app -g
创建 react项目
create-react-app 目录 | npx create-react-app 目录 | npm init react-app 目录
yarn eject 解构出所有的配置文件 可选
yarn start | npm start 开发
yarn build | npm run build 打包
//调试 需要安装给chrome浏览器一个插件 react-dev-tools
环境解析
- react: 核心包,解析组件,识别jsx
- react-dom: 编译 -> 浏览器
- react-scrpts: react的项目环境配置
- manifest.json 生成一个网页的桌面快捷方式时,会以这个文件中的内容作为图标和文字的显示内容
- registerServiceWorker.js支持离线访问,所以用起来和原生app的体验很接近,只有打包生成线上版本的react项目时,registerServiceWorker.js才会有效。服务器必须采用https协议
- 对Internet Explorer 9,10和11的支持需要polyfill。
第三方脚手架
yeomen/dva/umi
第一个React程序
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="box">
</div>
</body>
</html>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>, //JavaScript中竟然可以写HTML代码,这就是JSX
document.getElementById('box')
);
/*
也可以这样写:
const h = <h1>Hello, world!</h1>;
ReactDOM.render(
h,
document.getElementById('box')
);
*/
</script>
注意:
1、script标签的type取值为:text/babel
2、在script标签里写的html代码,不用双引号或者单引号,这种写法是下面要讲解的jsx。
总结:一个react的程序,就是把JSX通过ReactDOM.render()函数渲染到网页上。
另外,React的代码也可以单独写在一个文件里,扩展名为js,或者jsx。引入时,记住type=“text/babel”
JSX
1、JSX的介绍
什么是JSX:JSX=javascript xml,就是Javascript和XML结合的一种格式。是 JavaScript 的语法扩展,换句话说:只要你把HTML代码写在JS里,那就是JSX。
在实际开发中,JSX 在产品****打包阶段****都已经编译成纯 JavaScript,不会带来任何副作用,反而会让代码更加直观并易于维护。官方定义是:类 XML 语法的 ECMAScript 扩展。
2、特点:
JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
它是类型安全的,在编译过程中就能发现错误。
使用 JSX 编写模板更加简单快速。
3、JSX的语法
JSX就是把html代码写在javascript中,那么,写在javascript中有啥要求(与原来在html中的区别),这就是jsx语法要说的内容。
示例:
var msg="哥们!"
const element = <h1>Hello, world {
msg}</h1> 没有双引号,不是字符串
const List = () => (
<ul>
<li >list item</li>
<li>list item</li>
<li>list item</li>
</ul>
);
XML基本语法
-
只能有一个根标签,养成外面加上圆括号的习惯。
-
标签要闭合(单边标签得有斜杠)
元素类型
-
小写首字母对应 HTML的标签,组件名首字母大写。
-
注释使用 / * 内容 */ html标签内注释{/* 最外层有花括号*/}
元素属性
-
内联样式的style:
style属性不能直接按照html的写法写。应该用 JavaScript的写法(json对象)。
样式名以驼峰命名法表示, 如font-size需写成fontSize。默认像素单位是 px(px不用写)
let _style = { backgroundColor:"red" }; ReactDOM.render( <h1 style={_style}>Hello, world!</h1>, document.getElementById('box') );
-
外部样式的class:HTML中曾经的 class 属性改为 className(因为class是js的关键字),外部样式时使用
ReactDOM.render( <h1 className="bluebg">Hello, world!</h1>, document.getElementById('box') );
-
for 属性改为 htmlFor(因为for是js的关键字)。(for属性在html标签中是扩充单选框|复选框的选择范围)
-
事件名用驼峰命名法。HTML是全小写(onclick),JSX中是驼峰(onClick)
javascript表达式
-
使用单花括号。
react里没有vue中的指令,在JSX的任何地方需要javascript的变量,表达式等等,只需要套上 单花括号就行。
const element = <h1>Hello, {120+130}!</h1> const element = <h1>Hello, {getName("张三疯")}!</h1> <input type="text" value={val ? val : ""} />
注意:单花括号里只能写表达式,不能写语句(如:if,for)
总结:
对比原生,JSX相当于把原生里的html和javascript代码混写在一起,
vue中有很多的指令,react中只需要使用单花括号就行。
ReactDOM.render()函数
ReactDOM.render 是 React 的最基本方法。用于将JSX写的模板(经过模板渲染(把javascript的结果替换单花括号里的内容))转为 HTML 语言,并渲染到指定的HTML标签里。
ReactDOM.render( JSX写的html模板,dom容器对象);
总结:一个react的程序,就是把JSX通过ReactDOM.render()函数渲染到网页上。
程序员完成的是JSX的编写。
条件渲染
var sex='女';
if(sex=='男'){
var sexJSX=<p>我是男的</p>;
}else{
var sexJSX=<p>我是女的</p>;
}
ReactDOM.render(
<ul>
{sexJSX}
</ul>,
document.getElementById('box')
);
注意:if语句不要写在单花括号里。
列表渲染
1)、渲染数组:
//数组里存放jsx
var arr=[
<li>张三疯</li>,
<li>张四疯</li>,
<li>张五疯</li>
]
const show = ()=> (
<ul>{arr}</ul>
)
ReactDOM.render(show(),document.getElementById("box"));
2)使用Javascript的map()或者普通for循环进行列表的渲染
//普通for循环
let arr = ["铅笔","油笔","钢笔","毛笔"];
var arr2 =[];
for(let i in arr){
arr2.push(<li>{arr[i]}</li>);
}
const show = ()=> (
<ul>{arr2}</ul>
)
ReactDOM.render(show(),document.getElementById("box"));
//map
const goods = ['铅笔','钢笔'];
const goodsJSX = goods.map(function(val,index){
return <li>{val}</li>
});
ReactDOM.render(
//以下相当于react里的模板,不能出现js的语句,可以有表达式
<ul>
{goodsJSX}
</ul>,
document.getElementById('box')
);
组件
react中定义组件有三种写法:函数方式,ES5的写法,ES6(类)的写法
函数方式的组件
函数的返回值是JSX就行。即就是:如果一个函数的返回值是JSX,那么就可以当标签的方式使用。
// 定义组件,组件名首字母大写(大驼峰)
function MyCom(){
const msg="hello";
return (
<ul>
<li>{msg}:三国演义</li>
<li>{msg}:红楼梦</li>
</ul>
)
}
ReactDOM.render(
<MyCom/>, //使用组件
document.getElementById('box')
);
ES5的写法:
React.CreateClass()函数(React16后,已经被废弃了)
var MyCom = React.createClass({
render:function(){ //vue中也有render函数,是完成模板的代码
return (
<div>
<h1>Hello, world!</h1>
</div>
);
}
});
ES6类定义组件:
定义一个类,继承自 React.Component,并且,在该类里,必须有个render()函数,render函数返回一个JSX代码。
换句话说:一个普通的ES6的类,继承自React.Component,有一个render()函数,并且render()函数返回一个JSX,那么就是组件。
// 定义组件
class MyCom extends React.Component{
constructor(props){ //props:是外部传入的数据,相当于vue中的props
super(props);
// state是内部的数据,相当于vue中的date
this.state={
name:"田哥"
}
}
render(){
const msg="hello";
return (
<ul>
<li>{this.state.name}:三国演义</li>
<li>{msg}:红楼梦</li>
</ul>
)
}
}
多个组件
// 标题
function MyTitle(){
return (
<h1>商品列表</h1>
)
}
// 详情
function MyDetail(){
return (
<ul>
<li>铅笔</li>
<li>钢笔</li>
</ul>
)
}
ReactDOM.render(
<div>
<MyTitle/>
<hr/>
<MyDetail/>
</div>,
document.getElementById('box')
);
props
props 是组件对外的接口。接收外部传入的数据。是组件的属性(等同于html标签的属性)。
注意:Props对于使用它的组件内部来说,是只读的。一旦赋值不能修改。
外部传值:
<组件名 属性名1=值1 属性名2=值2 .. />
属性值=“静态值”
属性值={js数据}
组件内部使用:
1)、函数组件:
{props.属性名}
示例:
function MyPerson(props){
return (
<div>
<p>{props.name}</p>
<p>{props.sex}</p>
</div>
)
}
ReactDOM.render(
<MyPerson name="张三疯" sex="男"/>,
document.getElementById('box')
);
2)、类组件:
{this.props.属性名}
示例:
class MyPerson extends React.Component{
render(){
return (
<div>
<p>{this.props.name}</p>
<p>{this.props.sex}</p>
</div>
)
}
}
ReactDOM.render(
<MyPerson name="张三疯" sex="男"/>,
document.getElementById('box')
);
**补充:**如果传递数据多的话,可以使用对象,但是必须使用扩展运算符(…)
扩展运算符: https://blog.csdn.net/jiang7701037/article/details/103192777
class MyPerson extends React.Component{
// constructor(props){
// super(props);
// }
render(){
return (
<div>
<p>{
this.props.name}</p>
<p>{
this.props.sex}</p>
</div>
)
}
}
let person={
name:"张三疯",
sex:"男"
}
ReactDOM.render(
<MyPerson {
...person}/>,
document.getElementById('box')
);
props的默认值
1)、用 ||
function MyPerson(props){
let sex1 = props.sex || "女";
return (
<div>
<p>性别:{sex1}</p>
</div>
)
}
ReactDOM.render(
<MyPerson />,
document.getElementById('box')
);
2)、defaultProps
格式:
//1)、函数式组件和类组件都可以:
组件名.defaultProps={
属性名: 默认值
}
//2)、若为类组件,也可以在类的内部使用static修饰。
static defaultProps={
属性名: 默认值
}
示例:
function MyPerson(props){
let sex1 = props.sex || "女";
return (
<div>
<p>姓名:{props.name}</p>
<p>性别:{sex1}</p>
</div>
)
}
MyPerson.defaultProps={
name:"无名氏"
}
ReactDOM.render(
<MyPerson />,
document.getElementById('box')
);
props的类型检查:
注意:react15.5后,React.propTypes已经移入到另外一个库里,请使用prop-types。
https://cdn.staticfile.org/prop-types/15.6.1/prop-types.js
//类型约定:
组件名.propTypes={
属性名1:PropTypes.类型名,
属性名2:PropTypes.类型名
//必传参数
属性名: PropsTypes.类型名.isRequired
}
类型名的可以取值为:
PropTypes.array,
PropTypes.bool,
PropTypes.func,
PropTypes.number,
PropTypes.object,
PropTypes.string,
PropTypes.symbol,
如:
function MyPerson(props){
return (
<div>
<p>年龄:{props.age}</p>
</div>
)
}
MyPerson.propTypes={
//注意大小写
age:PropTypes.number.isRequired
}
ReactDOM.render(
<MyPerson age={12} />,
document.getElementById('box')
);
state状态机(类组件)
state 是状态,状态就是数据,state表示组件的内部数据(相当于vue组件中的data)。而props是外部传入组件的数据,类组件可以直接使用state,但是函数式组件就得用hooks(useState)
定义并初始值
class App extends React.Component {
constructor(props){
super(props);
//在react中,如果希望状态的变化会引起其它数据或者界面的变化,那么,把数据定义在state里。对应着vue中的data。
this.state={
//设置状态的初始值
属性名:属性值
}
}
}
读取状态
this.state.属性名
修改状态
必须调用setState()函数,不要直接赋值,而且,setState()函数是异步的。
这是因为react框架的响应式原理和vue框架的响应式原理根本就不一样。
1)、react框架的setState方法内部会调用render函数,这样就实现了重新渲染
2)、vue框架用的是数据劫持。
**1、修改状态时,**必须要调用setState。因为,只要调用了setState函数,那就会调用了render()。如果直接赋值,不会把数据响应式地渲染到DOM上。(即:没有调render()函数)
class Book extends React.Component{
constructor(props){
super(props);
this.state = {
copyright:"版权归2011"
}
// 千万不能这么干,因为,数据修改后,不会响应式的呈现页面上(不会再次渲染DOM)
// this.copyright="版权归2011";
// 下面这句话,可以先不管
this.changeVal = this.changeVal.bind(this);
}
changeVal(){
// this.copyright = "哈哈哈";
// console.log("this.copyright ",this.copyright);
// 这不行,这个不会把数据响应式地渲染到DOM上。(即:没有调render()函数)
// this.state.copyright = "哈哈哈";
// 这 行,因为,只要调用了setState函数,那就会调用了render();
this.setState({
copyright:"哈哈哈"
});
}
render=()=>{
console.log("render");
return (
<ul>
<li>书名:{this.props.name}</li>
<li>书龄:{this.props.age}</li>
<li>{this.state.copyright}</li>
<li>{this.copyright}</li>
<input type="button" value="修改" onClick={this.changeVal} />
</ul>
)
}
}
Book.propTypes = {
"name":PropTypes.string.isRequired,
"age":PropTypes.number
}
const jsx = <div><Book name="西游记" age={15} /></div>
ReactDOM.render(jsx,document.getElementById("box"));
//浅合并state
this.setState({
属性名:属性值
})
示例代码:
//1、假设定义的状态是:
this.state = {
person:{
name:"魏鹏",
sex:"男"
}
}
//2、如果修改时,使用下面的方式,肯定不行。因为,是浅合并,不会进入到下一级的属性进行对比
this.setState({
person:{
name:"鹏鹏"
}
})
vue和react在响应式上的区别:
1、vue:背后使用了数据劫持和观察者模式
2、react,修改数据时,调用setState,setState内部调用了render。
2、setState()函数是异步的
//改变状态前想做一些事情:
this.setState((prevState,prevProps)=>{
//一般是用于在setState之前做一些操作,this.state==prevState==修改之前的state
return {
sname:value
}
})
//改变状态后想做一些事情(如:使用新值):
this.setState({
属性名:属性值
}, () => {
//一般是用于在setState之后做一些操作。
//this.state == 修改之后的state
})
//改变状态前后都想做一些事情:
this.setState((prevState)=>{
// prevState:是旧值
console.log("prevState",prevState)
return {
age:15
}
},()=>{
// this.state:就是新值。
console.log(this.state.age);
});
class Book extends React.Component {
constructor() {
super();
this.msg = "hi";
this.state = {
name: "三国演义",
author: "李茂军02"
}
this.changeVal = this.changeVal.bind(this);
}
changeVal() {
// setState函数是异步的
// this.setState({
// name:"ddd"
// });
//console.log("this.state.name", this.state.name);//三国演义
// 在修改状态之前做事情
// this.setState((prevState)=>{
// console.log("prevState",prevState); //三国演义
// console.log("this.state",this.state);//三国演义
// return {
// name:"ddd"
// }
// });
// 在修改之后做一些事情
this.setState({
name: "ddd"
}, () => {
console.log("this.state.name", this.state.name);//ddd
console.log(document.getElementById("pId").innerHTML);//ddd
});
}
render() {
console.log("render");
return (
<div>
<p>{
this.msg}</p>
<p id="pId">书名:{
this.state.name}</p>
<p>作者:{
this.state.author}</p>
<p>价格:{
this.props.price}</p>
<input type="button" value="修改" onClick={
this.changeVal} />
</div>
)
}
}
const h = (<div>
<h1>hello 我是react</h1>
<div>
<Book author="李茂军" price="9.9" />
</div>
</div>);
ReactDOM.render(
h,
document.getElementById("box")
);
注意:
1、 直接修改state属性的值不会重新渲染组件 ,如:this.state.msg=“hello”; //亲,千万不要这么干
补充:
给引用类型的某个属性赋值
class Book extends React.Component{
constructor(props){
super(props);
this.state = {
book:{
name:"做一个有钱的人",
age:22
}
}
this.changeVal = this.changeVal.bind(this);
}
changeVal(){
// 这是不行的
// this.setState({
// "book.name":"做一个有意义的人"
// });
// 得这样做(浪费了空间):就得这么写
let b = {
...this.state.book,
name:"做一个有意义的人"
};
this.setState({
book:b
});
//或者这样做(节约了空间):这样不推荐,因为,这样破坏了setState的异步
let b = this.state.book;
b.name = "大学";
this.setState({
book:b
});
}
render=()=>{
return (
<ul>
<li>{this.state.book.age}</li>
<li>{this.state.book.name}</li>
<input type="button" value="修改" onClick={this.changeVal} />
</ul>
)
}
}
示例代码:
1)、基本示例:
class MyPerson extends React.Component{
constructor(props){
super(props);
this.state={
age:12
}
this.changeAge = this.changeAge.bind(this);
}
changeAge(){
this.setState({
age:this.state.age+1
});
}
render(){
return (
<div>
<input type="button" value="修改年龄" onClick={this.changeAge} />
<p>年龄:{this.state.age}</p>
</div>
);
}
}
2)、setState是异步的示例:
class MyPerson extends React.Component{
constructor(props){
super(props);
this.state={
age:12,
isAdult:"未成年"
}
this.changeAge = this.changeAge.bind(this);
}
/*
这种写法达不到效果:
changeAge(){
this.setState({
age:this.state.age+1
});
// setState是异步的,所以,以下的打印不是最新的值
console.log(this.state.age);
// setState是异步的,所以,以下的判断达不到效果
if(this.state.age>=18){
this.setState({
isAdult:"已成年"
});
}
}
*/
changeAge(){
this.setState({
age:this.state.age+1
},()=>{
console.log(this.state.age);
if(this.state.age>=18){
this.setState({
isAdult:"已成年"
});
}
});
}
render(){
return (
<div>
<input type="button" value="修改年龄" onClick={this.changeAge} />
<p>年龄:{this.state.age}</p>
<p>年龄:{this.state.isAdult}</p>
</div>
);
}
}
无状态组件
无状态组件就是组件内部没有(不需要)state,无状态组件也可以理解为展示组件,仅做展示用,可以根据外部传来的props来渲染模板的内容,内部没有数据。相当于木偶(没脑子)。你说显示啥,咱就显示啥。
var MyFooter = (props) => (
<div>{props.xxx}</div>
);
仅仅只是一个函数,就ok了。
有状态组件
有状态组件就是不但外部可以传入,内部也有state。
有状态组件也可以理解为容器组件,用来容纳展示组件,在容器组件里处理数据的逻辑,把结果在展示组件里呈现。容器组件也叫智能组件
创建有状态组件如下:
class Home extends React.Component { //容器组件
constructor(props) {
super(props);
this.state={
Hots:[],
Goodslist:[]
}
}
getHots(){
发送axios请求获取数据
}
getGoodsList(){
发送axios请求获取数据
}
render() {
return (
<div>
<Hot hots={this.state.Hots} ></Hot> //展示组件
<Goodslist goodslist={this.state.Goodslist} ></Goodslist >//展示组件
</div>
)
}
}
注意:
做项目时,经常会把有状态组件和无状态组件进行结合使用。
1)、 有状态组件:一般会使用类组件,处理数据的业务逻辑,包括和后端进行交互,获取数据。把数据传给无状态组件
2)、 无状态组件:一般会使用函数式组件,只做展示用,没有自己的数据,会接收有状态组件传来的数据。
事件处理
React事件的特点:
1、React 事件绑定属性的命名采用驼峰式写法,而不是小写。如:onClick。
2、如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
3、阻止事件的默认行为,不能使用return false, 必须使用 preventDefault。
4、事件处理函数里的this是undefined(啊………),如果希望事件处理函数里的this是React组件对象本身,则需要用bind。
事件语法
1、格式
<JSX元素 onClick={this.实例方法|函数体} />
示例:
class Home extends React.Component{
fn01() {
console.log("fn01");
}
render(){
return (
<div>
<input type="button" value="测试01" onClick={
this.fn01} />
<input type="button" value="测试02" onClick={
()=>{
console.log("事件绑定箭头函数")}} />
</div>
)
}
}
2、事件处理函数里的this
事件处理函数里的this是undefined,如何让事件处理函数里的this是React组件对象本身
一、使用bind
1)、构造器(构造函数)里:this.方法=this.方法.bind(this)
2)、在事件绑定时写,onClick={this.方法.bind(this)}
二、使用箭头函数
3)、定义方法时,直接使用箭头函数: 方法=()=>{箭头函数定义方法}
4)、事件绑定时,使用箭头函数:onClick={()=>this.方法()}
如:
class MyPerson extends React.Component{
constructor(props){
super(props);
this.state={
age:12,
isAdult:"未成年"
}
this.changeAge = this.changeAge.bind(this);
}
changeAge(){
………………………………
}
//直接使用箭头函数
changeAge=()=>{
this指向会找上一级
}
render(){
return (
<div>
<input type="button" value="修改年龄" onClick={this.changeAge} />
<input type="button" value="修改年龄" onClick={()=>{this.changeAge()}} />
<input type="button" value="修改年龄" onClick={this.changeAge} />
<p>年龄:{this.state.age}</p>
<p>年龄:{this.state.isAdult}</p>
</div>
);
}
}
3、事件对象
event对象是经过react处理过的。
如何获取事件对象------直接声明即可。
实例方法(ev) ev 代理事件对象 ,ev.target 返回真实DOM
事件对象的获取:
1)、直接声明(没有其它参数的情况下)
changeAge1=(ev)=>{
console.log(ev);
console.log(ev.target);
}
<input type="button" value="修改年龄2" onClick={(ev)=>this.changeAge1(ev)} />
<input type="button" value="修改年龄2" onClick={this.changeAge1} />
2)、箭头函数里直接声明(有其它参数的情况下)
changeAge2(ev,num){
console.log(ev);
console.log(ev.target);
console.log(num);
}
//注意:给onClick绑定的函数,还是只有一个参数,这个参数就是事件对象,此处在绑定的函数里再调用另外一个函数进行传参
<input type="button" value="修改年龄2" onClick={(ev)=>this.changeAge2(ev,2)} />
注意:给事件属性绑定的函数,永远只会有一个参数,该参数就是事件对象。
4、阻止浏览器的默认行为:
只能用preventDefault,不能在事件函数里使用return false
组件的内容 :children
组件的内容,使用 props.children属性获取
function Button(props) {
console.log("props",props);
return (
<>
<div>Button_children:{
props.children}</div>
</>
)
}
<Button >我是内容</Button>
refs
获取DOM的。
表示对组件真正实例(也就是html标签,也就是DOM对象)的引用,其实就是ReactDOM.render()返回的组件实例;ref可以写在html官方标签里,也可以写在组件(自定义标签里),和vue的ref是同样的意思。
官方建议: 勿过度使用 Refs(尽量不要操作DOM),在对逻辑进行处理的时候尽量优先考虑state(数据驱动)
用法
1). 赋值为 字符串(官方不推荐使用)
<input type="text" ref="username" />
this.refs.username.value
2). 赋值为 回调函数
当给 HTML 元素添加 ref 属性时,ref 回调接收了底层的 DOM 元素作为参数。
ref 回调会在componentDidMount 或 componentDidUpdate 这些生命周期回调之前执行。
//ref的值赋成回调函数时,回调的参数就是当前dom元素。
// callback refs 回调
<jsx ref={el => this.定义一个实例属性 = el} //el就是dom对象
this.定义一个实例属性 //后期用作访问jsx元素
//当组件挂载时,将 DOM el元素传递给 ref 的回调
//当组件卸载时,则会传递 null。
//ref 回调会在 componentDidMount 和 componentDidUpdate 生命周期之前调用
如:
<input type="text" ref={(currDom) => this.input1 = currDom} />
<input type="text" ref={(currDom) => this.input2 = currDom} />
3). React.createRef() (React16.3提供)
使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
//1、在构造函数里
// 实例化了ref对象
this.firstRef = React.createRef() //发生在构造器
//2、挂载在ref属性上
<jsx ref={this.firstRef} />
//3、获取dom原始
this.firstRef.current //current是官方的属性
受控元素(组件)
一个标签(组件)受react中控制,受数据,受函数,等等(其实,就是一个标签(组件)里用了react里的东西)
表单的value受控,受数据控制,
<input type="text" value={this.state.数据名} /> //model 控制 view。
<input type="text" onChange={this.方法} /> //view 控制 model
双向绑定
class MyCom extends React.Component{
constructor(){
super();
this.state={
username:"李祥"
}
}
changeVal(e){
this.setState({
username:e.target.value
})
}
render(){
return (
<div>
<input type="text" value={this.state.username} onChange={(e)=>this.changeVal(e)} />
</div>
)
}
}
处理多个输入元素(双向绑定的封装)
可以为每个元素添加一个 name 属性(通常和数据名一致),处理函数根据 event.target.name 的值来选择要做什么
class MyCom extends React.Component{
constructor(props){
super(props);
this.state={
userName:'',
content:''
}
}
changeVal(ev){
this.setState({
[ev.target.name]:ev.target.value
});
}
render(){
return (
<div>
<p>{this.state.userName}</p>
<input name="userName" type="text" onChange={(ev)=>this.changeVal(ev)} />
<p>{this.state.content}</p>
<input name="content" type="text" onChange={(ev)=>this.changeVal(ev)} />
</div>
)
}
}
非受控元素(组件)
要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据
<input type="text" value="哈哈" ref="xx" />
默认值
表单元素上的 value
将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新,指定一个 defaultValue
属性,而不是 value
可选案例:
增删改查
生命周期及其钩子函数
react组件生命周期经历的阶段:初始化阶段 -----> 运行阶段(更新期)-----> 销毁阶段
初始化阶段 (挂载):(在这个阶段完成了vue中数据挂载和模板渲染)
组件实例被创建并插入 DOM 中时,其生命周期钩子函数的调用顺序如下(粗体为使用比较多的):
1)、constructor
构造函数里,可以做状态的初始化,接收props的传值
2)、componentWillMount: 在渲染前调用,相当于vue中的beforeMount
3)、render
渲染函数,不要在这里修改数据。 vue中也有render函数。
4)、componentDidMount 相当于vue中的 mounted
渲染完毕,在第一次渲染后调用。之后组件已经生成了对应的DOM结构, 如果你想和其他JavaScript框架(swiper)一起使用,可以在这个方法中使用,包括调用setTimeout, setInterval或者发送AJAX请求等操作,相当于vue的mounted
运行中阶段(更新)(相当于vue中更新阶段)
当组件的 props 或 state 发生变化时会触发更新(严谨的说,是只要调用了setState()或者改变了props时)。组件更新的钩子函数调用顺序如下:
1)、shouldComponentUpdate(nextProps, nextState) 是否更新? 需要返回true或者false。如果是false,那么组件就不会继续更新了。
2)、componentWillUpdate,即将更新。相当于vue中的 beforeUpdate
3)、 componentWillReceiveProps(nextProps): 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。nextProps 是props的新值,而 this.props是旧值。相当于vue中的 beforeUpdate
4)、render
不要在这里修改数据
5)、componentDidUpdate。相当于vue中的 updated
在组件完成更新后立即调用。在初始化时不会被调用。 相当于vue中的updated
销毁阶段(卸载)
componentWillUnmount()
即将卸载,可以做一些组件相关的清理工作,例如取消计时器、网络请求等
示例:
class MyPerson extends React.Component{
constructor(props){
super(props);
console.log("====constructor===");
this.state={
age:12
}
}
changeAge2(ev,num){
this.setState({
age:this.state.age+1
});
}
componentWillMount(){
console.log("====首次渲染前:componentWillMount===");
}
componentDidMount(){
console.log("====首次渲染完毕:componentDidMount===");
}
shouldComponentUpdate(){
console.log("====希望更新组件吗?(state发生变化了):shouldComponentUpdate===");
return true;
}
componentWillUpdate(){
console.log("====组件更新前(state发生变化了):componentWillUpdate===");
}
componentWillReceiveProps(){
console