2019-06-07 15:44:55 qq_35676160 阅读数 63
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57468 人正在学习 去看看 李宁

React 也并非只能用于开发单页面应用。

React Router 安装

这里使用的 React Router 的大版本号是 v4, 这也是目前最新版本。

React Router 包含3个库, react-router、react-router-dom、和 react-router-native。

react-router 提供最基本的路由功能,实际使用,我们不会直接安装 react-router,而是根据应用运行的环境选择安装:

react-router-dom(在浏览器中使用),react-router-native(在 react-native中使用)。

react-router-dom 和 react-router-native 都依赖 react-router,所以在安装时, react-router 也会自动安装。 创建 Web应用,使用

npm install react-router-dom

创建 navtive 应用,使用

npm install react-router-native

路由器

React Router 通过 Router 和 Route 两个组件完成路由功能。

Router 可以理解成路由器,一个应用中需要一个 Router 实例,所有跌幅配置组件 Route 都定义为 Router 的子组件。

在 Web应用中,我们一般会使用对 Router 进行包装的 BrowserRouter 或 HashRouter 两个组件 。

BrowserRouter使用 HTML5 的 history API(pushState、replaceState等)实现应用的 UI 和 URL 的同步。

HashRouter 使用 URL 的 hash 实现应用的 UI 和 URL 同步。

BrowserRouter 创建的 URL 形式如下:

http://example.com/some/path

HashRouter 创建的 URL 形式如下:

http://example.com/#/some/path

使用 BrowserRouter 时,一般还需要对服务器进行配置,让服务器能正确地处理所有可能的URL。

例如,当浏览器发生 example.com/some/pathexample.com/some/path2 两个请求时,服务器需要能返回正确的 HTML 页面(也就是单页面应用中唯一的 HTML 页面)

HashRouter 则不存在这个问题,因为 hash 部分的内容会被服务器自动忽略,真正有效的信息是 hash 前端的部分,而对于单页应用来说,这部分是固定的。

Router 会创建一个 history 对象,history 用来跟踪 URL, 当URL 发生变化时, Router,的后代组件会重新渲染。React Router 中提供的其他组件可以通过 context 获取 history 对象,这也隐含说明了 React Router 中其他组件必须作为 Router 组件后代使用。但 Router 中只能唯一的一个子元素,例如:

路由器组件参数配置

Route 是 React Router中用于配置路由信息的组件,也是 React Router 中使用频率最高的组件。

每当有一个组件需要根据 URL 决定是否渲染时,就需要创建一个 Route。

1) path

每个 Route 都需要定义一个 path 属性。

当使用 BrowserRouter 时,path 用来描述这个Router匹配的 URL 的pathname;

当使用 HashRouter时,path 用来描述这个 Route 匹配的 URL 的 hash。

例如,使用 BrowserRouter 时,<Route path=''foo' /> 会匹配一个 pathname 以 foo 开始的 URL (如: example.com/foo)。

URL 匹配一个 Route 时,这个 Route 中定义的组件就会被渲染出来。

2)match

当 URL 和 Route匹配时,Route 会创建一个 match 对象作为 props 中的一个 属性传递给被渲染的组件。这个对象包含以下4个属性。

  1. params: Route的 path 可以包含参数,例如 <Route path="/foo/:id"> 包含一个参数 id。params就是用于从匹配的 URL 中解析出 path 中的参数。例如,

当 URL = 'example.ocm/foo/1' 时,params= {id: 1}。

  1. isExact: 是一个布尔值,当 URL 完全匹时,值为 true; 当 URL 部分匹配时,值为 false.例如,当 path='/foo'、URL="example.com/foo" 时,是完全匹配;

当 URL="example.com/foo/1" 时,是部分匹配。

  1. path: Route 的 path 属性,构建嵌套路由时会使用到。
  2. url: URL 的匹配的方式

3)Route 渲染组件的方式

(1)component

component 的值是一个组件,当 URL 和 Route 匹配时,Component属性定义的组件就会被渲染。例如:

<Route path='/foo' component={Foo} >

当 URL = "example.com/foo" 时,Foo组件会被渲染。

(2) render render 的值是一个函数,这个函数返回一个 React 元素。这种方式方便地为待渲染的组件传递额外的属性。例如:

<Route path='/foo' render={(props) => { <Foo {...props} data={extraProps} /> }}> </Route>

Foo 组件接收了一个额外的 data 属性。

(3)children children 的值也是一个函数,函数返回要渲染的 React 元素。

与前两种方式不同之处是,无论是否匹配成功, children 返回的组件都会被渲染。但是,当匹配不成功时,match 属性为 null。例如:

