JiM-W

keep Moving


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

React-router

发表于 2017-05-16 | 分类于 react

以下是我结合官方文档的学习笔记,文末有官方文档的链接.

1 react-router中一个属性exact 的使用,默认是true,详见API文档解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)

如上,当 URL is /about / /about两者都会render

如下所示,当url是 /repo 的时候 Repo 和RepoTest两者也是都会渲染.当然也可以通过设置exact属性进行精确匹配.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Router>
<div>
<ul>
<li>
<Link to='/about'>About</Link>
</li>
<li>
<Link to='./repo'>Repo</Link>
</li>
<li>
<Link to='./repo/test'>RepoTest</Link>
</li>
</ul>
<Route exact path='/' render={()=>(<div>Welcome</div>)}></Route>
<Route path='/repo' component={Repo}> </Route>
<Route path='/repo/test' component={RepoTest}></Route>
</div>
</Router>

2 match对象

其中path属性值就是Route组件的path属性值,url是根据地址栏动态解析的,params是解析URL地址的时候params对象

match对象是实时生成的,当url地址不同 的时候,match对象也会跟着变化,大家可以多点击几次,看下math对象所有的属性是什么值.

当url地址栏匹配到Route对应的路径的时候,组件中的match对象才存在,否则为null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const CustomLinkExample = () => (
<Router>
<div>
<OldSchoolMenuLink activeOnlyWhenExact={true} to="/" label="Home"/>
<OldSchoolMenuLink to="/about" label="About"/>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</div>
</Router>
)
const OldSchoolMenuLink = ({ label, to, activeOnlyWhenExact }) => (
<Route path={to} exact={activeOnlyWhenExact} children={({ match }) => {
console.log(match);//对于官网的demo做了如下改动,当url路径为 / 的时候,第一个OldSchoolMenuLink组件返回的Route对象children函数中match对象存在,而第二个OldSchoolMenuLink组件返回的Route组件中的children函数中却没有match对象,为null
return (<div className={match ? 'active' : ''}>
{match ? '> ' : ''}<Link to={to}>{label}</Link>
</div>)
}
}/>
)
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
const About = () => (
<div>
<h2>About</h2>
</div>
)
export default CustomLinkExample

A match object contains information about how a <Route path> matched the URL. match objects contain the following properties:

  • params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
    • key值是 :id 之类的变量名 value 是即时匹配到的路径字符串
  • isExact - (boolean) true if the entire URL was matched (no trailing characters)
  • path - (string) The path pattern used to match. Useful for building nested <Route>s
  • url - (string) The matched portion of the URL. Useful for building nested <Link>s

You’ll have access match objects in various places:

  • Route component as this.props.match
  • Route render as ({ match }) => ()
  • Route children as ({ match }) => ()
  • withRouter as this.props.match
  • matchPath as the return value

If a Route does not have a path, and therefore always matches, you’ll get the closest parent match. Same goes for withRouter.

Route组件要渲染的组件component,通过Router可以向该组件传递match对象;对于官网的demo我做了一些改动,方便理解

通过点击不同的链接,观察地址栏和控制台的输出,应该可以有一个基础的认识.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
console.log(Route);
console.log(<Route/>);
const Home = ()=>(
<div>
<h2>this is my Home</h2>
</div>
)
const About = ()=>(
<div>
<h2>this is about handsome me</h2>
</div>
)
// const Topics = ()=>(
// <div>
// <h2>this is all Topics</h2>
// </div>
// )
const Topic = ({ match }) => {
console.log('Topic',match);//改动1
return (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
}
const Topics = ({match})=>{
console.log('Topics',match);//改动2
return (
<div>
<h2>Topics</h2>
<ul>
<li >
<Link to={`${match.url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/porps`}>props</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic}></Route>
<Route exact path={match.url} render={()=>(<h3>please select a topic</h3>)}></Route>
</div>
)
}
const BasicExample = ()=>(
<Router>
<div>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/about'>About</Link>
</li>
<li>
<Link to='/topics'>Topics</Link>
</li>
<hr/>
<Route path='/' component={Home}/>
<Route path='/about' component={About}/>
<Route path='/topics' component={Topics}/>
</ul>
</div>
</Router>
)
export default BasicExample

3 Router中的组件的props对象

一般情况下,我们的组件的props属性直接通过定义可以取到,如下所示,可以通过props获取name属性的值

