| react-create-app 脚手架
| Router、Route
| Link、Links router的导航组件,用来切换路由
| browserHistory、hashHistory
| activeStyle、activeClassName 路由设置样式
| query、params 变量
| Redirect

react-create-app 脚手架

$ sudo npm i create-react-app -g            // 全局安装,以后在安装就直接使用create-react-app命令来创建就可以

$ create-react-app my-app            // 创建+创建项目目录名

$ cd my-app

$ yarn start

$ yarn start、yarn build             // 启动服务、打包

$ yarn add antd        // 安装Antd UI框架

    在index.js中引用ant公用样式
    import 'antd/dist/antd.css';

$ yarn add react-router-dom -D            // react-router是安装4.0之前版本,react-router-dom是4之后版本

$ yarn add redux react-router-redux -D

$ npm run eject            // 注意如果对构建工具和配置选择不满意可以使用eject运行, 这里是单项操作只能一次不能回去

一、create-react-app关闭eslint提醒

    需要先npm run eject 生成本地webpack配置文件,在修改package.json中的配置

    "eslintConfig": {
        "extends": "react-app",
        "rules": {
            "no-undef": "off",
            "no-restricted-globals": "off",
            "no-unused-vars": "off"
        }
    }

二、配置反向代理

    "proxy": {
        "/api/*": {
            "target": "http://goucai.diyicai.com",
            "pathRewrite": {
                "^/api/": "/"
            },
            "changeOrigin": true,
            "secure": false
        }
    }

三、使用.scss文件

    1、$ npm install file-loader sass-loader node-sass --save-dev            // 安装包

    2、node_modules/react-scripts/config/webpack.config.dev.js 和 webpack.config.prod.js文件中module中配置下面两项

        {
            exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/, /\.scss/],            // 这里加添加一个/\.scss/
            loader: require.resolve('file-loader'),
            options: {
                name: 'static/media/[name].[hash:8].[ext]',
            }
        },
        // 解析scss文件
        {
            test: /\.scss$/,
            loaders: ['style-loader', 'css-loader', 'sass-loader'],
        }


四、引入样式

    1、<div style={{width: "1000px"}}></div>

    2、import "./main.css"

        <div className="main"></div>

    3、import Styles from './main.css'

        <div className={Styles.main}></div>


五、常见问题

    1、<src>引用本地图片无效

        import JzkUrl from '../../assets/img/jzk.png'

        <img src={JzkUrl} />

六、build的打包的时候注意的地方

    在package.json中的有一个homePage: '.',这个在打包的时候图片会成为相对路径,如果改成 homePage: '/'


七、修改webpack配置加一个全局配置

    function resolve (dir) {
        return path.join(__dirname, '..', dir)
    }

    resolve: {
        alias: {
            'react-native': 'react-native-web',
            '@': resolve('src')
        }
    }

    在页面里可以直接引用@来找到src目录

        import User from '@/views/User/'

PropTypes验证

React.PropTypes 提供很多验证器来验证传入数据的有效性

当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 propTypes。下面用例子来说明不同验证器的区别: 

Example:

    import PropTypes from 'prop-types';
    class Greeting extends React.Component {
        render() {
            return (
                <h1>Hello, {this.props.name}</h1>
            );
        }
    }
    Greeting.propTypes = {
        name: PropTypes.string
    };

propTypes类型: {

    // 可以声明 prop 为指定的 JS 基本类型。默认
    // 情况下,这些 prop 都是可传可不传的。
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // 所有可以被渲染的对象: 数字,
    // 字符串,DOM 元素或包含这些类型的数组。
    optionalNode: React.PropTypes.node,

    // React 元素
    optionalElement: React.PropTypes.element,

    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // 指定的多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
        React.PropTypes.string,
        React.PropTypes.number,
        React.PropTypes.instanceOf(Message)
    ]),

    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // 特定形状参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
        color: React.PropTypes.string,
        fontSize: React.PropTypes.number
    }),

    // 以后任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,

    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,

    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接
    // 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
        if (!/matchme/.test(props[propName])) {
            return new Error('Validation failed!');
        }
    }
}

react-router 4.0

v3到v4版本的变化是静态路由到动态路由的变化,并且页面的嵌套路路由嵌套决定

react-router: 是浏览器和原生应用的通用部分, 核心文件

react-router-dom: 用于浏览器

react-router-native: 是用于原生(react-native)应用的    

react-router-redux: React Router 和 Redux 的集成

react-router-config: 静态路由配置的小助手