<Route path='/foo' render={(props) => { <div className={props.match ? 'active': ''}> <Foo {...props} data={extraProps} /> </div> }}> </Route>

如果 Route 匹配当前 URL,待渲染元素的根节点 div 的 class 将设置成 active.

4)Switch 和 exact

当URL 和多个 Route 匹配时,这些 Route 都会执行渲染操作。

如果只想让第一个匹配的 Route 沉浸,那么可以把这些 Route 包到一个 Switch 组件中。

如果想让 URL 和 Route 完全匹配时,Route才渲染,那么可以使用 Route 的 exact 属性。

Switch 和 exact 常常联合使用,用于应用首页的导航。例如:

<Router> <Switch> <Route exact path='/' component={Home}/> <Route exact path='/posts' component={Posts} /> <Route exact path='/:user' component={User} /> </Swith>

</Router>

如果不使用 Switch,当 URL 的 pathname 为 "/posts" 时,<Route path='/posts' /> 和 <Route path=':user' /> 都会被匹配,

但显然我们并不希望 <Route path=':user' /> 被匹配,实际上也没有用户名为 posts 的用户。如果不使用 exact, "/" "/posts" "/user1"等

几乎所有 URL 都会匹配第一个 Route,又因为Switch 的存在,后面的两个 Route永远不会被匹配。使用 exact,保证 只有当 URL 的 pathname 为 '/'时,第一个Route才会匹配。

5)嵌套路由

嵌套路由是指在Route 渲染的组件内部定义新的 Route。例如,在上一个例子中,在 Posts 组件内再定义两个 Route:

const Posts = ({match}) => { return ( <div> {/* 这里 match.url 等于 /posts */} <Route path={`${match.url}/:id`} component={PostDetail} /> <Route exact path={match.url} component={PostList} /> </div> ) }

链接

Link 是 React Router提供的链接组件,一个 Link 组件定义了当点击该 Link 时,页面应该如何路由。例如:

Link 使用 to 属性声明要导航到的URL地址。to 可以是 string 或 object 类型,当 to 为 object 类型时,可以包含 pathname、search、hash、state 四个属性,例如:

除了使用Link外,我们还可以使用 history 对象手动实现导航。history 中最常用的两个方法是 push(path,[state]) 和 replace(path,[state]),push会向浏览器记录中新增一条记录,replace 会用新记录替换记录。例如:

 

2019-04-30 09:40:51 obf13 阅读数 327
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57468 人正在学习 去看看 李宁

参考文档: react router 文档

import React from "react"
import { withRouter } from "react-router-dom"

class ShowTheLocation extends React.Component {

  toOtherRoute = () => {
    const urlObj = {
      pathname: 'url/url',
      state: {
        data: 'data'
      }
    }
    this.props.history.push(urlObj)
  }

  render() {
    return (
      <div>
        <div>hello word</div>
        <div onClick={ this.toOtherRoute }>跳转路由</div>
      </div>
    )
  }

}
ShowTheLocation = withRouter(ShowTheLocation)

在跳转过去的组件接收参数:

const { data } = this.props.location.state

总结: 多看官方文档 QAQ

2018-01-10 22:18:51 Mr_Dave 阅读数 996
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57468 人正在学习 去看看 李宁

一、React-Router环境搭建
1、react 环境搭建
参考:我的上一篇博文
2、导入react router依赖包
npm install react-router-dom - -save-dev
二、React-Router基础用例
1文件简介
public
bundle.js —-打包生成文件(输出文件)
index.html—-入口布局文件
component—组件
About.js—-about组件
App.js—app组件
Repos—repos组件
index.js—-入口文件
index.html—-输出布局文件(网页入口)
如图
2、源代码
**public
index.html**

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <div id="app"></div>
  <script type="text/babel" src = "../src/index.js"></script>
  <script src="bundle.js"></script>
</body>
</html>

about.js

import React from 'react'
class About extends React.Component{
  render() {
    return <div>This is About page</div>
  }
}
export default About;

App.js

import React from 'react'
class App extends React.Component{
  render() {
    return (
      <div>
        This is App page; 
      </div>
    )
  }
}
export default App;

Repos.js

import React from 'react'

class Repos extends React.Component{
  render() {
    return <div>Thi is Repos page</div>
  }
}
export default Repos;

index.js

import React from 'react'
import {render} from 'react-dom'
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
import App from './component/App'
import About from './component/About'
import Repos from './component/Repos'

render((
    <Router>
        <ul>
            <li><Link to="/">App</Link></li>
            <li><Link to="/repos">Repos</Link></li>
            <li><Link to="/about">About</Link></li>
            <Route exact path="/" component={App}/>
            <Route path="/repos" component={Repos}/>
            <Route path="/about" component={About}/>
        </ul>
    </Router>
), document.getElementById('app'))