1
2
3
4
5
6
function Welcome(props){
console.log(props)
return <h1>hello {props.name}</h1>
}
<Welcome name = 'Jhon'/>

当一个组件作为Route的component属性的值的时候,此时组件中的属性会多了location history match这三个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const ParamsExample = () => (
<Router>
<div>
<h2>Accounts</h2>
<ul>
<li><Link to="/netflix">Netflix</Link></li>
<li><Link to="/zillow-group">Zillow Group</Link></li>
<li><Link to="/yahoo">Yahoo</Link></li>
<li><Link to="/modus-create">Modus Create</Link></li>
</ul>
<Route path="/:id" component={Child}/>
</div>
</Router>
)
//改动官网的demo,便于理解
const Child = (props) => {
console.log(props);//此时props对象包括 {location ,match,history}
return (
<div>
<h3>ID: {props.match.params.id}</h3>
</div>
)
}
export default ParamsExample
1
2
3
4
5
6
7
//官方源代码如下,两者达到的效果是一样的
const Child = ({ match }) => (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
//这里用到了ES6中对象的解构赋值

3 withRouter Redict

对象的解构赋值,前面仅仅是一个标记,后面才是变量

1
2
3
4
5
<script>
var name = 'Jhon'
const {com:COM} = {com:name} ;
console.log(COM);//Jhon
</script>

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var withRouter = function withRouter(Component) {
var C = function C(props) {
var wrappedComponentRef = props.wrappedComponentRef,
remainingProps = _objectWithoutProperties(props, ['wrappedComponentRef']);
return _react2.default.createElement(_Route2.default, { render: function render(routeComponentProps) {
return _react2.default.createElement(Component, _extends({}, remainingProps, routeComponentProps, { ref: wrappedComponentRef }));
} });
};
C.displayName = 'withRouter(' + (Component.displayName || Component.name) + ')';
C.WrappedComponent = Component;
C.propTypes = {
wrappedComponentRef: _propTypes2.default.func
};
return (0, _hoistNonReactStatics2.default)(C, Component);
};
exports.default = withRouter;

学习了这么几天,我算是明白了.自己还是原来的自己,这么多年来一直没变.遇到问题总是刚正面,有时候确实很浪费时间和精力,其实有时候多看一些,多了解一些再去正面硬刚这些源码和问题,可能效率会更高.

react-routerAPI文档解析

官方文档

React createElement源码解读

发表于 2017-05-15 | 分类于 React

1 先看下大概的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var ReactElement = function(type, key, ref, self, source, owner, props) {
var element = {
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
// ...
return element;
};
ReactElement.createElement = function(type, config, children) {
// ...
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props
);
};

可以看出React.createElement函数返回的是一个对象,大概是这个形式

1
2
3
4
5
6
7
{
type,
key,
props: {
children
}
}

2 我们平时进行元素渲染的时候 ,一般都是这么写

1
2
3
4
5
6
7
8
React.render(
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);

其实 <div /> 这样用 尖括号包起来的部分可以看做是jsx的一个语法糖,他其实是封装了React.createElement方法,注意返回值是一个javascript对象

1
2
3
4
5
6
7
8
React.render(
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);

箭头函数中的运用 如果要返回一个对象,需要用 ( ) 将表达式包起来

1
2
3
4
5
6
7
const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)

如上代码 < > 包起来的jsx代码返回的是一个对象,所以该箭头函数返回的就是一个js对象

3 React.createElement函数的参数

1
ReactElement.createElement = function(type, config, children)

从源码来看,我们可以看出其接受三个参数,第一个是type类型,也就是我们这个组件是一个HTML元素,还是一个函数组件

1
2
3
4
5
6
7
console.log(<div />); //type是字符串 div
//-------------------------
function Welcome(props){
return <h1>hello {props.name}</h1>
}
var element = <Welcome name = 'Jhon'/>
console.log(element);//type 是function

React会根据type类型首字母判断是一个HTML元素还是一个组件函数,小写字母会被当成字符串,作为type类型,大写字母会被当成组件函数,进行递归渲染.

1
2
3
4
{
type: (string | ReactClass),
props: Object
}

4 ReactDOM.render函数

简单理解,该函数接受两个参数,一个是要渲染的javascript对象,一个是渲染后的DOM元素要放置的位置

