react 入门学习

webstrom自动格式化代码

命令css

js框架 MVC
安装
npm install create-react-app -g
生成项目(项目名npm发包包命名规范 /^[a-z0-9_-]$/)
create-react-app 项目名字
查看全局安装目录
npm root -g
复制代码

文件html

public 存放的是当前项目的HTML页面(单页面应用放index.html便可)
html 导入的地址应该写成绝对路径  %PUBLIC_URL%   public的文件夹
	不能用相对路径

src  项目结构最主要的目录,后期的js,路由组件都放这里
	 index.js 是当前目录的入口文件
react-scripts 是webpack的全部配置
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
  执行命令 npm run start /yarn start
复制代码

React脚手架的深刻剖析vue

暴露webpack配置项
为告终构的美化,把全部的webpack配置等都隐藏到了node_modules中(react-script)
yarn eject 
首先会提示确认是否执行eject操做,操做是不可逆转的,一旦暴露出来配置项,就没法再隐藏回去了
报错信息
Remove untracked files, stash or commit any changes, and try again.

若是当前的项目基于git管理,在执行eject的时候,若是尚未提交到历史区内容,须要先提交历史区,而后再eject才能够,不然报错
* 须要在vcs 里面commit 进行git操做
再进行 yarn eject

以后多个两个文件夹
config  存放的是webpack的配置文件
	webpack.config.dev.js 开发环境下的配置项(yarn start)
	webpack.config.prod.js 生产环境下的配置项(yarn build)
scripts   存放的是可执行脚本的js文件
	start.js  yarn start执行的就是这个js
	build.js  yarn  build 执行的就是这个就是
	
配置less
yarn add less less-loader
less 开发和生产都须要改
参考
https://www.cnblogs.com/esofar/p/9631657.html
https://www.cnblogs.com/jayxiangnan/p/9116663.html

set HTTPS=true&&npm start  开启HTTPS协议模式
set PORT=63341  修改端口号

复制代码

配置sass

yarn add node-sass sass-loader -D
若是yarn不能安装就用cnpm
less相似同样的
    {
                  test:/\.scss$/,
                  loaders:['style-loader','css-loader','sass-loader'],
              },
复制代码

npm install less less-loader --save-dev
复制代码

去掉webstorm 报灰色线node

Editor -> Colors & Fonts -> General , 在Errors and Warnings里把Wadk Warning里的 两项勾选去掉。react

撤销工做区的修改webpack

已修改,未暂存(撤销工做区的修改)ios

git reset --hard
复制代码

react & react-domgit

渐进式框架
咱们应该把框架的功能进行拆分,用户想用什么,让其本身自由组合便可
全家桶
渐进式框架N多部分的组合
VUE全局桶(vue-cli/vue/vue-router/vuex/axios(fetch)/vue element(vant))
REACT全家桶: create-react-app/react/react-dom/react-router/redux/react-redux/axios/ant/sage/mobx
react:REACT框架的核心部分,提供了Component类能够共咱们进行组件开发,提供了钩子函数(生命周期函数)
全部的生命周期函数都是基于回调函数完成的
react-dom :把JSX语法(REACT特有的语法)渲染为真实DOM(可以展现的结构都叫真实的DOM)

* 不推荐jsx的容器是body
* 只能出现一个根元素
* 给元素设置样式类用的是className而不是class * style={{}}
* jsx元素设置属性 ,属性值对应大括号中 对象,函数均可以放(也能够放js表达式)
将数据嵌入jsx中,能够嵌入变量或者直接的数据值
let name='xxx';
ReactDOM.render(<div> <h1>{name}</h1> <h2>{'haha'}</h2> </div>)
不能嵌入对象(代指: {} /^$/ 日期对象 函数  数据中的某一项是前面也不行)    
能够嵌入基本类型值(null/undefined/布尔值都是空元素,也就是不显示任何内容)

把JSX(虚拟DOM) 变为真实的DOM(不太理解)--(8,9)

循环建立jsx元素须要设置标识key,而且在当前循环的时候,这个key须要惟一
let name='珠峰培训',
    data=[{id:1,title:'xxx'},{id:2, title: 'xxx'}];
ReactDOM.render(<ul style={{color:'red'}} className={'box clearfix'} OnClick={(ev)=>{ console.log(ev); }}> {data.map((item,index)=>{ return <li key={index}> {item.id} {item.title} </li> })} </ul>,root);
使用三元运算符解决判断操做(if和swich是不能够的)
复制代码

React是如何把jsx元素转换为真实的DOM元素而且添加到页面中web

基于babel/babel-loader: 把jsx语法编译为react.create-Element这种模式vue-router

create-Element 至少有两个参数,没有上限

第一个: 标签名(字符串)

第二个:属性(没有给元素设置null)

其余:当前元素的全部子元素内容

执行create-Element 把传递的参数最后处理成为一个对象

React 组件

组件/模块管理 ,就是把一个程序划分为一个个组件来单独处理

优点

  • 有助于多人协助开发
  • 提升复用性

React建立组件有两种方式

  • 函数声明式组件
  • 基于继承component类来建立组件

src->component 这个文件夹存放开发的组件

create-element 在处理的时候,遇到一个组件,返回的对象中type就再也不是字符串,而是一个函数(类),可是属性仍是props中