3、打包编译
(1)webpack -p // 打包
(2) webpack-dev-server //开启服务器

三、组件嵌套
1、App.js组件中嵌套其他组件
其他组件不修改

import React from 'react'
import {Link} from 'react-router-dom'

class App extends React.Component{
  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><Link to="/about">About</Link></li>
          <li><Link to="/repos">Repos</Link></li>
        </ul>
      </div>
    )
  }
}
export default App;

2、打包运行
webpack -p
webpack-dev-server
四、组件作为参数传递
1、代码文件结构
截图
2、源代码
(1)、About.js

import React from 'react'

class About extends React.Component{
  render() {
    return <div>About</div>
  }
}
export default About;

(2)、App.js

import React from 'react'
import {Link} from 'react-router-dom'
import NavLink from "./NavLink"

class App extends React.Component{
    render() {
        return (
            <div>
                <h1>React App</h1>
                <ul role="nav">
                    <li><NavLink to="/about">About</NavLink></li>
                    <li><NavLink to="/repos">Repos</NavLink></li>
                </ul>
                {this.props.children}{/*获取父组件所有子属性*/}
            </div>
        )
    }
}
export default App;

(3)、NavLink.js

import React from 'react'
import {Link} from 'react-router-dom'

class NavLink extends React.Component{
  render() {
    return <Link {...this.props} activeClassName="active"/>//父组件属性传递
  }
}
export default NavLink;

(4)、Repos.js

import React from 'react'

class Repos extends React.Component{
    render() {
        return <div>Repos</div>
    }
}
export default Repos;

(5)、index.js

import React from 'react'
import {render} from 'react-dom'
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
import App from './component/App'
import About from './component/About'
import Repos from './component/Repos'

render((
    <Router>
        <ul>
            <li><Link to="/">App</Link></li>
            <li><Link to="/repos">Repos</Link></li>
            <li><Link to="/about">About</Link></li>
            <Route exact path="/" component={App}/>
            <Route path="/repos" component={Repos}/>
            <Route path="/about" component={About}/>
        </ul>
    </Router>
), document.getElementById('app'))

(6)运行
webpack -p
webpack-dev-server
五、多层嵌套
1、简介
将Repo组件做为Repos的子组件嵌套,点击Repos链接跳转到Repo中,并将标签名作为参数传递给子组件Repo。
2、源代码
(1)、App.js

import React from 'react'
import NavLink from "./NavLink"

class App extends React.Component{
    render() {
        return (
            <div>
                <h1>React App</h1>
                <ul role="nav">
                    <li><NavLink to="/about">About</NavLink></li>
                    <li><NavLink to="/repos">Repos</NavLink></li>
                </ul>
                {this.props.children}{/*获取父组件所有子属性*/}
            </div>
        )
    }
}
export default App;

(2)、NavLink.js

import React from 'react'
import {Link} from 'react-router-dom'

class NavLink extends React.Component{
  render() {
    return <Link {...this.props} className="active"/>//父组件属性传递
  }
}
export default NavLink;

(3)、Repo.js

import React from 'react'

class Repo extends React.Component{
    render() {
        return (
            <div>
                <h2>{this.props.match.params.repoName}</h2>
            </div>
        )
    }
}
export  default Repo;

(4)、Repos.js

import React from 'react'
import {Link} from 'react-router-dom'
class Repos extends React.Component{
    render() {
        return(
            <div>
                <h2>Repos</h2>
                <ul>
                    <li><Link to='/repos/reactjs/react-router'>React Router</Link></li>
                    <li><Link to="/repos/facebook/react">React</Link></li>
                </ul>
            </div>
        )
    }
}
export default Repos;

(5)、About.js

import React from 'react'

class About extends React.Component{
  render() {
    return <div>About</div>
  }
}
export default About;

(6)、index.js

import React from 'react'
import {render} from 'react-dom'
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
import App from './component/App'
import About from './component/About'
import Repos from './component/Repos'
import Repo from './component/Repo'

render((
    <Router>
        <ul>
            <li><Link to="/">App</Link></li>
            <li><Link to="/repos">Repos</Link></li>
            <li><Link to="/repos/:userName/:repoName">Repo</Link></li>
            <li><Link to="/about">About</Link></li>
            <Route exact path="/" component={App}/>
            <Route path="/repos" component={Repos}/>
            <Route path="/repos/:userName/:repoName" component={Repo}/>
            <Route path="/about" component={About}/>
        </ul>
    </Router>
), document.getElementById('app'))

6、通过输入框搜索加载组件
1、源代码
(1)、About.js

import React from 'react'
class About extends React.Component{
    render() {
        return <div>About</div>
    }
}
export default About;

