react初体验

ReactFacebook2013年5月推出的开源的函数式编程的前端框架,用于构建用户界面的。拥有较高的性能,声明式设计(采用声明范式)、高效、灵活、JSX语法、组件、单向响应的数据流等特点,已经成为Web开发的主流框架。JSX是JavaScript中的一种语法扩展,类似于XML格式的语言。

React简介

Facebook在 2013年5月 推出的开源的函数式编程的前端框架

React开发环境的搭建

  • npm i create-react-app -g 全局安装Create-react-app脚手架
  • create-react-app my-app 使用脚手架初始化项目
  • cd my-app 进入工程目录
  • npm start 启动项目
  • npm run build 打包项目
  • npm run eject 将webpack等配置显示出来便于自定义修改

组件化理解

  • 组件的封装:封装视图、数据、变化逻辑
  • 组件的复用:、props传递、复用

JSX本质是什么

JSX是语法糖, 需要被解析成JS才能运行, 核心方法是 React.createElement()函数

React基础

  • react 双向绑定 和 插值用 {} {this.state.inputValue}
  • react 实现类似双向绑定是 state或props发生改变, render函数就会重新执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // 受控组件  form表单元素 value受 state 控制  只能通过 setState 修改
    import React, { Component } from 'react'
    class Todo extends Component {
    constructor(props){
    super(props)
    this.state = { inputValue: '' }
    }
    render(){
    return (
    <div>
    <input value={this.state.inputValue} onChange={this.changeInput.bind(this)} />
    <div>{this.state.inputValue}</div>
    </div>
    )
    }
    changeInput(e){
    this.setState({ inputValue: e.target.value })
    }
    }
    // 非受控组件 通过 ref 控制 表单元素值不受state控制
    import React, { Component } from 'react'
    class Todo extends Component {
    //需要状态时,需要构造函数
    constructor(props){
    super(props)
    }
    render(){
    return (
    <div>
    <input value={this.state.inputValue}
    ref={(textInput) => {this.textInput = textInput}}
    />
    <div>{this.textInput}</div>
    </div>
    )
    }
    }
    export default Todo
  • 事件绑定 onClick 事件名大写 JSX使用变量 {变量}

    1
    <button onClick={this.btnHandle.bind(this)} >提交</button>
  • props 属性 组将像一个函数,接受特定得输出,但不能修改

  • state 状态 组件内部得数据,可以动态改变 this.setState()更新state得唯一路径

  • 注释

    1
    2
    3
    4
    {
    //todolist deomo 注释方法一 单行注释必须换行写
    }
    {/* todolistdemo 注释方法二 */}
  • Fragment 占位符不会在页面中渲染出来

    1
    2
    3
    4
    import React, {Fragment} from 'react'
    <Fragment>
    <div className="box" ref = 'box'></div>
    </Fragment>
  • 不能使用class定义类名 class必须写成className

    1
    <input className = "input" />
  • style样式

    1
    <p style="{{fontSize:'40px', color:'blue'}}">
  • 展示html标签

    1
    2
    3
     <li
    dangerouslySetInnerHTML = {{__html: item}}
    ></li>
  • 使用htmlFor代替label中的for

    1
    2
    <label htmlFor="ipt">关联input</label>
    <input id = "ipt" className = "input" />
  • 组件使用及父子组件传值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /* 父组件 */
    // 引入
    import TodoItemList from './TodoItemList'
    // 使用 传值
    <TodoItemList list = {this.state.list} deleteItem = {this.handleItem.bind(this)}/>

    /* 子组件 */
    // props 接收父组件属性传入的值
    render () {
    return (
    <ul>
    {
    this.props.list.map((item, index) => {
    return <li onClick = {this.handleItem.bind(this, index)}>{item}</li>
    })
    }
    </ul>
    )
    }
    handleItem (index) {
    // 调用父组件方法
    this.props.deleteItem(index)
    }
  • context 提供了在组件中共享值得方法 不必通过props单向传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // theme-context.js  创建context对象
    import React from 'react'
    const ThemeContext = React.createContext()
    export default ThemeContext
    // app.js
    import ThemeContext from './theme-context'
    import ThemeBar from './component/ThemeBar'
    const themes = {
    light: { className: 'btn btn-primary', bgColor: '#333333'},
    dark: { className: 'btn btn-light', bgColor: '#eeeeee'}
    }
    // 使用 ThemeContext.Provider 包裹
    <ThemeContext.Provider value={themes[this.state.theme]}>
    <button className="btn btn-light"
    onClick={() => { this.setState({theme: 'light'})}}
    >浅色主题</button>
    <button className="btn btn-secondary"
    onClick={() => { this.setState({theme: 'dark'})}}
    >深色主题</button>
    <ThemeBar />
    </ThemeContext.Provider>
    // ThemeBar.js
    import React from 'react'
    import ThemeContext from '../theme-context';
    const ThemeBar = () => {
    return (
    <ThemeContext.Consumer>
    { theme => {
    return (
    <div className="alert mt-5" style={{backgroundColor:theme.bgColor,color:theme.color}}>
    <p>样式区域</p>
    <button className={theme.className}>样式按钮</button>
    </div>
    )
    }}
    </ThemeContext.Consumer>
    )
    }
    export default ThemeBar
  • 无状态组件(函数组件):只根据props渲染页面,没有内部的state,只有render函数 它就是一个函数 无状态组件性能高

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import React from 'react'
    import { Input, Button, List } from 'antd';
    const TodoListUI = (props) => {
    return (
    <div className="todo">
    <div className="input-box">
    <Input placeholder="Basic usage" value={ props.inputValue } onChange={ props.InpuChange }/>
    <Button type="primary" onClick={ props.buttonClick }>todo</Button>
    </div>
    <List size="small" bordered dataSource={ props.list }
    renderItem={ (item,index) =>
    <List.Item onClick={() => { props.itemDeleteHandle(index)}}>{ item }</List.Item>
    }
    />
    </div>
    )
    }
    export default TodoListUI
  • props类型检查与默认值
    Typechecking With PropTypes – React

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import PropTypes from 'prop-types'
    // 类型校验
    MyComponent.propTypes = {
    list: PropTypes.oneofType([Proptypes.array, Proptypes.string]),
    deleteItem: PropTypes.func,
    test: PropTypes.string.isRequired
    }
    // 默认值
    MyComponent.defaultProps = {
    test: 'test'
    }
  • props,state与render函数的关系

    1
    2
    // 1. 当组件的state或者props发生改变时, render函数就会重新执行
    // 2. 当父组件的render函数被执行时, 它的子组件的render都将被重新执行

