React基础

简介

React用于动态构建用户界面的JavaScript库(只关注与视图,由Facebook开源)

特点

声明式编码;组件化编码;React Native编写原生应用;高效(优秀的Diffing算法)

高效原因:

1)使用虚拟(virtual)DOM,不总是直接操作页面真实DOM

2)   DOM Diffing算法,最小化页面重绘

JSX:

1)全称:JavaScript XML

2)react定义的一种类似于XML的JS扩展语法:JS+XML

3)本质是React.createElement(component,props,...children)方法的语法糖

4)作用:用来简化创建虚拟DOM

a.写法:var ele = (<h1 id="title"><span>Hello JSX!</span></h1>)

b.注意1:它不是字符串,也不是HTML/XML标签

c.注意2:它最终产生的就是一个JS对象

5)标签名任意:HTML标签或其他标签

JSX语法规则:

1.定义虚拟DOM时,不要写引号

2.标签中混入JS表达式时要用{}

3.样式的类名指定不要用class,要用className

4.内联样式,要用style={{key:value}}的形式去写

5.只有一个根标签

6.标签必须闭合

7.标签首字母

1)若小写字母开头,则将标签转为html中同名元素,若html中无该标签对应的同名元素,则报错

2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错

注意:区分js语句(代码)与js表达式

1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

1)a  2)a+b 3)demo()  4)arr.map()  5)function test(){}

2.语句(代码)

1)if(){}  2)for(){} 3)switch(){case:xxx}

虚拟DOM:

1)本质是object类型的对象(一般对象)

2)虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多属性;

3)虚拟DOM最终会被React转化为真实DOM,呈现在页面上

创建函数式组件:

//类型是babel,babel将jsx语句转换为js语句
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
    console.log(this);
//此处的this是undefined,因为babel编译之后开启了严格模式,严格模式下禁止自定义函数里面的this指向window,因此是undefined
    return <h2>我是组件</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>

执行了ReactDOM.render(<MyComponent/>.....之后,发生了什么?)

1、React解析组件标签,找到了MyComponent组件

2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面上

注意:

由于创建函数式组件之后,经过babel编译开启了严格模式,严格模式下禁止自定义函数里面的this指向window,因此这里函数中的this是undefined,从而不能使用react原型链上的state\ref等方法属性,反而类式组件是可以继承react原型链上的方法属性;只不过在最新版本的react中,官方新增了hooks方法,可以令函数式组件也能够使用react原型链上的方法属性。

复习一:类的基本知识:

class Person {
    constructor(name,age){
//构造器中的this是谁--类的实例对象
    this.name=name
    this.age=age
    }
}
//一般方法
speak(){
//speak方法放在哪里--类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
    console.log(`我叫${this.name},我年龄是${this.age}`)
}
//创建一个Student类,继承于Person类
class Student extend Person{
    constructor (name,age,grade){
        super(name,age)
        this.grade=grade
        this.school="家里蹲大学"
    }
    //类中可以直接写赋值语句,如下代码含义是:给Student的实例对象添加一个属性,名为a,值为1.
    a = 1
    //给类的实例加上一个属性
    static demo = 100