(2).App.js

import React from 'react';  //引入react组件
import  NavLink from './NavLink'
 class App extends React.Component{
    render() {
        return (
            <div>
                <h1>React Router</h1>
                <ul>
                    <li><NavLink to="/about">About</NavLink></li>
                    <li><NavLink to="/repos">Repos</NavLink></li>
                     <Route path="/repos" component={Repos}/>
                     <Route path="/repos/:userName/:repoName" component={Repo}/>
                     <Route path="/about" component={About}/>
                </ul>
                {this.props.children}
            </div>
        )
    }
}
export default App;

(3)NavLink.js

import React from 'react'
import { Link } from 'react-router-dom'

class NavLink extends React.Component{
  render() {
    return <Link {...this.props} className="active"/>
  }
}
export default NavLink;

(4).Repo.js

import React from 'react'

class Repo extends React.Component{
  render() {
      const { userName, repoName } = this.props.match.params
      return (
          <div>
              <h2>{userName} / {repoName}</h2>
          </div>
      )
  }
}
export default Repo;

(5).Repos.js

import React from 'react'
import {withRouter} from 'react-router-dom';//按需加载组件,v4.0新功能
import NavLink from './NavLink'
class Repos extends React.Component {
    constructor(props,context){
        super(props,context);
        this.context.router;
        console.log("context",this.context)
    };
    handleSubmit(event) {
        event.preventDefault();
        const userName=event.target.elements[0].value;
        const repo=event.target.elements[1].value;
        const path=`/repos/${userName}/${repo}`;
        this.props.history.push(path);//contextTypes已经过期
    }

    render() {
        return (
            <div>
                <h2>Repos</h2>
                <ul>
                    <li><NavLink to="/repos/reactjs/react-router">React Router</NavLink></li>
                    <li><NavLink to="/repos/facebook/react">React</NavLink></li>
                    <li>
                        <form onSubmit={this.handleSubmit.bind(this)}>
                            <input type="text" placeholder='userName'/>{''}
                            <input type="text" placeholder='repo'/>{''}
                            <button type="submit">Go</button>
                        </form>
                    </li>
                </ul>
                {this.props.children}
            </div>
        )
    };
}
export default withRouter(Repos);//withRouter包装任何自定义组件,将react-router 的 history,location,match 三个对象传入

(6).index.js

import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import About from './component/About'
import App from './component/App'
import Repos from './component/Repos'
import Repo from './component/Repo'
render((
    <Router>
        <div>
            <ul>
                <li><Link to="/about">About</Link></li>
                <li><Link to="/repos">Repos</Link></li>
                <li><Link to="/repos/:userName/:repoName">Repo</Link></li>
            </ul>
            <hr/>
            <Route path="/" component={App}/>
            <Route path="/repos" component={Repos}/>
            <Route path="/about" component={About}/>
            <Route path="/repos/:userName/:repoName" component={Repo}/>
        </div>
    </Router>
), document.getElementById('app'))
2019-07-15 09:39:00 weixin_42307490 阅读数 120
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57468 人正在学习 去看看 李宁

React Router

React Router现在的版本是5, 于2019年3月21日搞笑的发布,搞笑的官网链接, 本来是要发布4.4的版本的,结果成了5。从4开始,使用方式相对于之前版本的思想有所不同。之前版本的思想是传统的思想:路由应该统一在一处渲染, Router 4之后是这样的思想:一切皆组件

React Router包含了四个包:

包名 Description
react-router React Router核心api
react-router-dom React Router的DOM绑定,在浏览器中运行不需要额外安装react-router
react-router-native React Native 中使用,而实际的应用中,其实不会使用这个。
react-router-config 静态路由的配置

主要使用react-router-dom

使用方式

正常情况下,直接按照官网的demo就理解 路由的使用方式,有几个点需要特别的强调:

  • Route组件的exact属性

exact属性标识是否为严格匹配, 为true是表示严格匹配,为false时为正常匹配。

  • Route组件的render属性而不是component属性

怎么在渲染组件的时候,对组件传递属性呢?使用component的方式是不能直接在组件上添加属性的。所以,React Router的Route组件提供了另一种渲染组件的方式 render, 这个常用于页面组件级别的权限管理。

  • 路由的参数传递与获取

  • Switch组件

总是渲染第一个匹配到的组件

  • 处理404与默认页

  • withRoute高阶组件的使用

  • 管理一个项目路由的方法

  • code spliting

  • HashRouter和BrowserRouter的区别,前端路由和后端路由的区别。这个在Vue里应该有讲过了。

React Router基本原理