一、安装

    $ npm i react-router react-router-dom --save

    import { BrowserRouter as Router, Route, Switch, Link, Redirect, withRouter } from 'react-router-dom';


二、使用路由

    1、react-router和react-router-dom,不需要都引用,只是后者多<Link> <BrowserRouter> 这样的 DOM 类组件

    2、如果搭配 redux ,你还需要使用 react-router-redux

    index.js: 在入口文件加上路由配置

        import React from 'react'
        import { Router, Route, Link } from 'react-router'

        // render中加入路由配置
        React.render((
            <Router>
                <Route path="/" component={App}>
                    <Route path="about" component={About}/>
                    <Route path="users" component={Users}>
                        <Route path="/user/:userId" component={User}/>
                    </Route>
                    <Route path="*" component={NoMatch}/>
                </Route>
            </Router>
        ), document.body)


二、<BrowserRouter> HTML5 history路由组件

    1、basename - 为所有位置添加一个基准的url

        <BrowserRouter basename="/minooo" />
        <Link to="/react" /> // 最终渲染为 <a href="/minooo/react">

    2、getUserConfirmation() - 导航到此页执行的函数

        const getConfirmation = (message, callback) => {
            const allowTransition = window.confirm(message)
            callback(allowTransition)
        }
        <BrowserRouter getUserConfirmation={getConfirmation('Are you sure?', yourCallBack)} />

    3、forceRefresh - bool当浏览器不支持 HTML5 的 history API 时强制刷新页面

        const supportsHistory = 'pushState' in window.history
        <BrowserRouter forceRefresh={!supportsHistory} />    

    4、keyLength - 路由的长度

    5、children - 渲染唯一子元素


三、<HashRouter> 该技术只是用来支持旧版浏览器, Hash history 不支持 location.key 和 location.state


四、<Route> 最重要的组件,页面访问地址与Router的path进行匹配,渲染出对应的UI界面

    <Route path="/" component={Main}> - 渲染的组件

    <Route> 自带三个 render method 和三个 props

    1、render methods 分别是:

        <Route component> - 当访问地址和路由匹配时,一个组件才会被渲染,此时此组件接受match、location、history

            * <Route path="/home" component={Home} />

        <Route render> - 适用于内联渲染,不会产生重复装载问题

            * <Route path="/home" render={() => <h1>Home</h1} />

        <Route children> -

    2、props分别是: match、location、history

            <Route path="/:id" component={ListBasic}></Route>          // 参数传给子组件
            this.props.match.params.id            // 获取参数值

    3、path: 路由路径

         exact: true,表示独一无二的路由

            // 如果不加exact, "/" 时加载main, "/login" 会显示main和login两个组件,如果加了exact在"/"路由时只会显示main组件
            <div style={{ height: "500px", backgroundColor: "#ccc"}}>
                <Route path="/" exact component={Main} />
                <Route path="/login" component={Login} />
                <Route path="/todoList" component={TodoList} />
            </div>

         strict: true,如果path为'/one/'将不能匹配'/one',但可以匹配'/one/two'


五、路由跳转

    1、Link、NavLink

        <Link to="/course" />
        <Link to={{
				pathname: '/course',
				search: '?sort=name',
				state: { price: 18 }
			}} />

        // 给导航使用的
        <NavLink
            to="/about"
            activeStyle={{ color: 'green', fontWeight: 'bold' }}
        >MyBlog</NavLink>

    2、JS跳转

        export default class SelectCard extends Component {
            constructor(props){
                super(props);
            }
            goHomePage(){
                this.props.history.push('/main');            // 跳转
            }
            render(){
                return {
                    <div>
                        <button onClick={this.goHomePage.bind(this)}>跳转</button>
                    </div>
                }
            }
        }

        /** 注意如果使用redux子组件会取不到this.props.history **/
        解决方法: 在子组件中
        import PropTypes from 'prop-types'

        子组件名.contextTypes = {
            router: PropTypes.object.isRequired
        }

        console.log( this.context.router.history.push('/') );


六、<switch> 只渲染出第一个与当前访问地址匹配的 <Route> 或 <Redirect>

    // v3
    <Route path='/' component={App}>
        <IndexRoute component={Home} />
        <Route path='about' component={About} />
        <Route path='contact' component={Contact} />
    </Route>

    v4中使用<switch>来取代<IndexRouter>, 当react渲染时switch下第一个子<route>会被渲染
    // v4
    const App = () => (
        <Switch>
            <Route exact path='/' component={Home} />
            <Route path='/about' component={About} />
            <Route path='/contact' component={Contact} />
        </Switch>
    )


