资讯专栏INFORMATION COLUMN

路由

Aklman / 478人阅读

摘要:由命名路由与子路由构成整体结构,我们用它构建如下页面。以下两张图说明路由和子路由是如何工作的。继续修改好友信息的路由部分添加好友信息为组件添加动态路由为动态路由添加为路径参数添加数据下发为组件添加,并使用它。

不使用vue-router的情况

代码
官方给出下面的例子在不使用vue-router的情况下来实现一个路由。
该示例结合了H5历史管理API、单文件组件、JS模块相关内容来实现路由

后面说的页面只是一条浏览器历史记录,关于历史记录管理见历史记录管理(window.history)
simple_router.js为入口,进行组件的加载,和其他一些功能初始化工作。

//simple_router.js
data: {
    currentRoute: window.location.pathname
},
computed: {
    ViewComponent() {
        const matchingView = routes[this.currentRoute]
        return matchingView
            ? require("./pages/" + matchingView + ".vue").default
            : require("./pages/404.vue").default
    }
},
render(h) {
    return h(this.ViewComponent)
}

属性currentRoute是一个状态转换器(在VLink.vue中改变状态),在下层组件中通过改变它来随时切换页面。它在转换页面前,会去路由表中查找对应的页面路径。

routes.js为路由表

//routes.js
export default {
    "/":"Home",
    "/about":"About"
}

每个路由的页面都是一个组件,切换页面就是在这些组件中切换。要来回切换这些组件需要先加载它们,我们在ViewComponent计算属性中完成这个工作,通过currentRoute定位好对应的组件,在通过require(组件路径).default在运行时动态的加载它,并在渲染函数中使用它。

特别注意的是,如果你使用vue-loader@1.4+版本,这个require后面要加上default,否则报个加载模板失败的错误,因为在这个版本及更高版本上,vue文件导出的全是esModule,而require加载的是commonjs格式的模块,在这里具体就是它返回的是一个包含default的对象的对象。
具体查看vue-loader API esModule选项、ES6入门模块部分、记升级vue-loader版本时遇到的一个坑

这里入口中最重要的是别忘了,使用window.onpopstate管理历史记录,激活后退按钮的功能

window.addEventListener("popstate", () => {
    app.currentRoute = window.location.pathname
})

之后就是定义需要切换的几个组件,Home.vueAbout.vue404.vue,它们大同小异




这些组件通过Main.vue完成具体的布局




接着就是在Main.vue中使用子组件VLink.vue




VLink.vue很也是很关键,定义了一个链接,在开始入口文件中的currentRoute 这个状态转换器,就是通过点击链接改变状态的,而这个改变值this.href是通过布局组件Main.vueProps数据下发下来的(),最后别忘记把这个新状态作为URL推入历史记录管理的状态栈中。

总结下就是 通过simple_router.js定义如何加载页面(运行时动态加载),通过VLink.vue触发这个加载,通过布局文件将这两部分结合在一起。其中夹杂了历史记录的管理。
使用vue-router的情况

该插件整体结构比较简单,而细节非常繁琐(用来解决各种各样的缺陷和bug)。

命名路由子路由构成整体结构,我们用它构建如下页面。

目录结构如下

代码在这vue-router v0.1

//router.js
export default new VueRouter({
    routes: [
        { path: "/", component: Home },
        {
            path: "/settings", component: UserSettings,
            children: [
                { 
                    path: "userinfo", 
                    component:UserInfo
                },
                {   
                    path: "useremail", 
                    component:UserEmail
                }
            ]
        },
        {
            path: "/interaction", component: UserInteraction,
            children: [
                { path: "userfriend", component: UserFriend },
                { path: "userFollow", component: UserFollow}
            ]
        }
    ]
})

顶层路由及其对应的组件


vue-router 测试页



第二层路由(子路由)及其对应组件,这里。

以下两张图说明路由和子路由是如何工作的。

第一张图说明当我们点击链接,经过路由就可以把对应的组件,放到页面指定的中。而同样经过子路由就可以把对应的组件,放到顶层组件中指定的中。

而第二张,就是对路由过程的补充,通过路由或子路由去寻找对应组件,找到的组件再反过来放入视图中。

命名路由

有时候我们需要在一个组件中使用多块,以便增加组件的重用,就可以使用命名路由。

比如我们需要在用户设置中的基本信息更换邮箱中都加一段广告。

增加一个广告组件Advertisement.vue


UserSettings.vue中添加一段命名的


在路由文件中为命名的添加命名路由。default路由对应那些未命名的