React Router甚至大部分的前端路由都是依赖于history.js的,它是一个独立的第三方js库。可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。

  • 老浏览器的history: 通过hash来存储在不同状态下的history信息,对应createHashHistory,通过检测location.hash的值的变化,使用location.replace方法来实现url跳转。通过注册监听window对象上的hashChange事件来监听路由的变化,实现历史记录的回退。
  • 高版本浏览器: 利用HTML5里面的history,对应createBrowserHistory, 使用包括pushStatereplaceState方法来进行跳转。通过注册监听window对象上的popstate事件来监听路由的变化,实现历史记录的回退。
  • node环境下: 在内存中进行历史记录的存储,对应createMemoryHistory。直接在内存里pushpop状态。

Immutable.js

JavaScript数据修改的问题

看一段大家熟悉的代码

const state = {
  str: 'xxxx',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state

console.log(newState === state) // true

由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存地址的, 所以结果是newState和state是相等的。

尝试修改一下数据

const state = {
  str: 'XXXX',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state

newState.str = 'XXXXX'

console.log(state.str, newState.str)

可以看到,newState的修改也会引起state的修改。要解决这个问题,js中提供了另一种修改数据的方式,要修改一个数据之前先制作一份数据的拷贝,像这样

const state = {
  str: 'XXXX',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)

newState.str = 'XXXXX'

console.log(state.str, newState.str)

我们可以使用很多方式在js中复制数据,比如, Object.assign, Object.freeze, slice, concat, map, filter, reduce等方式进行复制,但这些都是浅拷贝,就是只拷贝第一层数据,更深层的数据还是同一个引用,比如:

const state = {
  str: 'XXXX',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)

newState.obj.y = 2
newState.arr.push(4)

console.log(state, newState)

可以看到,当在更改newState更深层次的数据的时候,还是会影响到state的值。如果要深层复制,就得一层一层的做递归拷贝,这是一个复杂的问题。虽然有些第三方的库已经帮我们做好了,比如lodashcloneDeep方法。深拷贝是非常消耗性能的。

import { cloneDeep } from 'lodash'

const state = {
  str: 'XXXX',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = cloneDeep(state)

newState.obj.y = 2
newState.arr.push(4)

console.log(state, newState)

什么是不可变数据

不可变数据 (Immutable Data )就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是持久化数据结构( Persistent Data Structure),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的s性能损耗,Immutable 使用了 结构共享(Structural Sharing),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

[外链图片转存失败(img-iVVqYzcP-1563154648197)(./images/structure-sharing.png)]

immutable.js的优缺点

优点:

  • 降低immutable带来的复杂度
  • 节省内存
  • 历史追溯性(时间旅行):时间旅行指的是,每时每刻的值都被保留了,想回退到哪一步只要简单的将数据取出就行,想一下如果现在页面有个撤销的操作,撤销前的数据被保留了,只需要取出就行,这个特性在redux或者flux中特别有用
  • 拥抱函数式编程:immutable本来就是函数式编程的概念,纯函数式编程的特点就是,只要输入一致,输出必然一致,相比于面向对象,这样开发组件和调试更方便。推荐一本函数式编程的在线免费书《JS 函数式编程指南》, 此书可以推荐给学生做为课外补充阅读。

缺点:

  • 需要重新学习api
  • 资源包大小增加(源码5000行左右)
  • 容易与原生对象混淆:由于api与原生不同,混用的话容易出错。

使用Immutable.js

参考官网重点讲解数据不可变数据的创建、更新及比较方式 。对于就业班来说,掌握以下知识点即可。

Map

import { Map } from 'immutable'

const map = Map({
  a: 1,
  b: 2,
  c: 3
})

const newMap = map.set('b', 20) // immutable数据每次都是生成新的再重新调用set进行修改,所以需要 重新赋值给一个新的变量

console.log(map, newMap) // immutable.Map不是原生的对象
console.log(map.b, newMap.b) // immutable.Map不是原生的对象, 所以是undefined
console.log(map.get('b'), newMap.get('b')) // 要取值,需要调用get(key)方法,可以看到,两个值不一样

const obj = {
  a: 1,
  b: 2,
  c: 3
}

console.log(Map.isMap(map), Map.isMap(obj)) // true false, 使用Map.isMap来判断是否是一个immutable.Map类型

List

import { List } from 'immutable'

const list = List([1, 2, 3, 4])
const newList = list.push(5)
console.log(list, newList)
console.log(list[4], newList[4]) // undefined undefined
console.log(list.get(4), newList.get(4)) // undefined 5
console.log(list.size, newList.size) // 4 5

const arr = [1, 2, 3, 4]

console.log(List.isList(list), List.isList(arr)) // true false

equals & is

import { Map, is } from 'immutable'

const map = Map({
  a: 1,
  b: 2,
  c: 3
})

const anotherMap = Map({
  a: 1,
  b: 2,
  c: 3
})

console.log(map == anotherMap) // false
console.log(map === anotherMap) // false
console.log(map.equals(anotherMap)) // 使用equals进行比较 true
console.log(is(map, anotherMap)) // 使用is进行比较 true

List常用api

import { List } from 'immutable'

const list = List([1, 2, 3, 4])
const list1 = list.push(5)
const list2 = list1.unshift(0)
const list3 = list.concat(list1, list2)
const list4 = list.map(v => v * 2)

console.log(list.size, list1.size, list2.size, list3.size, list4.toJS()) // 4 5 6 15, [2, 4, 6, 8]

Map常用api

import { Map } from 'immutable'

const alpha = Map({
  a: 1,
  b: 2,
  c: 3
})
const objKeys = alpha.map((v, k) => k)
console.log(objKeys.join()) // a, b, c


const map1 = Map({
  a: 1,
  b: 2
})
const map2 = Map({
  c: 3,
  d: 4
})
const obj = {
  d: 400,
  e: 50
}

const mergedMap = map1.merge(map2, obj)

console.log(mergedMap.toObject())
console.log(mergedMap.toJS())

嵌套数据结构

const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });

const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }

