为什么选择React?
- 组件化的开发构思,项目便于维护
- 只需关注业务逻辑,高效快速更新DOM
- 海量的周边生态,友好的开发环境
hello React
<div id="root"></div>
<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>
const title = React.createElement('h1', null, 'hello React')
ReactDOM.render(title, document.getElementById('root'));
</script>
使用JSX语法
JSX相当于是语法糖,需要bebel进行编译,JSX语法规则如下:
1.定义虚拟dom时,不要用引号
2.标签中混入js表达式时用{}
3.样式的类名用className
4.内联样式,常用style={{}}
5.只有一个根标签
6.标签必须闭合
7.标签首字母 若是小写,则转换味html中同名元素;若是大写,则当组件处理
<div id="root"></div>
<!-- 虚拟dom本质是Object类型的对象 -->
<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>
<script type="text/babel">
const title = 'hello JSX语法';
const vDOM =(<h1 style={{color: 'red' }}>{title}</h1>)
ReactDOM.render(vDOM,document.getElementById('root'));
</script>
解决React使用JSX语法在Vscode自动保存后报错问题:
下载此插件
卸载此插件
JSX语法练习
<div id="root"></div>
<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>
<script type="text/babel">
const data = ['vue','react','angular'];
ReactDOM.render(
<div>
<h1 style={{color: 'red'}}>前端三大主流框架</h1>
<ul>
{data.map((item,index)=>{
return <li key={index}>{item}</li>
})}
</ul>
</div>,document.getElementById('root'));
</script>
组件基础
React中定义组件的方式有:函数组件与 class 组件
<div id="root1"></div>
<div id="root2"></div>
<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>
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
return <h1>组件基础--函数式组件【适用于简单组件--无状态】</h1>
};
//渲染到页面
ReactDOM.render(<MyComponent/>,document.getElementById('root1'));
//2.创建类组件
class MyComp extends React.Component {
//render是在MyComp的原型对象,供实例使用
render(){
console.log(this) //组件实例对象
return (
<h1>组件基础--类组件【适用于复杂组件--有状态】</h1>
)
}
}
ReactDOM.render(<MyComp/>,document.getElementById('root2'));
</script>
执行了ReactDOM.render(<MyComp/>,document.getElementById('root2'))之后发生了什么?
1.React解析组件标签,找到了MyComp组件;
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用原型上的render方法;
3.将render返回的虚拟dom转为真实dom,随后渲染到页面。
组件实例属性state
<div id="root"></div>
<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>
<script type="text/babel">
//注意:类中的方法局部开启了严格模式 若不是组件实例调用则不会指向实例 指向undefined
class Weather extends React.Component{
constructor(props){
console.log('constructor') // 调用1次
super(props)
this.state = {isHot:false}
// this.changeWeather = this.changeWeather.bind(this) 改变类中方法this的指向 生成一个新的函数
this.demo = this.changeWeather.bind(this)
}
render(){
console.log('render') //调用1+n次 n为更新状态的次数
// console.log(this) 组件实例对象
return (
<div>
<h1>组件核心之一state</h1>
<h2 onClick={this.demo}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h2>
</div>
)
}
changeWeather(){
//调用n次
const isHot = this.state.isHot
//借助内置api更改state
this.setState({
isHot:!isHot
})
}
}
ReactDOM.render(<Weather/>,document.getElementById('root'));
简写如下:
//同上效果
<script type="text/babel">
class Weather extends React.Component {
state = {isHot:true} //这样写也是实例上的属性
render(){
const {isHot} = this.state
return (
<div>
<h1>组件核心之一state</h1>
<h2 onClick={this.changeWeather}>今天天气{isHot ? '炎热' : '凉爽'}</h2>
</div>
)
}
//箭头函数中this指向声明时上下文
changeWeather = ()=>{
const {isHot} = this.state
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>,document.getElementById('root'));
</script>
组件实例属性props
当react元素作为自定义组件,将jsx所接受的属性转换成单个对象传递给组件,这个对象被称为“props”(就是父组件传递给子组件的对象)
<div id="root"></div>
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.min.js"></script>
<script type="text/babel">
class Person extends React.Component {
render(){
const {name,age} = this.props //注意:props是只读的
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
</ul>
)
}
}
//类型验证
Person.propTypes = {
name: PropTypes.string.isRequired,
age:PropTypes.number,
}
//默认值
Person.defaultProps = {
age:18
}
ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'))
// const batch = {name:'jiangyao',age:21}
// ReactDOM.render(<Person {...batch}/>,document.getElementById('root'));
</script>
也可以这样写:
<script type="text/babel">
class Person extends React.Component {
//构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props
constructor(props){
super(props)
console.log('constructor',this.props)
}
//静态属性
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
}
static defaultProps = {
age:18
}
render(){
const {name,age} = this.props
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
</ul>
)
}
}
ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'))
</script>
函数式组件虽然不可以用实例的属性state和refs,但可以用props,如下:
<script type="text/babel">
function Person(props){
//函数式组件也可以用props
const {name,age} = props
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
}
Person.defaultProps = {
age:18
}
ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'));
</script>
组件实例属性refs
定义ref的三种形式:
1.字符串形式的ref可能会在未来的版本被废弃,效率不高
2.使用回调函数(内联函数)形式的ref,如下(第二个input示例),使用回调函数(内联函数)形式的ref,在更新过程中它会被执行两次
3.函数直接绑定在类上(第三个input示例)
<script type="text/babel">
class Demo extends React.Component{
render(){
return (
<div>
<input ref="input" type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showData}>点我</button>
<input onBlur={this.showData2} ref={currentNode=>this.input2=currentNode} type="text" placeholder="失去焦点显示数据"/>
<input type="text" ref={this.showData3}/>
</div>
)
}
showData = ()=>{
console.log(this) //组件实例有refs
console.log(this.refs.input) //得到真实dom
const {input} = this.refs
alert(input.value)
}
showData2 = ()=>{
const {input2} = this //注意区别,直接把input2挂在组件实例上
alert(input2.value)
}
showData3 = (currentNode)=>{
this.input3 = currentNode
console.log(currentNode)
}
}
ReactDOM.render(<Demo/>,document.getElementById('root'));
</script>
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点
<script type="text/babel">
class Demo extends React.Component{
myRef = React.createRef()
showData = ()=>{
console.log(this.myRef)
alert(this.myRef.current.value)
}
render(){
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showData}>点我</button>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.getElementById('root'))
</script>
React中事件处理
1.通过onXxxx属性指定事件处理函数
- React使用的是自定义(合成)事件,而不是使用原生DOM的事件---为了更好的兼容
- React中的事件是通过事件委托方式处理的(委托给最外层根元素)
2.通过
event.target得到发生事件的DOM元素对象
<script type="text/babel">
//当发生事件的节点和操作的节点是同一个时可以用event获取值,不要过度使用ref
class Demo extends React.Component {
render(){
return (
<input onBlur={this.showVal} type="text"/>
)
}
showVal = (event)=>{
alert(event.target.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('root'));
</script>
非受控组件
<script type="text/babel">
//非受控组件---现用现取
class Login extends React.Component {
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
用户名:<input ref={current=>this.username=current} type="text" name="username"/>
密码:<input ref={current=>this.password=current} type="password" name="password"/>
<button>登录</button>
</form>
)
}
handleSubmit = (event)=>{
event.preventDefault() //阻止表单默认提交
const {username,password} = this
alert(`用户名:${username.value} 密码:${password.value}`)
}
}
ReactDOM.render(<Login/>,document.getElementById('root'));
</script>
受控组件
类似vue的里面的双向绑定指令v-model
<script type="text/babel">
class Login extends React.Component {
//初始化状态
state = {
username:'',
password:''
}
render(){
const {username,password} = this.state
return (
<form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
用户名:<input onChange={this.handleName} type="text" name="username" value={username}/>
密码:<input onChange={this.handlePass} type="password" name="password" value={password}/>
<button>登录</button>
</form>
)
}
handleSubmit = (event)=>{
event.preventDefault() //阻止表单默认提交
const {username,password} = this.state
alert(`用户名:${username} 密码:${password}`)
}
handleName = (event)=>{
this.setState({username:event.target.value})
}
handlePass = (event)=>{
this.setState({password:event.target.value})
}
}
ReactDOM.render(<Login/>,document.getElementById('root'));
</script>
引入生命周期
在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。
当 A 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)”。
同时,当 DOM 中 A 组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)”。
我们可以为 class 组件声明一些特殊的方法,当组件挂载或卸载时就会去执行这些方法,这些方法叫做“生命周期方法”。
<script type="text/babel">
//创建组件
class Test extends React.Component {
state = {
opacity:0.5
}
//卸载组件
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
//生命周期--组件挂载完毕后调用
componentDidMount(){
this.timer = setInterval(()=>{
let {opacity} = this.state
opacity -= 0.1
if(opacity <= 0) opacity = 1
this.setState({opacity})
},200)
}
//生命周期--组件将要卸载
componentWillUnmount(){
clearInterval(this.timer)
}
render(){
//在这里写定时器会无限递归 改变状态便会重新调用render
// setInterval(()=>{
// let {opacity} = this.state
// opacity -= 0.1
// if(opacity <= 0) opacity = 1
// this.setState({opacity})
// })
return(
<div>
<h1 style={{opacity:this.state.opacity}}>react生命周期学习</h1>
<button onClick={this.death}>点击</button>
</div>
)
}
}
ReactDOM.render(<Test/>,document.getElementById('root'));
</script>
旧版生命周期
应用示例:
<div id="root"></div>
<div id="test"></div>
<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>
<script type="text/babel">
//创建组件
class LifeCycle extends React.Component{
//挂载时--构造器
constructor(props){
console.log('constructor')
super(props)
this.state = {count:0}
}
add = ()=>{
const {count} = this.state
this.setState({count:count+1})
}
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
force = ()=>{
//强制更新
this.forceUpdate()
}
//挂载时--组件将要挂载的钩子
componentWillMount(){
console.log('componentWillMount')
}
//挂载时--组件挂载完毕的钩子
componentDidMount(){
console.log('componentDidMount')
}
//控制组件更新的钩子
shouldComponentUpdate(){
console.log('shouldComponentUpdate')
return true
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('componentWillUpdate')
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('componentDidUpdate')
}
//组件将要卸载的钩子
componentWillUnmount(){
console.log(' componentWillUnmount')
}
//render挂载钩子
render(){
console.log('render')
const {count} = this.state
return (
<div>
<h2>当前数字为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>点击卸载组件</button>
<button onClick={this.force}>不更改数据,强制更新</button>
</div>
)
}
}
class Father extends React.Component{
constructor(props){
super(props)
this.state = {name:'jack'}
}
test = ()=>{
const {name} = this.state
this.setState({name:'sandwich'})
}
render(){
const {name} = this.state
return (
<div>
<div>Father</div>
<button onClick={this.test}>点击改变数据</button>
<Son name={name}/>
</div>
)
}
}
class Son extends React.Component{
//组件将要接收新的props的钩子
componentWillReceiveProps(props){
console.log(props)
console.log('componentWillReceiveProps')
}
render(){
return (
<div>Son:{this.props.name}</div>
)
}
}
ReactDOM.render(<Father/>,document.getElementById('test'))
ReactDOM.render(<LifeCycle/>,document.getElementById('root'));
</script>
新版生命周期
新增钩子:getDerivedStateFromProps() getSnapshotBeforeUpdate()
即将废弃的钩子:componentWillMount() componentWillReceiveProps() componentWillUpdate()