React笔记整理

----------------------react----------------------

干嘛的:前端框架,把用户界面抽象成一个个的组件,按需组合成页面

官网:https://reactjs.org/
http://react.css88.com/

不同点:
        angularJs     vue       react     angularTS
控制器    √ 1.x        -           -       2.x +  
过滤器     √           √           -       √
指令        √         √           -       √
组件      -           组件      组件       √
模板      √           √           -       √
引擎                          表达式

共同点:
虚拟DOM(angularJs除外 angular有),数据驱动

JSX:js + jsx 类XML语法
oo.js/ oo.jsx 合法的
语法要求:
标签要闭合
元素必须要有一个顶层元素
变量首字母大写代表组件,小写是变量
html属性,小驼峰命名 tabindex -> tabIndex

精髓:多组件组合,jsx+函数式编程(运算写成一系列的函数嵌套)

let 变量 = jsx元素 <div><aa></aa></div>


环境搭建:

a)  webpack + webpack-dev-server
    / 指向 index所在位置,图片,数据 都指向 / 
    js / css / jsx 相对定位


b) npm install create-react-app -g   官方脚手架
    create-react-app 目录名| .
    yarn start 开发模式
    yarn build 打包

    更改默认的端口号:
    1.  node_modules\react-scripts\scripts
    const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3001;

    2.  npm run eject / yarn eject
     修改script/start.js


    本地资源导入(import) 不可以导入src之外的包

    问题: jsx前景图片, ./ 和 / 都指向了 public目录
    解决:1.模块化导入 src目录里 要在jsx里src的图片
          2.相对或者绝对导入public目录下的图片


    去除eslint 警告:
        npm run eject
        找到项目下config/webpack.config.dev|prod
        注释关于eslint的导入和rules规则

    打包 + 预览:
        npm run build / yarn build
        serve -s build --port 端口 --open 
        serve 服务器命令行工具(npm i serve -g)

        public目录里面的资源会直接copy到build目录,src里面的资源会打包模块化

    help: https://github.com/facebook/create-react-app

    解决方案:
        项目资源,尽量从src目录下引入,打包时会模块化
        图片资源模块化引入,如何来自库只能放到public目录下
        
c) yeomen 目录        需要安装
npm install
npm run dev

ES6 创建组件:
import React from 'react';
class 组件名 extends React.Component{
constrctor(props){ //组件有状态,需要处理props constrctor就出现
super(props) //类如果有继承 super就要出现
需要在组件构造器内处理传递过来的props时,props参数就出现

        this.state={ // 本地状态

        }
    }
    render(){
        return html|jsx
    }
    方法1(){} 自定义的方法
    方法2(){}
}

ES5 创建组件:
var React = require('react');
let 组件名 = React.createClass({
getInitialState:function(){ //组件状态
return {
数据:值
}
}
render:function(){
return html|jsx
}
});
使用组件:
<HelloWorld/>
<HelloWorld></HelloWorld>

渲染(描画)页面
import ReactDom from 'react-dom';
var RactDom = require('react-dom');

ReactDom.render(组件/jsx,插入点)

组件属性(props):
调用组件:<组件名 属性名=值 属性名2=值2 .. /> 传递属性
组件内部: {this.props.属性名} jsx表达式 使用属性
this 代表的是组件本身

值类型:字符||{表达式}
this.props.属性名==array 需要循环
arr值:this.props.属性名.map(function(){
    return html
})
json {json} 报错, 对象无法直接通过{obj}展示->{obj.key}

事件:驼峰命名 es6 事件函数内部this会丢失
<元素 onClick={this.方法}

修正this的指向

onClick={this.方法.bind(null,值)}
onClick={()=>{this.方法()}}
this.方法=this.方法.bind(this)

方法(ev)  ev 代理事件对象 ev.target 返回虚拟dom √

refs: 获取jsx元素 获取的是真实dom

给jsx元素 绑定
<jsx元素
<div
<input
<Header ref="名字"

this.refs.名字

访问的是 真实DOM


给jsx元素 设置ref属性=名字
this.refs.名字