console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6

const nested3 = nested2.updateIn([ 'a', 'b', 'd' ], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }

const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }

##在redux中使用immutable.js

redux官网推荐使用redux-immutable进行redux和immutable的集成。几个注意点:

redux中,利用combineReducers来合并多个reduce, redux自带的combineReducers只支持原生js形式的,所以需要使用redux-immutable提供的combineReducers来代替

// 使用redux-immutable提供的combineReducers方法替换redux里的combineReducers
import {combineReducers} from 'redux-immutable'
import reducerOne from './reducerOne'
import reducerTwo from './reducerTwo'
 
const rootReducer = combineReducers({
    reducerOne,
    reducerTwo
});
 
export default rootReducer;

reducer中的initialState也需要初始化成immutable类型, 比如一个counter的reducer

import { Map } from 'immutable'

import ActionTypes from '../actions'

const initialState = Map({
  count: 0
})

export default (state = initialState, action) => {
  switch (action.type) {
    case ActionTypes.INCREAMENT:
      return state.set('count', state.get('count') + 1) // 使用set或setIn来更改值, get或者getIn来取值
    case ActionTypes.DECREAMENT:
      return state.set('count', state.get('count') - 1)
    default:
      return state
  }
}

state成为了immutable类型,connectmapStateToProp也需要相应的改变

const mapStateToProps = state => ({
  count: state.getIn(['counter', 'count']) // 永远不要在mapStateToProps里使用`toJS`方法,因为它永远返回一个新的对象
})

shouldComponentUpdate里就可以使用immutable.is或者instance.equals来进行数据的对比了。

2018-08-01 20:12:02 kangzai2012 阅读数 122
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    57468 人正在学习 去看看 李宁

React Router4是一个流行的纯React重写的包。现在的版本中已不需要路由配置,现在一切皆组件。

本文涵盖了开始使用React Router构建网站所需要的一切知识。我们将会为本地运动队制作一个网站。

代码

想看网站最终效果,查看demo

安装

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

进行网站(将会运行在浏览器环境中)构建,我们应当安装react-router-domreact-router-dom暴露出react-router中暴露的对象与方法,因此你只需要安装并引用react-router-dom即可。

npm install --save react-router-dom

路由器(Router)

在你开始项目前,你需要决定你使用的路由器的类型。对于网页项目,存在<BrowserRouter><HashRouter>两种组件。当存在服务区来管理动态请求时,需要使用<BrowserRouter>组件,而<HashRouter>被用于静态网站。

通常,我们更倾向选择<BrowserRouter>,但如果你的网站仅用来呈现静态文件,那么<HashRouter>将会是一个好选择。

对于我们的项目,将设将会有服务器的动态支持,因此我们选择<BrowserRouter>作为路由器组件。

历史(History)

每个路由器都会创建一个history对象并用其保持追踪当前location[注1]并且在有变化时对网站进行重新渲染。这个history对象保证了React Router提供的其他组件的可用性,所以其他组件必须在router内部渲染。一个React Router组件如果向父级上追溯却找不到router组件,那么这个组件将无法正常工作。

渲染<Router>

路由器组件无法接受两个及以上的子元素。基于这种限制的存在,创建一个<App>组件来渲染应用其余部分是一个有效的方法(对于服务端渲染,将应用从router组件中分离也是重要的)。

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))

<App>

应用通过<App>组件定义。简化一下,我们将应用拆分成两个部分。<Header>组件包含网站的导航链接。<Main>组件则呈现其余内容。