今天上班时候在地铁上看的,来到公司简单总结下.

参考:前端早读课

React 概览学习笔记

发表于 2017-05-12 | 分类于 react

这是一些比较碎的知识点整理,不建议大家阅读,仅仅是自己的一些总结而已

1 组件的props属性

  • 可以通过扩展运算符展开进行写入组件,这种方式省去了我们要一个个写属性的麻烦
  • 属性键值相同的话,后面的会覆盖前面的
1
2
3
var props = { foo: 'default',bar:'baz' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'

2 组件的ref属性

  • 组件的ref属性可以用在DOM元素上,如果用在DOM元素上,那么该属性将会等于一个callback函数,该函数接受此DOM元素作为参数

  • When the ref attribute is used on a custom component declared as a class, the refcallback receives the mounted instance of the component as its argument

    如果组件的ref属性用在class声明的组件上,那么回调函数接受的参数是实例化的组件

  • 组件在装载或者卸载之后,ref指向的callback函数会立即执行,可以在组件中通过componentDidMount函数来进行判断

  • ​

React Redux Router学习路线

发表于 2017-05-12 | 分类于 redux

1 React Redux Router学习

首先英文得要过关,因为需要看英文文档 ;

第一步 react demo react概览

第二步 redux demo 源码实现

第三步 router demo API

学习方法推荐,基础最重要,源码很重要

  • github上搜索 分别单独搜索 react redux react-router react-redux react-redux -router
  • google搜索 说句实话,谷歌要比百度靠谱太多,google搜索都是干货,百度甩给你的可能是bug
  • 知乎搜索 react ,有一些话题以及问题会由比较好的链接

2 项目理解

react-redux-router

react-redux 推荐:一步步实现Redux

react-router

React和redux 通过react-redux结合

3 我的博客也有一些自己学习的总结,感兴趣也可以看下.

后期会补充,欢迎交流

从react到react-redux到react-redux-react-redux

发表于 2017-05-11 | 分类于 redux

前言,对于初学者来说,这个demo理解起来相对简单,只需要create-react-app ,然后 npm install react-redux npm install redux 即可.(react官方文档和redux官方文档)

本文主要理解redux在react中期的作用,以及react-redux如何将react和redux连接起来

Provider connect 源码解读

createStore源码

Provider connect2 源码解读

一个比较好的案例

1 react

通过setState改变state状态,触发ReactDOM.render函数,重新刷新UI组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React ,{Component,PropTypes} from 'react'
import ReactDOM from 'react-dom'
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
count: prevState.count+1
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.count}
</button>
);
}
}
console.dir(Toggle);
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

2 react-redux