({
    type:Dialog,
    props:{
        lx:1,
        con:'xxx',
        children:一个值或者一个数组
    }
}
首先判断type是什么类型,若是是字符串就建立一个元素标签,若是函数或者类,就把函数执行,把props中的每一项(包含children传递给函数)
React.Children.map

基于继承component类建立组件

基于create-element 把jax 转换为一个对象,当react渲染这个对象的时候,遇到type是一个函数或者类,不是直接建立元素,而是先把方法执行:
	* 若是是函数式声明的组件,就把它看成普通方法执行(方法中的this是undefiend),把函数返回的jsx元素进行渲染
	* 若是是类声明式的组件,会把房钱类new它执行,建立类的一个实例(当前本次调用组件就是它的实例)执行constructor以后,会执行this.render(),把render中返回的jsx拿过来渲染,因此 类声明式组件,必须有一个render的方法,方法中须要一个jsx元素
	可是无论是哪种方式,最后都会拿解析出来的props属性对象做为实参给对应的函数或者类
复制代码

建立组件有两种方式"函数式","建立类式"

函数式

  • 简单
  • 能实现的功能也很简单,只是简单的调取和返回jsx而已
ReactDOM.render(<div> /*单闭合*/ <Vote title={xxx}/> /*双闭合*/ <Vote> <p>11111111</p> </Vote> </div>) import React from 'react'; exprot default function Vote(props){ return <div className={'penel panel-default'}> {props.Children} {props.Children.map(props,children,item=>{return item;})} </div> } 复制代码

建立类式

  • 操做相对复杂一些,可是也能够实现更为复杂的业务功能

  • 可以使用生命周期函数操做业务

  • 函数式能够理解为静态组件(组件中的内容调取的时候就已经固定了,很难在修改,而这种类的组件能够基于组件内部的状态来动态更新渲染的内容

  • import React from 'react';
    
    //ref是react操做DOM的方案
    //* 给须要操做的元素设置ref(保持惟一性,不然会冲突覆盖)
    //* 在实例上挂载了refs属性,他是一个对象,存储了全部设置ref的元素(ref值:元素对象)
    export default class Vote extends React.Component{
        constructor(props){
            super(props); //React.Component.call(this)能够把component中的私有实例继承过来,this.props/this.state(this.setState)/this.content/this.refs/this.updater
            //初始化状态
            this.state={
                n:0,
                m:0,
            }
        }
        //状态处理方式
        render(){
            let {title,children}=this.props,
                {n,m}=this.state;
            return <div> 支持:<span>{m}</span> 反对: <span>{n}</span> </div>
        }
        //DOM处理方式
            render(){
            let {title,children}=this.props,
                {n,m}=this.state;
            return <div> 支持:<span ref={'AA'}>0</span> 反对: <span ref={'BB'}>0</span> 平局值: <span ref={'CC'}>0</span> </div>
        }
        suport=ev=>{
            this.refs.AA.innerHTML++;
            this.computed();
        }
           suport=ev=>{
            this.refs.BB.innerHTML++;
            this.computed();
        }
           computed=()=>{
               let {AA,BB}=this.refs;
               //而后再进行操做
           }
        supprot=ev=>{
            this.refs.AA.innerHTML++;
            //使用箭头函数式为了保证方法中this永远是实例自己(不管在哪执行这个方法)
            //ev.target获取当前操做的事件源(dom元素)
            this.setState({
                //修改状态信息而且通知render从新渲染(异步操做:若是有其余代码执行,先执行其余代码,而后再通知状态修改)
                n:this.state.n+1
            },()=>{
                //回调函数通常不用,当通知状态修改完成,而且页面从新渲染完成后,执行回调
            })
        }
    }
    复制代码
yarn add prop-types
基于这个插件咱们能够给组件传递的属性设置规则
设置的规则不会影响页面的,可是会控制台报错
复制代码

  1. 显示当前文件的最新版本信

  2. 返回上一个版本

生命周期函数(钩子函数)

  • 描述一个组件或者程序从建立到销毁的过程,咱们能够再过程当中基于钩子函数完成一些本身的操做

  • 生命周期

  • 基本流程
    constructor 建立一个组件
    componentWillMout  第一次渲染以前
    render 第一次渲染
    componentDidMout 第一次渲染以后
    
    修改流程:当组件的状态数据发生改变(setState)或者传递给组件的属性发生改变
    shouldComponentUpdate 是否容许组件从新渲染(容许则执行后面函数,不容许直接结束便可)
    componentWillUpdate 从新熏染以前
    render 第二次之后从新渲染
    componentDidUpdate 从新渲染以后
    
    属性改变:
    componentWillReceiveProps(nextProps/nextState):父组件把传递给组组建的属性发生改变后出发的钩子函数
    接受最新属性以前,基于this.props.xxx 获取的是原有的属性信息,nextProps存储的是最新传递的属性信息
    shouldComponentUpdate
    	是否容许组件更新, 返回true是容许,返回false 是不在继续走
    	componentWillUpdate:
    	更新以前: 和should同样,方法中经过this.state.xxx 获取的仍是更新前的状态信息,方法有两个参数:nextProps/nextState存储的是最新的属性和状态
    	render 更新
    	componentDidUpdate  更新以后
    
    卸载
    componentWillUnmount :卸载组件以前(通常不用)
    复制代码

React是face-Book 公司开发的一款MVC版js 框架

MVC Model (数据层) View(视图层) controller(控制层)

核心思想: 经过数据的改变来影响视图的渲染(数据)

属性的属性是只读的:只能调用组件时候传递进来,不能本身在组建内部修改(可是能够设置默认值和规则)

复合组件

  1. 复合组件:父组件嵌套子组件

传递信息的方式

  • 父组件须要把信息传递给子组件

    属性传递:调取子组件的时候,把信息基于属性的方式传递给子组件(子组件props中存储传递的信息),这种方式只能父组件把信息传递给子组件,子组件没法直接的把信息传递给父组件,也就是属性传递信息是单向传递的;

    export default class Vote extends React.Component {
        static defaultProps = {
            title: '标题不知道,随便投',
        };
    
        constructor(props) {
            super(props);
        }
        render() {
            let {title} = this.props;
            return   <VoteHead title={title}/> 复制代码

    双下文传递:父组件先把须要给后代元素(包括孙子元素)使用的信息都设置好(设置在上下文中),后代组件须要用到父组件中的信息,主动去父组件中调用使用便可

    /* 在父组件中 设置子组件上下文属性类型 static childContextTypes={} 获取子组件的上下文(设置子组件的上下文属性信息) getChildContext(){} */
        static childContextTypes = {
            //设置上下文信息值的类型
            n: PropTypes.number,
            m: PropTypes.number,
        };
    
        getChildContext() {
            //return 是啥就是想当于给子组件设置下上文
            let {count: {n = 0, m = 0}} = this.props;
            return {
                n,
                m,
            }
        }
      /* * 子组件设置使用传递进来的上下文类型 * 设置那个的类型,子组件上下文中才有那个属性,不设置是不容许使用的 * this.context.xxx * 指定的上下文属性类型须要和父组件指定的类型保持一致,不然报错 * */
        static contextTypes={
            n:PropTypes.number,
            m:PropTypes.number
        };
        constructor(props,context){
            super(props,context);
            this.context.n=1000
        }
    复制代码

    属性VS 上下文

    1. 属性操做起来简单,子组件是被动接受传递的值(组件内的属性是只读的),只能父传子(子传父不行,父传孙也须要处理:子传子,子再传孙)

    2. 上下文操做起来相对复杂一些,子组件是主动获取信息使用的(子组件是能够修改获取的上下文信息,可是不会影响到父组件中的信息,其余组件不受影响),一旦父组件设置了上下文信息,他后代组件均可以直接拿到,不须要不层层的传递

    其实子组件也能修改父组件中的信息

    1. 利用回调函数机制: 父组件把一个函数经过属性或者上下文的方式传递给子组件,子组件要把这个方法执行便可

      (也就是子组件中执行了父组件方法,还能够传递一些值过去),这样组件在这个方法中,想把本身的信息改为啥就改为啥

    /*  在父组件中
             设置子组件上下文属性类型
             static childContextTypes={}
             获取子组件的上下文(设置子组件的上下文属性信息)
             getChildContext(){}
             */
        static childContextTypes = {
            n: PropTypes.number,
            m: PropTypes.number,
            callBack: PropTypes.func
        };
    	getChildContext() {
            //return 是啥就是想当于给子组件设置下上文
            //只要render从新渲染,就会执行这个方法,从新更新父组件中的上下文信息,若是父组件上下文
            //信息更改了,子组件在从新调取的时候,会使用最新的上下文信息(render=>context=>子组件调取渲染)
            let {n, m} = this.state;
            return {
                n,
                m,
                callBack: this.updateContext
            }
        }
       updateContext = type => {
            //type :'support'/'against'
            if (type === 'support') {
                this.setState({n: this.state.n + 1});
                return;
            }
            return this.setState({m:this.state.m-1})
        };
    
    //子组件
     <button className={'btn btn-danger'} onClick={
                    ()=>{
                        callBack('against');
                    }
                }>反对</button>
    复制代码
  1. 平行组件:兄弟组件或者毫无关系的两个组件
  • 让两个平行组件拥有一个共同的父组件
  • 基于redux来进行状态管理,实现组件之间的信息传递
  1. redux能够应用在任何项目中(vue/jq/react的均可以),react-redux才是专门给react项目提供的方案
yarn add redux react-redux
复制代码
//index.js

//建立一个容器: 须要把reducer 传递进来(登记了全部状态更改的信息)
import {createStore} from 'redux';
/*reducer 做用: 1. 记录了全部状态修改的信息(根据行为标识走不一样的修改任务) 2. 修改容器中的状态信息 [参数] state:容器中原有的状态信息(若是第一次使用,没有原有状态,给一个厨师默认值 action: dispatch 任务派发的时候传递的行为对象(这个对象中必有一个type属性,是操做的行为标识, reducer 就是根据这个行为标识来识别修改状态信息 * */
let reducer = (state = {n: 0, m: 0}, action) => {
    switch (action.type) {
        case 'VOTE_SUPPORT':
            //vote_support
            state = {...state, n: state.n + 1};
            break;
        case 'VOTE_AGAINST':
            //vote_against
            state = {...state, m: state.m + 1};
            break;
    }
    return state;// 只有把最新的state返回,原有的状态才会修改
};
let store = createStore(reducer);
/* * 建立store 提供三个方法: * dispatch: 派发行为(传递一个对象,对象中有一个type属性,通知reducer修改状态信息 * subscribe: 事件池追加方法 * getState: 获取最新管理的状态信息 * */
<Vote title={'英格兰对战巴拿马,协力必胜'}
          count={{
              n: 100,
              m: 78
          }}
          store={store}/>
复制代码
//Vote.js
import React from 'react';
import PropTypes from 'prop-types';
import VoteHead from './VoteHead';
import VoteBody from './VoteBody';
import VoteFooter from "./VoteFooter";


export default class Vote extends React.Component {
    static defaultProps = {
        title: '',
        count: {
            n: 0,
            m: 0,
        }
    };

    constructor(props) {
        super(props);
    };

    render() {
        let {store} = this.props;
        return <section className={'panel-heading'} style={{width: '50%', height: '20px auto'}}>
            <VoteHead title={this.props.title}/>
            <VoteBody store={store}/>
            <VoteFooter store={store}/>
        </section>
    }
}
复制代码
//VoteBody.js

import React from 'react';
import PropTypes from 'prop-types';


export default class VoteBody extends React.Component {
    constructor(props) {
        super(props);
        //init state
        let {store: {getState}} = this.props,
            {n, m} = getState();
        this.state = {n, m};
    }

    componentDidMount() {
        let {store: {getState, subscribe}} = this.props;
        let unsubscribe = subscribe(() => {
            let {n, m} = getState();
            this.setState({
                n,
                m
            })
        });
        //unsubscribe(): 当前追加的方法移出,接触绑定的方式
    }

    render() {
        let {n, m} = this.state,
            rate = (n / (n + m)) * 100;
        if (isNaN(rate)) {
            rate = 0;
        }


        return <div className={'panel-body'}> 支持人数: <span>{n}</span> <br/> 反对人数: <span>{m}</span> <br/> 支持比率: <span>{rate.toFixed(2) + '%'}</span> </div>;
    }
}
复制代码
//VoteFooter.js
import React from 'react';
import PropTypes from 'prop-types';

export default class VoteFooter extends React.Component {

    constructor(props, context) {
        super(props, context);
    }

    render() {
        let {store: {dispatch}} = this.props;
        return <div className={'panel-footer'}> <button className={'btn btn-success'} onClick={() => { dispatch({ type: 'VOTE_SUPPORT' }) }} >支持 </button> &nbsp;&nbsp; <button className={'btn btn-danger'} onClick={ () => { dispatch({ type: 'VOTE_AGAINST' }) } }>反对 </button> </div>
    }
}
复制代码

Redux

redux:进行状态统一管理的类库(适用于任何技术体系的项目)

  • 只要两个或者多个组件之间想要实现信息的共享,均可以基于redux解决,把共享的信息到redux容器中进行管理
  • 还可使用redux作临时存储:页面加载的时候,把从服务器获取的数据信息存储到redux中,组件渲染须要的数据,redux中,这样只要页面不刷新,路由切换的时候,再次渲染组件不须要从新从服务器拉取数据,直接从redux中获取便可:页面刷新,从头开始(这套方案代替了localStorage本地存储来实现数据缓存)

Redex管理文件夹

store REDUX管理文件夹
 *   action 派发行为集合
 *          vote.js
 *          ...
 *          index.js 全部分模块行为的汇总
 *        
 *   reducer 管理员集合
 *          vote.js
 *          ...
 *          index.js 全部管理员的集合汇总
 *
 *   action-types.js 全部行为标识
 *   index.js 建立REDUX容器
复制代码

Redex工程化案例

store/index.js

/* * store * reducer: 存放每个模块 reducer * vote.js * personal.js * ... * index.js 把每个模块reducer最后合并成reducer * action 存放每个模块须要进行的派发任务(ActionCreator) * * */
import {createStore} from 'redux';
import reducer from './reducer';

let store = createStore(reducer);

export default store;
复制代码

store/action-types.js

/* * 管控当前项目中全部redux任务派发中须要的行为标识action-type * */
//vote_support
export const vote_support = 'vote_support';
export const vote_against = 'vote_against';
//personal
export const personal_init = 'personal_init';
复制代码

store/action/

index.js
======
/* * 合并全部的action-creator,相似于reducer合并,为了防止冲突,合并后的对象是以版块名称单独划分管理 * */
import vote from './vote';
import personal from './personal';

let action ={
    vote,
    personal
};
export default action;

personal.js
======
import * as TYPE from '../action-types';

let personal = {};
export default personal;

vote.js
======
/* * 每一个版块单独的action-creator :就是把dispatch派发时候须要传递的action对象进一步统一封装处理 * react-redux中会体验到他的好处 * */
import * as TYPE from '../action-types';
let vote={
    support(){
        //dispatch 派发的时候须要传递啥就返回啥便可
        return {
            type: TYPE.vote_support
        };
    },
    against(){
        return {
            type: TYPE.vote_against
        }
    }
};
export default vote;
    
复制代码

store/reducer

index.js
=====
/* * 把每个模块可是设置的reducer函数最后合并成为总的reducer * 为了保证合并reducer过程当中,每一个模块管理的状态信息不会相互冲突 * redux在合并的时候容器中的状态进行分开管理(一合并reducer时候设置的属性名做为状态 * 划分的属性名,把各个版块管理的状态放到本身的属性下 * state={ * vote:{ n:0, m:0 * }, * personal:{ * baseInfo:{} * } * } * store.get-state().vote.n之后获取状态信息的时候,也须要把vote加上 * */
import {combineReducers} from 'redux';
import vote from './vote';
import personal from './personal';

let reducer=combineReducers({
    vote,
    personal
});
export default reducer;    

personal.js
======
import * as TYPE from '../action-types';
export default function vote(state = { baseInfo:{} }, action) {
   //...
    return state;
}   

vote.js
=====
//vote版块的reducer
// state: 原始redux管理的状态管理(设置初始值)
// action: dispatch派发的时候传递的行为对象(type,)

import * as TYPE from '../action-types';
//把模块中全部导出的内容所有导出,并从新命名为type
export default function vote(state = { n: 0, m: 0, }, action) {
    switch (action.type) {
        case TYPE.vote_support:
            state = {...state, n: state.n + 1};
            break;
        case TYPE.vote_against:
            state = {...state, m: state.m + 1};
            break;
    }
    return state;
}    
复制代码

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'

import Vote from './component/Vote/Vote';

import store from './store';


ReactDOM.render(<main> <Vote title={'英格兰对战巴拿马,协力必胜'} count={{ n: 100, m: 78 }} store={store}/> </main>, document.querySelector('#root')); 复制代码

component/Vote

Vote.js
=====
    render() {
        let {store} = this.props;
        return <section className={'panel-heading'} style={{width: '50%', height: '20px auto'}}>
            <VoteHead title={this.props.title}/>
            <VoteBody store={store}/>
            <VoteFooter store={store}/>
        </section>
    }
    
VoteBody.js
=====
import React from 'react';
import PropTypes from 'prop-types';


export default class VoteBody extends React.Component {
    constructor(props) {
        super(props);
        //init state
        let {n, m} = this.props.store.getState().vote;
        this.state = {n, m};
    }

    componentDidMount() {
        this.props.store.subscribe(() => {
            let {n, m} = this.props.store.getState().vote;
            this.setState({
                n,
                m
            })
        });
        //unsubscribe(): 当前追加的方法移出,接触绑定的方式
    }

    render() {
        let {n, m} = this.state,
            rate = (n / (n + m)) * 100;
        if (isNaN(rate)) {
            rate = 0;
        }


        return <div className={'panel-body'}>
            支持人数: <span>{n}</span>
            <br/>
            反对人数: <span>{m}</span>
            <br/>
            支持比率: <span>{rate.toFixed(2) + '%'}</span>
        </div>;
    }
}    

VoteFooter.js
=====
    import React from 'react';
import PropTypes from 'prop-types';
import action from '../../store/action';

export default class VoteFooter extends React.Component {

    constructor(props, context) {
        super(props, context);
    }

    render() {
        let {store: {dispatch}} = this.props;
        return <div className={'panel-footer'}>
            <button className={'btn btn-success'}
                    onClick={() => {
                       this.props.store.dispatch(action.vote.support())
                    }}
            >支持
            </button>
            &nbsp;&nbsp;
            <button className={'btn btn-danger'} onClick={
                () => {
                    this.props.store.dispatch(action.vote.against())
                }
            }>反对
            </button>
        </div>
    }
}
复制代码

react-redux

react-redux 是把redux进一步封装,适配react项目,让redux操做更简洁,store文件夹中内容和redux如出一辙

在组件调取使用的时候能够优化一些步骤

Provider 根组件

  • 当前整个项目都在Provider组件下,做用就是把建立的store能够供内部组件使用(基于上下文)
  • Provider 组件中只容许出现一个组件
  • 把建立的store基于属性传递给Provider(这样后代组件中均可以使用这个store)

connect 高阶组件

  • 相对传统的redux,咱们作的步骤优化

  • 导出的不在是咱们建立的组件,而是基于connect构造后的高阶组件

```js
 export default connect([mapStateToProps],[mapDispatchToProps])(本身建立的组件)
```
复制代码

之前咱们须要本身基于subscribe向事件池追加方法,以达到容器状态信息改变,执行咱们追加的方法,从新渲染组件的目的,可是如今不用了,react-redux帮咱们作了这件事:'全部用到redux容器状态信息的组件,都会向事件池中追加一个方法,当状态信息改变,通知方法执行,把最新的状态信息做用属性传递给组件,组件的属性值改变值改变了,组件也会从新渲染

////把redux容器中的状态信息遍历,赋值给当前组件的属性(state)
    let mapStateToProps=state=>{
        //state就是redux容器中状态信息
        //咱们返回的是啥,就把它挂载到当前组件的属性上(redux存储不少信息,咱们想用啥就返回啥便可)
        return {
            ...state.vote
        }

    };
    //把redux的dispatch 派发行为遍历,也复制给组件的属性(ActionCreator)
    let mapDispatchToProps=dispatch=>{
        //dispatch:store中存储的dispatch方法
        //返回的是啥,就想当于把啥挂载到组件的属性上(通常咱们挂载一些方法,这
        // 些方法完成dispatch派发信息
        return {
            init(initData) {
                dispatch(action.vote.init(initData));
            }
        }
    };
    export default connect([mapStateToProps],[mapDispatchToProps])(VoteBase)
    =======================
    export default connect({state=>({...state.vote})},action.vote)(VoteBase)
    react-redux把action-creator中编写方法(返回action对象的方法),自动构造dispatch派发任务的方法,也就是mapDispatchToProps这种格式把redux容器中的状态信息遍历,赋值给当前组件的属性(state)

复制代码

[todo](file:\H:\珠峰\2018年第二期源码、笔记\2018年第二期源码、笔记\WEEK12\day2)实例

单页面应用(SPA)多页面应用(MPA)

  • 多页面应用(MPA)

    一个项目由不少页面组成,使用这个产品,主要就是页面之间的跳转(pc端多页面应用居多);

    基于框架开发的时候,须要在webapck中配置多入口,每个入口对应一个页面;

  • 单页面应用(SPA)

    只有一个页面,全部须要展现的内容都在这一个页面中实现切换,webapck只须要配置一个入口便可

    移动端单页面应用居多或者pc端系统类也是单页面应用为主

  • 如何实现单页面应用

    弊端: 因为首页中的内容包含了全部模块的信息,因此第一次加载速度很慢

    解决vue/react 实现模块化组件化开发,基于他们提供的路由实现SPA单页面应用,基于webpack打包

路由

yarn add react-router-dom

BrowerRouter

浏览器路由

基于H5中history API(pushState,replaceState,popstate)来保持url和ui的同步

真实项目中应用很少,通常只有当前是基于服务器渲染的,咱们才会使用浏览器路由

<BrowserRouter basename="/calendar">
  <Link to="/today" /> </BrowserRouter>
//最后呈现的地址 /calendar/today
复制代码

basename:string

  • 全部位置的基准URL,basename的正确格式是前面有一个斜杠,可是尾部不能有

getUserConfirmation:func (这个有点不太懂)

  • 用于确认导航函数,默认使用window.confirm

    // 使用默认的确认函数
    const getConfirmation = (message, callback) => {
      const allowTransition = window.confirm(message)
      callback(allowTransition)
    }
    <BrowserRouter getUserConfirmation={getConfirmation}/>
    复制代码

    keyLength:number

    • location.key的长度,默认是6
    • <BrowserRouter keyLength={12} />

    children:node

    单个子元素

HashRouter

哈希路由

真实项目中(先后端分离的项目:客户端渲染),咱们常用哈希路由来完成的

基于原生js构造了一套相似于history API的机制,每一次路由切换都是基于window.location.hash 完成的

  • hash-router只能出现一个子元素

route

  • path:设置匹配地址,可是默认不是严格匹配,当前页面哈希地址只要包含完整的它(内容是不变的,都能匹配上)
    • path='/' 和它匹配的只有要斜杆就能够
  • component :一旦哈希值和当前router的path相同了,则渲染component执行的组件
  • exact: 让path的匹配严谨一些
  • strict:不经常使用,可是要要加/
  • render: 权限校验,渲染组件以前验证是否存在权限,不存在作一些特殊处理
Switch组件能够解决这个问题,和switch case 同样,只要有一种校验成功,就再也不向后校验  
<HashRouter>
        <Switch>
            <Route path={'/'} exact component={A}>AAA</Route>
            <Route path={'/user'} component={B}/>
			<Route path={'/pay'}  render={() => {
                let flag = localStorage.getItem('flag');
                if (flag && flag === 'safe') {
                    return <C/>
                }
                return '权限不安全';
            }}/>
			//上述都设置完成后,会在末尾设置一个匹配:以上都不符合的状况下,咱们路由地址是违法的
			//(不设置path就是匹配全部的地址规则)
            <Route render={ ()=>{
            return <div>404</div>
            }}/>
			//重定向
			//to [string]
			//
			<Redirect to='/'/>
        </Switch>
    </HashRouter>
复制代码

basename:string 全部位置的基准URL

Link

react-router提供的路由切换组件

原理: 基于Link组件渲染,渲染后的结果就是A标签,To对应的信息最好变成href中的内容

to:string 一个字符串链接

to:object 一个对象的时候,下面属性

  • pathname - 要连接到的路径
  • search - 查询参数
  • hash - URL 中的 hash,例如 #the-hash
  • state - 存储到 location 中的额外状态数据
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: {
    fromDashboard: true
  }
}} />
复制代码

replace:bool(默认是false) 替换history stack中当前的地址(true),还能够追加一个新的地址(false)

react-router中提供的组件必须在HashRouter或者BrowerRouter里面

NavLink

跟link相似,都是为了实现路由切换跳转的,不一样在于,nav-link组件在当前页面hash和组件对应地址相吻合的时候,会默认给组件加一个active样式,让其选中态

Nav-Link不是点击谁,谁有选中的样式(可是能够路由切换),当前页面哈希后的地址和Nav-Link中的to进行比较,哪一个匹配了,哪一个才有选中的样式

to和replace等属性都有,用法同样

activeClassName:把默认的active样式改成本身设定的

activeSyle:把匹配的这个nav-link设置行内样式

exact & strict控制匹配的时候是不是严格匹配

isActive: 匹配后执行对应的函数

with-Router

把一个非路由管控的组件,模拟成为路由管控的组件

export default withRouter(connect()(组件));
先把nav基于connect高阶一下,返回的是一个代理组件proxy,
把返回的代理组件受路由管控
复制代码

受路由管控组件的一些特色:

  1. 只有当前页面的哈希地址和路由指定的地址匹配,才会把对应的组件渲染(with-router是没有地址匹配,都被模拟成为受路由管控的)
  2. 路由切换的原理,凡是匹配的路由,都会把对应的组件内容,从新添加到页面中,相反,不匹配的都会在页面中移出掉,下一次从新匹配上,组件须要从新渲染到页面中,每一次路由切换的时候(页面的哈希路由由地址改变),都会从一级路由开始从新校验一遍

  1. 全部受路由管控的组件,在组建的属性props上默认添加了三个属性

    history

    • push 向池子中追加一条新的信息,达到切换到指定路由地址的目的

      this.props.history.push('/plan')js中实现路由切换

    • go 跳转到指定的地址(传的是数字 0当前 -1上一个 -2上两个...)

    • go-back =>go(-1) 回退到上一个地址

    • go-forward =>go(1) 向前走一步

    location 获取当前哈希路由渲染组件的一些信息

    • pathname: 当前哈希路由地址
    • search : 当前页面的问号传参值
    • state: 基于redirect/link/nav-link中的to,传递的是一个对象,对象中编写state,就能够再location.state中获取

    match :获取当前路由匹配的一些结果

    • params: 若是当前路由匹配的是地址路径参数,则这里能够获取传递参数的值

Redirect

重定向3xx

to:string 要重定向的地址

to:object 要重定向的位置

<Redirect to={{
  pathname: '/login',
  search: '?utm=your+face',
  state: {
    referrer: currentLocation
  }
}} />
复制代码

push 重定向会将新的位置推入历史记录

<Redirect push to="/somewhere/else" />

<Redirect from={'/custom'} to={'/custom/list'}/>
当请求的是/custom的时候,直接跳转到/custom/list的路由地址
复制代码

qs

yarn add qs

问号传参

OA和ERP

OA:企业办公管理系统(偏向于有助于平常办公)

ERP: 企业战略资源管理系统(偏向于有管理思想)

  • 钉钉
  • TAPD
  • 今目标
  • 纷享销客

CRM:客户管理系统

CMS:内容管理系统(内容分发平台)

IM:即时通讯系统

redux中间件

redux-logger:可以在控制台清晰的展现当前redux操做的流程和信息(原有状态,派发信息,修改后的状态)

redux-thunk: 处理异步的dispatch派发

redux-promise: 在dispatch派发的时候支持promise操做

yarn add redux-logger redux-thunk redux-promise

store/index.js
=====
import {createStore, applyMiddleware} from 'redux';//applyMiddleware导入中间件
import reduxLogger from 'redux-logger';
import reduxThunk from 'redux-thunk';
import reduxPromise from 'redux-promise';
import reducer from './reducer';

let store = createStore(reducer, applyMiddleware(reduxLogger, reduxThunk, reduxPromise));
export default store;

    //=>PROMISE中间件的语法
    create(payload) {
        return {
            type: TYPES.CUSTOM_CREATE,
            //=>传递给REDUCER的PAYLOAD须要等待PROMISE成功,把成功的结果传递过去
            payload: new Promise(resolve => {
                setTimeout(() => {
                    resolve(payload);
                }, 3000);
            })
        }
    }

 create(payload) {
        //=>THUNK中间件的使用语法:在指定执行派发任务的时候,等待3000MS后在派发
        return dispatch => {
            //=>DISPATCH都传递给咱们了,咱们想何时派发,本身搞定便可
            setTimeout(() => {
                dispatch({
                    type: TYPES.CUSTOM_CREATE,
                    payload
                });
            }, 3000);
        };
    }
复制代码

[路由问号传参案例](file:\H:\珠峰\2018年第二期源码、笔记\2018年第二期源码、笔记\WEEK12\day3\src)

Ant Desion UI框架

yarn add antd;

复习

{React.createElement('a',{href:'http://www.baidu.com'},'Hello')}
//标签  属性   子元素
class HelloMessage extends Component{
    render(){
        let child=React.createElement('li',{className:'ddd'},'我是子页面');
        return <div>Hello {this.props.name}
            {React.createElement('a',{href:'http://www.baidu.com'},'Hello')}
            <br/>
            {React.createElement('ul',{className:'ccc'},child
                )}
        </div>
    }
}
style 属性应该由CSS属性构成的JS对象
* className=''
* style={{fontSize:50,backgroundColor:'red'}}   // zIndex  多峰命名
let styles={
        fontSize:50,
        fontWeight: 'bold',
        backgroundColor: 'red',
      };
{{styles}}

模拟 if
{this.state.tags.length === 0 && '等于0'}     //A为真返回B
   renderTags(){
      if(this.state.tags.length===0) return <p>里面没有元素</p>
      return  <ul>
        {this.state.tags.map((tag,index)=><li key={index}>{tag}</li>)}
        <hr />
      </ul>
    }
{this.renderTags()}

props 属性
state  组件的状态  能够经过 this.setState进行更改

无状态组件
const Hellos = (props) => <div>Hello {props.name}</div>;
<Hellos name={'zhangsan'}/>
有状态组件
//定义一个时间的方法,挂载前开始定时器执行这个方法,卸载后清除挂载前的那个定时器方法
export default class LikeButton extends React.Component {
    constructor(props) {
        super(props);
        //初始化状态
        this.state = {
            data: new Date()
        }
    };
    componentDidMount(){
        this.timerId=setInterval(
            ()=>this.tick()
        )
    }
    //方法
    tick(){
        this.setState({
            data:new Date()
        })
    }
    componentWillUnmount(){
        clearInterval(this.timerId);
    }
    render() {
        return <div>
            <h3>{this.state.data.toLocaleTimeString()}</h3>
        </div>
    }
}

props 对于使用他的组件来讲是只读的,只能经过父组件进行修改
state  能够经过 this.setState({  }) 进行修改
注意不要在push pop shift unshift  splice 等方法修改
应该用concat  slice   filter  会放回一个新数组

原生事件
能够再componentDidMount方法中经过addEventListener 绑定浏览器原生事件
componentWillUnmount 方法解除 removeEventListener

在dom 中 设置 ref属性指定一个名称    经过 this.refs.指定名称获取

组合组件

父组件 <Avatar username="pwh" />
    
const Avatar = (props) => {
  return (
    <div>
      //子组件   经过属性传递
      <ProfilePic username={props.username} />
      <ProfileLink username={props.username} />
    </div>
  );
}

循环的时候必需要给key
// arr是在父组件里面声明的arr数组的属性
<ul>{this.props.arr.map((v,i)=>{
                return <li key={i}>{v}</li>
            })}</ul>
组件标签里面包含的子元素经过 this.props.children

<LikeButton username={'pwh'} arr={[1,2,3,4]}>
        <span>12123</span>
        <p>我是一个p标签</p>
    </LikeButton>
props.children一般是一个组件对象的数组,当 props.children是惟一的子元素,那就不是数组

点击事件的内联样式
onClick={  ()=>(console.log(1))}
第二种方法
onClick={this.handleClick}  //记得不要再这里带(e)参数 会报错的
<div onClick={this.handleClick.bind(this)}>${this.props.name}</div>
handleClick=(e)=>{
        console.log(e.target.innerHTML);
    };   //函数建议使用箭头函数的写法

经过  setState来修改state 
this.setState({
      count: this.state.count+1
    })
复制代码
store有四个方法。
getState: 获取应用当前 state。
subscribe:添加监听器。
dispatch:分发 action。更新 state。
replaceReducer:替换 store 当前用来处理 state 的 reducer。

二者的关系是: state=store.getState()

经常使用的是dispatch,这是修改State的惟一途径,使用起来也很是简单。
import {createState} from 'redux';
function counter(state=0,action) {
    switch(action.type){
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state-1
        default:
            return state
    }
}
let store=createState(counter);
store.subscribe(()=>{
    console.log(store.getState());
});
store.dispatch({type:"INCREMENT"});

action 惟一的约束仅仅就是包含一个type
复制代码
store由redux的createStore(reducer)生成的

    state经过store.getState()获取的

    action本质上是一个包含type属性的普通对象

    改变 state必须dispatch一个action

    reducer 本质上action.type  来更新的

实际上state就是全部reducer返回的汇总

redux有五个API
	createStore(reducer,[])
	combineReducers(reducers)
	applyMiddleware(...middlewares)
	bindActionCreators(actionCreatore,dispatch)
	compose(...functions)

Redux 强调三大基本原则
* 惟一数据源
* 保持状态只读
* 数据改变只能经过纯函数完成
复制代码

过程总结

建立一个操做指令action ->建立一个reducer -> 经过createStore(reducer) 建立一个store

经过store dispath(action) 执行reducer 中更新操做,更新store中的数据

学习redux

咱们大多数人的学习过程通常是——一个按部就班、逐步迭代的过程,而redux的学习却不是这样,你不看概念,就无法理解示例demo,你不敲示例demo,你就没法彻底理解redux的各类基础概念。因此最终发现,redux的最好的学习方式就是,通读一个个的概念,敲出示例demo,再根据示例demo,反过来理解概念,循环往复,总能学的懂!!

const todo={
    TOGGLE_TODO:'TOGGLE_TODO',
    GET_TODOS:'GET_TODOS',
    toggleTodo({ items,id }) {
        return {
            type: todo.TOGGLE_TODO,
            items:items,
            id:id
        }
    },
    getTodos({items}){
        return {
            type:todo.GET_TODOS,
            items:items
        }
    }
};
export default todo;

// ======

export default function(state = {默认值}, action) {
    switch(action.type){
        case GET_TODOS:
            return {todos:[...action.items]}
        case TOGGLE_TODO://deleted=true
            return {
                todos:action.items.map((i)=>{
                    if(i.id===action.id){
                        return {
                            ...i,
                            status:i.status===0?1:0
                        }
                    }else{
                        return {
                            ...i
                        }
                    }
                })
            }
        default:
            return state;
    }
}

const store = createStore(todo);
复制代码

redux-hook

dva.js

npm install dva-cli -g

入门文档

put 用于触发action put({type:types.ADD})

call 用于调用异步逻辑,支持promise

call(第一个参数是一个方法,第二个是传入这个方法的参数)

take 等待dispatch 匹配某个action

yield 方法(传入方法的参数) //跟上面等同

run() 方法是添加功能 * yield

effect 都是一个简单对象

select 用于从state里获取数据

定义路由

router/Products.js

import React from 'react';

const Products = (props) => (
  <h2>List of Products</h2>
);

export default Products;

添加路由
router.js
<Route path='/products' exact component={Products}/>
复制代码
编写组件

components/编写组件.js

定义Model
model/..js
    export default {
      namespace:'count',
      state:0,
      reducers:{
        add(count){return count+1},
        minus(count){return count-1}
      }
    }
在indexjs 载入
    app.model(require('./models/products').default);

function IndexPage(props) {
  return (
    <div> <h3> {props.count}</h3> <button key={'add'} onClick={()=>{props.dispatch({type:'count/add'})}}>+</button> <button key={'munus'} onClick={()=>{props.dispatch({type:'count/minus'})}}>-</button> </div>
  );
}

IndexPage.propTypes = {};

export default connect(({count}) => ({count}))(IndexPage);
复制代码

react react-redux react-saga

react-sage是更好的处理异步

dva科普

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
 yield* 表达式用于委托给另外一个generator 或可迭代对象。
复制代码

yarn add roadhog

UMI.js

全局安装umi cnpm install -g umi

路径似路由

umi div 执行

umi g 建立一些页面

umi g page index    是根路径
建立 umi 项目
yarn create 项目名
复制代码
import React from 'react'
import dva from 'dva'
import Counter from './Counter'

//dva 是一个函数 经过执行它能够拿到一个app 对象
let app = dva();

// function delay(ms) {
// return new Promise(function (resolve, reject) {
// setTimeout(function () {
// resolve()
// }, ms)
// })
// }
//
// function getAmount() {
// return fetch('http://localhost:3000/amount').then(res => res.json());
// }

//app.router app.start() app.model
//一个模板就是一个状态,而后把reducer和状态写在一块儿
//添加一个模型
app.model({
  //命名空间: 由于一个应用会有不少个模型,每个模型有一个对象
  namespace: 'counter',
  //此命名空间的默认状态
  state: {current: 0, height: 0},
  //他是用来接收action ,修改仓库状态
  reducers: {
    //reducer接受老状态和动做,返回新状态
    //state老状态(parameter) action:AnyAction
    add(state, action) {
      // let current = state.current + action.payload;
      // return {current, height: current > state.height ? current : state.height}
      return {current: state.current + 1}
    },
    // minus(state, action) {
    // return {...state, current: state.current - action.payload}
    // }
  },
  //它是用来执行反作用的,好比说异步操做,调用api接口
  effects: {
    //表示这是一个generator effect=redux-saga/effects
    // * add(action, {call, put}) {
    // //表示这是一个generator
    // //amount 是接口中的变量 call 调用方法
    // let {amount}=yield call(getAmount);
    // //type;'方法'
    // yield put({type:'add',payload:amount})
    // // yield call(delay, 1000);
    // // yield put({type: 'minus'})//能够不加前缀,counter/minus/派发其余的能够写
    // }
  },
});
//参数是一个函数,此应用自己就是要熏染函数的返回值
app.router(() => <Counter />);
//本质是启动应用,就是经过app.router获取组件,经过ReactDOM渲染到容器上
app.start('#root');

import React from 'react';
import {connect} from 'dva';
import './Counter.css';

class Counter extends React.Component {
    render() {
        return (<div> <div className={'container'}> <p>当前记录{this.props.current}</p> <button onClick={() => this.props.dispatch({type: 'counter/add'})}>add</button> </div> </div>)
    }
}

export default connect(
    state => state.counter
)(Counter);

复制代码
相关文章
相关标签/搜索