// this component will be rendered by our <___Router>
const App = () => (
  <div>
    <Header />
    <Main />
  </div>
)

注意:你可以按你喜欢的方式对应用布局,但是将路由与导航拆分出来对于这个入门教程会成家简单。

路由(Route)

<Route>组件是React Router中主要的结构单元。在任意位置只要匹配了URL的路径名(pathname)你就可以创建<Route>元素进行渲染。

路径(Path)

<Route>接受一个数为string类型的path,该值路由匹配的路径名的类型。例如:<Route path='/roster'/>会匹配以/roster[注2]开头的路径名。在当前path参数与当前location的路径相匹配时,路由就会开始渲染React元素。若不匹配,路由不会进行任何操作[注3]。

<Route path='/roster'/>
// 当路径名为'/'时, path不匹配
// 当路径名为'/roster'或'/roster/2'时, path匹配
// 当你只想匹配'/roster'时,你需要使用"exact"参数
// 则路由仅匹配'/roster'而不会匹配'/roster/2'
<Route exact path='/roster'/>

注意:在匹配路由时,React Router只关注location的路径名。当URL如下时:

http://www.example.com/my-projects/one?extra=false

React Router去匹配的只是'/my-projects/one'这一部分。

匹配路径

path-to-regexp包用来决定route元素的path参数与当前location是否匹配。它将路径字符串编译成正则表达式,并与当前location的路径名进行匹配比较。除了上面的例子外,路径字符串有更多高级的选项,详见[path-to-regexp文档]。
当路由地址匹配成功后,会创建一个含有以下属性的match对象

  • url :与当前location路径名所匹配部分

  • path :路由的地址

  • isExact :path 是否等于 pathname

  • params :从path-to-regexp获取的路径中取出的值都被包含在这个对象中

使用route tester这款工具来对路由与URL进行检验。

注意:本例中路由路径仅支持绝对路径[注4]。

创建你的路由

可以在路由器(router)组件中的任意位置创建多个<Route>,但通常我们会把它们放在同一个位置。使用<Switch>组件来包裹一组<Route>。<Switch>会遍历自身的子元素(即路由)并对第一个匹配当前路径的元素进行渲染。

对于本网站,我们希望匹配一下路径:

  • / : 主页

  • /roster : 团体列表

  • /roster/:number :运动员页面,使用运动员的编号作为标识

  • /schedule :团队的赛程表

为了在应用中能匹配路径,在创建<Route>元素时必须带有需要匹配的path作为参数。

<Switch>
  <Route exact path='/' component={Home}/>
  {/* both /roster and /roster/:number begin with /roster */}
  <Route path='/roster' component={Roster}/>
  <Route path='/schedule' component={Schedule}/>
</Switch>

<Route>是如何渲染的?

当一个路由的path匹配成功后,路由用来确定渲染结果的参数有三种。只需要提供其中一个即可。

  • component : 一个React组件。当带有component参数的route匹配成功后,route会返回一个新的元素,其为component参数所对应的React组件(使用React.createElement创建)。

  • render : 一个返回React element的函数[注5]。当匹配成功后调用该函数。该过程与传入component参数类似,并且对于行级渲染与需要向元素传入额外参数的操作会更有用。

  • children : 一个返回React element的函数。与上述两个参数不同,无论route是否匹配当前location,其都会被渲染。

<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
  <Page {...props} data={extraProps}/>
)}/>
<Route path='/page' children={(props) => (
  props.match
    ? <Page {...props}/>
    : <EmptyPage {...props}/>
)}/>

通常component参数与render参数被更经常地使用。children参数偶尔会被使用,它更常用在path无法匹配时呈现的'空'状态。在本例中并不会有额外的状态,所以我们将使用<Route>的component参数。

通过<Route>渲染的元素会被传入一些参数。分别是match对象,当前location对象[注6]以及history对象(由router创建)[注7]。

<Main>

现在我们清楚了根路由的结构,我们需要实际渲染我们的路由。对于这个应用,我们将会在<Main>组件中渲染<Switch>与<Route>,这一过程会将route匹配生成的HTML放在<main>节点中。

import { Switch, Route } from 'react-router-dom'
const Main = () => (
  <main>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/roster' component={Roster}/>
      <Route path='/schedule' component={Schedule}/>
    </Switch>
  </main>
)

注意:主页路由包含额外参数。该参数用来保证路由能准确匹配path。

嵌套路由

运动员路由/roster/:number并未包含在上述<Switch>中。它由<Roster>组件负责在路径包含'/roster'的情形下进行渲染。

