react-1-初识

react

jsx语法规则:

1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key : value}}的形式去写。5.只有一个根标签
6.标签必须闭合
7.标签首字母
    (1).若小写字母开头,则将改标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
    (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。|

模块化和组件化开发

模块:提供特定功能的js程序,即js文件 作用:复用js,简化js的编写,提高js运行效率e
组件:用来实现局部功能效果的代码和资源的集合(html/css/js/image)等等 作用:复用编码,简化项目编码,提高运行效率

模块化:应用js都以模块来编写。
组件化:应用以多组件的方式实现。

react-虚拟dom

js创建虚拟dom

<script type="text/javascript">
    // 创建虚拟dom
    const VDOM = ReactDOM.createElement('h1',{id:'title'},ReactDOM.createElement('span',{},'hello,react'))
    // 渲染虚拟dom到页面
    ReactDOM.render(VDOM,document.getElementById('test'))
</script> 

jsx创建虚拟dom

<script type="text/babel">
    //创建虚拟dom
    const VDOM = (  //翻译后: const VDOM = ReactDOM.createElement('h1',{id:'title'},ReactDOM.createElement('span',{},'hello,react'))就是原生js
        <h1>
            <span>Hello,react</span>
        </h1>
    )
    //渲染到页面
    ReactDOM.render(VDOM,document.getElementById('test'))
</script> 

关于虚拟DOM:
1.本质是0bject类型的对象(一般对象)
2.虚拟DOM懂较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。

函数组件、类式组件

  • {}可写js表达式

  • react如何区分原生标签与react自定义组件标签? 通过标签首字母区分,原生小写,react标签为大写。

【函数式组件】类名大写,直接返回dom
【类式组件】必须继承React.Component,并且定义render(){}方法 在render方法中返回dom

//1.创建类式组件
class MyComponent extends React.Component{
    render(){
        //render是放在哪里的?—MyComponent的原型对象上,供实例使用。
        return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
    }
}

三大核心属性

类实例对象重要属性:【1、state】

可把数据存放到该属性上 其本质:state = {}

  • 原生html的标签属性有所更改,如:class写className、onclick写onClick、onblue写onBlue

  • 事件绑定
    1、无法直接通过方法名引用方法,因为方法需要实例引用。
    2、组件类外定义的方法无法获取到组件类属性state
    3、只有通过组件类实例调用方法时,方法内this才是组件类实例。才能通过this调用state。
    4、建议组件类内部定义实现某一功能的方法,render(){}标签内属性通过this直接引用方法。需要绑定方法的this,通过【this.xxx(实例对象的) = this.xxx(原型链上的).bind(this)】绑定。

定义方法:function demo(){}
组件类构造方法中绑定this:this.demo = this.demo.bind(this)

事件错误使用:return <h1 onclick={demo()}></h1>
事件正确使用:return <h1 onclick={this.demo}></h1>
  • 关于this(个人理解)
自定义组件没有state state是【React.Component】的原型上的
方法中绑定this:【this.xxx(实例对象的) = this.xxx(原型链上的).bind(this)】 
通过此方法中的this就指向了【React.Component】的this,可以通过this.state调用state了。
前面的xxx是变量名,后面的xxx必须是在原型链上存在的。两者定义的【名称】可以不一样。在render中用的是前面的xxx。
  • 状态(state)不可直接更改。
this.state.isHot = !isHot //错误的写法 【直接赋值】。
this.setState({isHot:!isHost})  //正确写法 【调用内置函数赋值】,该种修改state中的值是合并不是替换,即state中的数据同名覆写,不同名保持
  • 自己创建的组件
    ① 构造器:调用一次。
    ② render方法: 调用1+n次 【1=初始化,n=每次更新状态调一次,更新几次调用几次】

  • 问题:如果有多个方法this需要绑定,在构造方法需要定义非常多的【this.xxx(实例对象的) = this.xxx(原型链上的).bind(this)】
    解决:通过箭头函数解决

changeweather = ()=>{
      const isHot = this.state.isHot;
      this.setstate({isHot : !isHot});
}

state小结

  • 类有原型,类创建的对象实例自身和原型不关联。【类对象实例->类->类继承的父类】则构成原型链,当调用一个什么的时候,当前找不到就顺着原型链往上找。
  • state可以直接在自定义组件类上面赋值,不必在构造器中赋值。
  • 自定义方法**–**用赋值语句形式+箭头函数。这样方法就不用绑定了。箭头函数没有自己的this,就会往上找,
  • state无法直接修改,需通过内置方法修改

类实例对象重要属性:【2、props】

props 组件传递过来的参数,如在使用组件标签时定义的属性

ReactDOM.render(<Person name="jerry" /> ,document.getElementById('test'))  //props中默认搜集标签中的属性如:name props中会包含[name]

const p = {name:'老刘',age: 18, sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test3')) // 通过【...】传递属性  props中会包含[name,age,sex]

【…】 展开运行符

  • 【props】属性限制
**代码在组件类外写**

1、类型限制:
定义规则方式:
组件名称.propTypes = {
    //name:React.PropTypes.string  // PropTypes 15.5版本之前React有该属性 可以这样用【React.PropTypes】,16版本的时候不从这里取了,封装了【prop-types.js】
    //name:PropTypes.string          // 引入【prop-types.js】有了全局对象PropTypes
    //name:PropTypes.string.isRequired  //isRequired 表示 name属性必须传递过来,否则保错。
}
类型:
string 字符串
number 数字
func 函数


2、设置默认值:使用标签时没传递相关属性,在render中也可以读到,读到的是默认设置的
定义规则方式:
组件名称.defaultProps = {
    name:'xiaoai'   //name默认值为xiaoai
}
  • 【props】默认是只读的

  • 如何把【props】属性限制定义到组件类中
    只要保证类自身拥有规则属性,就能进行规则限制。所以可以直接在类中定义规则,把组件名称.去掉,如:

**代码在组件类中写**

static defaultProps = {
    name:'xiaoai'   //name默认值为xiaoai
}
  • 构造函数与props
constructor(props){
      //构造器是否接受props,是否传递给super,取决于:是否希望在构造器中通过this访问props
      super(props)
      console.log('constructor' ,this.props);
}
  • 函数式组件使用props
    函数式组件只可以使用组件三大核心属性中的props。
    属性限定只能定义在函数外。
  • 为什么可以使用props?因为函数可以接收props
function Person (props){
    const {name,age,sex} = propsreturn(
    <ul>
        <li>姓名: {name}</li>
        <li>性别: { sex}</li>
        <li>年龄: {age}</li>
    </ul>
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" sex="女" age={18}/>, document.getElemertById('test1'))

类实例对象重要属性:【3、refs】

  • 组件内的标签可以定义【ref属性】来标识自己。 组件类中属性refs会搜集组件类中所有标签ref属性。

  • 通过【refs.ref名称】可以拿到ref对应的真实dom节点。

  • 1、字符串形式的ref

**代码在组件类中**
render(){
    return(
        <div>
              <input ref="input1" type="text" placeholder="点击按钮提示数据"/>  //ref通过回调函数把当前节点放到了实例对象中
              <button onclick={this.showData}>点我提示左侧的数据</button>
        </div>
    )
}
//展示左侧输入框的数据
showData = ()=>{
    const {input1} = this.refs
    alert(input1.value) 
}
  • 2、回调函数的ref
**代码在组件类中**

render(){
    return(
        <div>
              <input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/>  //ref通过回调函数把当前节点放到了实例对象中
              <button onclick={this.showData}>点我提示左侧的数据</button>
        </div>
    )
}
//展示左侧输入框的数据
showData = ()=>{
    const {input1} = this //由于ref已把input1放了进来,所以可以在this中获取
    alert(input1.value) 
}

回调ref中执行次数:
内联方式=render更新过程中会被执行两次,第一次传参null,第二次传参当前dom节点。
回调函数通过定义成class绑定函数的方式可避免该问题。
两者没啥影响,一般还是直接写内联比较多。

  • 3、createRef
    当前比较推荐的一种形式
    React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的,只保存一个ref
**以下代码都在组件类内**

//定义一个ref
myRef2 = React.createRef()

//绑定ref
<input onBlur={this.showData2] ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>

//使用ref
showData2 = ()=>{
    alert(this.myRef2.current.value);
}

高阶函数

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

  • 1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
  • 2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。I常见的高阶函数伺

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

生命周期

挂载(mount):当clock 组件第一次被渲染到DOM中的时候,就为其设置一个计时器。
卸载(unmount):当DOM中clock组件被删除的时候,应该清除计时器。

  • 生命周期钩子调用顺序和个人调用顺序没关系,由框架决定。
    函数名称:生命周期回调函数、生命周期钩子函数、生命周期函数、生命周期钩子

  • 1、构造器函数 【constructor(){}

  • 2、挂载前 【componentWillMount(){}

  • 3、初始化 【render(){}】 // 初始化渲染、状态更新后

  • 4、挂载后 【componentDidMount(){}】 //组件挂载完毕执行

  • 5、卸载组件前 【componentwillUnmount(){}】 // 组件将要卸载时执行

卸载组件:【ReactDOM.unmountComponentAtNode(document.getElementById(‘test’)) 】

其他钩子函数:
旧生命周期图

  • 1、setState() 修改状态更新 --> 【shouldComponentUpdate(){}】相当于阀门,决定是否更新,返回true往下走,false则终止。默认返回true -->
  • 2、forceUpdate() 强制更新 --> 【componentWillUpdate(){}】更新前执行的函数 -->
  • –>【componentDidUpdate(){}】更新后执行的函数。

组件父子关系: 在A组件中引用B组件,A即是B组件的父组件。
props的传递: 在引用的B标签中直接定义标签属性传递过去。

  • 3、【compenentWillReceiveProps(props){}】子组件将要接收传递过来的props前执行,第一次接收props时不会调用。

新生命周期图

新版生命周期把旧版中带【will】的钩子函数进行了改进。

  • static getDerivedStateFromProps(props,state){}】 需返回[从props派生的state]或[null]。若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
  • static getSnapshotBeforeUpdate(prevProps,prevState){}】 需返回[快照值]或[null]。返回值作为参数传递给【componentDidUpdate()】。

虚拟DOM中key作用:

状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

  • 1、旧虚拟DOM中找到与新虚拟DOM相同的key:
    内容没变,直接使用之前的真实DOM
    若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

  • 2、旧虚拟DOM中未找到与新虚拟DOM相同的key:
    根据数据创建新的真实DOM,随后渲染到到页面

  • 用index作为key可能会引发的问题:
    ① 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新–>界面效果没问题,但效率低。
    ② 如果结构中还包含输入类的DOM:会产生错误DOM更新–>界面有问题。

注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。开发中最好用唯一标识作为key

react脚手架

三个比较重要的文件

【./public/index.html】react应用容器

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!-- %PUBLIC_URL%代表public文件夹路径 -->
    <!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> 可以用以下相对路径的写法 -->
    <link rel="icon" href="./favicon.ico" />
    <!-- 开启理想视口,用于做移动端网页的适配 -->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
    <meta name="theme-color" content="#000000" />
    <!-- 描述信息 -->
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <!-- 指定网页添加大手机主屏幕后的图标(仅支持苹果) -->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!-- 应用加壳时的配置文件 -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <!-- 浏览器不支持js则展示标签中的内容 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <!-- react单页面应用容器 -->
    <div id="root"></div>
    
  </body>
</html>

【./src/index.js】应用入口
【./src/App.js】 外壳组件App

  • 如何区分应用中组件js文件和普通js文件
    1、组件js文件:首字母大小,普通js文件首字母小写
    2、组件js文件后缀=jsx,普通js文件后缀=js

  • 组件模块化
    同一组件资源统一放到【./src/component/xx组件名】目录下,

  • 样式模块化
    如果不做样式模块化,有可能不同css文件中存在同一名称样式,在App.js引用各个组件时产生样式冲突,后引入的就会覆盖前引入的
    样式冲突解决:
    ① 保证引入组件间css文件没有同名样式,
    ② 在各组件样式文件样式前加统一嵌套前缀,如【.hello{.xx{} .xx{}}】
    ③ 样式模块化:

-1、样式文件加【.module】后缀,如:index.modle.css
-2、在组件引入样式时先接受样式再使用:
import React,{component} from 'react'
import hello from './index.module.css' //接收样式
export default class Hello extends Component{
    render(){
        return <h2 className={hello.title}>He1lo,React!</h2> //hello.title使用样式
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值