当前位置: 移动技术网 > IT编程>网页制作>Html5 > React实战一

React实战一

2019年03月28日  | 移动技术网IT编程  | 我要评论

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 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 />
  )
}

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网