在<Roster>组件中,我们将为两种路径进行渲染:

  • /roster :对应路径名仅仅是/roster时,因此需要在exact元素上添加exact参数。

  • /roster/:number : 该路由使用一个路由参数来获取/roster后的路径名。

    const Roster = () => (
    <Switch>

    <Route exact path='/roster' component={FullRoster}/>
    <Route path='/roster/:number' component={Player}/>

    </Switch>
    )

    组合在相同组件中分享共同前缀的路由是一种有用的方法。这就需要简化父路由并且提供一个区域来渲染具有相同前缀的通用路由。

例如,<Roster>用来渲染所有以/roster开始的全部路由。

const Roster = () => (
  <div>
    <h2>This is a roster page!</h2>
    <Switch>
      <Route exact path='/roster' component={FullRoster}/>
      <Route path='/roster/:number' component={Player}/>
    </Switch>
  </div>
)

路径参数

有时路径名中存在我们需要获取的参数。例如,在运动员界面,我们需要获取运动员的编号。我们可以向route的路径字符串中添加path参数

如'/roster/:number'中:number这种写法意味着/roster/后的路径名将会被获取并存在match.params.number中。例如,路径名'/roster/6'会获取到一个对象:

{ number: '6' } // 注获取的值是字符串类型的

<Player>组件可以使用props.match.params对象来确定需要被渲染的运动员的数据。

// 返回运动员对象的API
import PlayerAPI from './PlayerAPI'
const Player = (props) => {
  const player = PlayerAPI.get(
    parseInt(props.match.params.number, 10)
  )
  if (!player) {
    return <div>Sorry, but the player was not found</div>
  }
  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>{player.position}</h2>
    </div>
)

你可以通过阅读path-to-regexp文档来了解更多。

除了<Player>组件,我们的页面还包含<FullRoster>, <Schedule>以及 <Home>组件。

const FullRoster = () => (
  <div>
    <ul>
      {
        PlayerAPI.all().map(p => (
          <li key={p.number}>
            <Link to={`/roster/${p.number}`}>{p.name}</Link>
          </li>
        ))
      }
    </ul>
  </div>
)
const Schedule = () => (
  <div>
    <ul>
      <li>6/5 @ Evergreens</li>
      <li>6/8 vs Kickers</li>
      <li>6/14 @ United</li>
    </ul>
  </div>
)
const Home = () => (
  <div>
    <h1>Welcome to the Tornadoes Website!</h1>
  </div>
)

Link

现在,我们应用需要在各个页面间切换。如果使用锚点元素(就是)实现,在每次点击时页面将被重新加载。React Router提供了<Link>组件用来避免这种状况的发生。当你点击<Link>时,URL会更新,组件会被重新渲染,但是页面不会重新加载。

import { Link } from 'react-router-dom'
const Header = () => (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)

<Link>使用'to'参数来描述需要定位的页面。它的值即可是字符串也可是location对象(包含pathname,search,hash与state属性)。如果其值为字符床将会被转换为location对象。

<Link to={{ pathname: '/roster/7' }}>Player #7</Link>

注意:本例的link的pathname属性只能是绝对路径[注4]。

例子

一个完整的网站例子点击预览

获取路由

希望当下你已准备好深入构建你自己的网站了。

我们已经了解了构建网站所需要的所有必须组件(<BrowserRouter>, <Route>, 以及 <Link>)。当然,还有一些我们没有涉及的组件。所幸React Router拥有优质的文档,你可以查看并从中了解更多的信息。文档也提供一系列的例子与源代码。

注释:

[1] locations 是一个含有描述URL不同部分属性的对象:

// 一个基本的location对象
{ pathname: '/', search: '', hash: '', key: 'abc123' state: {} }

[2] 你可以渲染无路径的<Route>,其将会匹配所有location。此法用于访问存在上下文中的变量与方法。

[3] 如果你使用children参数,即便在当前location不匹配时route也将进行渲染。

[4] 当需要支持相对路径的<Route>与<Link>时,你需要多做一些工作。相对<Link>将会比你之前看到的更为复杂。因其使用了父级的match对象而非当前URL来匹配相对路径。

[5] 这是一个本质上无状态的函数组件。内部实现,component参数与render参数的组件是用很大的区别的。使用component参数的组件会使用React.createElement来创建元素,使用render参数的组件则会调用render函数。如果我们定义一个内联函数并将其传给component参数,这将会比使用render参数慢很多。

<Route path='/one' component={One}/>
// React.createElement(props.component)
<Route path='/two' render={() => <Two />}/>
// props.render()

[6] <Route>与<Switch>组件都会带有location参数。这能让你使用与实际location不同的location去匹配地址。

[7] 可以传入staticContext参数,不过这仅在服务端渲染时有用。

React Router4

阅读数 275

react router

阅读数 80

没有更多推荐了,返回首页