虚拟DOM

虚拟DOM就是一个JS对象, 用它来描述真实的DOM

优点:
1. 大大的提升了性能(减少了真实DOM的创建与对比,虚拟DOM创建的是js对象)
2. 它使得跨端应用得以实现. React Native

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// JSX --> createElement --> 虚拟DOM(JS对象) --> 真实DOM
// 虚拟DOM中的Diff算法 虚拟DOM同层比对 DOM key值相同比对
真实DOM
/*
1. state数据
2. JSX 模板
3. 数据 + 模板 结合, 生成真实的DOM 来显示
4. state 发生改变
5. 数据 + 模板 结合生成真实的DOM, 替换原始的DOM
缺陷: 第一次生成完整DOM 第二次生成完整DOM 用第二次DOM替换第一次DOM, 非常耗性能
*/

虚拟DOM
/*
1. state 数据
2. JSX 模板
3. 数据 + 模板生成虚拟DOM
['div', {id: 'test'}, ['span', {} ,'hello world']]
4. 用虚拟DOM的结构生成真实的DOM,来显示
<div id="test"><span>hello world</span></div>
5. state 发生变化
6. 数据 + 模板生成新的虚拟DOM
['div', {id: 'test'}, ['span', {} ,'hi react']]
7. 比较原始虚拟DOM和新的虚拟DOM区别, 找到区别是span中的内容变化
8. 直接操作DOM, 改变span中的内容
*/

  • ref的使用 获取DOM元素

    1
    2
    3
    4
    5
    6
    // ref 是箭头函数   this.input 指向 input DOM对象
    // setState是异步函数 在获取DOM对象会有问题
    <input ref = {(input) => {this.input = input}} />
    this.setState({
    inputValue: this.input.value
    })
  • setState是异步函数 第二个参数解决 异步导致DOM获取问题已经数值更新后的操作

    1
    2
    3
    4
    5
    6
    this.setState(prevState => ({
    list: [...this.state.list,this.state.inputValue],
    inputValue: ''
    }), () => {
    console.log(this.ul.querySelectorAll('div').length)
    })
  • setState为何需要异步以及过程

    1
    2
    3
    // 1. 可能会一次执行多次setState操作
    // 2. 没必要每次setState都重新渲染,考虑性能
    // 3. 无法规定 限制用户如何使用setState

