文章目录
最近跟着教程,简单了解了一下react
搭建react环境
1.安装node,npm
npx create-react-app my-app
cd my-app
npm start
插件Simple React Snippets
快捷 imrc , cc
目录介绍
public:是放公共文件的
- mainifest.json:移动端配置文件,这个会在以后的课程中详细讲解。
src放自己编写的文件
-
index.js: 入口文件。
-
serviceWorker.js: 这个是用于写移动端开发的,PWA必须用到这个文件,有了这个文件,就相当于有了离线浏览的功能。
组件引入
1.在index.js文件引入
React,ReactDOM是必要的
App就是我们传说中的react组件了(组件化开发)
调用ReactDOM.render()方法把你的组件挂载到dom节点上
ReactDOM.render(组件,dom节点)
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />,document.getElementById('root'))
2.编写App.js组件
react的组件是写成类的样子
类里有个render()方法写html结构
在示例中这种类似 XML 的写法被称为 JSX。
import React, { Component } from 'react'
//相当于 import React from 'react'
//const Component = React.Component 解构赋值
class App extends Component {
render () {
return (
<div>
Hello React!
</div>
)
}
}
export default App;
npm start 就可以看到页面了
JSX
JSX就是Javascript和XML结合的一种格式。
上述app.js组件里 render() return的就是jsx
可以理解为js和html混用
1.语法
当遇到<
,JSX就当作HTML解析,遇到{
就当JavaScript解析.
ReactDOM.render(
<div>
<h1>{i == 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
jsx不能使用if else,但可以使用三目运算符
jsx推荐使用内联样式,在指定元素数字后自动添加px
注释写在{/* 注释 */}中 ,vscode快捷键ctrl+/
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>菜鸟教程
{/* 注释 */}
</h1>,
document.getElementById('example')
);
JSX 允许在模板中插入数组,数组会自动展开所有成员:
var arr = [
<h1>菜鸟教程</h1>,
<h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
2.引入css文件用classname
a.在组件里引入css文件
import '地址'
b.使用时class替换成className
3.将字符串以html进行解析
class Xiaojiejie extends Component{
constructor(props){
super(props)
this.state = {
content:'<h1>标题<h1>'
}
}
render(){
return(
<div>
<div dangerouslySetInnerHTML={{__html:this.state.content}}></div>
</div>
)
}
}
export default Xiaojiejie
在显示时,将内容写入__html对象中
第一{}代表jsx语法开始,第二个是代表dangerouslySetInnerHTML接收的是一个对象键值对
dangerouslySetInnerHTML={{__html:this.state.content}}
3.label标签
label标签的for 和 input 的id 一般共同使用,是对应关系
但是在jsx里for要用htmlFor代替
<div>
<label htmlFor="jspang">加入服务:</label>
<input id="jspang" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
</div>
一个例子
1.写一个组件Xiaojiejie.js
1.在constructor里定义data
2.事件绑定
注意: a.需要用bind改变this指向 b.在react里用this.setState(变量:改变值)改变变量
react里面禁止直接操作state!!
import React,{Component} from 'react'
class Xiaojiejie extends Component{
constructor(props){
super(props)
this.state = {
inputValue: '',
list:['111','222']
}
}
render(){
return(
<div>
<div><input value = {this.state.inputValue} onChange={this.inputChange.bind(this)} /><button onClick={this.addList.bind(this)}>增加服务</button></div>
<ul>
{
this.state.list.map((item,index)=>{
return (
<li key={item+index}
onClick = {this.deleteItem.bind(this,index)}
>{item}</li>
)
})
}
</ul>
</div>
)
}
inputChange(e){
//如果不绑定this,this是undefined,绑定后指向的是这个类的s(?)
this.setState({
inputValue:e.target.value
})
}
addList(){
this.setState({
list:[...this.state.list,this.state.inputValue]
})
}
deleteItem(index){
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
//不要直接修改state的数据,后续性能优化会有问题
//this.state.list.splice(index,1)
}
}
export default Xiaojiejie
我们需要最外用一个div进行包裹
如果不想用div可以用Fragment代替
但是要引入
import React,{Compnent,Fragment} from 'react'
2.在index.js引入并且使用
组件拆分
我们想将上述例子的每一项li作为组件拆出来
新建组件xiaojiejieitem.js
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
state = { }
render() {
return ( <div>选项1</div> );
}
}
export default XiaojiejieItem;
在Xiaojiejie的组件里导入并使用
class Xiaojiejie extends Component{
...
render(){
return(
<div>
<div><input value = {this.state.inputValue} onChange={this.inputChange.bind(this)} /><button onClick={this.addList.bind(this)}>增加服务</button></div>
<ul>
{
this.state.list.map((item,index)=>{
return (
<div>
<XiaojiejieItem key={index+item}></XiaojiejieItem>
</div>
)
})
}
</ul>
<div dangerouslySetInnerHTML={{__html:this.state.content}}></div>
</div>
)
}
}
传值
不同组件的通信
父子组件传值
父组件传值给子组件依靠属性传递
Xiaojiejie.js
<XiaojiejieItem content={item}></XiaojiejieItem>
XiaojiejieItem.js
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
render() {
return (
<div>{this.props.content}</div>
);
}
}
export default XiaojiejieItem;
子组件不能直接操作父组件的数据,只能通过父组件的方法进行修改
想实现点击选项删除该选项
父组件需要传递 1.index 2.删除的方法(传递的方法也要绑this)
Xiaojiejie.js
<XiaojiejieItem
key={index+item}
content={item}
index={index}
deleteItem={this.deleteItem.bind(this)} />
子组件新写一个方法调用传递的方法进行响应
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
constructor(props){
super(props)
//在constructor里绑定性能更好
this.handleClick = this.handleClick.bind(this)
}
render() {
return (
<div onClick={this.handleClick}>{this.props.content}</div> );
}
handleClick(){
this.props.deleteItem(this.props.index)
}
}
export default XiaojiejieItem;
PropType对接收传递值的类型进行规定
XiaojiejieItem.js
1.导入 2.使用
import PropTypes from 'prop-types'
class XiaojiejieItem extends Component{..}
XiaojiejieItem.propTypes = {
content: PropTypes.string,
index: PropTypes.number,
deleteItem: PropTypes.func
}
...
这样如果传值类型不对就会报错
isRequired
关键字了,它表示必须进行传递,如果不传递就报错。
content: PropTypes.string.isRequired
defalutProps
就可以实现默认值的功能
XiaojiejieItem.defaultProps = {
content:'1111'
}
ref的使用
ref是用来绑定dom的
使用
如下用ref绑定了input,在方法里用this.input 调用
<input
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
//关键代码——----------start
ref={(input)=>{this.input=input}}
//关键代码------------end
/>
inputChange(){
this.setState({
inputValue:this.input.value
})
}
坑
首先要知道this.setState()这个函数是异步的,在这个函数和后面使用ref操作dom,就会this.setState()还没执行完,后面的操作就执行了
但是this.setState()的第二个函数是一个回调函数,我们可以把ref的操作放在这里
例子: 用ref绑定了ul, addList方法中使用
<ul ref={(ul)=>{this.ul = ul}}>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index}
deleteItem={this.deleteItem.bind(this)} />
)
})
}
</ul>
addList(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue: '',
},()=>{
console.log('对的'+ this.ul.querySelectorAll('div').length)
})
console.log('错的'+ this.ul.querySelectorAll('div').length)
}
生命周期
Initialization
:初始化阶段。Mounting
: 挂在阶段。Updation
: 更新阶段。Unmounting
: 销毁阶段
Mounting阶段
Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里边有三个小的生命周期函数,分别是:
-
componentWillMount
: 在组件即将被挂载到页面的时刻执行。 -
render
: 页面state或props发生变化时执行。 -
componentDidMount
: 组件挂载完成时被执行。在这个周期请求数据
componentWillMount(){
console.log('componentWillMount----组件将要挂载到页面的时刻')
}
componentDidMount(){
console.log('componentDidMount----组件挂载完成的时刻执行')
}
render(){
console.log('render---组件挂载中.......')
}
componentWillMount
和componentDidMount
这两个生命周期函数,只在页面刷新时执行一次,而render
函数是只要有state和props变化就会执行
Updation
阶段
Updation
阶段,也就是组件发生改变的更新阶段,这是React生命周期中比较复杂的一部分,它有两个基本部分组成,一个是props
属性改变,一个是state
状态改变。
1.shouldComponentUpdate函数
shouldComponentUpdate
函数会在组件更新之前,自动被执行。要求返回一个布尔类型的结果,必须有返回值。返回值true/false。就是返回true,就同意组件更新;返回false,就反对组件更新。
shouldComponentUpdate(){
console.log('shouldComponentUpdate---组件发生改变前执行')
return true
}
解决子组件频繁无用渲染render
的性能问题
shouldComponentUpdate有两个参数:
- nextProps:变化后的属性;
- nextState:变化后的状态;
在子组件中加入
shouldComponentUpdate (nextProps, nextState) {
if (nextProps.content !== this.props.content) {
return true
} else {
return false
}
}
2.componentWillUpdate函数
componentWillUpdate
在shouldComponenUpdate
之后被执行,组件更新之前执行。
//shouldComponentUpdate返回true才会被执行。
componentWillUpdate(){
console.log('componentWillUpdate---组件更新前,shouldComponentUpdate函数之后执行')
}
3.componentDidUpdate
componentDidUpdate
在组件更新之后执行,它是组件更新的最后一个环节。
componentDidUpdate(){
console.log('componentDidUpdate----组件更新之后执行')
}
几个函数的执行顺序
1-shouldComponentUpdate---组件发生改变前执行
2-componentWillUpdate---组件更新前,shouldComponentUpdate函数之后执行
3-render----开始挂载渲染
4-componentDidUpdate----组件更新之后执行
4.compoenentWillReceiveProps
子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行。
unmount阶段
componentWillUnmount函数
组件从页面中删除的时候执行
一个例子
用react做一个css的动画
主要是再熟悉一下react吧。。
点击按钮实现元素的出现和隐藏
import React, { Component } from 'react';
class Sunwukong extends Component {
render () {
return (
<div>
<div className={this.state.isShow ? 'show' : 'hide'}>孙悟空</div>
<button onClick={this.toToggle}>点我</button>
</div>
);
}
constructor(props) {
super(props)
this.state = {
isShow: true
}
this.toToggle = this.toToggle.bind(this)
}
toToggle () {
this.setState({
isShow: this.state.isShow ? false : true
})
}
}
export default Sunwukong;
在主组件引入的css文件写
.show{
opacity: 1 ;
transition: all 1.5s ease-in;
}
.hide{
opacity: 0 ;
transition: all 1.5s ease-in;
}
理论:单项数据流
子组件不能直接操作父组件的数据,只能通过父组件的方法进行修改
可以理解父组件将值传递给子组件时,子组件不能直接修改,所有数据是单向的