目录
一.简介
二.安装方式
三.基本使用方式
四.相关组件的功能说明
五.导航条的基本结构
六.简单的设置导航条的基本样式
七.设置导航条的默认配置
八.更改导航条的设置
九.隐藏导航条
十.嵌套导航条
十一.深入理解 NavConfiger 和 Navigator
十二.导航条的高级使用技巧:自定义导航条
十三.设置导航条的各种方式
内容
一.简介
nav-react 是React版本的导航条,后续还会开发 Vue、Angular 版本;
在项目中,导航条是很常用的,大多数页面都需要导航条,所以,在合理的项目结构中,应该把导航条抽离出来共用;于是,通过良好的设计,我便开发出了具备:超级可定制、可扩展、可以实现导航条根据页面内容而改变等强大能力的导航条 nav-react;
如果您在使用 nav-react 的过程中遇到了问题 或者 有更好的想法 和 建议,可以通过以下方式告诉我:
- 邮箱:guobinyong@qq.com
- QQ:guobinyong@qq.com
- 微信:keyanzhe
二.安装方式
目前,安装方式有以下2种:
方式1:直接下载原代码
您可直接从 nav-react的Git仓库 下载,此仓库里包含了 nav-react 和 下文的示例代码;nav-react 库在 nav-react的Git仓库 项目的 package/lib 目录下,您可以直接把 lib 目录拷贝到您的项目中去;然后使用如下代码在您的项目中引入 NavConfiger
和 Navigator
:
import { NavConfiger,Navigator } from "path/to/lib/Navigator.jsx";
方式2:通过 npm 安装
npm install --save nav-react
三.基本使用方式
- 导入
NavConfiger
和Navigator
:import { NavConfiger,Navigator } from "nav-react";
- 用
NavConfiger
包在需要导航的内容的外层; - 将
Navigator
放在导航条的位置,并确保Navigator
是被NavConfiger
包住的;<NavConfiger> <div> <Navigator /> <p>页面内容</p> </div> </NavConfiger>
效果如下:
这是没有经过任何配置的 Navigator
,只有一个返回图标,点击可返回,导航条也没有背景色;我之所以没有给 Navigator
设置太多默认的样式,是因为:项目的导航条是因项目而异的,几乎没有太多共同的样式,我设置默认的样式越多,在您使用此组件时,需要修改的就越多;
四.相关组件的功能说明
- NavConfiger : 设置 Navigator 的桥梁,为使用者提供了设置导航条的相关的方法;
- Navigator : 导航条;
注意:
- Navigator 必须被 包含于 NavConfiger 中;
- NavConfiger 和 Navigator 可以不在同一组件中,即: NavConfiger 可以在 Navigator 所以在组件的祖先组件中,只要确保 Navigator 是被 NavConfiger 包着的就行;
五.导航条的基本结构
在上图所示的导航条结构中,area 表示的是导航条的布局结构,相当容器;item 是被布局的项目,所有的 area 和 item 的组合就构成了导航条;具体的解释如下:
- navArea:就是导航条的容器,它是导航条最外层的元素;它里面有3个区域(area),分别用来放置导航条的 左、中、右 item;item 是导航条中 显示的、可操作的项目,可以是 字符串 或者 React元素;
- leftArea:在导航条的左部,用来放置 leftItem ,如:返回按钮 等等;
- centerArea:在导航条的中部,用来放置 centerItem ,如:导航条的标题 等等;
- rightArea:在导航条的右部,用来放置 rightItem ;
- leftItem:导航条左边的用来显示 或者 操作的 项目,可以是 字符串 或 React元素;
- centerItem:导航条中间的用来显示 或者 操作的 项目,可以是 字符串 或 React元素,如:页面的标题 等等;
- rightItem:导航条右部的用来显示 或者 操作的 项目,可以是 字符串 或 React元素;
六.简单的设置导航条的基本样式
您可以在 Navigator
上设置 className
、style
等普通 HTML 元素具有的 props ,Navigator
会把它设置到根元素上,即 navArea 上;如:
<Navigator className="navAreaClass" style={navAreaStyleObj} />
七.设置导航条的默认配置
可以通过 Navigator 的prop navConfig 给 Navigator 设置 默认的配置,示例如下:
<Navigator navConfig={defaultConfigObj} />
navConfig 接受一个配置对象,该配置对象可配置的字段如下:
- hide ?: boolean, // 设置是否显示 导航条
- left ?: string || element, //设置leftItem
- center ?: string || element, //设置centerItem
- right: ?: string || element, //设置rightItem
- leftAction ?: function, //设置 leftItem 的 click 事件处理函数
- rightAction ?: function, //设置 rightItem 的 click 事件处理函数
- navClass ?: string, //设置 navArea 的 css 类
- leftAreaClass ?: string, //设置 leftArea 的 css 类
- centerAreaClass ?: string, //设置 centerClass 的 css 类
- rightAreaClass ?: string, //设置 rightArea 的 csss 类
示例:
Index.jsx
//导入
import React, { Component } from 'react';
import './Index.css'
import { NavConfiger, Navigator } from 'nav-react';
class Index extends Component {
rightClickHandle(){
alert("您点击了导航条的rightItem!");
}
navConfig = {
left: "返回",
center: "默认的标题",
right: <p onClick={this.rightClickHandle} >弹窗</p>,
navClass: "nav_area_default"
};
render() {
return (
<NavConfiger>
<fieldset className="page1" >
<legend>基本导航</legend>
<Navigator navConfig={this.navConfig} />
</fieldset>
</NavConfiger>
);
}
}
export { Index };
Index.less
.nav_area_default {
background-color: #00ff00;
}
效果:
八.更改导航条的设置
在实际的项目中,通常会有很多页面都有导航条,为每个页面都加个导航条是编程思想不成熟的一种体现,最好的做法是:把导航条抽离成一个单独的组件,并且各个页面共享一个导航条;这就是本组件 Navigator
的任务,但是通常并非所有页面的导航条都一样,比如:有些页面需要给导航条换个样式,有些页面的导航条右侧有按钮,每一个页面的标题不一样...等等,这些都是常见的情况;为了实现页面能够个性化定制导航条,Navigator 提供了这样的机制,使用方法如下:
页面组件定制导航条:
-
导入
PropTypes
;import PropTypes from 'prop-types';
-
给组件类添加 contextTypes 属性;
// ES6的设置方式 static contextTypes = { pushNavConfig: PropTypes.func, popNavConfig: PropTypes.func, }; // 普通的添加方式 PageComponent.contextTypes = { pushNavConfig: PropTypes.func, popNavConfig: PropTypes.func, };
-
在需要设置导航条时,通过 pushNavConfig 方法将导航条的配置对象推入导航条的配置栈;
let navConfig = { left: "默认", center: "页面1" }; this.configID = this.context.pushNavConfig(navConfig);
-
在需要移除对导航条的设置时,通过 popNavConfig 方法将相应的导航条配置对象推出导航条配置栈;
this.context.popNavConfig(this.configID);
完整的示例代码如下:
Page1.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Page1 extends Component {
// 定义 contextTypes ,以使该组件能够接收到 context
static contextTypes = {
pushNavConfig: PropTypes.func,
popNavConfig: PropTypes.func,
};
configID;
navConfig = {
left: "默认",
center: "页面1",
navClass: "page1_nav"
};
componentWillMount() {
this.configID = this.context.pushNavConfig(this.navConfig); //设置导航条
}
componentWillUnmount() {
this.context.popNavConfig(this.configID); //移除对导航条的设置
}
render() {
return (
<fieldset className="page1" >
<legend>页面1</legend>
我是页面1的内容
</fieldset>
);
}
}
export {Page1};
效果:
用同样的方法,再添加个页面2,让 页面2 对 导航条的配置 与 页面1 不一样,可以试下来这2个页面做切换的效果,具体代码我就不再写了,但可以导示下示例效果,如下:
说明:
- pushNavConfig : (navConfig)-> configId //往导航条配置栈中推入一个导航条的配置对象 navConfig,并返回配置id,该 id 可用于通过 popNavConfig 方法推出该 id 对应的配置对象;
- popNavConfig : (configId)->void //从导航条配置栈中推出 configId 对应的配置对象;configId 是 执行 pushNavConfig 方法的返回值;
- 在您需要改变导航条时 通过 pushNavConfig 方法设置导航条,在需要还原改变时,通过 popNavConfig 方法还原导航条;
- 对于一个 React 组件,您可以在
constructor
、componentWillMount
、componentDidMount
这些方法中执行pushNavConfig
推入导航条的配置对象 ,在componentWillUnmount
中执行pushNavConfig
推出导航条的配置对象; - 我个人建议:最好在
componentWillMount
或者componentDidMount
中 执行pushNavConfig
,不建议在constructor
中执行;
九.隐藏导航条
可以通过导航条的配置对象 navConfig 的 hide 字段来设置导航条是否需要隐藏;
十.嵌套导航条
有时在组件的层级树中可能需要多个导航条,比如:在有导航的页面中打开一个带有导航的子页面...等等; Navigator
支持这种使用场景,在 Navigator
中,这种机制叫做 嵌套导航;
嵌套导航的使用方式和普通导航的使用方式没什么区别,您只需要在原来的 NavConfiger
所包的 内容 或者子组件中 再添加一对 NavConfiger
和 Navigator
即可;
示例代码:
Index.jsx
class Index extends Component {
navConfig = {
left: "上一页",
center: "外层导航",
right: <NavLink to={"/nested/page1"} >下一页</NavLink>,
navClass: "nav_area_default"
};
render() {
return (
<NavConfiger>
<fieldset className="page1" >
<legend>嵌套导航</legend>
<Navigator navConfig={this.navConfig} />
<div className="content_box" >
<p>我是外层页面的内容</p>
<Route path={`${this.props.match.path}/page1`} component={Page1} />
</div>
</fieldset>
</NavConfiger>
);
}
}
Index.css
.nav_area_default {
background-color: #00ff00;
}
Page1.jsx
class Page1 extends Component {
// 定义 contextTypes ,以使该组件能够接收到 context
static contextTypes = {
pushNavConfig: PropTypes.func,
popNavConfig: PropTypes.func,
};
configID;
navConfig = {
left: "默认",
center: "页面1",
right: <NavLink to={`${this.props.match.url}/page2`} >子导航</NavLink>,
navClass: "page1_nav"
};
componentWillMount() {
this.configID = this.context.pushNavConfig(this.navConfig); //设置导航条
}
componentWillUnmount() {
this.context.popNavConfig(this.configID); //移除对导航条的设置
}
render() {
return (
<fieldset className="page1" >
<legend>页面1</legend>
<p>我是页面1</p>
<Route path={`${this.props.match.path}/page2`} component={Page2} />
</fieldset>
);
}
}
Page1.css
.page1_nav {
background-color: red;
}
Page2.jsx
class Page2 extends Component {
static contextTypes = {
pushNavConfig: PropTypes.func,
popNavConfig: PropTypes.func,
};
configID;
myNavConfig = {
left: "页面1",
center: "页面2",
navClass: "page2_nav"
};
defaultNavConfig = {
left: "上一页",
center: "内层导航",
right: <NavLink to={`${this.props.match.url}/page3`} >下一页</NavLink>,
navClass: "nav_area_default"
};
componentWillMount() {
this.configID = this.context.pushNavConfig(this.myNavConfig);
}
componentWillUnmount() {
this.context.popNavConfig(this.configID);
}
render() {
return (
<NavConfiger>
<fieldset className="page2" >
<legend>页面2</legend>
<Navigator navConfig={this.defaultNavConfig} />
<div className="content_box" >
<p>我是内层导航的内容</p>
<Route path={`${this.props.match.path}/page3`} component={Page3} />
</div>
</fieldset>
</NavConfiger>
);
}
}
Page2.css
.page2_nav {
background-color: blue;
}
Page3.jsx
class Page3 extends Component {
// 定义 contextTypes ,以使该组件能够接收到 context
static contextTypes = {
pushNavConfig: PropTypes.func,
popNavConfig: PropTypes.func,
};
configID;
navConfig = {
left: "上一页",
center: "页面3",
right: <NavLink to={"/"} >列表</NavLink>,
navClass: "page3_nav"
};
componentWillMount() {
this.configID = this.context.pushNavConfig(this.navConfig); //设置导航条
}
componentWillUnmount() {
this.context.popNavConfig(this.configID); //移除对导航条的设置
}
render() {
return (
<fieldset className="Page3" >
<legend>页面3</legend>
<p>我是页面3</p>
</fieldset>
);
}
}
Page3.css
.page3_nav {
background-color: yellow;
}
示例效果:
NavConfiger 和 Navigator 的嵌套使用规则
为了使用更加简单,我把 NavConfiger
设计成可以管理多个 Navigator
,但是由于 React 在切换组件时,会先创建和挂载新组件,然后再卸载被替换掉的组件,导致这个特性只能在以下情况中使用:
- 如果:多个
Navigator
总是同时销毁,那么:这几个Navigator
可以共同一个NavConfiger
,也可以每个Navigator
单独用一个NavConfiger
管理 ; - 如果:多个
Navigator
并不是同时销毁,那么:应该用多个NavConfiger
分别管理不同时销毁的Navigator
; 否则,将可能产生与您预期不符的现象;
关于造成这种使用限制的原因,可参考: 下面的《NavConfiger 和 Navigator 的工作原理》并结合 “《React特性精华》/5. 组件/组件的渲染”;
十一.深入理解 NavConfiger 和 Navigator
NavConfiger 有以下几个功能:
- 为子组件在上下文中提供
pushNavConfig
和popNavConfig
方法来 推入 和 弹出 导航条的配置对象; - 提供一个 导航条栈 ,管理着子组件中的导航条;
Navigator 有以下几个功能:
- 提供一个导航条配置对象栈,管理着多个导航条的配置对象;
- 把配置栈中栈顶的配置对象渲染成导航条;
导航条栈 和 导航条配置对象栈 的工作机制:
- 导航条栈 由 NavConfiger 实例管理;导航条配置对象栈 由 Navigator 实例管理;
- 每当 Navigator 实例被将要被挂载时,会把自己推入到祖先组件中最近的 NavConfiger 实例的 导航条栈 的 栈顶;
- 每当 Navigator 实例在将根被卸载时,会把自己从祖先组件中最近的 NavConfiger 实例的 导航条栈 导航条栈中 弹出;
- 每当在组件中调用
pushNavConfig
方法时,会把传入的配置对象推入到该组件的祖先组件中最近的 NavConfiger 实例的 导航条栈 中 位于栈顶的 Navigator 实例的 导航条配置对象栈 的栈顶; - 每当在组件中调用
popNavConfig
方法时,会从该组件的祖先组件中最近的 NavConfiger 实例的 导航条栈 中 位于栈顶的 Navigator 实例的 导航条配置对象栈 中弹出与传入的 configID 对应的导航条配置对象;
十二.导航条的高级使用技巧:自定义导航条
通过 navConfig 配置对象,您可以充分配置 Navigator 实例,通常,这已经能够满足绝大部分场景;尽管如此,我仍然给 Navigator 设计了允许您完全定制导航条的接口,您可以通过 Navigator 的 children 实现自定义的导航条,具体规则如下:
通过 Navigator 实例的 children 来定制 导航条的规则:
- 当 Navigator 实例有1个 children 时,该 children 会作为整个导航条;
- 当 Navigator 实例有2个 children 时,第1个 children 会作为导航条的 leftItem,第2个 children 会作为导航条的 rightItem;
- 当 Navigator 实例有3个 children 时,第1个 children 会作为导航条的 leftItem,第2个 children 会作为导航条的 centerItem,第3个 children 会作为导航条的 rightItem;
- 所有的 children 都会接收一个表示当前生效的配置对象的prop
navConfig
,所以,可以在自定义的 children 组件中 通过props.navConfig
访问当前生效的导航条配置对象;
完全自定义导航条
<Navigator>
<CustomNav />
</Navigator>
CustomNav 是您自定义的导航条,CustomNav 会接收一个表示当前生效的配置对象的prop navConfig
, 所以,在 CustomNav 中,可以通过 this.props.navConfig 访问当前生效的配置对象;
示例代码:
function CustomNav(props) {
return <p className="custom_nav" >自定义:{props.navConfig.center}</p>
}
class Index extends Component {
rightClickHandle() {
alert("您点击了导航条的rightItem!");
}
navConfig = {
left: "返回",
center: "默认的标题",
right: <p onClick={this.rightClickHandle} >弹窗</p>,
navClass: "nav_area_default"
};
render() {
return (
<NavConfiger>
<fieldset className="page1" >
<legend>自定义导航</legend>
<Navigator navConfig={this.navConfig} >
<CustomNav />
</Navigator>
<div className="content_box" >
<Route path={`${this.props.match.path}/page1`} component={Page1} />
<Route path={`${this.props.match.path}/page2`} component={Page2} />
<br />
<NavLink to={`${this.props.match.url}/page1`} >跳转到:页面1</NavLink>
</div>
</fieldset>
</NavConfiger>
);
}
}
示例效果:
自定义导航条的 左部 和 右部
<Navigator>
<Left />
<Right />
</Navigator>
自定义导航条的 左部、中部、右部
<Navigator>
<Left />
<Center />
<Right />
</Navigator>
示例代码:
Index.jsx
function Left(props) {
return <p className="custom_nav" >左:{props.navConfig.left}</p>
}
function Center(props) {
return <p className="custom_nav" >中:{props.navConfig.center}</p>
}
function Right(props) {
return <p className="custom_nav" >右:{props.navConfig.right}</p>
}
class Index extends Component {
rightClickHandle() {
alert("您点击了导航条的rightItem!");
}
navConfig = {
left: "LeftItem",
center: "Title",
right: "RightItem",
navClass: "nav_area_default"
};
render() {
return (
<NavConfiger>
<fieldset className="page1" >
<legend>自定义导航</legend>
<Navigator navConfig={this.navConfig} >
<Left />
<Center />
<Right />
</Navigator>
<div className="content_box" >
<Route path={`${this.props.match.path}/page1`} component={Page1} />
<Route path={`${this.props.match.path}/page2`} component={Page2} />
<br />
<NavLink to={`${this.props.match.url}/page1`} >跳转到:页面1</NavLink>
</div>
</fieldset>
</NavConfiger>
);
}
}
Page1的 navConfig 对象:
navConfig = {
left: "1左",
center: "页面1",
right:"1右",
navClass: "page1_nav"
};
Page2的 navConfig 对象:
navConfig = {
left: "2左",
center: "页面2",
right:"2右",
navClass: "page2_nav"
};
示例效果:
十三.设置导航条的各种方式
看到这里,您应该发现:有多种配置导航条的方式;现总结如下:
- 给 Navigator 标签设置 prop
<Navigator prop={propValue} />
,如:className
、style
等等;
这种方式只能设置导航条的样式; - 给 Navigator 设置默认配置对象 ,如:
<Navigator navConfig={this.navConfig} >
;
这种方式只是提供导航条的默认配置对象; - 通过context的方法
pushNavConfig
、popNavConfig
,来设置 导航条的配置对象;
这种方式用于每个页面根据自己的情况配置单独配置导航条;这种方式提供的配置对象 会 覆盖 Navigator 的默认配置对象; - 通过 Navigator 的 children 来自定义导航条;
这种方式可以自定义导航条,可能完全改变导航条的结构;