美文网首页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