react入门笔记

一、简述

 1. react是什么?

                只针对页面的前沿框架,由facebook开发,现如今被国内一线大厂广泛应用

  2. 为什么要学react?

        原生javascript的缺点

                1. 操作DOM繁琐,效率低(DOM-API操作UI);

                2. 直接操作DOM,浏览器会进行大量的重绘重排;

                3. 没有组件化编码方案,代码复用率低

        React的优点:

                 1. 采用组件化模式、声明式编码,提高开发效率及组件复用率。

                  2. 在React Native中可以使用React语法进行移动端开发。

                   3. 使用虚拟DOM+加优秀的Diffing算法,尽量减少与真实DOM的交互

3. react依赖

        1). 依赖文件

各依赖文件说明:

        babel.min.js  : ES6 ==> ES5

                                jsx ==> js

react.development.js  : react核心库

react-dom.development.js  : react扩展库

二、 基本语法

1. jsx语法规则:

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

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

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

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

        5. 只有一个根标签

        6. 标签首字母

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

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

二、组件

1. 函数式组件:

<script type="text/babel" >
		//1.创建函数式组件
        function Demo(){
            return <h2>我是函数式组件</h2>
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>, document.getElementById('test'))
	</script>

2. 类式组件:

   

 3. 组件实例三大属性

        1. state :

                        实例的状态

                         参数传入状态中,通过get或者setstate得到或者设置

        2. props:

                        props是属性传入,是只读的

                        规则和默认值:

                

//定义规则
			static propTypes = {
				name: PropTypes.string.isRequired,
				sex: PropTypes.string,
				age: PropTypes.number,
			}
			//定义默认值
			static defaultProps = {
				sex: '不男不女',
				age: 18,
			}
			

        3. ref

        用于标识input等标签的。

        ref的三种用法:

          1. 字符串形式的ref:(React不推荐)

                

           2. 回调形式的ref:(React推荐,会调用两次render,开发中比较常见)

           3. createRef():(React最为推荐)

三、 组件的生命周期

详见下方声明周期api(新与旧)

四、 Diffing算法

经典面试题:

        1). react/vue中的key有什么作用?(key的内部原理是什么?)

      2). 为什么遍历列表时,key最好不要用index?

/*
   经典面试题:
      1). react/vue中的key有什么作用?(key的内部原理是什么?)
      2). 为什么遍历列表时,key最好不要用index?
      
			1. 虚拟DOM中key的作用:
					1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

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

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

									b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
												根据数据创建新的真实DOM,随后渲染到到页面
									
			2. 用index作为key可能会引发的问题:
								1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
												会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

								2. 如果结构中还包含输入类的DOM:
												会产生错误DOM更新 ==> 界面有问题。
												
								3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
									仅用于渲染列表用于展示,使用index作为key是没有问题的。
					
			3. 开发中如何选择key?:
								1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
								2.如果确定只是简单的展示数据,用index也是可以的。
   */

五、react脚手架

1. 创建项目

npx create-react-app my-app
cd my-app
npm start

2.项目目录文件详解:

        

node_modules:   webpack等脚手架工具包

public:   静态资源目录

        favicon: web标签图标

        index.html  :  引用目录下的静态资源(相当于提供一个接口)

        manifest.json: 应用加壳(例如:web文件外层加一个安卓的壳就可以作为安卓界面展示) 

        robots.json : 爬虫规则(哪些资源可以爬,哪些不可以爬)

src : 

        App.js: 主页面

        index.js : 项目入口

        reportWebVitals.js : 查看页面性能情况

.gitignore: git的忽略文件格式(git的文件,与react无关)

README.md  : git的说明文档(git的文件,与react无关) 

package.json : 包的说明文件

yarn.lock : yarn的缓存文件

3.css样式模块化

        如果不同模块下存在相同的css,由于最终都会被app.js主页面引用,所以后引用的会覆盖之前引用的css。

    解决办法

        在index.css中间加module:

 并且按如下方式引用:

4. 功能界面的组件化编码流程

    1. 拆分组件: 拆分界面,抽取组件

     2. 实现静态组件: 使用组件实现静态效果

     3. 实现动态组件

           3.1 动态显示初始化数据

                3.1.1 数据类型

                3.1.2 数据名称

                3.1.3 保存在哪个组件?

           3.2 交互(从绑定事件监听开始)

