美文网首页程序员我爱编程让前端飞
非常好用的导航条组件:nav-react

非常好用的导航条组件:nav-react

作者: 科研者 | 来源:发表于2018-01-28 23:12 被阅读0次

    目录

    一.简介
    二.安装方式
    三.基本使用方式
    四.相关组件的功能说明
    五.导航条的基本结构
    六.简单的设置导航条的基本样式
    七.设置导航条的默认配置
    八.更改导航条的设置
    九.隐藏导航条
    十.嵌套导航条
    十一.深入理解 NavConfiger 和 Navigator
    十二.导航条的高级使用技巧:自定义导航条
    十三.设置导航条的各种方式
    

    内容

    一.简介

    nav-react 是React版本的导航条,后续还会开发 Vue、Angular 版本;
    在项目中,导航条是很常用的,大多数页面都需要导航条,所以,在合理的项目结构中,应该把导航条抽离出来共用;于是,通过良好的设计,我便开发出了具备:超级可定制、可扩展、可以实现导航条根据页面内容而改变等强大能力的导航条 nav-react;

    如果您在使用 nav-react 的过程中遇到了问题 或者 有更好的想法 和 建议,可以通过以下方式告诉我:

    二.安装方式

    目前,安装方式有以下2种:

    方式1:直接下载原代码

    您可直接从 nav-react的Git仓库 下载,此仓库里包含了 nav-react 和 下文的示例代码;nav-react 库在 nav-react的Git仓库 项目的 package/lib 目录下,您可以直接把 lib 目录拷贝到您的项目中去;然后使用如下代码在您的项目中引入 NavConfigerNavigator

    import { NavConfiger,Navigator } from "path/to/lib/Navigator.jsx";
    

    方式2:通过 npm 安装

    npm install --save nav-react
    

    三.基本使用方式

    1. 导入 NavConfigerNavigator:
      import { NavConfiger,Navigator } from "nav-react";
      
    2. NavConfiger 包在需要导航的内容的外层;
    3. Navigator 放在导航条的位置,并确保 Navigator 是被 NavConfiger 包住的;
      <NavConfiger>
         <div>
           <Navigator />
           <p>页面内容</p>
         </div>
      </NavConfiger>
      

    效果如下:

    默认

    这是没有经过任何配置的 Navigator ,只有一个返回图标,点击可返回,导航条也没有背景色;我之所以没有给 Navigator 设置太多默认的样式,是因为:项目的导航条是因项目而异的,几乎没有太多共同的样式,我设置默认的样式越多,在您使用此组件时,需要修改的就越多;

    四.相关组件的功能说明

    • NavConfiger : 设置 Navigator 的桥梁,为使用者提供了设置导航条的相关的方法;
    • Navigator : 导航条;

    注意:

    • Navigator 必须被 包含于 NavConfiger 中;
    • NavConfiger 和 Navigator 可以不在同一组件中,即: NavConfiger 可以在 Navigator 所以在组件的祖先组件中,只要确保 Navigator 是被 NavConfiger 包着的就行;

    五.导航条的基本结构

    Navigator结构图

    在上图所示的导航条结构中,area 表示的是导航条的布局结构,相当容器;item 是被布局的项目,所有的 area 和 item 的组合就构成了导航条;具体的解释如下:

    • navArea:就是导航条的容器,它是导航条最外层的元素;它里面有3个区域(area),分别用来放置导航条的 左、中、右 item;item 是导航条中 显示的、可操作的项目,可以是 字符串 或者 React元素;
    • leftArea:在导航条的左部,用来放置 leftItem ,如:返回按钮 等等;
    • centerArea:在导航条的中部,用来放置 centerItem ,如:导航条的标题 等等;
    • rightArea:在导航条的右部,用来放置 rightItem ;
    • leftItem:导航条左边的用来显示 或者 操作的 项目,可以是 字符串 或 React元素;
    • centerItem:导航条中间的用来显示 或者 操作的 项目,可以是 字符串 或 React元素,如:页面的标题 等等;
    • rightItem:导航条右部的用来显示 或者 操作的 项目,可以是 字符串 或 React元素;

    六.简单的设置导航条的基本样式

    您可以在 Navigator 上设置 classNamestyle 等普通 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;
    }
    

    效果:

    默认navConfig

    八.更改导航条的设置

    在实际的项目中,通常会有很多页面都有导航条,为每个页面都加个导航条是编程思想不成熟的一种体现,最好的做法是:把导航条抽离成一个单独的组件,并且各个页面共享一个导航条;这就是本组件 Navigator 的任务,但是通常并非所有页面的导航条都一样,比如:有些页面需要给导航条换个样式,有些页面的导航条右侧有按钮,每一个页面的标题不一样...等等,这些都是常见的情况;为了实现页面能够个性化定制导航条,Navigator 提供了这样的机制,使用方法如下:

    页面组件定制导航条:

    1. 导入 PropTypes

      import PropTypes from 'prop-types';
      
    2. 给组件类添加 contextTypes 属性;

      // ES6的设置方式
      static contextTypes = {
           pushNavConfig: PropTypes.func,
           popNavConfig: PropTypes.func,
       };
      
       // 普通的添加方式
       PageComponent.contextTypes = {
           pushNavConfig: PropTypes.func,
           popNavConfig: PropTypes.func,
       };
      
      
    3. 在需要设置导航条时,通过 pushNavConfig 方法将导航条的配置对象推入导航条的配置栈;

      let navConfig = {
        left: "默认",
        center: "页面1"
       };
      this.configID = this.context.pushNavConfig(navConfig);
      
    4. 在需要移除对导航条的设置时,通过 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};
    

    效果:

    页面1

    用同样的方法,再添加个页面2,让 页面2 对 导航条的配置 与 页面1 不一样,可以试下来这2个页面做切换的效果,具体代码我就不再写了,但可以导示下示例效果,如下:


    页面切换

    说明:

    • pushNavConfig : (navConfig)-> configId //往导航条配置栈中推入一个导航条的配置对象 navConfig,并返回配置id,该 id 可用于通过 popNavConfig 方法推出该 id 对应的配置对象;
    • popNavConfig : (configId)->void //从导航条配置栈中推出 configId 对应的配置对象;configId 是 执行 pushNavConfig 方法的返回值;
    • 在您需要改变导航条时 通过 pushNavConfig 方法设置导航条,在需要还原改变时,通过 popNavConfig 方法还原导航条;
    • 对于一个 React 组件,您可以在 constructorcomponentWillMountcomponentDidMount 这些方法中执行 pushNavConfig 推入导航条的配置对象 ,在 componentWillUnmount 中执行 pushNavConfig 推出导航条的配置对象;
    • 我个人建议:最好在 componentWillMount 或者 componentDidMount 中 执行 pushNavConfig,不建议在 constructor 中执行;

    九.隐藏导航条

    可以通过导航条的配置对象 navConfig 的 hide 字段来设置导航条是否需要隐藏;

    十.嵌套导航条

    有时在组件的层级树中可能需要多个导航条,比如:在有导航的页面中打开一个带有导航的子页面...等等; Navigator 支持这种使用场景,在 Navigator 中,这种机制叫做 嵌套导航;

    嵌套导航的使用方式和普通导航的使用方式没什么区别,您只需要在原来的 NavConfiger 所包的 内容 或者子组件中 再添加一对 NavConfigerNavigator 即可;

    示例代码:

    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 有以下几个功能:

    • 为子组件在上下文中提供 pushNavConfigpopNavConfig 方法来 推入 和 弹出 导航条的配置对象;
    • 提供一个 导航条栈 ,管理着子组件中的导航条;

    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"
        };
    

    示例效果:

    自定义导航条Item

    十三.设置导航条的各种方式

    看到这里,您应该发现:有多种配置导航条的方式;现总结如下:

    • 给 Navigator 标签设置 prop <Navigator prop={propValue} />,如: classNamestyle 等等;
      这种方式只能设置导航条的样式;
    • 给 Navigator 设置默认配置对象 ,如:<Navigator navConfig={this.navConfig} >
      这种方式只是提供导航条的默认配置对象;
    • 通过context的方法 pushNavConfigpopNavConfig,来设置 导航条的配置对象;
      这种方式用于每个页面根据自己的情况配置单独配置导航条;这种方式提供的配置对象 会 覆盖 Navigator 的默认配置对象;
    • 通过 Navigator 的 children 来自定义导航条;
      这种方式可以自定义导航条,可能完全改变导航条的结构;

    相关文章

      网友评论

        本文标题:非常好用的导航条组件:nav-react

        本文链接:https://www.haomeiwen.com/subject/ljktzxtx.html