七、添加图像、字体和文件

    组件中加载图片
    import logo from './logo.png';        导入图片
    <img src={logo} alt="Logo" />;

    css中加载图片
    .logo{
        background-image: url('./logo.png');
    }


八、Redirect重定向 和 未匹配路由跳转到404

    // 跳404用法
    render() {
    return (
    <Router>
      <div className="main-container">
        <Route path="/main" component={Main} />
        <Route component={Page404}/>                // 没有定义path就是没有路径匹配时
      </div>
    </Router>
    )
    }

    // Redirect 用法不渲染组件直接重定到其它页面
    render() {
        if(!this.state.logined){
            return (
                <Redirect to="/todoList" />
            )
        }
        return (
            <div>
                这里是Login组件
            </div>
        )
    }


九、动态路由

    1、路由变量

        定义变量: <Route path="/:msg" component={Message} />
        获取变量: props.match.params.定义的名

        <Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
        <Route path="/hello(/:name)">       // 匹配 /hello, /hello/michael 和 /hello/ryan
        <Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg

        Example:

            class Message extends Component {
                render(props){
                    return (
                        <div>
                            // 这里this.props.params.msg 接收的是to的名字
                            <h1>{this.props.match.params.jumpUrl || 'hello'}</h1>
                            <Links />
                        </div>
                    )
                }
            }

            const Links = () =>{
                <nav>
                    <Link to="/">Hello</Link>
                    <Link to="/yong">Yong</Link>
                    <Link to="/feng">Feng</Link>
                </nav>
            }

            class ParamsRoute extends Component {
                render() {
                    return (
                        <Router history={hashHistory}>
                            <Route path="/(:msg)" component={Message} />
                        </Router>
                    );
                }
            }
            export default ParamsRoute;


    2、query: 获取URL中的参数

        1) 获取参数

            http://localhost:8080/#/?message=ssssss

            const Page = (props) =>
                <div>
                    <h1>{props.location.query.message || 'Hello'}</h1>            // props.location.query.message输出 ssssss
                </div>

        2) <Link>请求时带参数

            const Page = (props) =>
                <div>
                    <h1>{props.location.query.message || 'Hello'}</h1>
                </div>

            const Links = () =>
                <nav>
                    <Link to={{ pathname: "/", query: {message: "ssssss"} }} />         
                </nav>

        3) js中请求参数 

            goToMessage(){
                that.props.history.push({ 
                    pathname : '/success',
                    query : { messageTitle: '充值成功', messageContent: '请取您的充值单请取您的充值单请取您的充值单'} 
                })
            }


十、路嵌套和展示

    // v2/3版本 就是一个路由配置文件,在放子路由的地方使用
    <div>
        { this.props.childre }                // 加载子路由的位置
    </div>

    // v4版本
    // router.jsx
    export default class Index extends Component {
        render() {
            return (
                <Router>
                    <Switch>
                        <Layout />
                    </Switch>
                </Router>
            )
        }
    }

    // layout.jsx
    export default class Layout extends Component {
        render() {
            let menuHtml = () => {
                const menuItem = [];
                this.state.menuList.forEach(function(menu, idx, arr){
                    menuItem.push(
                        <li>
                            <Link to={menu.path}>{menu.title}</Link>
                        </li>
                    )
                })
                return menuItem
            }

            return (
                <div>
                    <header style={{height: "100px"}}>
                        菜单: { menuHtml() }
                    </header>
                    <main style={{ height: "500px", backgroundColor: "#ccc"}}>
                        // 这下面都是子路由的展示位置
                        <Route path="/" exact component={Main} />
                        <Route path="/login" component={Login} />
                        <Route path="/todoList" component={TodoList} />
                        <Route component={Page404}></Route>                        // 注: 如果没有匹配路由指到到404页面, 不需要使用Redirect
                    </main>
                </div>
            )
        }
    }

    // login.jsx
    export default class Login extends Component {
        constructor(props){
            super(props);
            this.state = {
                logined: true
            }
        }

        render() {
            /* 这里可以不渲染组件直接重定到其它页面 */
            if(!this.state.logined){
                return (
                    <Redirect to="/todoList" />
                )
            }
            return (
                <div>
                    这里是Login组件
                </div>
            )
        }
    }

https://www.jianshu.com/p/7bb4c1a0530d
https://www.jianshu.com/p/548674270455


