美文网首页WEB开发
react学习笔记

react学习笔记

作者: 情有千千节 | 来源:发表于2019-05-30 22:33 被阅读0次

    React笔记

    1. 创建项目
    npx create-react-app  demo
    cd demo
    npm start
    
    1. 入口文件
    
    /index.js
    
    import React from 'react'; // 这里要引入react之后,下面的jsx语法才能被识别
    import ReactDOM from 'react-dom';
    import TodoList from './TodoList';
    
    ReactDOM.render(<TodoList />, document.getElementById('root'));
    
    /public/index.html
    挂载dom
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <meta name="theme-color" content="#000000" />
        <title>todolist</title>
      </head>
      <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
      </body>
    </html>
    
    
    
    1. 组件构成
    <!-- 引入 React, Component -->
    
    import React, { PureComponent, Fragment } from 'react'
    
    <!-- 引入组件 -->
    import TodoItem from './TodoItem';
    <!-- 引入样式 -->
    import './style.css';
    <!-- 组件 -->
    class A extends PureComponent {  // 等价于 class A extends React.Component
        <!-- 组件初始化会首先加载constructor函数 -->
        constructor (props) {
            super(props);
            this.state = {
                inputVal: '',
                list: []
            }
            this.handleInputChange = this.handleInputChange.bind(this)
            this.handleBtnClick = this.handleBtnClick.bind(this)
            this.handleItemDelete = this.handleItemDelete.bind(this)
        }
        <!-- 每次state更新都会触发render函数更新dom -->
        render () {
            return (
                <!-- 这里书写jsx -->
                // 同vue 最外层也只能是一个标签包裹,但是如果不想使用标签可以使用react自带的一个占位组件
                <Fragment>
                    {/* 这是在jsx中的注释书写格式 */}
                    {/* jsx中对于label的for会识别为循环的for,所以要改成htmlFor */}
                    <label htmlFor="insert">输入内容</label>
                    <input
                        id="insert"
                        // 因为class和类的class冲突,所以属性使用className
                        className="input"
                        value={ this.state.inputVal }
                        // 所有的事件都要大些写首字母
                        onChange={ this.handleInputChange }
                        ref={(input) => {this.input = input}}
                    />
                    <button onClick={ this.handleBtnClick }>提交</button>
                    <ul>
                        { this.getTodoItem() }
                    </ul>
                </Fragment>
            )
        }
        <!-- 方法 -->
        getTodoItem () {
            return this.state.list.map((item, index) => {
                return (
                    <TodoItem
                        key ={ index }
                        content={item} // 向子组件传参
                        index = {index}
                        handleDel = { this.handleItemDelete }></TodoItem> <!-- 向子组件传递方法,子组件调用该方法-->
                )
            })
        }
        <!-- 修改state中的数据 -->
            // 点击添加
        handleBtnClick (e) {
            // prevState 等价于this.state
            this.setState((prevState) => ({
                list: [...prevState.list, prevState.inputVal],
                inputVal: ''
            }))
        }
        // 实现数据的双向绑定
        handleInputChange (e) {
            let value = e.target.value
            <!-- 使用ref的时候 -->
            let value = this.input.value
            this.setState(() => ({  // 异步函数更改数据
                inputVal: value
            }))
        }
    }
    
    <!-- 暴露 -->
    export default A
    
    1. 子组件对父组件的校验等
    import React, { Component } from 'react'
    // 参数校验要单独引入
    import PropTypes from 'prop-types'
    
    class TodoItem extends Component {
      constructor (props) {
        super(props);
        this.handleClick = this.handleClick.bind(this)
      }
      render(h) {
        const { content, test } = this.props;
        return (
                    <!-- 这样可以正确编译语句中的html标签 -->
                    <div onClick={this.handleClick} dangerouslySetInnerHTML={{__html:`${test} - ${content}`}}></div>
          )
      }
      handleClick () {
        const { handleDel, index } = this.props;
        // 父组件传递函数的时候要将this绑定在父组件上,不然在子组件会找不到该方法
        handleDel(index)
      }
    }
    // 对父组件的传值进行校验, 和vue中的props中的类型啥都一样,注意这里小写
    TodoItem.propTypes = {
      test: PropTypes.string.isRequired, // 必传校验
      content: PropTypes.oneOfType(PropTypes.string, PropTypes.number), // 多种类型
      handleDel: PropTypes.func,
      index: PropTypes.number
    }
    
    // 给父组件的传值设置默认值
    TodoItem.defaultProps = {
      test: 'hello'
    }
    export default TodoItem
    
    
    
    1. React的虚拟Dom实现原理
    • state数据
    • JSX模板
    • 生成虚拟Dom(虚拟Dom就是一个JS对象,用它来描述真实的Dom)(损耗了性能)['div',{id: 'abc'}, ['span', {}, 'hello world']]
    • 用虚拟DOM的结构生成真实的Dom,用来显示<div id="abc"><span>hello world</span></div>
    • state发生变化
    • 数据+模板生成新的虚拟Dom,(极大地提升了性能)['div',{id: 'abc'}, ['span', {}, 'niubi']]
    • 比较原始虚拟DOM和新的虚拟DOM的区别,找到区别中的内容(极大地提升了性能)
    • 直接操作DOM,改变span中的内容

    虚拟Dom为JS对象,在js中比较非常方便,性能需求较小
    JSX->createElement->虚拟Dom(JS对象)->真实的DOM

    render () {
        return (
            <div><span>hello</span></div>
        )
    }
    等同于
    render () {
        return React.createElement('div', {}, React.createElement('span', {}, 'hello))
    }
    
    1. diff算法
    • 同层比对,只要同层有不同,下面的全部重新渲染
    • key值比对(为什么不要用index做key值的原因)
    1. 生命周期

    生命周期函数: 在某一时刻组件会自动调用执行的函数


    react生命周期.png
    • initialization
    setup props and state
    
    • mounting
    componentWillMount()
    render()
    componentDidMount()
    
    • updation
    <!-- 1. -->
    /*
        当一个组件要从父组件接受参数
        只要父组件的render函数重新执行了,子组件的这个生命周期函数就会被执行
    */
    componentWillReciveProps()
    <!-- 2.组件被更新之前自动执行 -->
    shouldComponentUpdate () // 需要返回一个波尔值,以确认组件是否需要更新,true的话接下来调用componentWillUpdate,在一些父组件更新子组件不需要更新的情况下,返回false,以提升性能,不会老师触发子组件的render
    <!-- 3. 组件确认更新后,组件更新之前调用-->
    componentWillUpdate ()
    <!-- 4.render -->
    render()
    <!-- 5. 组件更新之后调用-->
    componentDidUpdate ()
    
    • unmounting
    <!-- 组件即将被从页面中移除 的时候执行 -->
    conponentWillUnmount()
    

    因为组件继承自Component,所以其他的声明函数都内置了,都可以不写,render生命周期函数除外,所以render函数必须有

    1. 性能提升
    • 在constructor中绑定this作用域
    • setState改变数据使用了异步函数,可以将多次虚拟dom的改变转为1次,减少虚拟dom的比对
    • 虚拟dom的比对 同层比对和key值比对
    • 借助 shouldComponentUpdate 提升组件性能
    1. 动画
    • 简单的过渡动画和动画都可以通过css实现
    • 复杂的可以引入react-transition-group来实现,基本上和vue差不多
    1. Redux


      Redux工作流程.png

    Redux = reducer + Flux
    /**

    • state是存储的原始的数据,action是接受到的方法
    • 这个在组件中通过dispatch提交给store,store将自身数据和action再提交到reducer中
    • reducer接受到store传来的数据和action,复制一份数据,然后根据action改变后返回给store,store改变
      */
    yarn add redux
    
    store.js
    
    import { createStore } from 'redux';
    import reducer from './reducer';
    const store = createStore(
        reducer,
        window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // redux谷歌插件显示
        );
    
    export default store;
    
    reducer.js
    
    reducer必须是纯函数:给定固定的输入就有固定的输出,并且没有副作用,例如里面有date,ajax请求或者异步就不是了
    
    const defaultState = {
      inputVal: 'no',
      list: ['1','asd']
    }
    
    export default (state=defaultState, action) => {
        let newState = JSON.parse(JSON.stringify(state)); // 对state进行深拷贝
      switch (action.type) {
        case 'input_change_val':
          newState.inputVal = action.inputVal;
          return newState;
        case 'add':
          newState.list.unshift(action.value)
          newState.inputVal = ''
          return newState;
        default:
        return state;
      }
    }
    
    todilist.js
    
    constructor(props) {
        super(props)
            this.handleStoreChange = this.handleStoreChange.bind(this)
        this.hanldeAdd = this.hanldeAdd.bind(this)
        /**
         * 订阅store,store改变的时候执行该方法
         */
        store.subscribe(this.handleStoreChange)
    }
      // 实现动态改变
      handleInputChange(e) {
        store.dispatch({
          type: 'input_change_val',
          inputVal: e.target.value
        })
      }
      handleStoreChange () {
        // 将store中改变的数据同步到state
        this.setState(store.getState())
      }
    
    1. Redux-thunk Redux-saga

    可以使action的返回值为函数而不仅仅是对象的中间件

    yarn add redux-thunk
    
    
    1. redux-thunk

    react-redux 可以让组件在reducer中使用函数异步获取数据

    npm i redeux-thunk -S
    
    store/index.js
    import { createStore, compose, applyMiddleware } from 'redux'
    import thunk from 'redux-thunk'
    import reducer from './reducer'
    
    // 调用redux-devtools
    const composeEnhancers =
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
    
    const store = createStore(reducer, composeEnhancers(
      applyMiddleware(thunk)
    ));
    
    export default store;
    
    .router.js
    import React, { PureComponent } from 'react'
    import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
    import { Provider } from 'react-redux'
    import store from './store'
    import App from './App'
    import Home from './pages/home/Home'
    
    class IRouter extends PureComponent {
      render () {
        return (
          <Provider store={store}>
            <BrowserRouter>
              <Switch>
                <Route exact path="/" render={() => (
                  <Redirect to="/home"/>
                )}/>
                <Route path="/home" component={Home} />
                <Route path="/app" component={App} />
              </Switch>
            </BrowserRouter>
          </Provider>
        )
      }
    }
    
    export default IRouter
    

    相关文章

      网友评论

        本文标题:react学习笔记

        本文链接:https://www.haomeiwen.com/subject/jqobtctx.html