通过给store注册render函数,每次dispatch的时候,都会触发render函数(dispatch函数执行的时候,会执行传入的reducer和绑定的所有的监听函数 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import React ,{Component,PropTypes} from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
const increaseAction = {type :'INCREASE'}
function reducer(state = {count: 0},action){
switch(action.type){
case 'INCREASE' :
return {count : state.count+1}
default :
return state
}
}
const store = createStore(reducer)
console.log(store);
console.log(store.getState());
//=========================如果没有这段代码,视图将不会更新,但是state状态确实是改变了的
function render(){
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
}
store.subscribe(render)
//============================================
class Toggle extends React.Component {
render() {
return (
<button onClick={()=>{store.dispatch(increaseAction);console.log(store.getState().count);
}}>
{store.getState().count}
</button>
);
}
}
console.dir(Toggle);
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

3 react redux react-redux

通过第二部分代码我们可以看出来,redux确实可以帮助我们管理代码,但是有一点不好的地方就是每次state的状态改变的时候,都需要重新手动刷新视图.

react-redux 提供两个函数,一个是Provider,该组件函数定义的时候,大概实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Provider extends Component {
getChildContext() {
return {
store : this.props.store
};
}
render() {
return this.props.children;
//表示渲染Privider组件的子元素
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
}

一个是connect函数 const WrapToggle = connect(mapStateToProps,mapDispatchToProps)(Toggle),高阶组件

该函数的作用是通过mapStateToProps和mapDispatchToProps函数,将将一些属性添加到Toggle组件的props上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import React ,{Component,PropTypes} from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import {Provider,connect} from 'react-redux'
const increaseAction = {type :'INCREASE'}
function reducer(state = {count: 0},action){
switch(action.type){
case 'INCREASE' :
return {count : state.count+1}
default :
return state
}
}
const store = createStore(reducer)
console.log(store);
console.log(store.getState());
class Toggle extends React.Component {
render() {
console.log(this.props);//connect函数中stateProps dispatchProps ownProps 三者融合后的结果传递给UI组件props对象
//对象的解构赋值
const {value,onIncreaseClick} = this.props
return (
<button onClick= {onIncreaseClick}>
{value}
</button>
);
}
}
const mapStateToProps = (state, ownProps) => {
console.log(state);
console.log(ownProps);
//state就是通过Provider传递进来的store.getState()的结果
//ownProps就是connect返回的新组件,这里是WrapToggle组件上的Props属性对象
return {
value: state.count
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
console.log(dispatch);
console.log(ownProps);
//dispatch就是 通过Provider传递进来的store.dispatch函数
//ownProps就是connect返回的新组件,这里是WrapToggle组件上的Props属性对象
return {
onIncreaseClick: () => {
dispatch(increaseAction)
}
}
}
const WrapToggle = connect(mapStateToProps,mapDispatchToProps)(Toggle)
console.dir(Toggle);
console.dir(WrapToggle);
//注意 WrapToggle组件添加了一个属性,方便一会输出对比,这些属性就是ownProps对象
ReactDOM.render(
<Provider store={store}>
<WrapToggle wrapProps='WarpToggleProps'/>
</Provider>,
document.getElementById('root')
);

4 react如何响应store的变化,也就是说何时重新渲染页面

4.1 单纯的react中,通过setState函数,改变state状态树,setState函数每次执行都会重新渲染UI视图

4.2 react搭配redux的时候,通过store链接,react的状态可以通过redux来进行管理,此时redux创建的store中存储了react中的state状态,此时如果想要更新UI视图,需要手动绑定事件,此时唯一改变state的函数是dispatch,通过该函数改变state,从createStore源码中可以看出来

  • 先执行reducer,改变state状态
  • 然后会执行通过subscribe注册的所有的监听事件

4.3 react搭配redux的时候, 通过react-redux进行react和redux的连接 ;

  • Provider函数作用:
    • 1)在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,而Provider组件定义的时候,render函数返回的是子组件,所以渲染的还是子组件
    • 2) 接收Redux的store作为props,通过context对象传递给子孙组件上的connect
  • 在connect内部,当state树发生变化的时候,最终会触发setState函数,所以会直接触发UI视图的重新渲染
    • 1)将store对象上的整个state状态树上的某个属性传递给被包裹的组件,这里组件是Toggle,传递的属性是value
    • 2)将store对象上的触发dispatch函数的onIncreaseClick传递给被包裹组件,这里是Toggle,传递的属性是onIncreaseClick
    • 3)connect函数会将这些属性一起合并到Toggle组件的属性props上
  1. 4 connect函数参数解析 connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)

    1)mapStateToProps(state, [ownProps]):函数: 每次state状态更新的时候都会调用该函数

    • 该函数在每次store中的state状态树更新的时候都会调用该函数,通过本案例点击按钮看到控制台输出可以深化理解
    • 该函数返回的对象必须是plain object
    • 默认为空,不会将state状态添加到component组件中
    1
    2
    3
    function mapStateToProps(state) {
    return { todos: state.todos };
    }

    2)mapDispatchToProps(dispatch, [ownProps]):函数:

    默认会将dispatch函数添加到component组件中props属性上.

    1
    2
    3
    4
    5
    6
    7
    function mapDispatchToProps(dispatch) {
    return {
    todoActions: bindActionCreators(todoActionCreators, dispatch),
    counterActions: bindActionCreators(counterActionCreators, dispatch)
    };
    }
    //这里面mapDispatchToProps函数接受的参数其实就是store.dispatch函数,bindActionCreators函数接受两个参数,一个是actionCreator,一个是dispatch函数

    3)mergeProps(stateProps, dispatchProps, ownProps):

    将mapStateToProps() 与 mapDispatchToProps()返回的对象和容器组件自身的props(本案例就是WrapToggle组件的props: wrapProps=’WarpToggleProps)合并成新的props并传入被包裹的组件(Toggle)。默认返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。

    这里也就解释了为什么Toggle组件中props属性中有stateProps, dispatchProps, ownProps 这些对象的组合结果了.

