刚开始接触React,整理一下今天所看到的一部分内容。
react是一个JavaScript语言的工具库。需要安装node.js。react本身并不依赖于Node.js,但是我们开发中用到的诸多工具需要Node.js的支持。安装node.js的同时也就安装了npm,npm是node.js的安装包管理工具,在开发中会大量使用现有的安装包。
1. create-react-app工具
create-react-app是Facebook提供的一个快速开发react应用的工具,这个工具的目的是将开发人员从配置文件中解脱出来,无需过早关注这些技术栈细节。
npm install --global create-react-app //安装结束之后,命令行就会有create-react-app这样一个可以执行的命令
create-react-app first_react_app //这个命令是在当前目录下创建一个名为first_react_app的目录,在这个目录中会自动添加一个应用的框架
cd first_react_app
npm start //启动一个开发模式的服务器,浏览器自动打开一个网页指向本机地址http://localhost:3000/
2. 增加一个新的react组件
- React是基于组件开发的框架,组件就是网页上的一部分,能完成某个特定功能的独立的、可重用的代码。在react中,组件名称必须以大写字母开头。
- 在开发过程中,我们主要关注src目录中的内容,这个目录中是所有的源代码。
React中的 src/index.js是入口文件 ,这个应用只是渲染一个名为App的组件,App组件在同目录下的App.js文件。
页面上展示的内容 是 App.js文件中的render函数return中的内容 所决定的,public下的index.html 是项目页面的HTML文件。 - 使用import导入组件,import是ES6语法中导入文件模块的方式,ES6语法的JavaScript代码会被webpack和babel转译成所有浏览器都支持的ES5语法,create-react-app已经替我们完成了这些工作。
在index.js中引入react,目的是让浏览器能够理解下面的组件语法。
在index.js中引入react-dom,目的是把一个组件挂载到页面的一个DOM节点上,它会把这个组件渲染到public的index.html的id=root的div标签里面去。
import ClickCounter from './ClickCounter';
ReactDOM.render(< TodoList />, document.getElementById('root'));
//把TodoList组件渲染到html的id=root的标签上去。
- 我们调用ReactDOM.render()函数,并传入TodoList组件作为参数,React调用TodoList组件,TodoList组件将内部的return内容作为返回值,React DOM 将DOM高效地更新。
- 在创建组件的第一行都会从react库中引入React和Component,Component是所有组件的基类,提供了很多组件共有的功能。
import React,{Component} from 'react';
class ClickCounter extends Component{ //使用ES6创建组件类
}
注意: 这里我们导入的Component在组件定义中使用了,但是React没有被使用,但是不能删除,如果删除会被报错——在使用JSX的范围内必须要有React。在使用JSX的代码文件中,即使代码中并没有直接使用React,也需要导入,因为JSX最终会被转译成依赖于React的表达式。
定义一个组件:让一个类继承react.Component,组件展示render返回的。
import React,{Component} from 'react';
class 类名 extends Component{
render(){ 负责这个组件要显示的内容
return ( 就是组件要显示的内容
);
}
}
或者
import React from 'react';
class 类名 extends React.Component{
render(){ 负责这个组件要显示的内容
return ( 就是组件要显示的内容
);
}
}
- JSX :在JavaScript中可以编写像HTML一样的代码。让我们在react之中可以直接去使用这样标签形式的语法,可以在标签语法中通过花括号的形式写js表达式,但是不是js语句,不需要在js外面加引号,就需要引入react。
JSX和HTML的区别:
1) 在JSX中,不仅仅可以用HTML标签,还可以使用自己定义的标签。如果我们要使用自己创建的组件,直接通过标签形式来使用我们定义的组件名就可以,但是组件的名字开头必须以大写字母开头(react判断一个元素是HTML元素还是React组件的原则)。
2) 在JSX中可以通过onClick这样的方式给一个元素添加一个事件处理函数。
JSX的onClick事件处理函数和HTML中的onclick事件处理函数的不同:
1) onclick添加的事件处理函数是在全局环境下执行的,会污染全局环境。onClick挂载的函数都可以控制在组件范围内,不会污染全局空间。
2) 给很多DOM元素添加onclick事件,会影响网页的性能。在JSX中,使用了事件委托的方式处理点击事件,无论有多少个onClick出现,最后都只在DOM树上添加了一个事件处理函数。
3) 对于使用onclick的DOM元素,如果动态地从DOM树中删除的话,需要把对于的时间处理函数注销,否则可能会内存泄漏。react控制了组件的生命周期,可以清除事件处理函数,不存在内存泄漏。
在定义的组件中:
1)可以直接返回视图标签不报错。
2)外层必须要有一个根标签包裹着。
3)根标签内部可以写 js 表达式,会自动编译,js 语句会报错。
4)index.js 中 render 中引用组件时,组件名写在标签符号内。
在JSX中写注释,只在开发上有意义,控制台不会展示出来。
{/*普通的js注释*/}
或者
{
//单行注释
}
分解react的应用
在package.json文件中可以看到安装的一些内容。start命令实际上是调用了react-scripts命令,
"scripts": {
"start": "react-scripts start", //启动
"build": "react-scripts build", //创建生产环境优化代码
"test": "react-scripts test", //单元测试
"eject": "react-scripts eject" //把隐藏在react-scripts中的一系列技术栈配置都弹射到应用的顶层。eject命令是不可逆的。
},
在调用npm run eject命令时,在当前目录会添加两个目录——scripts和config,同时在package.json文件中eject这行命令消失。
react的工作方式:
-
UI=render(data);——UI是用户看到的界面,是render函数的执行结果,只接受数据作为参数。这个函数是一个纯函数,输出完全依赖于输入的函数。想要更新用户界面,只需要更新data。react.js是一种新的编程思想,函数式、响应式编程。优势:开发者的效率大大提高,开发出来的代码可维护性和可读性也大大增强。
-
相比于vue.js灵活性更大一些,处理比较复杂的业务时,技术方案会有更多一点的选择,Vue提供了更丰富的API,实现功能会更简单,但是因为API多,所以它的灵活性会受到限制。
react官网提供的react脚手架create-react-app,使用很简单,可定制性很强,调试代码也非常方便。 -
react中的响应式设计思想和事件绑定:
响应式设计思想:不直接操作DOM,操作数据,数据改变,react页面会响应数据的变化。
在react中,使用state保存数据状态,当改变状态时,需要使用setState()方法。
this.setState({
inputValue:e.target.value
});
- 编写todolist功能:
第一步:把TodoList.js文件引入到index.js中并挂载。
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
第二步:在TodoList.js文件的return中开始写标签语言
注意: render函数return出去的内容只能是一个大的JSX,如果有多于1个的JSX区块会报错,需要把多个JSX区块包裹在一个大的JSX区块中。这样在查看的时候会多一个div标签,我们可以使用React.Fragment标签,这样查看源代码时就root下不会多一层标签,Fragment也是一个组件。
import React,{Component,Fragment} from 'react';
class TodoList extends Component{
render(){
return(
<Fragment>
<div>
<input value={this.state.inputValue} onChange={this.handleInputChange} />
<button onClick={this.handleBtnClick}>add</button>
</div>
<ul>{this.getToDoItems()}</ul>
</Fragment>
}
第三步:想要在点击add按钮时,给ul中添加内容,需要给button按钮绑定一个事件,react中事件绑定是C大写的onClick,js语法要放在{}花 括号中,this指的是这个组件。
//添加数据项:
constructor(props){ //当TodoList这个组件刚被创建时,constructor会被自动执行,
super(props); //super做一些初始化
this.state={ //一创建组件时,我们在TodoList中就创建了state这个数据项,state里面可以存放很多的数据内容
list:[
"learn react",
"learn english"
]
};
};
// 怎样添加方法:新增的都是一样的,
handleBtnClick(){
//this.state.list.push("x");这样是有错的,报错是找不到state,因为在下面button上的this是指的这个组件,但是handleBtnClick这个函数中的this指的是button按钮
//解决方法是把下面button中的方法用bind函数指向组件的this,onClick={this.handleBtnClick.bind(this)}
//但是虽然不报错了,列表中的内容并没有变化,解决方法是需要调用react中的方法,this.setState去改变state中的数据,改变的是list数据,
this.setState({
list:[...this.state.list,"hello,world"] //使用的是ES6中的展开运算符,来表示list的形式,数据发生变化,页面就发生变化
});
// 还是会有一个警告,警告的意思是每个li标签上都应该有一个key值,如果不想要警告,可以在下面li中写一个key,map可以获取下标,
// {this.state.list.map((item,index)=>{
// return <li key={index+1}>{item}</li>
// })}
};
render(){
return(
<div>
<div>
<input />
<button onClick={this.handleBtnClick.bind(this)}>add</button>
</div>
<ul>
{this.state.list.map((item,index)=>{
return <li key={index+1}>{item}</li>
})}
/** map()是对数组做循环的函数 */
</ul>
</div>
);
}
上面的内容是每次add后,都增加的一样的内容。
下面实现新增列表项时新增的内容不一样的。
input框只要有变化就会触发 onChange 事件,所以给input框绑定一个onChange事件,
handleInputChange(e){ //将input框的内容赋给inputValue数据项。
this.setState({
inputValue:e.target.value
});
};
constructor(props){
super(props);
this.state={
list:[],
inputValue:'' //新增一个数据项,保存input框中的内容。
};
};
handleBtnClick(){
this.setState({
list:[...this.state.list,this.state.inputValue] //将inputValue的内容添加到list中
});
};
<input onChange={this.handleInputChange.bind(this)} />
有一个问题: 每次点击add后,需要手动删除input框中的内容,希望每次点击add后,input框可以清空。
解决方法:
handleBtnClick(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue:""
});
};
<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)} />
原因:在这里是input框发生变化,会触发onChange事件,然后inputValue会变成value的值,然后value又绑定了inputValue的值,所以这时候value的值是inputValue的值,然后点击add按钮之后,inputValue变成空,相对value的值也会变为空。
删除操作:
每次删除某一行时,需要知道这一行的id(id可以唯一的标识一行),点击事件是onClick。
handleItemClick(index){ //用来删除的,index可以用来获取删除的id
const list = [...this.state.list]; //获取一个副本
list.splice(index,1); //删除id=index的一个
this.setState({
list:list //如果键值是一样的,可以省略, ==this.setState({list})
});
//可以直接操作state中的list,但是那样性能和可调式会变差,所以推荐使用副本的写法。
};
{this.state.list.map((item,index)=>{
return <li key={index} onClick={this.handleItemClick.bind(this,index)}>{item}</li>
})}