    speak(){
        console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`)
    }
    study(){
//study方法放在哪里--类的原型对象上,供实例使用
//通过Student实例调用study时,study中的this就是Student实例
        console.log("哈哈哈")
    }
}

const s1 = new Student('小张',15,"高一")
console.log(s1)
s1.speak()
s1.study()

总结:

1、类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。

2、如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。

3、类中所定义的方法,都是放在了类的原型对象上。

复习二:类中的方法this指向

class Person {
    constructor(name,age){
        this.name = name
        this.age = age
    }
    study(){
        //study方法放在了哪里?————类的原型对象上,供实例使用
        //通过Person实例调用study时,study中的this就是Person实例
        console.log(this);
    }
}
//方式一:
const p1 = new Person('tom',18)
p1.study()//通过实例调用study方法,输出的this为person的实例对象
//方式二:
const x = p1.study//直接将函数赋值给x
x()//x直接调用输出的this为undefined

创建类式组件:

1、创建类式组件
class MyComponent extends React.Component {
    render(){
    //render是放在哪里的?--MyComponent的原型对象上,供实例使用
    //render中的this是谁?--MyComponent的实例对象<=>MyComponent组件实例对象
        console.log('render中的this:',this)
        return<h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
    }
}
2、渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById("test"))

执行了ReactDOM.render(<MyComponent/>.....之后,发生了什么?)

1、React解析组件标签,找到了MyComponent组件

2、发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法

3、将render返回的虚拟DOM转为真实DOM,随后呈现在页面上

复习三:原生绑定事件(三种方式)

<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick = "demo()">按钮3</button>

<script>
    const btn1 = document.getElementById("btn1")
    btn1.addEventListener('click' ,()=>{
        alert("按钮1被点击了")
    })

    const btn2 = document.getElementById("btn2")
    btn2.onclick = ()=>{
        alert("按钮2被点击了")
    }

    function demo(){
        alert("按钮3被点击了")
    }
</script>

组件实例三大属性:1、State

1)理解:

state是组件对象中最重要的属性,值是对象(可以包含多个key-value的组合);

组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)。

2)强烈注意:

组件中render方法中的this为组件实例对象;

组件自定义的方法中this为undefined,如何解决?

        a.强制绑定this:通过函数对象的bind();

        b.赋值语句+箭头函数;

状态数据:不能直接修改或更新,需要调用原型上的this.setState方法进行调用。

//1、创建代码
class Weather extends React.Component{
    //构造器调用几次?--1次
    constructor(props){
        super(props)
        //初始化状态
        this.state = {isHot:false}
        //解决this指向问题方法一:
        this.demo= this.changeWeather.bind(this)
        //bind方法可以生成一个新函数,新函数绑定的this是实例对象,后续onClick调用的回调函数是通过bind生成的新函数
    }
    //render调用几次?--1+n次,1是初始化那次,n是状态更新的次数
    render(){
        //读取状态
        const {isHot} = this.state
        return<h1 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h1>
    }
    //changeWeather调几次?--点几次调几次
    changeWeather(){
        //changeWeather放在哪里?————Weather的原型对象上,供实例使用
        //通过Weather实例调用changeWeather时,changeWeather中的thia就是Weather的实例
        //由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
        //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
        
        //获取原来的isHot值
        const isHot = this.state.isHot
        //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
        this.setState({isHot:!isHot})

        //严重注意:状态state不可以直接更改,下面这行就是直接更改!!!
        //this.state.isHot = !isHot //这是错误的写法
    }
}
//2、渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

精简后代码:

//1、创建代码
class Weather extends React.Component{
//类中可以直接写赋值语句;
//初始化状态
    state = {isHot:false}
    render(){
        const {isHot} = this.state
        return<h1 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h1>
    }
//使用赋值语句,直接将changeWeather放在实例自身;箭头函数没有自己的this,它会自己通过层级关系去自己的最近外层找到this的指向
//自定义方法--要用赋值语句加箭头函数的形式
    changeWeather = ()=>{
        const isHot = this.state.isHot
        this.setState({isHot:!isHot})
        console.log(this)
    }
}
//2、渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

组件实例三大属性:2、Props

1)理解:

每个组件对象都会有props(properties的简写)属性;

组件标签的所有属性都保存在props中

2)作用:

通过标签属性从组件外向组件内传递变化的数据;

注意:组件内部不要修改props数据

3)编码写法:

一般写法:

//创建组件
class Person extend React.Component{
    render(){
        console.log(this);
        const(name,age,sex) = this.props
        return(
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age}</li>
            </ul>
        )
    }
}
//渲染组件到页面 
ReactDOM.render(<Person name="haha" age="18" sex="男"/>,document.getElementById('test1'))

精简写法:

//创建组件
class Person extend React.Component{
    render(){
        console.log(this);
        const(name,age,sex) = this.props
        return(
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age}</li>
            </ul>
        )
    }
}
//渲染组件到页面 
const p = {name:"haha",age:"18",age:"男"}
console.log(...p)//空值,无法展开
ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))
//这里由于react和babel双重加持下,可以允许使用...展开一个对象;但是只能用在标签传递中,别的地方都不行

复习四:展开运算符

使用场景1:
let arr1 = [1,3,5,7]
let arr2 = [2,4,6,8]
console.log(...arr1)//展开一个数组
let arr3 = [...arr1,...arr2]//连接数组

使用场景2:
//在函数中使用
function sum(...number){
    //函数的参数可以接到一个number的数组,数组上有一个求和的方法reduce,接收到两个参数,第一个参数是之前的值,第二个参数是当前的值
    return numbers.reduce((preValue,currentValue)=>{
        return preValue + currentValue
    })
}
console.log(sum(1,2,3,4))

使用场景3:
//构造字面量对象时使用展开语法
let person = {name:"tom",age:18}
console.log(...person);//报错,展开运算符不能展开对象
let person2 = {...person}//解构赋值,克隆了一个对象
person.name = "1"
console.log(person2)//name不会改变
console.log(person)//name发生改变

使用场景4:
//合并
let person3 = {...person,name:"222"}
console.log(person3)//name发生改变

对props的限制

//创建组件
class Person extend React.Component{
    render(){
        console.log(this);
        const(name,age,sex) = this.props
//props是只读的,不允许修改
//this.props.name = "jack"//此行代码会报错,因为props是只读的
        return(
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age + 1}</li>
            </ul>
        )
    }
}

//对props进行规则限制
Person.propTYpes = {
    name:PropTypes.string.isRequired,//属性类型和必要性限制
    sex:PropTypes.string,
    age:PropTypes.number,
    speak:PropTypes.func,//限制为函数类型
}
//默认值
Person.defaultProps = {
    sex:"男",
    age:18,
}

//渲染组件到页面 
const p = {name:"haha",age:18,age:"男"}
ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))
ReactDOM.render(<Person name="haha" age={18} sex="男" speak={speak}/> document.getElementById('test1'))

function speak(){}

props精简写法

//创建组件
class Person extend React.Component{
    
    //对props进行规则限制
    static propTYpes = {
        name:PropTypes.string.isRequired,//属性类型和必要性限制
        sex:PropTypes.string,
        age:PropTypes.number,
        speak:PropTypes.func,//限制为函数类型
    }
    //默认值
    static  defaultProps = {
        sex:"男",
        age:18,
    }
    render(){
        console.log(this);
        const(name,age,sex) = this.props
//props是只读的,不允许修改
//this.props.name = "jack"//此行代码会报错,因为props是只读的
        return(
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age + 1}</li>
            </ul>
        )
    }
}


//渲染组件到页面 
const p = {name:"haha",age:18,age:"男"}
ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))
ReactDOM.render(<Person name="haha" age={18} sex="男" speak={speak}/> document.getElementById('test1'))

function speak(){}

函数式组件中使用props
 

//创建组件
function Person(props) {
        console.log(props);
        const(name,age,sex) = props
        return(
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{sex}</li>
                <li>年龄:{age + 1}</li>
            </ul>
        )
    }
}
    //对props进行规则限制
    Person.propTYpes = {
        name:PropTypes.string.isRequired,//属性类型和必要性限制
        sex:PropTypes.string,
        age:PropTypes.number,
        speak:PropTypes.func,//限制为函数类型
    }
    //默认值
    Person.defaultProps = {
        sex:"男",
        age:18,
    }


//渲染组件到页面 
ReactDOM.render(<Person name="haha" age={18} sex="男"/> document.getElementById('test1'))

构造器作用:

1)通过给this.state赋值对象来初始化内部state

2)为事件处理函数绑定实例

注意:

构造器可写可不写,如果写了必须要接props,并且要用super进行调用props,否则this.props是undefined;构造器是否接收props,是否传递props,取决于是否希望在构造器中通过this访问props

组件实例三大属性:3、refs和事件处理

1)理解:组件内的标签可以定义ref属性来标识自己

2)编码:

字符串形式的ref

//创建组件
class Demo extends React.Component{
    //展示左边的输入框数据
    showData = ()=>{
        const {input1} = this.refs
        alert(input1.value)
    }
    //展示右边的输入框数据
    showData2 = ()=>{
        const{input2} = this.refs
        alert(input2.value)
    }
    render(){
        return(
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a='1' b='2'/>,document.getElementById('test'))

回调函数形式的ref

//创建组件
class Demo extends React.Component{
    state = {isHot:false}
    //展示左边的输入框数据
    showData = ()=>{
        const {input1} = this
        alert(input1.value)
    }
    //展示右边的输入框数据
    showData2 = ()=>{
        const{input2} = this
        alert(input2.value)
    }
    //切换天气
    changeWeather = ()=>{
        //获取原来的状态
        const {isHot} = this.state
        //更新状态
        this.setState({isHot:!isHot})
    }
    render(){
        return(
            const {isHot} = this.state
            <div>
                <h1>今天天气很{isHot ? "凉爽" : "炎热"}</h1>
                <button onClick={this.changeWeather}>点我切换天气</button>
                <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a='1' b='2'/>,document.getElementById('test'))

注意:如果ref回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null,然后第二次会传入参数DOM元素,这是因为每次渲染时会创建一个新的函数实例,所以React清空旧的ref并且设置新的,通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的,即可以忽略。

//创建组件
class Demo extends React.Component{
    state = {isHot:false}
    //展示左边的输入框数据
    showData = ()=>{
        const {input1} = this
        alert(input1.value)
    }
    //展示右边的输入框数据
    showData2 = ()=>{
        const{input2} = this
        alert(input2.value)
    }
    //切换天气
    changeWeather = ()=>{
        //获取原来的状态
        const {isHot} = this.state
        //更新状态
        this.setState({isHot:!isHot})
    }
    saveInput = (c)=>{
        this.input1 = c
    }
    render(){
        return(
            const {isHot} = this.state
            <div>
                <h1>今天天气很{isHot ? "凉爽" : "炎热"}</h1>
                <button onClick={this.changeWeather}>点我切换天气</button>
                //jsx中的注释:                
                {/*<input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/>&nbsp;*/}
                <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a='1' b='2'/>,document.getElementById('test'))

createRef

//创建组件
class Demo extends React.Component{
    //React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”
    myRef = React.createRef()
    myRef2 = React.createRef()
    //展示左边的输入框数据
    showData = ()=>{
        alert(this.myRef.current.value)
    }
    //展示右边的输入框数据
    showData2 = ()=>{
        const{input2} = this.refs
        alert(this.myRef2.current.value)
    }
    render(){
        return(
            <div>
                <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a='1' b='2'/>,document.getElementById('test'))

事件处理

1)通过onXxx属性指定事件处理函数(注意大小写)

        a.React使用的是自定义(合成)事件,而不是使用的原生DOM事件--为了兼容性

        b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效

2)通过event.target得到发生事件的DOM元素对象--发生事件的元素正好是操作的元素,可以避免使用ref,不要过度使用ref.

//创建组件
class Demo extends React.Component{
    //展示右边的输入框数据
    showData2 = (event)=>{
        alert(event.target.value)
    }
    render(){
        return(
            <div>
                <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
            </div>
        )
    }
}
//渲染组件到页面
ReactDOM.render(<Demo a='1' b='2'/>,document.getElementById('test'))

收集表单数据

包含表单的组件分类:

a.非受控组件

//创建组件
Class Login extends React.Component{
    handleSubmit = (event)=>{
        event.preventDefault()//阻止表单提交
        const{username,password} = this
        alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`)
    }
    render(){
        return(
            <form onSubmit={this.handleSubmit}> 
                用户名:<input ref={c=>this.username = c} type="text" name="username"/>
                密码:<input ref={c=>this.password = c} type="password" name="password"/>
                <button>登录</button>
            </form>
        )
    }
}
//渲染组件
ReactDOM.render(<Login>,document.getElementById('test'))

