LingYanSi / blog

博客
https://github.com/LingYanSi/blog/issues
9 stars 0 forks source link

React #21

Open LingYanSi opened 8 years ago

LingYanSi commented 8 years ago

React 代码在这里

LingYanSi commented 8 years ago

单页面应用spa React-router

react-router 为react提供路由管理,看起来如下伪代码

import  { createHistory, useBasename } from 'history' 
import {Router , Link, Route, } from 'react-router'
// history的作用在于,使用html5的history api 而不是使用hash
const history = useBasename(createHistory)({
  basename: ''
});

class App extends Component{
    constructor(){
        super(...arguments)
    }
    render(){
        return <div>
            <Nav />
            <div className="main">
                <Sidebar/>
                <div className="content">
                    {this.props.children}
                </div>
            </div>
            <Footer />
        </div>
    }
} 

render((
    <Router history={history}>
        <Route path='/' component={ App }>
            <Route path='home' component={ Home } />
            <Route path='article' component={ Article } />
            <Route path='article/:id' component={ ArticleInfo } />
            <Route path='message' component={ Message } />
            <Route path='about' component={ About } />
            {/* path='*' 表示默认路由 */}
            <Route path='*' component={ NotFound } />
        </Route>
    </Router>
) ,
    document.getElementById('app')
)
LingYanSi commented 8 years ago

redux

redux是flux的实现,主要用于解决store -> view -> action -> store单向数据流的问题 在react中,通常的做法是每个组建都有很多的state,当应用比较小的时候,做起来好像没有什么问题 一旦应用打起来后,就比较麻烦了,比如说子组件去更改兄弟组件、父组件、关系不太大的组建的状态,最终会导致逻辑混乱,不易管理

其实,我们应该明白,view只是数据的展现,用户可以通过view触发一个action,整个action可能会修改数据,数据修改后view可能会发生改变 store -> view -> action -> store

从函数式编程的角度来讲,我们期望任何时间,同样的输入会得到同样的输出

对于react来讲,我么可以把store通过父组件注入,子组件的数据通过props由父组件传递过来 不过总是写props是不是有点太恶心呢? 可以不可以子组件的数据也是从store获取呢? 这样做的话,也是会变得混乱起来,因为数据来源不统一?有待观察

redux的几个概念

store

整个应用的数据来源

action

action经过reducer返回一个新的store

reducer

reducer其实就是一个action的switch,返回一个新的store

dispatch

action通过dispatch派发

middleware

中间件,通过修改dispatch而获得,因为action是通过dispatch派发的,因此我们可以在dispatch内拦截action,在action被reducer会里后,还可以对返回值进行处理,不过一般我们是不会通过中间件对store进行修改的,更多的是做logger之类的事情,也可以通过中间件完成异步任务

LingYanSi commented 8 years ago

React-Router 实现

import React,{Component} from 'react'

class Router extends Component{
    componentDidMount(){
        Router.self = this
    }
    getChild(children){
        // return this.props.children
        return React.Children.map( children, Item => {
            // 这里是一个路由的匹配校验
            if(Item.props.path === Router.currentURL){
                let Com = Item.props.component
                // 把url参数传递给组件
                return <Com params={'111'}>
                    {this.getChild(Item.props.children)}
                </Com>
            }
        })
    }
    render(){
        return <div>
            {this.getChild(this.props.children)}
        </div>
    }
}

Router.urlChange = function ( url) {
    // url变化后,强制更新Router
    Router.currentURL = url
    Router.self.forceUpdate()
}

Router.currentURL = '/'

class Link extends Component{
    constructor(){
        super()
    }
    render(){
        let props = this.props

        return <a onClick={Router.urlChange.bind(this, props.to)}>
            {props.title}
            {props.children}
        </a>
    }
}

class Route extends Component{
    render() {
        // 匹配路由,如果匹配的上就渲染,不然就return null

        return null
    }
}

export {Router, Route, Link}

使用

import { Router, Route, Link } from 'module/router/index.js'
class Step1 extends Component{
    render(){
        return <div>
            念去去,千里烟波
            <Link to="/step2">下一步</Link>
            {this.props.children}
        </div>
    }
}

class Step2 extends Component{
    render(){
        console.log(this.props.params);
        return <div>
            哈哈哈哈哈
            <Link to="/">下一步</Link>
        </div>
    }
}

class Home extends Component{
    render(){
        return  <Router>
                <Route path="/" component={Step1}>
                    <Route path="/" component={Step2}></Route>
                </Route>
                <Route path="/step2" component={Step2}></Route>
            </Router>
    }
}
LingYanSi commented 8 years ago

Spread Attributes

import Fuck from './fuck'

class You extends Component{
    render(){
        let data = {
            name: '薄熙来',
            job: '平西王'
        }
        return <div id="shenfen" color="red">
            <Fuck {...data}/>
        </div>
    }
}

其中的

...data

属于es6的spread[解构赋值]语法

首先从react的jsx语法说起 上面的代码会被babel转译成如下伪代码

let You = React.createElement(
    'div', 
    {id:'shenfen', color:'red'},
    React.createElement(
        Fuck, 
        {name:'薄熙来', job:'平西王'}
    )
)

也就是说

{...data} => {name: '薄熙来', data: '平西王'}

再说spread语法

let sth = {a: 111, b:6666}
let data = {...sth, {b:22}}
data // {a:111, b:22} 前面的属性会被后面的属性覆盖
LingYanSi commented 8 years ago

写react也有一段时间,但发现其实并没有完全理解React,甚至对于其原理也是理解的比较模糊

class Fuck extends Component{
    render(){
        return <div className="fuck">
            达到反革命
        </div>
    }
}

//Fuck是一个组建component

// 渲染
React.render(Fuck, rootElement)

//render方法接收到Fuck,执行后,会得到render方法返回的对象
//render方法会被转译成如下代码

React.createElement('div'
    ,{'class': 'fuck'}, 
    React.createElement(
        'text',
        {}
    )
)
LingYanSi commented 7 years ago

Class 中方法的this问题

class A extends Component{
    fuck(){
        console.log(this) // this指向div
    }
    // 属性
    state = {
        name: '嘿嘿'
    }
    static ni = '静态属性'
    static you = ()=>{
        console.log('静态方法')
    }
    // 方法
    fuck = () => {
        console.log(this) // this指向组件的实例
    }
    render(){
        return <div onClick={this.fuck}></div>
    }
}
LingYanSi commented 7 years ago

fix click delay

function tap(func){
    // 判断及其类型
    if(window.navigator.userAgent.toLowerCase().match(/phone|android|ipad|ipod/g)){
        var notMove = true
        return {
            onTouchEnd(...args){
                notMove && func(...args)
                notMove = true
            },
            onTouchMove(){
                notMove = false
            }
        }
    } else {
        return {
            onClick: func
        }
    }
}

export default tap
import onTap from 'tap'
class A extends Component{
    tap = ()=>{
        alert('被点击了')
    }
    render(){
        return <div {...onTap(this.tap)}></div>
    }
}
LingYanSi commented 7 years ago

自定义Tap事件

问题描述

如果在a标签上绑定了Tap事件,而在父元素上也绑定了Tap事件 如果没有组织事件冒泡,在切换到新页面之前,老页面会被销毁,但此时React的时间系统依然会向父元素派发事件 当然,react会先校验下,这个元素是否存在,如果不在,会throw一个错误

Uncaught Invariant Violation: React DOM tree root should always have a node reference.

解决方案

对于切换页面的Tap事件,请阻止事件冒泡!!!!