5. 组件之间的参数传递

1. 动态初始化列表,如何确定将数据放在哪个组件的state中?

        --某个组件使用:放在其自身的state中

        --某些组件使用:放在他们共同的父组件state中(状态提升)

2. 关于父子之间通信:

        1. 父组件给子组件传递数据:通过props传递

        2. 子组件给父组件传递数据:通过props传递,要求父提前给子传递一个函数

3. 状态在哪里,操作状态的方法就在哪里

六、路由

1. axios发送网络请求

axios.get(`/api1/search/users2?q=${keyWord}`).then(
			response => {
				//请求成功后通知List更新状态
				PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
			},
			error => {
				//请求失败后通知App更新状态
				PubSub.publish('atguigu',{isLoading:false,err:error.message})
			}

2. fetch发送网络请求

//发送网络请求--使用fetch发送(优化)
    try {
      const response = await fetch(`/api1/search/users2?q=${keyWord}`)
      const data = await response.json()
      console.log(data)
      PubSub.publish('raxcl',{isLoading:false,users:data.items})
      
    } catch (error) {
      console.log('请求出错',error)
      PubSub.publish('raxcl',{isLoading:false,err:error.message})
    }

3.组件之间通信

//发版消息
    PubSub.publish('raxcl',{isFirst:false,isLoading:true})

//订阅消息
this.token = PubSub.subscribe('raxcl',(_,stateObj)=>{
      this.setState(stateObj)
    })


//取消订阅消息
PubSub.unsubscribe(this.token)

4. 向路由组件传递参数(三种方式)

        1. params参数

               

路由链接(携带参数):<Link to='/demo/test/tom/18'>详情</Link>
注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
接收参数: this.props.match.params

        2. search参数

                

路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test/" component={Test}/>
接收参数: this.props.location.search
备注: 获取到的search是urlencoded编码字符串,需要借助querystring解析

        3. state参数

路由链接(携带参数):<Link to={{path='/demo/test',state:{name:'tom',age:18}}}>详情</Link>
注册路由(声明接收):<Route path="/demo/test/" component={Test}/>
接收参数: this.props.location.state
备注:刷新页面也可以保留住传入的参数

5. push模式和replace模式

        push:将历史记录进行前进后退,会留下痕迹,可前进后退

        replace: 将历史记录进行替换操作,不可前进后退

6.  BrowserRouter与HashRouter的区别

1. 底层原理不一样:

        BrowserRouter使用的是H5的history API , 不兼容IE9及以下版本。

        HashRouter使用的是URL的哈希值。

2. path表现形式不一样

        BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

        HashRouter的路径包含#,例如: localhost:3000/#/demo/test

3, 刷新后对state参数的影响

        (1). BrowserRouter没有任何影响,因为state保存在history对象中。

        (2). HashRouter刷新后会导致路由state参数的丢失!!!

4. 备注: HashRouter可以用于解决一些路径错误相关的问题。

七、React 组件库 (蚂蚁的ant-design)

1. 安装

yarn add antd

八、Redux

 1. redux是什么?(管理状态的库)

            1 . redux是一个专门用于做状态管理的JS库(不是react插件库)

            2. 它可以用在react,angular,vue等项目中,但基本与react配合使用。

            3, 作用: 集中式管理react应用中多个组件共享的状态 

2. 什么情况下需要使用redux

        1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。

        2. 一个组件需要改变另一个组件的状态

        3. 总体原则: 能不用就不用,如果不用比较吃力才考虑使用

3. 安装

yarn add redux

4. redux异步action

        1. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回(非必须)

        2。 具体编码:

1. yarn add redux-thunk, 并配置在store中
2. 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
3. 异步任务有结果后,分发一个同步的action去真正操作数据。

        3. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action

九、react-redux

1. 基本概念:

        1). UI组件:不能使用任何redux的api,只负责页面的呈现、交互

        2). 容器组件: 负责和redux通信,将结果交给UI组件