Redux Provider Connect

发表于 2017-05-11 | 分类于 redux

我是一个不信邪的人,所以无论如何我也要把这个搞明白

先来看下直接在React中使用Redux

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var store = createStore(reducer)
class App extends Component{
componentWillMount(){
store.subscribe((state)=>this.setState(state))
}
//以下在React组件中直接通过store使用Redux提供的状态管理
render(){
return <Comp state={this.state}
onIncrease={()=>store.dispatch(actions.increase())}
onDecrease={()=>store.dispatch(actions.decrease())}
/>
}
}

那么为什么还要使用react-redux呢?

React推崇的是单向数据流,自上而下进行数据的传递,但是由下而上或者不在一条数据流上的组件之间的通信就会变的复杂。解决通信问题的方法很多,如果只是父子级关系,父级可以将一个回调函数当作属性传递给子级,子级可以直接调用函数从而和父级通信。

组件层级嵌套到比较深,可以使用上下文getChildContext来传递信息,这样在不需要将函数一层层往下传,任何一层的子级都可以通过this.context直接访问。

兄弟关系的组件之间无法直接通信,它们只能利用同一层的上级作为中转站。而如果兄弟组件都是最高层的组件,为了能够让它们进行通信,必须在它们外层再套一层组件,这个外层的组件起着保存数据,传递信息的作用,这其实就是redux所做的事情。

组件之间的信息还可以通过全局事件来传递。不同页面可以通过参数传递数据,下个页面可以用location.param来获取。其实react本身很简单,难的在于如何优雅高效的实现组件之间数据的交流。

###1Provider核心源码

Provider是一个组件,它接受store作为props,然后通过context往下传,这样react中任何组件都可以通过context获取store。也就意味着我们可以在任何一个组件里利用dispatch(action)来触发reducer改变state,并用subscribe监听state的变化,然后用getState获取变化后的值。但是并不推荐这样做,它会让数据流变的混乱,过度的耦合也会影响组件的复用,维护起来也更麻烦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Provider = function (_Component) {
_inherits(Provider, _Component);
Provider.prototype.getChildContext = function getChildContext() {
return { store: this.store };
};
function Provider(props, context) {
_classCallCheck(this, Provider);
var _this = _possibleConstructorReturn(this, _Component.call(this, props, context));
//这行代码是我加的测试代码,在控制台输出的就是一个Provider对象
console.log(_this);
_this.store = props.store;
return _this;
}
Provider.prototype.render = function render() {
return _react.Children.only(this.props.children);
};
return Provider;
}(_react.Component);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Provider {props: Object, context: Object, refs: Object, updater: Object}
context:Object
props:Object
children:Object
$$typeof:Symbol(react.element)
key:null
props:Object
ref:null
type:function App()
_owner:null
_store:Object
_self:null
_source:Object
__proto__:Object
store:Object
__proto__:Object
refs:Object
state:null
store:Object
updater:Object
_reactInternalIns

使用如下:provider的主要作用就是将store作为props对象中的一个属性传递给Provider实例化的之后的对象

1
2
3
4
5
6
7
8
9
const store = createStore(reducer)
const App = () => {
return (
<Provider store={store}>
<FilterLink />
</Provider>
)
};

此时store是Provider对象的props中的一个属性之一.

=========

after four hours 我有点信邪了 , 毕竟我是才学这个的小白.

Provider 内的任何一个组件(比如这里的 FilterLink),如果需要使用 store 中的数据,就必须是「被 connect 过的」组件——使用 connect 方法对「你编写的组件(Link)」进行包装后的产物。

1
2
3
4
const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link)

2 connect函数 connect(mapStateToProps, mapDispatchToProps, mergeProps)

Connects a React component to a Redux store.

It does not modify the component class passed to it. Instead, it returns a new, connected component class, for you to use.

connect函数的主要作用是将React组件和Redux的store联系起来

往根本去说其实是将redux的store对象中的 state dispatch 这些属性添加(映射)到 react组件的props属性上

1
2
3
4
5
6
7
function Welcome(props){
console.log(props)
return <h1>hello {props.name}</h1>
}
<Welcome name = 'Jhon'/>
//一般情况下我们组件中传递props参数的时候,是通过这种方式传递的