十一、withRouter 

    withRouter是专门用来处理数据更新问题,否则可能会出现路由地址改变但页面没有相应改变的bug

    import React, { Component } from 'react'
    import { withRouter } from 'react-router'
    import { Route, Redirect } from 'react-router-dom'

    class AuthRouter extends Component {
        render() {  
            const { component: Component, ...rest } = this.props
            const isLogged = sessionStorage.getItem("isLogin") === "1" ? true : false;

            return (
                <Route {...rest} render={props => {
                    return isLogged
                        ? <Component {...props} />
                        : <Redirect to="/login" />
                }} />
            )
        }
    }
    export default withRouter(AuthRouter);

dva+react+Ant 蚂蚁金服脚手架

单独加载ant UI框架,也可以使用dva-cli蚂蚁提供的脚手架,支持IE9及以上版本

一、单独添加ant  

    $ cnpm i antd --save

    import 'antd/dist/antd.css';         // 引入样式
    import { DatePicker } from 'antd';    // 这种方法的引用必须要添加babel-plugin-import的按需加载组件
    ReactDOM.render(<DatePicker />, mountNode);            

二、按需加载插件 babel-plugin-import

    $ cnpm i babel-plugin-import --save

    .babelrc添加参数:
    {
        "plugins": [
            ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] // `style: true` 会加载 less 文件
        ]
    }

三、dva-cli安装

    $ cnpm i dva-cli    

    $ dva new [项目名] && cd [项目名]        // 创建项目

    $ cnpm install antd babel-plugin-import --save        // 安装 ant 和 babel插件(用来按需加载 antd 的脚本和样式的)

        .webpackrc文件加参数
        {
            "extraBabelPlugins": [
                ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
            ]  
        }

四、运行

    $ npm start         // 生产环境运行

    $ npm run build        // 打包项目,发布线上环境在Dist目录下生成静态文件,js、css压缩

五、redux的使用

    有两个目录,models和components, model来定义state和action的,components来编写组件

    dva提供conect方法,将model和component串联起来

    import { connect } from 'dva';

    const Products = ({ dispatch, products }) => { ... }
    export default connect(({ products }) => ({
        products,
    }))(Products);

六、配置

    1、 .roadhogrc.mock.js  用于mock数据的配置

        export default {
            // 支持值为 Object 和 Array
            'GET /api/users': { users: [1,2] },

            // GET POST 可省略
            '/api/users/1': { id: 1 },

            // 支持自定义函数,API 参考 express@4
            'POST /api/users/create': (req, res) => { res.end('OK'); },
        };

    2、.webpackrc  

        {
            "extraBabelPlugins": [
                ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
            ],
            "entry": "src/index.js",        // 可以配置webpack的选项,参数见面下roadhog的文档连接
            "proxy": {                        // 设置代理
                "/api": {
                    "target": "http://jsonplaceholder.typicode.com/",
                    "changeOrigin": true,
                    "pathRewrite": { "^/api" : "" }
                }
            }
        }

    3、设置端口

        默认是8000,可以在package.json中设置
        "scripts": {
               "start": "PORT=3000 roadhog server",        // windowns设置 set PORT=3000&&roadhog dev
        }

    4、public目录会在build的copy到dist目录下,存放一些静态文件

https://github.com/sorrycc/blog/issues/18            // dva
https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md    // roadhog


七、fatch请求

    dva-cli中utils目录中request.js中定义了一个通过fatch来发ajax请求的方法

    业务中调用:
    function IndexPage() {
        var getUser = () => {
            Request('/api/posts', {method: 'GET'})
            .then((data)=>{
                debugger;
            })
        }
        getUser()

        return (
            <div className={styles.normal}>
                ...
            </div>
        );
    }
    exports default IndexPage;

收集组件

1、react-transition-group - 页面切换过渡效果

https://github.com/reactjs?page=2

| https://facebook.github.io/create-react-app/ // create-react-app 官网
| https://react-guide.github.io/react-router-cn/ // React-router 中文网
| https://reacttraining.com/react-router/web/example/basic // 官方示例**
| https://www.jianshu.com/p/25e9ba1ebafb // react源码分析
| https://github.com/YutHelloWorld/Blog/issues/4 // v2、3迁移v4
| https://www.jianshu.com/p/e0a0ed6c3b8a // 路由跳转登录验证
| https://www.jianshu.com/p/e3adc9b5f75c/
| https://segmentfault.com/a/1190000009349377 // router ^4
| https://github.com/reactjs/react-router-redux