b.受控组件(推荐使用)

//创建组件
Class Login extends React.Component{
    //初始化状态
    this.state = {
        username:''
        password:''
    }
    //保存用户名到状态中
    saveUsername = (event)=>{
        this.setState({username:event.target.value})
    }
    //保存密码到状态中
    saveUsername = (event)=>{
        this.setState({password:event.target.value})
    }
    handleSubmit = (event)=>{
        event.preventDefault()//阻止表单提交
        const{username,password} = this.state
        alert(`你输入的用户名是${username},你输入的密码是${password}`)
    }
    render(){
        return(
            <form onSubmit={this.handleSubmit}> 
                用户名:<input onChange={this.saveUsername} type="text" name="username"/>
                密码:<input onChange={this.savePassword} type="password" name="password"/>
                <button>登录</button>
            </form>
        )
    }
}
//渲染组件
ReactDOM.render(<Login>,document.getElementById('test'))

高阶函数_函数柯里化

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数:

1)若A函数,接收的参数是一个函数,那该函数就是高阶函数;

2)若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数;

常见的高阶函数有:Promise、setTimeout、arr.map()等

函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

function(a,b,c){
    return a+b+c
}
const x = function(1,2,3)
//函数柯里化后
function(a){
    return(b){
        return(c){
            return a+b+c
        }
    }
}
const result = function(1)(2)(3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值