何时用:
处理focus、文本选择或者媒体播放
触发强制动画
集成第三方DOM库


组件之间数据传递(props传递)
父到子 props 传递 <Child 属性=数据/>
子到父 props 传递 <Child 属性=父方法/>
{this.props.属性.bind(this,xx)}

所有 React 组件都必须是纯函数,并禁止修改其自身 props 

受控元素 :

react 默认是单项绑定  defaultValue 

value={this.state.数据名}  model->view
onChange={this.监听方法}   view->model(  setState )
监听方法: this.setState(...)

处理多个输入元素
可以为每个元素添加一个 name 属性(通常和数据名一致)
处理函数根据 event.target.name 的值来选择要做什么

name="inputUserName" name="inputContent"
this.setState({[ev.target.name]:ev.target.value})

react 处理 样式:

1) 在index.html/index.js : 引入  link/style  场景:应用的公共样式
2)在组件里面引入: import './css/xx.css'  是全局 注入口(程序) 公共样式  
    问题: 选择器冲突,
    解决:
        a) 命名空间
        b) 模块化: import 变量  from './css/xx.css' 模块   使用?
            webpack配置 "style-loader!css-loader?modules"
            <xx className={变量.类名}
jsx:
    className="类名 类名2" className={返回字符}
    style={{key:value,key:value}}
    
css3 动画 / ant.desing animation

create-react-app 脚手架资源默认配置

本地资源导入(import) 不可以导入src之外的包

jsx前景图片, ./ 和 / 都指向了 public目录

import 引入的css,内部的资源url 
    ./ 指向  src
    / 指向 public

生命周期流程:
实例化 -> 更新期 -> 销毁时

实例化:
    es5: 
        1.取得默认属性(getDefaultProps) 外部传入的props
        2.初始状态(getInitailState)  state状态
        3.即将挂载 componentWillMount
        4.描画VDOM  render
        5.挂载完毕 componentDidMount
    es6:
        1.取得默认属性(getDefaultProps) 外部传入的props
        2.初始状态(getInitailState)  state状态
            1 && 2 都在构造器里面完成 
            constructor(props){

                super(props) == getDefaultProps

                this.state={} == getInitailState

            }
        3.即将挂载 componentWillMount
        4.描画DOM  render
        5.挂载完毕 componentDidMount
更新期:
    1.是否更新 shouldComponentUpdate  指视图
    2.即将更新 componentWillUpdate
    3.描画vdom  render
    4.描画结束 componentDidUpdate
销毁时:
    即将卸载 componentWillUnmount

实例化->存在(更新)->销毁时
getDefaultProps->getInitialState->componentWillMount->render->componentDidMount
->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate->
->componentWillUnmount


数据交互:

官方脚手架 静态数据读取时,参考根指向public  '/data' == public/data

fetch   原生就有

fetch(url+数据,{配置}).then(成功函数(res))

res.ok -> true/false 成功/失败
res.status - > 状态码
res.body 数据 数据流(stream)
res.text() 转换 文本(string) 
    过程异步:   return res.text()
    同步: res.text().then((data)=>{}) data:转换后的数据
res.json() 转  对象
配置:
    method:'POST'
    headers:{"Content-type":"application/x-www-form-urlencoded"},
    body:'a=1&b=2'|{a:1,b:2}

jsonp: fetch不带jsonp请求 需要依赖第三库
npm install fetch-jsonp -D
import xxx from 'xxx'
用法:
fetchJsonp(url+数据,{配置}).then(success(res)).catch(error)
特点: 是个promise 返回promise 数据是个流
解析:res.json() -> 流转换数据 是异步
配置:
timeout: 延时 5000
jsonpCallback: 回调函数key callback
jsonpCallbackFunction: null


无状态(没有state)组件(简写)创建:
const 组件名=(props)=>(jsx)
const 组件名=props=>jsx
const 组件名=(props)=>{
let xx=props.xx
return html
}

无状态组件特点:
不能访问this对象(this.ref,this.state ... )
只能访问props
无需实例化,渲染性能高
this.方法/钩子(生命周期) 也不需要