React 生命周期函数

React 生命周期函数
生命周期函数是指在某一时刻组件会自动调用执行的函数
Mountting阶段
componentWillMount: 组件即将被挂载(render)到页面的时刻自动执行(第一次挂载执行)
componentDidMount: 组件在被挂载(render)到页面之后自动执行render之后(第一次挂载执行)
Updation阶段
更新的三种情形: New props setState() fourceUpdate
shouldComponentUpdate: 组件被更新之前自动执行,是否需要更新,必须返回布尔值
componentWillUpdate: 组件在被更新之前自动执行,但它在shouldComponentUpdate之后执行,如果shouldComponentUpdate返回true它才会执行,如果返回false,它不会执行
componentDidUpdate: 组件更新完成之后自动执行 (React更新DOM和refs会触发)
componentWillReceiveProps: 组件接收参数之前自动执行,需要满足两个条件,子组件要从父组件接收参数,且子组件以前已经存在于父组件中,才会执行,不能是第一次存在
Unmountting
componentWillUnmount: 当这个组件即将被从页面中剔除(卸载)的时候,自动执行

1
2
3
4
5
6
7
8
9
10
// 当父组件的render函数被执行时, 它的子组件的render都将被重新执行
// shouldComponentUpdate 避免子组件无谓的render渲染
shouldComponentUpdate (nextProps, nextState) {
console.log(nextProps)
if (nextProps.list !== this.props.list) {
return true
} else {
return false
}
}

  • PureComponent 定制了shouldComponentUpdate后的Component(浅比较)
  • react 性能优化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 1. this的绑定放在constructor里面
    constructor (props) {
    super(props)
    this.handleButton = this.handleButton.bind(this)
    }
    // 2. setState异步函数将多次数据改变结合成一次执行
    // 3. 虚拟DOM同层匹配 diff算法
    // 4. 使用shouldComponentUpdate避免子组件无谓的render渲染
    // 5. ajax最好放在componentDidMount中
  • 安装React Developer Tools chrome调试工具 调试react应用

React 动画

  • 使用react-transition-group实现动画 npm i react-transition-group -S
  • 使用CSSTransition实现单个元素过渡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import { CSSTransition } from 'react-transition-group'
    // in 控制变量 timeout 时长 classNames 自定义class前缀 test-enter
    // unmountOnExit 是否隐藏dom appear 是否首次也应用过渡
    // onEntered 过渡完成事件
    <CSSTransition
    in={this.state.status}
    timeout={1000}
    classNames="test"
    unmountOnExit
    appear={true}
    onEntered={(el)=>{ el.style.color = "blue"}}
    >
    <p>过渡{this.state.status?'显示':'隐藏'}</p>
    </CSSTransition>
    // transition.css
    .test-enter, .test-appear{ opacity: 0; }
    .test-enter-active,.test-appear-active{ opacity: 1;transition: opacity 1s ease-in}
    .test-enter-done{ opacity: 1;}
    .test-exit{ opacity: 1; }
    .test-exit-active{ opacity: 0;transition: opacity 1s ease-in }
    .test-exit-done{ opacity: 0; }
  • 使用TransitionGroup + CSSTransition 实现一组或多个元素过渡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import { CSSTransition, TransitionGroup } from 'react-transition-group'
    <TransitionGroup>
    {
    this.state.list.map((item, index) => {
    return (
    <CSSTransition
    timeout={1000}
    classNames="test"
    unmountOnExit
    appear={true}
    key={index}
    onEntered={(el)=>{ el.style.color = "blue"}}
    >
    <li>{item}</li>
    </CSSTransition>
    )
    })
    }
    </TransitionGroup>

React和Vue有何异同

  • 本质区别 Vue是MVVM框架 React是前端组件化框架
  • 模板的区别 Vue使用模板 React使用JSX
  • 共同点 都支持组件化 都是以数据驱动视图

react 路由

  • 安装 npm i react-router-dom
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 引入
    import { BrowserRouter, Route } from 'react-router-dom'
    // 使用 exact path必须相等 Route必须用元素包裹
    <BrowserRouter>
    <div>
    <Route path="/" exact component={ Home }></Route>
    <Route path="/detail" exact component={ Detail }></Route>
    </div>
    </BrowserRouter>

异步组件 react-loadable withRouter

模块热更新 react-hot-loader

React 服务端渲染

-------------本文结束感谢您的阅读-------------
0%