2. 如何创建一个容器组件-----靠react-redux 的connect函数

        connect(mapStateToProps,mapDispatchToProps)(UI组件)

                -mapStateToProps: 映射状态,返回值是一个对象

                -mapDispatchToProps: 映射操作状态的方法,返回值是一个对象

3. 注意点: 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

十、react扩展

1. setState

setState更新状态的2种写法

    (1). setState(stateChange, [callback])------对象式的setState
            1.stateChange为状态改变对象(该对象可以体现出状态的更改)
            2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
                    
    (2). setState(updater, [callback])------函数式的setState
            1.updater为返回stateChange对象的函数。
            2.updater可以接收到state和props。
            4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
        1.对象式的setState是函数式的setState的简写方式(语法糖)
        2.使用原则:
                (1).如果新状态不依赖于原状态 ===> 使用对象方式
                (2).如果新状态依赖于原状态 ===> 使用函数方式
                (3).如果需要在setState()执行后获取最新的状态数据, 
                    要在第二个callback函数中读取


2. lazyLoad

路由组件的lazyLoad

    //1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
    const Login = lazy(()=>import('@/pages/Login'))
    
    //2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
    <Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>


3. Hooks

1. React Hook/Hooks是什么?

(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性

2. 三个常用的Hook

(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()

3. State Hook

(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:
        参数: 第一次初始化指定的值在内部作缓存
        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

4. Effect Hook

(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
(3). 语法和说明: 
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
        componentWillUnmount() 

5. Ref Hook

(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样


4. Fragment

使用

<Fragment><Fragment>
<></>

作用

可以不用必须有一个真实的DOM根标签了


5. Context

理解

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用

1) 创建Context容器对象:
    const XxxContext = React.createContext()  
    
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
    <xxxContext.Provider value={数据}>
        子组件
    </xxxContext.Provider>
    
3) 后代组件读取数据:
​
    //第一种方式:仅适用于类组件 
      static contextType = xxxContext  // 声明接收context
      this.context // 读取context中的value数据
      
    //第二种方式: 函数组件与类组件都可以
      <xxxContext.Consumer>
        {
          value => ( // value就是context中的value数据
            要显示的内容
          )
        }
      </xxxContext.Consumer>

注意

在应用开发中一般不用context, 一般都用它的封装react插件


6. 组件优化

Component的2个问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

效率高的做法

只有当组件的state或props数据发生改变时才重新render()

原因

Component中的shouldComponentUpdate()总是返回true

解决

办法1: 
    重写shouldComponentUpdate()方法
    比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:  
    使用PureComponent
    PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
    注意: 
        只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  
        不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化


7. render props

如何向组件内部动态传入带内容的结构(标签)?

Vue中: 
    使用slot技术, 也就是通过组件标签体传入结构  <A><B/></A>
React中:
    使用children props: 通过组件标签体传入结构
    使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

children props

<A>
  <B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到 

render props

<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data} 


8. 错误边界

理解:

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}
​
componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

9. 组件通信方式总结

组件间的关系:

  • 父子组件

  • 兄弟组件(非嵌套组件)

  • 祖孙组件(跨级组件)

几种通信方式:

    1.props:
        (1).children props
        (2).render props
    2.消息订阅-发布:
        pubs-sub、event等等
    3.集中式管理:
        redux、dva等等
    4.conText:
        生产者-消费者模式

比较好的搭配方式:

    父子组件:props
    兄弟组件:消息订阅-发布、集中式管理
    祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

* 特殊章、buff点技能加成

1.demo()和demo的区别:

        demo()是把demo函数的值给onClick(即页面加载时就调用一次demo函数);

        demo是指定点击后调用demo方法;

2. 高阶函数和函数柯里化

        高阶函数:如果该函数符合如下两个条件的任意一个,那个该函数就是高阶函数。

                        1. 若A函数,接收的参数是A函数,那么A函数就是高阶函数。

                        2. 若A函数,调用的返回值依然是一个函数,那么A函数就是高阶函数。

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

看完这个栗子,是不是理解了多次接收,统一处理的概念了!!! 

3. 合并与展开

可以利用region与endregion关键字进行合并与展开 

4. 代码片段

        -----需要安装ES7插件

1. rcc : 类定义组件

2. rfc : 函数定义组件

** 用过的API

        生命周期API

声明周期(旧)

1. 卸载组件 : unmountComponentAtNode

//卸载组件
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))