react路由 4.x
资料:
API:https://reacttraining.com/react-router/web/guides/quick-start
CN:http://blog.csdn.net/sinat_17775997/article/details/77411324
redux:https://github.com/reacttraining/react-router/tree/master/packages/react-router-redux
区别:
V4
嵌套式路由(路由配置在组件内部),动态路由,包容性(多路由渲染)
舍去了路由钩子
V3
分离式(统一位置配置),静态路由,排他性(只有一个路由被渲染)
理念:
遵循Just Component的 API 设计理念 万物皆组件,路由规则位于布局和 UI 本身之间

安装引入 react-router-dom 

React Router被拆分成三个包:react-router,react-router-dom和react-router-native。react-router提供核心的路由组件与函数。其余两个则提供运行环境(即浏览器与react-native)所需的特定组件

BrowserRouter 使用 HTML5 提供的 history API 来保持 UI 和 URL 的同步
HashRouter 使用 URL 的 hash (例如:window.location.hash) 来保持 UI 和URL 的同步

结构:
    BrowserRouter|HashRouter 路由对象
        根组件(App)|其他组件
            NavLink|Link  导航 
            Route    匹配+展示
            Redirect 跳转
            404 <Redirect to="/error"/>
            默认路由 <Route exact path={match.path} render={fuc}
Route 属性
    path(string): 路由匹配路径。(没有path属性的Route 总是会 匹配);
    exact(bool):
        为true时,要求全路径匹配(/home)。V4 的路由默认为“包含”的(/和/home都匹配),这意味着多个 <Route> 可以同时进行匹配和渲染

    component:在地址匹配的时候React的组件才会被渲染,route props也会随着一起被渲染
    render:这种方式对于内联渲染和包装组件却不引起意料之外的重新挂载特别方便
Link:
    to:string/object:要跳转的路径或地址;
NavLink:是<Link> 的一个特定版本
    activeClassName(string):设置选中样式,默认值为 active;
    activeStyle(object):当元素被选中时, 为此元素添加样式;
Switch:该组件用来渲染匹配地址的第一个<Route>或者<Redirect>,仅渲染一个路由,排他性路由,默认全匹配(场景:侧边栏和面包屑,引导选项卡等
Redirect:
    <Redirect from='/' to='/home'/> 总是会被重定向
404:    <Route component={Error}/> 总是会匹配

参数数据:{history,location,match}==props
    传递:
        to={match.url+'/001'}
        to={`${match.url}/002?a=1&b=2`}
        to={{pathname:match.url+'/003',search:'?a=11&b=12',hash:'#a1'}}
        <Route path={match.path+'/:aid'} component={Detail}
            注意:
                url - (浏览器 URL 中的实际路径) URL 匹配的部分。 用于构建嵌套的 <Link>
                path - (路由编写的路径) 用于匹配路径模式。用于构建嵌套的 <Route>
    接收:
        接参数:{match.params.aid}
        接数据:{location.search} 
        接地址:{location.pathname}
    注意:
        无法从v4 中获取 URL 的查询字符串了。因为没有关于如何处理复杂查询字符串的标准。所以,作者让开发者去选择如何处理查询字符串。推荐query-string库
跳转:
    history.push('/user?a=1&b=2')
    history.push({pathname:'/user',search:'?a=11&b=22'})
    history.replace({pathname:'/user',search:'?a=111&b=222'})
    history.go(-1)

授权路由:自定义路由

    前置守卫
        <AuthRoute path="/user" component={User}/>  
        AuthRoute==授权路由==react组件==自定义路由
        条件:返回一个Route 组件
              Route的render函数内部判断加载目标||Redirect组件
        实现:
            AuthRoute = ({ component: Component, ...rest }) => (
              <Route {...rest} render={props =>
                  Math.random()<0.5 ? 
                    <Component {...props} />
                   : <Redirect to="/login" />
                }
              />
            )
        目标组件    Component == User
        延展剩余属性 rest
        路由信息 ...props User组件需要用到的路由信息
    数据预载:
        AuthRoute 组件构造器存状态和预载数据
        DidMount钩子里异步请求,获取状态和数据
            fetch(url).then(result=>this.setState({}))
        render钩子返回 Route
             <Route {...rest} render={props => Xxx?<Component data={预载数据}
             if(!this.state.hasAuthed) return null;初始渲染时,未发送认证请求,因此不渲染
Prompt:后置守卫,离开后守卫
    import { Prompt } from 'react-router-dom'
    <Prompt
      when={this.state.isBlocking}      
      message={location=>{return `未保存,是否去向${location.pathname}`}}
    />
    message: 后面可以跟简单的提示语,也可以跟函数,函数是有默认参数的。 
    when: when的属性值为true时防止跳转;


问题:子路由使用父路由的展示区(插槽) 
    <Route path="/a" render={()=>
        <div>
            <Switch>
                <Route path="/a/b" component={b}
                <Route path="/a/c" component={c}
                <Route path="/a" component={a}
            </Switch>
        </div>
    >

思想:
组件拆分目标:为了复用
组件如何拆:单一原则
状态应该给谁:
尽量给顶层组件(状态提升),->props->子组件

    可以从 props(属性) 或者 state(状态) 得到,那么它可能不应该在 当前state(状态) 中

    方法-》操作数据(数据|状态在哪,方法就应该在哪)

props取名:从组件本身的角度来命名, 而不是它被使用的上下文环境

纯函数:不会试图改变它们的输入,并且对于同样的输入,始终可以得到相同的结果,React 

组件都必须是纯函数,并禁止修改其自身 props
    function(a,b){不会随机,不会修改a和b的值,输出可控}

setState: 是异步的,会将多个 setState() 调用合并为一次更新,所以不能同步依赖上一个setState的值,作为下一个setState的参数
    解决:
        1) this.setState(function(prevState,props){})
          prevState 抓取之前this.setState的所有状态
          props 所有属性
          更新会被合并,浅合并
        2) 函数节流(异步操作)
        3) ev.target......