而connect函数的作用就是向props组件中添加属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {
constructor(props, context) {
// 从祖先Component处获得store
this.store = props.store || context.store
this.stateProps = computeStateProps(this.store, props)
this.dispatchProps = computeDispatchProps(this.store, props)
this.state = { storeState: null }
// 对stateProps、dispatchProps、parentProps进行合并
this.updateState()
}
shouldComponentUpdate(nextProps, nextState) {
// 进行判断,当数据发生改变时,Component重新渲染
if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
this.updateState(nextProps)
return true
}
}
componentDidMount() {
// 改变Component的state
this.store.subscribe(() = {
this.setState({
storeState: this.store.getState()
})
})
}
render() {
// 生成包裹组件Connect
return (
<WrappedComponent {...this.nextState} />
)
}
}
Connect.contextTypes = {
store: storeShape
}
return Connect;
}
}

2.1 看一个组件的声明

Link.js 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from 'react'
import PropTypes from 'prop-types'
//这里面的{active,children,onClick} 其实就是props,只不过是以对象的直接形式出现的,
//另外直接进行了解构赋值操作
const Link = ({ active, children, onClick }) => {
if (active) {
return <span>{children}</span>
}
return (
<a href="#"
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}
//限定组件属性的数据类型
Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}
console.log('link组件',<Link/>);
export default Link

为了方便大家看懂bable转码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"use strict";
var Link = function Link(_ref) {
var active = _ref.active,
children = _ref.children,
_onClick = _ref.onClick;
if (active) {
return React.createElement(
"span",
null,
children
);
}
return React.createElement(
"a",
{ href: "#",
onClick: function onClick(e) {
e.preventDefault();
_onClick();
}
},
children
);
};

2.2 这个时候我们需要注意下{ active, children, onClick }来自何方?

FilterLink.js(container容器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react'
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'
const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilter
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter))
}
})
console.log('Link',<Link/>);
const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link)
console.log('FilterLink',<FilterLink/>);
export default FilterLink
  • mapStateToProps(state,ownProps) 这个函数的作用就是将store对象中的state状态赋值给组件的props属性
  • mapDispatchToProps(dispatch, ownProps)这个函数的作用就是将store对象中dispatch方法给到props属性

源码暂时没有告破

参考

webpack模块路径解析规则

发表于 2017-05-11 | 分类于 javascript

模块解析

##1 首先来看下webpack支持的模块类型

###模块(Modules)

EDIT DOCUMENT**

在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。

每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。

Node.js 从最一开始就支持模块化编程。然而,在 web,模块化的支持正缓慢到来。在 web 存在多种支持 JavaScript 模块化的工具,这些工具各有优势和限制。webpack 基于从这些系统获得的经验教训,并将模块的概念应用于项目中的任何文件。

###什么是 webpack 模块

对比 Node.js 模块,webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD define 和 require 语句
  • css/sass/less 文件中的 @import 语句。
  • 样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)

webpack 1 需要特定的 loader 来转换 ES 2015 import,然而通过 webpack 2 可以开箱即用。

支持的模块类型

webpack 通过 loader 可以支持各种语言和预处理器编写模块。loader 描述了 webpack 如何处理 非 JavaScript(non-JavaScript) 模块,并且在bundle中引入这些依赖。 webpack 社区已经为各种流行语言和语言处理器构建了 loader,包括:

  • CoffeeScript
  • TypeScript
  • ESNext (Babel)
  • Sass
  • Less
  • Stylus

总的来说,webpack 提供了可定制的、强大和丰富的 API,允许任何技术栈使用 webpack,保持了在你的开发、测试和生成流程中无侵入性(non-opinionated)。

2 webpack可以解析的文件路径

(import语句会执行所加载的模块,可以有下面的写法)

绝对路径

1
2
import "/home/me/file";
import "C:\\Users\\me\\file";

由于我们已经取得文件的绝对路径,因此不需要进一步再做解析。

也就是说不需要webpack进行路径的解析.

相对路径

1
2
import "../src/file1";
import "./file2";

在这种情况下,使用 import 或 require 的资源文件(resource file)所在的目录被认为是上下文目录(context directory)。在 import/require 中给定的相对路径,会添加此上下文路径(context path),以产生模块的绝对路径(absolute path)。

也就是说webpack解析该文件的时候,会以资源文件所在目录为基准进行解析;

模块路径