2. 组件将要挂载 : componentWillMount() 

3. 组件挂载完毕 :componentDidMount()  (开发三大常用之一)

            //组件挂载完毕
            componentDidMount() {
                this.timer = setInterval(() => {
                    //获取原状态
                    let { opacity } = this.state
                    opacity -= 0.1
                    if (opacity <= 0) opacity = 1
                    //设置新的透明度
                    //简写形式,当前后相同时,可简写
                    this.setState({ opacity })
                }, 200);
            }

4. 组件即将销毁:componentWillUnmount()  (开发三大常用之二)(之三是render)

//组件即将销毁
            componentWillUnmount() {
                //清楚定时器
                clearInterval(this.timer)
            }

5. 是否应该更新状态 : shouldComponentUpdate() 不写默认返回true;

            //控制组件更新的阀门
            shouldComponentUpdate(){
                console.log("shouldComponentUpdate");
                return true;
            }

6. 组件将要更新的钩子: componentWillUpdate() 

            //组件将要更新的钩子
            componentWillUpdate() {
                console.log("componentWillUpdate");
                return true;
            }

7. 组件更新完毕的钩子: componentDidUpdate()

            //组件更新完毕的钩子
            componentDidUpdate() {
                console.log("componentDidUpdate");
                return true;
            }

8. 强制更新 : forceUpdate()

//强制更新按钮的回调
            force = () =>{
                this.forceUpdate()
            }

9. 子组件将要更新(有一个坑,第二次调用才会更新): componentWillReceiveProps()

声明周期(新)

 1. getDerivedStateFromProps(): 根据属性prop得到状态

//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
			static getDerivedStateFromProps(props,state){
				console.log('getDerivedStateFromProps',props,state);
				return null
			}

2. getSnapshotBeforeUpdate():更新完成的前一步(旧状态)

//更新完成的前一步(旧状态)
getSnapshotBeforeUpdate(){
				return this.refs.list.scrollHeight
			}

其他API 

1. 定时器:setInterval

this.timer = setInterval(() => {
                    //获取原状态
                    let { opacity } = this.state
                    opacity -= 0.1
                    if (opacity <= 0) opacity = 1
                    //设置新的透明度
                    //简写形式,当前后相同时,可简写
                    this.setState({ opacity })
                }, 200);

2. 透明度 :opacity

<h2 style={{ opacity: this.state.opacity }}>你好,我是魅魔!~~~</h2>

3. 滚动条 : overflow: auto

4.list.scrollTop =30  : 列表往上顶30px

5. list.scrollHeight   : 内容区的高度(相当于后端list.size())

6.  defaultChecked={true}  : 默认勾选

 <input type="checkbox" defaultChecked={true}/>

7. 鼠标移入移出:

<li style={{backgroundColor:mouse? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>

8.确认弹窗:

window.confirm('确定删除吗')

9.按键判断:

//判断按键方法入口
onKeyUp={this.handleKeyUp}

//判断是哪个键
//解构复制获取keyCode,target
    const {keyCode,target} = event
    //判断是否是回车按键
    if(keyCode !==13) return
    //添加的todo名字不能为空
    if(target.value.trim() === ''){
      alert('输入不能为空')
      return
    }

10. 获取文本框中的值:

target.value.trim()

11. 对数组做处理: reduce

    //已完成的个数
    const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)

12. 实现路由链接高亮效果:NavLink

<NavLink className="list-group-item " to="/about">About</NavLink>
              <NavLink className="list-group-item " to="/home">Home</NavLink>

13。 路由开启严格匹配: exact={true} 或者 exact

14. 重定向(当没有匹配的路由时,Redirect给了最后的底牌):<Redirect>

15. 让一般组件具有路由组件所特有的API   : withRouter 

import React, { Component } from 'react'
import {withRouter} from 'react-route-dom'

class index extends Component {
    render() {
        return (
            <div>
                
            </div>
        )
    }
}

export default withRouter(Header)

withRouter的返回值是一个新组件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值