组件传值|通讯|流动|流向:
1) 子A->父->子B props
2) 父方法(){this.refs.子.方法}

3) pub/sub模式 消息通知(观察者模式)  npm install npm install pubsub-js -D

    import PubSub from 'pubsub-js'

    订阅: token=PubSub.subscribe('消息名',回调函数('消息名',数据))
    发布:  PubSub.publish('消息名',数据)
    清除指定订阅:PubSub.unsubscribe(token);
    清除所有:PubSub.unsubscribeAll()

    注意:订阅方不存在了,相关的订阅注意清除
          先订阅,后发布

4) 状态管理(redux)

5) cookie localstorage

6) 路由

状态管理

flux(思想) vue实现(vuex) react实现(redux + react-redux)

状态管理(redux):可以同一个地方查询状态,改变状态,传播状态
何时用:中大项目,组件状态需要共享,在任何地方都可以拿到,组件需要改变全 局状态,一个组件需要改变另外一个组件的状态
思维:
在应用顶层创建store(状态)对象,其他底层组件共享这个store(状态)
数据流动:

component->action->reducer->state<-component

component: 展示结果(含处理结果代码)
action: 动作转发,异步请求, 
reducer: 业务处理逻辑,返回(return)新state
state:  状态收集,更新内部state状态,更新订阅(store.subscribe)state的组件(component)

通过store.dispatch发送 给 reducer
在组件内部 通过 this.props.store.getState() 抓state状态  特点 只抓一次
           this.props.store.subscribe() 订阅  数据更新,会触发
           getState放在subscribe内部

操作流程:
1. {createStore} from 'redux'
2. 生成默认state defaultState={}
3. 创建reducer
const reducer = (state=defaultState,action)=>{
let {type,payload}=action
swtich type
case XXXXX
更新copy后的state Object.assign(空,老,新)
default:
return state
}
4. 创建store对象
store = createStore(reducer,state)

5. store传递给组件
    <组件名 store={store}/>

6. 更新,状态获取
    组件内部:   this.props.store== store
        this.props.store.dispatch({type:xxx,payload:ooo}) 发送action给reducer
        this.props.store.subscribe(回调)  订阅 state  更新state时触发
        this.props.store.getState() 获取状态,执行一次