1
2
import "module";
import "module/lib/file";
  • 模块将在 resolve.modules 中指定的所有目录内搜索。 也就是说对于模块路径,解析的时候会在node_modules目录下进行解析

告诉 webpack 解析模块时应该搜索的目录。

绝对路径和相对路径都能使用,但是要知道它们之间有一点差异。

通过查看当前目录以及祖先路径(即 ./node_modules, ../node_modules 等等),相对路径将类似于 Node 查找 ‘node_modules’ 的方式进行查找。

使用绝对路径,将只在给定目录中搜索。

resolve.modules defaults to:

1
modules: ["node_modules"]

如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索:

1
modules: [path.resolve(__dirname, "src"), "node_modules"]
  • 你可以替换初始模块路径,此替换路径通过使用 resolve.alias 配置选项来创建一个别名。 此时在解析模块路径的时候,就不会再去node_modules里面查找对应的模块,而是通过我们配置的路径进行查找

比如App.js中

1
2
import store, { history } from 'STORE'
import routes from 'ROUTE'

from后面就是要解析的路径,此处的路径是模块路径;

webpack.config.js中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var path = require('path'),
webpack = require('webpack'),
NyanProgressPlugin = require('nyan-progress-webpack-plugin');
var rootPath = path.resolve(__dirname, '..'), // 项目根目录,__diename永远获取的是当前文件相对于磁盘根目录 react-demo/ 目录下
src = path.join(rootPath, 'src'), // 开发源码目录
env = process.env.NODE_ENV.trim(); // 当前环境
var commonPath = {
rootPath: rootPath,
dist: path.join(rootPath, 'dist'), // build 后输出目录
indexHTML: path.join(src, 'index.html'), // 入口基页
staticDir: path.join(rootPath, 'static') // 无需处理的静态资源目录
};
module.exports = {
commonPath: commonPath,
entry: {
app: path.join(src, 'app.js'),
// ================================
// 框架 / 类库 分离打包
// ================================
vendor: [
'history',
'lodash',
'react',
'react-dom',
'react-redux',
'react-router',
'react-router-redux',
'redux',
'redux-thunk'
]
},
output: {
path: path.join(commonPath.dist, 'static'),
publicPath: '/static/'
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
// ================================
// 自定义路径别名
// ================================
STORE: path.join(src, 'redux/store'),
ROUTE: path.join(src, 'routes'),//对应src/routers文件夹
}
},
};
resolveLoader: {
root: path.join(rootPath, 'node_modules')
},

3一旦根据上述规则解析路径之后,解析器将检查路径是否指向文件或者目录

  • 文件 : 如果以上提到的路径(相对路径,模块路径,绝对路径),指向的是一个文件

    • 如果文件具有文件扩展名,则将文件直接打包
    • 否则将使用[resolve.extensions]作为文件扩展名来解析,此选项告诉解析器在解析的时候能够接受那些扩展名
  • 文件夹 : 如果路径指向的是一个文件夹

    • 如果文件夹中包含 package.json 文件,则按照顺序查找 resolve.mainFields 配置选项中指定的字段。并且 package.json 中的第一个这样的字段确定文件路径。

    • If there is no package.json or if the main fields do not return a valid path, file names specified in the resolve.mainFiles configuration option are looked for in order, to see if a matching filename exists in the imported/required directory .

      如果目录中没有package.json文件,那么将会根据reslove(解析对象)中的默认配置resolve.mainFiles中的配置文件名去该目录中解析配置的文件名,默认配置如下 .

      1
      mainFiles: ["index"]

      也就是说会import index文件,该文件的导出的对象.

    • The file extension is then resolved in a similar way using the resolve.extensions option.

4 loaders的解析规则

loader解析遵循与文件解析器指定的规则相同.

但是resolveloader配置选项可以用来为Loader提供独立的解析规则

1
2
3
4
5
resolveloader : {
modules: ["node_modules"],
extensions: [".js", ".json"],
mainFields: ["loader", "main"]
}

5 从缓存中解析模块

Every filesystem access is cached, so that multiple parallel or serial requests to the same file occur faster. In watch mode, only modified files are evicted from the cache. If watch mode is off, then the cache gets purged before every compilation.

每一个文件都会被缓存,所以对于同一文件的并行的请求将可以更快的请求到模块,在观察者模式下,只用改变了的文件将会从缓存中抽出,如果观察者模式关闭,那么每次编译之前都会清理缓存.

