欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

React实战一

程序员文章站 2022-06-11 08:42:02
[toc] 1. 搭建环境 2. React知识点 1. 组件 组件:就是将整个UI拆分为很多小的,可重用的UI,每一个小的UI独立做自己的事情。 1.1 定义一个组件 一个组件需要继承 重写 函数,返回 注意点: 1.只要有JSX的地方必须引入 2.返回值中只能有一个元素包裹, 包裹整个JSX,如 ......

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

当组件之间传值的时候,接收一方往往需要对参数进行校验,这就是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

# 安装
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 />
  )
}