react-redux
基于redux思想,专门为react而生

思想: 容器组件, UI组件
App: 拿到store,修改、获取store
store:外面

UI组件:
    负责UI呈现,不带任何业务逻辑
    没有状态(不使用this   没有state)
    所有的数据有props提供
    不使用任何redux的API
容器组件:

index.js:
import {Provider,connect} from react-redux

<Provider store={store}>
    <容器组件/>
</Provider>

  1. 项目分析,组件安排

|-config
|-webpack.config.js
|-script
|-public
|- data
|- 数据
|- index.html
|-node_modules
|-src
|- components
|- App
|- NavBar / FootBar
|- Home / Follow / Column / User
|- Article / Login / Reg
|- Slider / Error / List
|- common(filters)
|- date.js / fillzero.js/...
|- assets
|- css
|- img
|- js
|- store
|- ..
Index.js
package.json


  1. 布局(切图,mint-ui,elementUI...ant.design),模板移植
    样式:全局引入
    src / import

  1. 路由搭建 route4
    样式冲突(命名冲突(错开))
    import 模块名 from '../css/xx.css'

    模块名.类名


  1. 数据交互
    fetch(url,{配置}).then().catch()

    home->newlist dataName="home" listData={this.state.listData}
    follow->newlist dataName="follow" listData={this.state.listData}

    newlist -》link pathname:'detail/'+item.id,
    query:{dataName:dataName}

    detail this.props.router.location.query.dataName

  2. 全局方法(过滤事件,补零)
    |-common
    date.js
    fillzero.js
    ...
    index.js
    import date/fillzero ..
    export {
    date,fillzero
    }


  1. 非状态管理,控制navbar / footbar / loading

    this.props.router.location.pathname -> navbar / footbar

6.5 全局loading
pubsub [App订阅, home/follow/.. 发布]
问题:用户速度切换,this.setState报错
分析:fetch中断--无法中断/isMounted属性(es5)/状态控制(组件卸载了,状态和事件都没了)
解决:高光盒(lightBox)√ / 自个维护一个组件属性 this.自定义属性


  1. 同级组件传值思路:
    a) 子A->父->子B
    父方法(){this.refs.子.方法}
    b) pub/sub模式 消息通知(观察者模式) npm install npm install pubsub-js -D
    var pubsub = new PubSub();
    订阅: onUserAdd=pubsub.subscribe('消息名',回调函数(数据,'消息名'))
    发布: pubsub.publish('消息名',数据)
    清除指定订阅:pubsub.unsubscribe(onUserAdd);
    清除所有:pubsub.unsubscribeAll()

     注意:发布方不存在了,相关的订阅注意清除
    

    c) 状态管理(redux)


中间件/路由守卫/路由懒加载/前后端分离/ant.design .......

路由懒加载:

异步组件:create-react-app 环境 webpack自动分片打包
方式1 import ("./ChildB.js").then(
ChildB=>console.log(ChildB)
)
方式2 const Child = asyncComponent(()=>import("./Child"))

    export default function asyncComponent(importComponent) {
      class AsyncComponent extends Component {
        constructor(props) {
          super(props);

          this.state = {
            component: null
          };
        }

        async componentDidMount() {
          const { default: component } = await importComponent();

          this.setState({
            component: component
          });
        }

        render() {
          const C = this.state.component;
          return C ? <C {...this.props} /> : null;
        }
      }

      return AsyncComponent;
    }
方式3 
  import Loadable from 'react-loadable';
  const Loading = () => <div>Loading...</div>;
  const Home = Loadable({
    loader: () => import('./routes/Home'),
    loading: Loading,
    loading:()=>{return null}
  });

  路由 <Route path=.. component={Home}/>  路由懒加载

前后端分离:
前端:fetch 请求头里面带凭证,带cookie
fetch('http:localhost:3000/cookies', {
credentials: 'include', //凭证
method:'get',
headers:{
'Accept':'application/json,text/plain,/',
'Content-type':'application/x-www-form-urlencoded;charset=utf-8'
},
body:数据
}).then(function(res) {
// ...
})


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345