//router.js
//...
children: [
    { 
        path: "userinfo", 
        components: {
            default:UserInfo,
            //添加命名路由
            adver:Advertisement
        }
    },
    {   
        path: "useremail", 
        components: {
            default:UserEmail,
            //添加命名路由
            adver:Advertisement
        }
    }
]
//...

具体代码 添加命名路由

为激活的链接添加样式

被激活的链接,vue-router会为其添加样式类router-link-active,我们在这个class中可以为其添加具体样式,上面代码中已经被添加了样式。

a.router-link-active,
li.router-link-active>a {
    background-color: gainsboro;
    color: #37C6C0;
}

这里被分成两种情况
当这种不带tag的写法

用户首页

被解析成

tag的写法

基本信息

被解析成

router-link-active被添加在tag上。

细心的你可以发现不管哪个链接被激活,用户首页上始终存在着router-link-active,这是因为它的路由是/,所以在其他路由被解析时,/也会被匹配。我们可以用exact解决这个问题,它使路径字符串要完全匹配。

用户首页

动态路由模糊匹配

代码 v0.3 动态路由匹配与Props属性

有时候一堆同级路由它们所对应的组件基本相同,我们就可以使用动态路由,匹配到同一个组件。
我们在我的好友中有一个好友列表。当点击某好友会显示他的信息。

下面只使用了最简单的匹配模式,关于更复杂的匹配,vue-router使用path-to-regexp动态匹配请求路径,具体查看PocketLibs(2)—— 请求相关 path-to-regexp

//router.js
//......
{
    path: "/interaction", component: UserInteraction,
    children: [
        { 
            path: "userfriend", component: UserFriend,
            //添加好友信息 为组件FriendInfo添加动态路由 
            children: [
                { path:"fd/:id",component:FriendInfo}
            ]
        },
        { path: "userFollow", component: UserFollow}
    ]
}
//......

修改组件UserFriend.vue,添加好友列表与好友信息视图


添加好友信息组件FriendInfo.vue


使用命名组件和Props改进我的好友模块

