• React实战一


    1. 搭建环境

    npm install -g creat-react-app
    # 创建一个项目
    create-react-app jianshu
    cd jianshu
    npm run start
    # 安装styled-components 全局统一管理css
    npm install --save styled-components
    # 全局样式引入
    https://meyerweb.com/eric/tools/css/reset/
    
    background-size: contain;
    

    2. React知识点

    1. 组件

    组件:就是将整个UI拆分为很多小的,可重用的UI,每一个小的UI独立做自己的事情。

    1.1 定义一个组件

    一个组件需要继承React.Component 重写render()函数,返回JSX

    注意点:

    1.只要有JSX的地方必须引入React

    2.返回值中只能有一个元素包裹,div包裹整个JSX,如下是错误的返回方式

    return (
     <div>
       hello world
     </div>
     <div>
       hello world
     </div>
    );
    
    import React, { Component } from 'react';
    class App extends Component {
      render() {
        return (
          <div>
            hello world
          </div>
        );
      }
    }
    export default App;
    

    1.2 组合与拆分组件

    引用官方的一句话:Don’t be afraid to split components into smaller components.

    尽可能的让每一个组件都能分工明确,减少重复,加大可重用。例如表格组件,就必须被拆分为一个单独的组件,它可能在各个地方被用到。

    组件拆分优点:让开发显得更加清晰明了。

    未拆分组件看起来就没有食欲

    return (
        <div className="Comment">
          <div className="UserInfo">
            <img className="Avatar"
              src={props.author.avatarUrl}
              alt={props.author.name}
            />
            <div className="UserInfo-name">
              {props.author.name}
            </div>
          </div>
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
     );
    

    1.案例解析

    将首页拆分为Header, Right,如下定义一个首页组件,在首页组件中分别引入Header和Right组件,这就是一个简单的组件拆分和组合.

    <React.Fragment />是一个虚拟的组件,仅仅是为了包裹其中的元素,并不会出现在页面上

    Index.js

    import React from 'react'
    import Header from './Header'
    import Right from './Right'
    class Index extends React.Component {
      render() {
        return (
          <React.Fragment>
            <Header />
            <Right />
          </React.Fragment>
        );
      }
    }
    export default Index
    

    Header.js

    import React from 'react'
    class Header extends React.Component {
      render() {
        return (
          <React.Fragment>
            <div>i am header component</div>
          </React.Fragment>
        );
      }
    }
    export default Header
    

    Right.js

    import React from 'react'
    class Right extends React.Component {
      render() {
        return (
          <React.Fragment>
            <div>i am right component</div>
          </React.Fragment>
        );
      }
    }
    export default Right
    

    1.3 组件传值

    组件传值分为以下几种情况:1. 父组件传值到子组件;2.子组件向父组件传值;3.隔代组件传值

    1. 父传子

    注意点: props是只读的

    例如Index.jsHeader.js组件传值

    	{/* Index.js 传递title属性 */}
    	return (
          <React.Fragment>
            <Header title="Header Title"/>
            <Right />
          </React.Fragment>
        );
    
    	{/*Header.js 接受值*/}
    	return (
          <React.Fragment>
            <div>{this.props.title}</div>
          </React.Fragment>
        );
    

    1.4 state

    state可以用来存储数据,记性状态管理。

    it is private and fully controlled by the component.

    render函数执行:组件state和props发生改变,render函数会被执行,当父组件重新渲染时候,子组件render函数也会被重新执行

    class Index extends React.Component {
    
      constructor(props) {
        super(props)
        this.state = {
          title: 'Header title'
        }
      }
    
      render() {
        return (
          <React.Fragment>
            <Header title={this.state.title}/>
            <Right />
          </React.Fragment>
        );
      }
    }
    export default Index
    

    1.5 PropTypes

    https://reactjs.org/docs/typechecking-with-proptypes.html

    当组件之间传值的时候,接收一方往往需要对参数进行校验,这就是PropTypes的作用

    import PropTypes from 'prop-types'
    
    class ReactUI extends React.Component {
      render() {
        return (
        	
        )
      }
    }
    //属性类型
    ReactUI.propTypes = {
      //表示name必须是string类型
      name: PropTypes.string
      //属性必传
      id: PropTypes.element.isRequired
    }
    

    默认值

    The propTypes typechecking happens after defaultProps are resolved

    class Greeting extends React.Component {
      static defaultProps = {
        name: 'stranger'
      }
    
      render() {
        return (
          <div>Hello, {this.props.name}</div>
        )
      }
    }
    

    1.5 生命周期函数

    在页面加载的过程中,特定时间点自动执行的函数称为生命周期函数。

    生命周期的四个阶段:

    1. Initialization

    这一阶段主要加载props和state

    1. Mounting:页面第一次加载的时候才会执行的挂载

    componentWillMount:在组件即将被挂载到页面的时刻执行

    render:

    componentDisMount:在组件挂载完成之后会被执行

    ​ 使用场景:用于发送ajax请求

    1. Updation:数据发生变化的时候会被执行
    • props:

    componentWillReceiveProps:

    ​ 子组件从父组件接受属性,第一次存在父组件,render函数执行,该函数不会被执行,当父组件render函数重新被执行,就会执行这个函数

    shouldComponentUpdate:组件是否需要被更新,返回boolean值

    ​ 使用场景:shouldComponentUpdate(nextProps, nextState);这两个参数来接受即将改变的props和state的值;演示:

      shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.title !== this.props.title) { //放生改变,则需要重新渲染
          return true
        } else {
          return false
        }
      }
    

    componentWillUpdate: 当组件被更新之前被执行,但是取决于shouldComponentUpdate的返回结果,

    render

    componentDidUpdate:组件更新完成之后会被执行

    • states:

    shouldComponentUpdate

    componentWillUpdate

    componentDidUpdate:

    1. Unmounting

    componentWillUnmount: 组件从页面移除的时候会被执行

    1.6 无状态组件

    无状态组件: 就是组件中只含有render()函数的组件,

    import React from 'react'
    
    class Header extends React.Component {
      render() {
        return (
          <React.Fragment>
            <div>xx {this.props.title}</div>
          </React.Fragment>
        );
      }
    }
    export default Header
    

    这种组件可以被简化为无状态组件

    export default (props, context) => {
      return (
        <React.Fragment>
          <div>xx {props.title}</div>
        </React.Fragment>
      );
    }
    

    1.7 List and Key

    案例:使用List集合渲染一个页面

    key的作用:

    Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity

    这个主要和虚拟DOM有关,可以提高虚拟DOM性能。

    兄弟姐妹之间的key必须独一无二

    import React from 'react'
    
    class Event extends React.Component {
    
      constructor(props) {
        super(props)
        this.state = {
          data: [1, 2, 3, 4, 5, 6, 7]
        }
    
        this.handClick = this.handClick.bind(this);
      }
      render() {
        return (
         <div>
           <ul>
             {
               this.state.data.map((item) => (
                <li key={item}>{item}</li>
               ))
             }
           </ul>
         </div>
        )
      }
    }
    export default Event
    

    2. JSX

    JSX 既不是string也不是html,是React独有的一种语法,jsx中需要注意的点:

    1. 标签的类class 如:<div class='show'></div> 会和ES6关键字冲突会使用className代替
    2. <label></label> for属性也会使用htmlFor 代替

    3. 虚拟DOM

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

    jsx转换成js对象

    <div className='show'>hello world</div>
    React.createElement('div',{className: 'show'}, 'hello world');
    

    流程讲解

    1. state数据
    2. jsx模板
    3. 数据 + 模板生成虚拟DOM
    4. 使用虚拟DOM来来生成真实DOM, 显示在页面上
    5. state发生改变
    6. 数据 + 模板生成新的虚拟DOM
    7. 比较原始的虚拟DOM和新的虚拟DOM之间的区别,找到不同之处
    8. 操作DOM,改变不同的地方

    diff算法

    上面的第七步骤是如何比对不同之处呢,这里使用了diff算法。

    同层比对:第一层有差异,则不会再进行比对,直接向下全部渲染,如果第一层相同,则向下继续比较。

    根据Key做关联来进行比对: 同层的key必须固定不变,这样才能再进行比对的时候准确的找到原始的dom节点。假如使用index作为key值,当删除一个元素,那个被删除元素后面的所有标签key值都会被改变,那么进行比对的时候,就会出现性能消耗。

    5. 函数绑定

    React events are named using camelCase, rather than lowercase.

    例如onClick, onBlur...

    import React from 'react'
    import './event.css'
    
    class Event extends React.Component {
    
      constructor(props) {
        super(props)
        this.state = {
          isShow: true
        }
      }
      render() {
        return (
         <div>
           <button onClick={this.handClick.bind(this)}>切换</button>
           <div className={this.state.isShow ? 'show':'hidden'}>你好</div>
         </div>
        )
      }
    
      handClick() {
        this.setState((state) => ({
          isShow: !state.isShow
        }))
      }
    }
    export default Event
    

    传递参数

    render() {
        return (
         <div>
           <button onClick={() => this.handClick(this.state.isShow)}>切换</button>
           <div className={this.state.isShow ? 'show':'hidden'}>你好</div>
         </div>
        )
      }
    
      handClick(isShow) {
        this.setState((state) => ({
          isShow: !isShow
        }))
      }
    

    传递参数的几种方式

    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
    <SearchSwitch onClick={() => switchItem(page, totalPage)}>换一批</SearchSwitch>
    

    3. Redux

    https://redux.js.org/introduction/getting-started

    # 安装
    npm install --save redux
    

    1. 了解是三个概念

    Actions: 就像一个快递,里面包含地址(行为),以及物品(数据)

    Reducers:就像一个快递员,负责分发不同的快递,根据地址(行为)派送到不同地方

    Store: 就像一个总站,可以存储这些快递,最后返回给用户。

    与物流之间的区别就是:store不可变,只可以返回数据,而原先的数据一直保留在store里面

    1.1 演示

    第一步:创建Store index.js

    这里借助redux createStore方法创建store,并且引入reducer

    import { createStore } from 'redux'
    import reducer from './Reducers'
    
    const store = createStore(
      reducer,
      // 这个可以使用Chrome redux 插件进行调试
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    )
    export default store
    

    第二步:创建一个reducer

    reducer 里面保存数据的初始状态,defaultState

    解析action的行为

    const defaultState = {
      list: ['hello', 'world']
    }
    
    const reducer = (state = defaultState, action) => {
      switch (action.type) {
        case 'add_list_action':
          return {list: action.data}
        default:
          return state
      }
    }
    export default reducer
    

    第三步:使用redux

    1. this.state = store.getState() 从store中获取数据
    2. store.subscribe(this.handSubscribe)订阅数据,监听数据的变化
    3. handClick()方法中主要定义action,然后利用store进行分发
    import React from 'react'
    import store from './store'
    
    class ReduxUI extends React.Component {
    
      constructor(props) {
        super(props)
        this.state = store.getState()
        this.handClick = this.handClick.bind(this)
        this.handSubscribe = this.handSubscribe.bind(this)
        store.subscribe(this.handSubscribe)
      }
    
      render() {
        return (
          <div>
            <button onClick={this.handClick}>点击我</button>
            <ul>
              {
                this.state.list.map((item) => {
                  return <li key={item}>{item}</li>
                })
              }
            </ul>
          </div>
        )
      }
      handClick() {
        //创建一个Action
        const addListAction = () => ({
          type: 'add_list_action',
          data: ['1', '2', '3']
        })
        //这是一个异步操作
        store.dispatch(addListAction())
      }
    
      handSubscribe() {
        this.setState(store.getState())
      }
    }
    export default ReduxUI
    

    2. react-redux中间件

    改造1>1.1 中的代码

    nmp install --save react-redux
    

    新增加一个父组件,统一管理store,

    这个组件拥有store,<Provider store={store}>表示改下面的所有元素都可以使用store中的数据

    import React, { Component } from 'react';
    import store from './pages/redux/store'
    import { Provider } from 'react-redux'
    import ReduxUI from './pages/redux/ReduxUI'
    class App extends Component {
      render() {
        return (
          <Provider store={store}>
            <ReduxUI/>
          </Provider>
        );
      }
    }
    
    export default App;
    

    步骤三中的修改

    第一步:添加connect 方法

    第三步:映射state中的属性,定义分发的方法

    第四步:连接组件

    import React from 'react'
    import {connect } from 'react-redux'
    
    class ReduxUI extends React.Component {
    
      render() {
        //从react-redux中接收store中的信息,以及分发action的方法
        const { list, handClick } = this.props
        return (
          <div>
            <div>
              <button onClick={handClick}>点击我</button>
              <ul>
                {
                  list.map((item) => {
                    return <li key={item}>{item}</li>
                  })
                }
              </ul>
            </div>
          </div>
        )
      }
    
    }
    //state就是store中state
    const mapStateToProps = (state) => ({
      list: state.list
    })
    //这里定义分发action的方法,dispatch 就是store的dispatch方法
    const mapDispatchProps = (dispatch) => ({
      handClick() {
        //创建一个Action
        const addListAction = () => ({
          type: 'add_list_action',
          data: ['1', '2', '3']
        })
        dispatch(addListAction())
      }
    })
    //连接组件,并且把属性和action行为传递给组件
    export default connect(mapStateToProps, mapDispatchProps)(ReduxUI)
    

    4. 其他

    1. Redux-thunk

    Redux Thunk middleware allows you to write action creators that return a function instead of an action

    npm install redux-thunk
    

    to enable Redux Thunk, useapplyMiddleware()

    这里配合redux-devtools-extension调试工具一起使用

    import { createStore, compose, applyMiddleware } from 'redux'
    import reducer from './reducer'
    import thunk  from 'redux-thunk'
    // 这个就是让调试插件配合中间件使用
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
    
    const store = createStore(
      reducer,
      composeEnhancers(applyMiddleware(thunk))
    )
    export default store
    
    // 定义一个方法,处理鼠标聚焦行为
    //然后dispatch分发action
    handFocus(list) {
        (list.size === 0) && dispatch(actionCreator.getSearchItems())
        dispatch(actionCreator.inputFocusAction())
    }
    
    //另外一个文件
    //===================================
    // 这里就是上面的action,这个action是一个方法,在该方法里面发送ajax异步请求,
    //异步请求之后继续分发另外一个action
    export const getSearchItems = () => {
      return (dispatch) => {
        axios.get("/api/item.json").then((res) => {
          dispatch(searchItemsAction(res.data.data))
        })
      }
    }
    

    正如官方文档所说:redux-thunk允许你分发一个方法类型的action

    2. Redux-saga

    asynchronous things like data fetching and impure things like accessing the browser cache

    npm install --save redux-saga
    
    import { createStore, compose, applyMiddleware } from 'redux'
    import reducer from './Reducers'
    import createSagaMiddleware from 'redux-saga'
    import reduxSaga from './reduxSaga'
    const sagaMiddleware = createSagaMiddleware()
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
    const store = createStore(
      reducer,
      composeEnhancers(applyMiddleware(sagaMiddleware))
    )
    //运行自己的reduxSaga
    sagaMiddleware.run(reduxSaga)
    
    export default store
    

    reduxSaga.js

    import { takeEvery, put } from 'redux-saga/effects'
    import axios from 'axios'
    
    function* getJson() {
      const res = yield axios.get('./data.json')
      yield put({type:"add_list_action", data: res.data}) //继续派发action
    }
    
    function* reduxSaga() {
      // 当store分发'add_list_action' 这个action的时候,会调用getJson方法
      yield takeEvery("add_list__pre_action", getJson)
    }
    export default reduxSaga
    

    5. immutable

    immutable可以保证数据一旦创建,就不会被改变

    npm install --save immutable
    
    //目的:将store变成不可变,这是一个简单reducer.js
    import * as constants from './constant' //es6语法
    import { fromJS } from 'immutable'
    //这里将defaultStore变成一个immutable对象
    const defaultStore = fromJS({
      focused: false,
      mouseIn: false,
      list: [],
      page: 1,
      totalPage: 1
    })
    
    export default (state = defaultStore, action) => {
      switch (action.type) {
        case constants.input_focused:
          //这里的set  immutable对象的一个值,其实并不是改变了state,而是返回一个新的对象
          //通过set可以简化我们的操作,不用重新构建全部的对象
          return state.set('focused', true)
        default:
          return state
      }
    }
    
    
    // 如何获取一个immutable对象呢?
    const mapStateToProps = (state) => ({
      //这里表示获取header组件下的一个immutable对象
       //这里是 redux-immutable 获取数据的形式
        // state.header.get('focused') 为immutable获取数据的格式
      focused: state.getIn(['header', 'focused']), //state.get('header').get('focused')
      mouseIn: state.getIn(['header', 'mouseIn']),
      list: state.getIn(['header', 'list']),
      page: state.getIn(['header', 'page']),
      totalPage: state.getIn(['header', 'totalPage'])
    })
    
    //通过action传递参数的时候也应该是一个immutable对象
    export const searchItemsAction = (data) => ({
      type: constants.render_list,
      data: fromJS(data),
      totalPage: Math.ceil(data.length / 10)
    })
    

    5.1 redux-immutable

    redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.jsstate.

    我们经常面临很多很多组件,但是如果把所有组件的store,reducer, action 维护在一个目录下,那将是惨目忍睹的,所以通常情况下我们会分开管理redux,然后将所有的reducer组合在一起

    npm install --save redux-immutable
    
    //import { combineReducers } from 'redux'
    import { combineReducers } from 'redux-immutable' //可以管理immutale对象
    import { reducer as headerReducer} from '../header/store'
    
    const reducer = combineReducers({
      header: headerReducer
    })
    export default reducer
    

    5. react-router-dom

    React Router is a collection of navigational components that compose declaratively with your application.

    npm install --save react-router-dom
    
    import { BrowserRouter, Route } from "react-router-dom"
    ...
    <BrowserRouter>
        {/* 表示路由到一个组件 */}
        <Route path="/home" exact component={Home} />
    </BrowserRouter>
    

    Route: 表示要路由的到哪一个组件

    Link: 表示一个连接,跳转到某个页面

    6. react-loadable

    一个项目,不同的页面应该按需加载,而不是当首页出来之后加载所有的组件,例如我们访问首页的时候只会加载首页相关的资源,访问详情页的时候加载详情页的代码,这样可以提高首页访问速度以及用户体验

    npm install --save react-loadable
    

    案例演示

    import React from 'react'
    import Loadable from 'react-loadable'
    const LoadableComponent = Loadable({
      //这里`reactUI`是一个组件,表示这个组件将会被按需加载
      loader: () => import('./reactUI'),
      // 这里是一个函数,可以是一个组件,表示页面加载前的状态
      loading: () => <div>正在加载</div>
    })
    //返回无状态组件
    export default () => {
      return <LoadableComponent/>
    }
    

    7. 在React 使用各种CSS

    1. styled-components

    npm install --save styled-components
    

    定义全局样式

    import { createGlobalStyle } from 'styled-components'
    export const ResetCSS = createGlobalStyle`
    	body {
        line-height: 1;
      }
    	....
    `
    // 引用全局样式
    import { ResetCSS } from './style'
    ...
    render() {
      return (
      	<ResetCSS />
      )
    }
    
  • 相关阅读:
    14:求满足条件的3位数
    1696:逆波兰表达式
    筛法求素数 6分
    1751:分解因数
    1750:全排列
    1788:Pell数列
    666:放苹果
    06:寻宝
    04:最匹配的矩阵
    雷电
  • 原文地址:https://www.cnblogs.com/hu1056043921/p/10608850.html
Copyright © 2020-2023  润新知