React概览

发表于 2017-05-09 | 分类于 javascript

1 React中有什么顶层API

1
2
3
4
5
6
<body>
<script src='../build/react-0.13.4.js'></script>
<script>
console.log(React);
</script>
</body>

控制台输出就是如下React对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var React = {
Children: {
map: ReactChildren.map,
forEach: ReactChildren.forEach,
count: ReactChildren.count,
only: onlyChild
},
Component: ReactComponent,
DOM: ReactDOM,
PropTypes: ReactPropTypes,
initializeTouchEvents: function(shouldUseTouch) {
EventPluginUtils.useTouchEvents = shouldUseTouch;
},
createClass: ReactClass.createClass,
createElement: createElement,
cloneElement: cloneElement,
createFactory: createFactory,
createMixin: function(mixin) {
// Currently a noop. Will be used to validate and trace mixins.
return mixin;
},
constructAndRenderComponent: ReactMount.constructAndRenderComponent,
constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID,
findDOMNode: findDOMNode,
render: render,
renderToString: ReactServerRendering.renderToString,
renderToStaticMarkup: ReactServerRendering.renderToStaticMarkup,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
isValidElement: ReactElement.isValidElement,
withContext: ReactContext.withContext,
// Hook for JSX spread, don't use this for anything else.
__spread: assign
};

顶层API的一般用法

createElement( )

1
2
3
4
5
React.createElement(
type,
[props],
[...children]
)

平常我们用 jsx 写的<Hello /> 其实就是对CreateElement函数的封装

1
2
3
4
5
6
7
8
9
10
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);

等价于

1
2
3
4
5
6
7
8
9
10
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);

React.Component( )

1
2
3
4
5
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

2 ReactDOM

ReactDOM.render()

1
2
3
4
5
ReactDOM.render(
element,
container,
[callback]
)
  • 该方法将在我们提供的container中渲染一个React元素
  • 如果可选择的callback被提供了,那么在组件渲染完毕之后,就可以执行callback函数
  • React将会替换我们提供的container中的所有元素为ReactElement(如果原来的容器有子元素的话)

unmountComponentAtNode( )

1
ReactDOM.unmountComponentAtNode(container)

Remove a mounted React component from the DOM and clean up its event handlers and state. If no component was mounted in the container, calling this function does nothing. Returns true if a component was unmounted and false if there was no component to unmount.

findDOMNode( )

1
ReactDOM.findDOMNode(component)

If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements. In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all. When render returns null or false, findDOMNode returns null.

React-Component

发表于 2017-05-09 | 分类于 javascript

React创建组件的三种方式

发表于 2017-05-09 | 分类于 javascript

1 React.createClass( )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="root"></div>
<script type='text/babel'>
var HelloWorld = React.createClass({ render : function(){ return
<h1>hello {this.props.name1}
<p>hello {this.props.name2}</p>
</h1>
} }) ;
ReactDOM.render(
<HelloWorld name1='Jhon' name2="JiM" />,
document.getElementById('root')
)
</script>
</body>

2 React.Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="root"></div>
<script type='text/babel'>
class Welcome extends React.Component {
render(){
return <h1>hello {this.props.name}</h1>
}
}
const element = <Welcome name = 'JiM'/>
ReactDOM.render(
element,
document.getElementById('root')
)
</script>

3 function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const Repo = ()=>(<div>this is Repo</div>)
const Category = (props)=>{
console.log(props);
return (<div>this is category</div>)
}
const MyTest =()=>(
<Router>
<div>
<ul>
<li>
<Link to='/about'>About</Link>
</li>
<li>
<Link to='./repo'>Repo</Link>
</li>
<li>
<Link to='./category'>Category</Link>
</li>
</ul>
<Route exact path='/about' render={(props)=>{console.log(props);return (<div>this is aabout</div>)
}}></Route>
<Route exact path='/repo' component={Repo}> </Route>
<Route exact path='/category' component={Category}> </Route>
<Route children={(props)=>{console.log(props);return (<div>this is a component build througth children</div>)
}}></Route>
</div>
</Router>
)
export default MyTest

ES6一般写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)
123…20
JiM-W

JiM-W

keep fighting!

195 日志
52 分类
90 标签
© 2017 JiM-W
由 Hexo 强力驱动
主题 - NexT.Pisces