在刚才的代码中,UserFriend.vue中有以下代码

  • 好友{{item}}
  • :to=""/interaction/userfriend/fd/" + item "有点违和。我们可以用命名路由改造一下。
    先在路由文件中为用户信息对应的路由添加成name属性,切换成命名路由。

    //router.js
    { 
        path: "userfriend", component: UserFriend,
        //添加好友信息 为组件FriendInfo添加动态路由 
        children: [
            //为动态路由添加name
            { path:"fd/:id",name:"fd",component:FriendInfo}
        ]
    }

    然后修改UserFriend.vue中的

    
    好友{{item}}

    效果与上面的一样。

    我们再仔细看看组件FriendInfo.vue

    
    

    在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。我们可以为路径参数添加Props数据下发。
    继续修改好友信息的路由部分

    //router.js
    { 
        path: "userfriend", component: UserFriend,
        //添加好友信息 为组件FriendInfo添加动态路由 
        children: [
            //为动态路由添加name
            //为路径参数添加Props数据下发
            { path:"fd/:id",name:"fd",component:FriendInfo,props:true}
        ]
    }

    为组件FriendInfo添加Props,并使用它。

    
    
    在路由中使用Props的其他模式

    如果在命名路由中使用Props数据下发,要为每一个对应组件,都设置Props。

    {
      path: "/user/:id",
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }

    Props还可以是一个对象

    { path:"fd/:id",name:"fd",component:FriendInfo,props:{id:1}}

    还可以是一个函数,函数接收一个$route对象

    function dynamicPropsFn (route) {
      const now = new Date()
      return {
        name: (now.getFullYear() + parseInt(route.params.years)) + "!"
      }
    }
    //...
    { path: "/dynamic/:years", component: Hello, props: dynamicPropsFn }
    钩子函数、请求数据和进度条

    代码 vue-router 0.4 钩子、请求数据与全局进度条
    我们使用axios发送请求,http://schematic-ipsum.heroku... 可以作为响应模拟一些基本的数据。
    改造好友信息组件FriendInfo.vue,我们在组件级钩子beforeRouteUpdate中发送请求。

    
    

    beforeRouteEnter钩子中第三个参数next(),调用它时,才可以继续其他操作(此时系统处于等待),我们在获取到响应时再调用它,因此在获取响应后才会看渲染效果。

    仔细观察可以发现,多次查看好友信息,信息不会改变,查看控制台发现beforeRouteEnter只调用了一次,只发送了一次信息。

    首次查看信息时,解析路由并使组件FriendInfo激活,我们调用beforeRouteEnter,之后每次查看,组件不会重新被渲染,只会被重复利用,因此不会再调用beforeRouteEnter。注意该钩子中不能使用this,因为在执行它时组件还没有被实例,但是next()的回调执行时,组件已被实例,它接收组件实例作为参数(示例中的vm)

    解决这个问题就靠beforeRouteUpdate,该钩子在当前路由改变,组件被复用时调用。比如从/interaction/userfriend/fd/1/interaction/userfriend/fd/2时它就会调用。下面我们就在FriendInfo中添加这个钩子。

    beforeRouteUpdate(to, from, next) {
        axios.post("http://schematic-ipsum.herokuapp.com/", reqObject)
            .then(response => {
                this.name = response.data.name;
                this.phone = response.data.phone;
                this.email = response.data.email;
                next();
            });
    },

    注意它是可以使用this的,它的next()不接收回调。

    我们看下youtube

    每次加载页面会有个进度条

    我们现在就使用全局钩子函数和NProgress实现这个功能,在入口app.js文件中添加全局路由钩子。

    import Vue from "Vue"
    import nprogress from "nprogress"
    import "nprogress/nprogress.css"
    import router from "./router"
    
    const app = function () {
      //在任何导航被触发前执行
      router.beforeEach((to, from, next) => {
        console.log("beforeEach")
        nprogress.start()
        next()
      })
      //导航中的最后一个钩子
      router.afterEach((to, from) => {
        console.log("afterEach")
        nprogress.done()
      })
    
      new Vue({
        router,
        el: "#app"
      })
    }
    
    export { app }

    给路由视图添加动画

    代码 vue-router 0.5 为添加动画
    为显示好友信息添加动画
    修改组件UserFriend

    
    

    添加过渡类

    .child-view {
      position: absolute;
      transition: all 1s cubic-bezier(.55,0,.1,1);
    }
    .slide-left-enter {
      opacity: 0;
      -webkit-transform: translate(60px, 0);
      transform: translate(60px, 0);
    }
    .slide-left-leave-active {
      opacity: 0;
      -webkit-transform: translate(-60px, 0);
      transform: translate(-60px, 0);
    }

    由于在切换查看信息时,组件FriendInfo不会重新渲染,即除第一次外不会有动画,因此需要设置key这里有个不可预期的错误,即在上设置key。

    
    

    千万不能这么做,key无法传递给FriendInfo的根节点。

    我们需要在组件FriendInfo上设置key

    //FriendInfo.vue
    

    重定向

    代码 vue-router 0.6 重定向和别名
    仿造我的好友,构造关注的人

    当点击单数用户时,显示用户信息,点击双数用户时,显示用户被销毁。
    用户信息为一个组件,销毁为另一个组件。
    路由配置如下:

    { 
        path: "userFollow", component: UserFollow,
        children: [
            {
                path:"fw/:id", name:"fw",
                redirect: to => {
                    if(to.params.id%2===0)
                        return "fd/:id"
                    else
                        return "fi/:id"
                },
                beforeEnter:(to, from, next) => {
                    console.log(from)
                    console.log(to)
                    next()
                }
            },
            {
                path:"fd/:id", component:UserDestroy, props:true
            },
            {
                path:"fi/:id", component:FollowInfo, props:true
            }
        ]
    }

    每个routerLink的链接路径为fw/:id,在redirect中配置重定向函数,路径参数id为单数重定向到路由fi/:id,双数时,重定向到fd/:idredirect还可以是个表示路由路径的字符串或命名路由对象({name:"foo",params:{bar:"baz"}})。

    另外在redirect属性所在的路由中,导航守卫不会执行,如上面的beforeEnter不会执行(全局的守卫也不会执行)。

    别名

    我们增加一个顶级导航系统公告

    为它添加路由

    {
        path: "/notification", component: SystemNotification
    }

    SystemNotification组件只是一个简单的标题

    之后我想把这个页面改成我的消息页面

    为此我们修改路由路径/usermessage,将原来的名字配置为别名alias:"/notification",并保持routerLink的路径不变。

    这样做一是其他网站引用该页面不会产生404,二是路由内部配置引用该路由也不会找不到。

    {
        path: "/usermessage", component: SystemNotification, alias:"/notification"
    },
    滚动行为

    代码 vue-router 0.7 滚动行为
    我的消息中配置路由及其视图,如下图:

    配置4个路由及其对应组件

    我的消息

    系统消息 好友消息 用户组消息 其他消息
    path: "/usermessage", component: SystemNotification, alias: "/notification",
    children: [
        { path: "system-msg", component: SystemMessage, meta: { scrollToTop: false } },
        { path: "friend-msg", component: FriendMessage, meta: { scrollToTop: true } },
        { path: "group-msg", component: GroupMessage, meta: { scrollToTop: true } },
        { path: "other-msg", component: OtherMessage, meta: { scrollToTop: true } }
    ]

    四个组件中定义一系列字符串列表。在其中插入下一个消息列表的routerLink,如

    效果如下

    使用历史记录回退时,保持原纪录位置,是正常的。但点击链接,跳到新页面,也是原来位置,这不是我们预期的行为,我们使用路由属性scrollBehavior解决这个问题。它与属性routes是同一级别的属性。

    //router.js
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        } else {
            const position = {}
            if(to.hash) {
                position.selector = to.hash
                if(to.hash === "#anchor") {
                    position.offset = { y: 100 }
                }
            }
            if (to.matched.some(m => m.meta.scrollToTop)) {
                position.x = 0
                position.y = 0
            }
            return position;
        }
    }

    为了后面定位到锚点,我们改造FriendMessage,为链接到下一个消息列表的连接添加hash#anchor

  • 用户组消息
  • 修改GroupMessage

    • 用户组消息{{item}}
    • 用户组消息30
    • 用户组消息{{item+30}}
    • 其他消息
    • 用户组消息{{item+50}}

    scrollBehavior接收3个参数tofromsavedPosition。以上函数中savedPosition当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时可用,这里与默认的效果没区别。我们获取to路由的元数据meta.scrollToTop(保存自定义的数据),当为true时我们切换到to对应的页面时,我们定位到{x:0,y:0},否则保持默认行为。如果to存在hash,设置{selector:to.hash}定位到锚点,可以具体定位锚点定位的偏移量{selector:to.hash,offset:{y:100}}

    History模式

    code 0.8

    在以上所有的请求路径都带#,这不是我们所期望的,但是可用于所有的浏览器,这种模式为默认的hash模式,如:

    http://localhost:8080/#/settings/useremail

    现在使用history模式,设置属性mode

    mode:"history",
    routes: [/*...*/],
    scrollBehavior(to, from, savedPosition) {/*...*/}

    现在路径就正常了,但它只能用于支持H5 History API的浏览器。

    http://localhost:8080/settings/useremail

    History API需要服务器的支持,否则当重载页面时,会发生404页面找不到,就像下面这样。

    这里使用webpack-dev-server,设置webpack的devServer.historyApiFallback为true,使其支持History API。如

    devServer: {
        historyApiFallback:true,
        noInfo: true
    },

    其他服务器的配置见官方文档后端配置例子

    然后又发现一个bug,像下面这样,显示http://localhost:8080/settings/app.js找不到

    其实这是个webpack的问题,插入js资源时,像下面这样

    它是相对于当前请求的路径,为了解决这个问题,我们要在webpack中设置output中的publicPath属性为/

    output: {
        path: path.resolve(__dirname, "./dist"),
        publicPath: "/",
        filename: "[name].js",
    },

    它在所有资源前加上虚拟路径/app.js就变为绝对路径localhost:8080/app.js,这下就没什么问题了。

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/94100.html

    相关文章

    • Angular路由导航的7个步骤

      摘要:路由过程的个步骤每次点击链接或浏览器改变时,路由器都会确保应用程序做出相应的反应。一旦所有的都已完成,其返回值是合并的路径的然后路由器继续步骤。你可以使用助记记住路由器经过的步骤的顺序。 路由过程的7个步骤 每次点击链接或浏览器URL改变时,Angular路由器都会确保应用程序做出相应的反应。 为了做到这一点,Angular路由器执行以下7个步骤的顺序: 解析(Parse):它解析...

      BLUE 评论0 收藏0
    • RouteReuseStrategy angular路由复用策略详解,深度刨析路由复用策略

      摘要:一个路由复用策略用例下面贴一个路由复用策略用例,应该是满足大部分人的业务要求,注意事项只能是末级路由的缓存,且路由切换的时候路由节点上的不能超过两个。 关于路由复用策略网上的文章很多,大多是讲如何实现tab标签切换历史数据,至于如何复用的原理讲的都比较朦胧,代码样例也很难适用各种各样的路由配置,比如懒加载模式下多级嵌套路由出口网上的大部分代码都会报错。我希望能通过这篇文章把如何复用路由...

      wendux 评论0 收藏0
    • 路由表】什么是路由

      摘要:默认路由表不允许删除编辑,默认路由表中的规则均为系统路由。路由表生效粒度为子网。每个子网必须要绑定且只能绑定一张路由表,一张路由表可被多个子网绑定。每张路由表最多支持条自定义路由规则。路由表简介名词解释默认路由表:创建VPC时系统默认为VPC创建的路由表。新创建的子网将默认绑定默认路由表。默认路由表不允许删除、编辑,默认路由表中的规则均为系统路由。自定义路由表:用户自主创建的路由表,并可自定...

      Tecode 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <