精华内容
参与话题
问答
  • 场景: 对登陆成功后的用户可能会有不同的身份权限, 看到的系统菜单以及功能不一样, 这个时候需要用到 动态路由的方式来处理 路由结构: |— initRoutes 默认是可以看到的路由,是所有用户都可以看到的路由菜单 |—...

    一、 addRoutes权限控制

    场景: 对登陆成功后的用户可能会有不同的身份权限, 看到的系统菜单以及功能不一样, 这个时候需要用到 动态路由的方式来处理

    路由结构:
    |— initRoutes 默认是可以看到的路由,是所有用户都可以看到的路由菜单
    |— asyncRouetes 需要登陆后确认权限才能看到的路由

    1.1 初始路由initRoutes

    import Vue from "vue";
    import VueRouter from "vue-router";
    
    Vue.use(VueRouter);
    
    import home from '../pages/home.vue';
    import news from '../pages/news.vue';
    
    var allRoutes = [{
        path:'/login',
        name:'login',
        meta:{
            title:'登陆'
        },
        component:()=>import('../pages/login.vue')
    },{
        path:'/home',
        name:'home',
        component:home,
        meta:{
            title:'首页'
        },
    },{
        path:'/news',
        name:'news',
        meta:{
            title:'新闻'
        },
        component:news
    }]
    export default new VueRouter({
        routes:allRoutes,
        mode:'hash', //history
        base:'/',
        //   vue-router 认为只有路由真正匹配时,才会加上 exact-active-link 这个 class,
        //   如果只有一部分重合,就会加上 active-menu。
        // fallback
        // 不是所有浏览器都支持前端路由的方式,如果不支持,设置 fallback: true,
        // vue 会自动 fallback 到 hash 模式。
        fallback: true,
        linkActiveClass: "active-menu",
        linkExactActiveClass: "exact-active-menu",
    })
    // 在main.js中把router 实例注入到 vue 根实例中,就可以使用路由了
    
    

    1.2 动态路由 asyncRouetes

    var asyncRouetes = [
        {
            path:'/finance',
            component:()=>import('../pages/finance.vue'),
            meta: {
                title: '财务信息',
                roles: ['admin']
            }
           },
           {
           path:'/staffs',
           component:()=>import('../pages/staffs.vue'),
           meta: {
               title: '员工信息',
               roles: ['admin','guest']
             }
           }
       ];
    
     export default  asyncRouetes;
    
    

    1.3 默认在vueRouters 实例化的时候, 只是传入初始的路由

    export default new VueRouter({
        routes:allRoutes,
        mode:'hash', //history
        base:'/',
        //   vue-router 认为只有路由真正匹配时,才会加上 exact-active-link 这个 class,
        //   如果只有一部分重合,就会加上 active-menu。
        // fallback
        // 不是所有浏览器都支持前端路由的方式,如果不支持,设置 fallback: true,
        // vue 会自动 fallback 到 hash 模式。
        fallback: true,
        linkActiveClass: "active-menu",
        linkExactActiveClass: "exact-active-menu",
    })
    // 在main.js中把router 实例注入到 vue 根实例中,就可以使用路由了
    
    在vue实例化的时候进行挂载
    

    1.4 进行登录处理,获取token和用户信息

     localStorage.setItem('token','XXXXXXXXXXX');
     localStorage.setItem('userRole','admin'); //submain, guest
    

    1.5 添加全局路由守卫

    import Vue from 'vue';
    
    import axios from './providers/axios2.js';
    import api from './providers/api.js';
    
    import App from './App.vue'
    import VueRouter from './router/index.js';
    
    //如果全局, 别的页面都不需要做任何处理, babel-plugin-component也不需要配置
    // import ElementUI from 'element-ui';
    // import 'element-ui/lib/theme-chalk/index.css';
    // Vue.use(ElementUI);
    
    Vue.prototype.$axios = axios;
    Vue.prototype.$api = api;
    
    window.EventEmitter = new Vue();
    
    //template模式
    // new Vue({
    //     el:'#app',
    //     data:{
    //         hello:'hello',
    //         msg:'world'
    //     },
    //     // template:`<div id="app1">
    //     //     <h1>{{msg}}</h1>
    //     // </div>`,
    //     components:{App}, //注册全局组件
    //     template:'<App/>'
    // });
    import asyncRouetes from './router/dynamic.js';
    var initRoutes = VueRouter.options.routes;
    
    //优化
    var allPaths = [];
    asyncRouetes.forEach((option)=>{
        allPaths.push(option.path);
    })
    VueRouter.beforeEach((to, from, next) => {
        var userRole = localStorage.getItem('userRole');
        var token = localStorage.getItem('token');
    
         //需要判断下是否已经添加过动态路由,不要重复添加
         // 方式: 判断默认和路由和 读取的路由是否一致  
            var isHAS =  VueRouter.options.routes.some((option)=>{
                return allPaths.includes(option.path)
             });   
             if(isHAS){
                 next();
                 return;
             }
    //判断是否存在token
        if(token && userRole){
            var asyncRouete =  asyncRouetes.filter((option,index)=>{
                return option.meta.roles.includes(userRole);
            });
            //将新路由添加到路由中, 如果不加组件component不能正确渲染
            VueRouter.addRoutes(asyncRouete);
             //为了正确渲染导航,将对应的新的路由添加到VueRouter中	
            VueRouter.options.routes = [...initRoutes,...asyncRouete];
            EventEmitter.$emit('allOption',VueRouter.options.routes)
            next();
        } else {
        // 跳转到登陆页面
            if(to.path=='/login') {
                next();
            } else {
                next('/login');
            }
        }
      
    })
    
    // render
    var vm = new Vue({
        el:'#app',
        data:{
            hello:'hello',
            msg:'world'
        },
        router:VueRouter,
        // render(createElement){
        //     return createElement('div',{
        //         id:'app1'
        //     },[
        //         createElement('h1',this.msg)
        //     ])
        // },
        //使用组件,利用render函数渲染
        // render(h){
        //      return h(App)
        // },
        render:h=>h(App)
    
    });
    
    

    二、 权限控制在项目中的实际使用

    2.1 配置必要的动态路由文件

    在router文件夹下添加 dynamic.js
    在src/pages文件夹下添加 finance.vue, staffs.vue作为测试

    var asyncRouetes = [
        {
            path:'/finance',
            component:()=>import('../pages/finance.vue'),
            meta: {
                title: '财务信息',
                roles: ['admin']
            }
           },
           {
           path:'/staffs',
           component:()=>import('../pages/staffs.vue'),
           meta: {
               title: '员工信息',
               roles: ['admin','guest']
             }
           }
       ];
    export default  asyncRouetes;
    

    2.2 登录成功以后需要获取toekn以及用户信息

     localStorage.setItem('token','XXXXXXXXXXX');
     localStorage.setItem('userRole','admin'); //submain, guest
    

    2.3 在入口文件main.js进行 导航守卫

    引入文件:

     import asyncRouetes from './router/dynamic.js';
    

    全局前置守卫配置:

    注意:路由守卫的时候是针对vueRouter实例对象

     VueRouter.beforeEach((to,from,next)=>{
         //如果自定义了标题就取标题,否则拿全局标题
       window.document.title = to.meta.title?to.meta.title:'测试系统';
    
         //这里可以获取登陆后的权限
         var UserToken = localStorage.getItem('token');
         var userRole = localStorage.getItem('userRole');
    
         //判断是否存在token
         if(UserToken && userRole){
             //已登录
             var asyncRouteMenu = asyncRouetes.filter((item,index)=>{
                 return item.meta.roles.includes(userRole)
             })
    
             //将新路由添加到路由中, 如果不加组件component不能正确渲染
             VueRouter.addRoutes(asyncRouteMenu); 
             //为了正确渲染导航,将对应的新的路由添加到VueRouter中
             
             var initRoutes = VueRouter.options.routes;
             VueRouter.options.routes = [...initRoutes,...asyncRouteMenu];
             next();
    
         } else {
             //是否处于登陆页面
             if(to.path=='/login'){ 
                 //如果是登录页面路径,就直接next()
                 next();
             } else {
                 //不然就跳转到登录;
                 next('/login');
             }
         } 
     })
    

    2.3 如何处理菜单的显示

    在App.vue里 ,原来的菜单部分

     <router-link to="/login" tag='li'>登陆</router-link> 
     <router-link to="/home?name=laney" tag='li'>主页</router-link>
     <router-link to="/news" tag='li'>新闻</router-link> 
    

    需要修改为动态的, 因为这里所有的路由 不是写死的, 需要从路由实例this.$router.options里获取

    可以在计算器属性里设置

    
      <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <title>权限控制- addRoutes</title>
       <link rel="stylesheet" href="css/animate.css">
       <style>
       	.active{
       		font-size:20px;
       		color:#ff7300;
       		text-decoration:none;
       	}
           .main-menu a {
               display: inline-block;
               margin-right: 10px;
           }
       </style>
       <script src="js/vue.js"></script>
       <script src="js/vue-router.js"></script>
    </head>
    <body>
       <div id="itapp">
       	<div class="main-menu">
               <!-- 写成动态的 -->
               <!-- $router.options.routes  可以从计算器属性-->
               <!-- <router-link v-for="(item,index) in $router.options.routes" :key="index" :to="item.path">{{item.meta.title}}</router-link> -->
               <router-link v-for="(item,index) in getMyRoutes" :key="index" :to="item.path">{{item.meta.title}}</router-link>
               
           </div>
       	<div>
       		<transition enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
       			<router-view></router-view>
       		</transition>
       	</div>
    
       	<hr>
       	<button @click="push">添加路由</button>
       	<button @click="replace">替换路由</button>
       </div>
    
       <template id="user">
       	<div>
       		<h3>用户信息</h3>
       		<ul>
       			<router-link to="/user/login?name=tom&pwd=123" tag="li">用户登陆</router-link>
                   <router-link to="/user/regist/alice/456" tag="li">用户注册</router-link>
       		</ul>
       		<router-view></router-view>
       	</div>
       </template>
    
       <script>
       	var Home={
       		template:'<h3>我是主页</h3>'
       	}
       	var User={
       		template:'#user'
       	}
       	var Login={
       		template:'<h4>用户登陆。。。获取参数:{{$route.query}},{{$route.path}}</h4>'
       	}
       	var Regist={
       		template:'<h4>用户注册。。。获取参数:{{$route.params}},{{$route.path}}</h4>'
           }
           var Finance={
       		template:'<h4>财务信息</h4>'
       	}
       	var News={
       		template:'<h3>我是新闻</h3>'
       	}
           //默认是可以看到的路由
       	var initRoutes=[
       		{
       			path:'/home',
       			component:Home,
       			// 路由元信息
                   meta: {
                       title: '首页'
                    }
       		},
       		{
       			path:'/user',
                   component:User,
                   meta: {
                       title: '用户'
                    },
       			// children:[
       			// 	{
       			// 		path:'login',
                   //         component:Login
       			// 	},
       			// 	{
       			// 		path:'regist/:username/:password',
                   //         component:Regist
                   //     }
    
       			// ]
               },
       		// {
       		// 	path:'*',
       		// 	redirect:'/home',
               //     hidden: true    //隐藏不需要渲染到页面上的路由
       		// }
       	];
    
       	 //需要登陆后确认权限才能看到的路由
       	 var asyncRouetes = [
            {
                path:'/finance',
                component:Finance,
                 meta: {
                    title: '财务信息',
                    roles: ['admin']
                }
       		},
       		{
               path:'/news',
               component:News,
               meta: {
                   title: '新闻中心',
                   roles: ['admin','guest']
                 }
               }
           ];
       	const routerAll=new VueRouter({
       		routes:initRoutes, //简写,相当于routes:routes
       		linkActiveClass:'active', //更新活动链接的class类名,默认的激活的 class
       		linkExactActiveClass:'active-extact',  //精确激活的 class
       		mode: "hash", //默认
       	});
    
       	导航守卫
       	//加入你获取了角色
       	routerAll.beforeEach((to, from, next) => {
       		// var auth = localStorage.getItem('userRole');
       		var auth = 'admin';
       		var asyncRouete =  asyncRouetes.filter((option,index)=>{
       			return option.meta.roles.includes(auth);
       		});
       		//将新路由添加到路由中, 如果不加组件component不能正确渲染
       		routerAll.addRoutes(asyncRouete);
       		 //为了正确渲染导航,将对应的新的路由添加到routerAll中	
       		routerAll.options.routes = [...initRoutes,...asyncRouete];
       		debugger
       		next();
       	})
       
       	new Vue({
               el:'#itapp',
       		router:routerAll, //注入路由
               computed:{
                   getMyRoutes(){
                       var thisData = this.$router.options.routes;
                       return thisData;
                   }
               },
       		methods:{
       			push(){
       				this.$router.push({path:'home'}); //添加路由,切换路由	
       			},
       			replace(){
       				routerAll.replace({path:'user'}); //替换路由,没有历史记录
       			}
               }
              
              
       	});
       </script>
    </body>
    </html>
    
    

    借助中心控制vue实例 , 利用eventEmitter

      <template>
         <div id="app">
            <h1>{{msg}} <button type="button" @click="logOut()">注销</button></h1>
            <div >
                <ul class="main-menu">
                 <router-link v-for="(item,index) in getMyRoutes" :key="index" :to="item.path" tag='li'>{{item.meta.title}}</router-link> 
                    <!-- <router-link to="/login" tag='li'>登陆</router-link> 
                    <router-link to="/home?name=laney" tag='li'>主页</router-link>
                    <router-link to="/news" tag='li'>新闻</router-link>  -->
                </ul>
            <!-- <ul @click="gotoPage($event)">
                <li tag='home'>主页</li>
                <li tag='news'>新闻</li>
            </ul>    -->
            </div>
            <router-view></router-view>
        </div>
    </template>
    
     <script>
        export default {
            name: 'app',
            data () {
                return {
                    msg: 'Welcome to ruanmou',
                    getMyRoutes:[]
                }
            },
            computed:{
                // getMyRoutes(){
                //     console.log('this.$router.options.routes')
                //     console.log(this.$router.options.routes)
                //     var thisData = this.$router.options.routes;
                //     return thisData;
                // }
            },
            methods:{
                 logOut(){
                    localStorage.clear();
                    this.$router.push({
                        path:'/login'
                    })
                    location.reload();
                  },
                gotoPage(ev){
                    var target = ev.target,
                        tag = target.getAttribute('tag');
                    switch(tag){
                        case 'home':
                            //相当于get方式
                            this.$router.push({
                                path:'/home',
                                query:{
                                    name:'laney'
                                }
                            })
                        break;
                        case 'news':
                            this.$router.push({
                                path:'/news',
                                query:{
                                    age:'10'
                                }
                            })
                        break;
                    }
                    }
            },
            mounted(){  
                EventEmitter.$on('allOption',(res)=>{
                    console.log('mounted')
                    console.log(res)
                    this.getMyRoutes = res;
                })
                console.log(this.$router.options.routes)
            }
        }
    </script>
    
    <style scoped>
    .main-menu li {
        display: inline-block;
        margin-right: 30px;
        background: #000;
        color: #fff;
        padding: 5px 20px;
        cursor: pointer;
    }
    .main-menu li.active-menu{
        background: #ff6600;
        
    }
    </style>
    
    
    展开全文
  • 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)

    万次阅读 多人点赞 2017-04-09 23:25:01
    在微服务架构中,需要几个关键的组件,服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个组件可以组建一个简单的微服务架构。客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务...

    转载请标明出处:
    https://www.fangzhipeng.com/springcloud/2017/06/05/sc05-zuul.html
    本文出自方志朋的博客

    个人博客纯净版:https://www.fangzhipeng.com/springcloud/2017/06/05/sc05-zuul.html

    最新Finchley版本,请访问:
    https://www.fangzhipeng.com/springcloud/2018/08/05/sc-f5-zuul.html
    或者
    http://blog.csdn.net/forezp/article/details/81041012

    在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简答的微服务系统如下图:

    在这里插入图片描述
    注意:A服务和B服务是可以相互调用的,作图的时候忘记了。并且配置服务也是注册到服务注册中心的。

    在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服。,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理(下一篇文章讲述),配置服务的配置文件放在git仓库,方便开发人员随时改配置。

    一、Zuul简介

    Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。

    zuul有以下功能:

    • Authentication
    • Insights
    • Stress Testing
    • Canary Testing
    • Dynamic Routing
    • Service Migration
    • Load Shedding
    • Security
    • Static Response handling
    • Active/Active traffic management

    二、准备工作

    继续使用上一节的工程。在原有的工程上,创建一个新的工程。

    三、创建service-zuul工程

    其pom.xml文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    
    	<groupId>com.forezp</groupId>
    	<artifactId>service-zuul</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>jar</packaging>
    
    	<name>service-zuul</name>
    	<description>Demo project for Spring Boot</description>
    
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-zuul</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    
    
    </project>
    
    
    

    在其入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能:

    @EnableZuulProxy
    @EnableEurekaClient
    @SpringBootApplication
    public class ServiceZuulApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ServiceZuulApplication.class, args);
    	}
    }
    
    
    

    加上配置文件application.yml加上以下的配置代码:

    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    server:
      port: 8769
    spring:
      application:
        name: service-zuul
    zuul:
      routes:
        api-a:
          path: /api-a/**
          serviceId: service-ribbon
        api-b:
          path: /api-b/**
          serviceId: service-feign
    
    

    首先指定服务注册中心的地址为http://localhost:8761/eureka/,服务的端口为8769,服务名为service-zuul;以/api-a/ 开头的请求都转发给service-ribbon服务;以/api-b/开头的请求都转发给service-feign服务;

    依次运行这五个工程;打开浏览器访问:http://localhost:8769/api-a/hi?name=forezp ;浏览器显示:

    hi forezp,i am from port:8762

    打开浏览器访问:http://localhost:8769/api-b/hi?name=forezp ;浏览器显示:

    hi forezp,i am from port:8762

    这说明zuul起到了路由的作用

    四、服务过滤

    zuul不仅只是路由,并且还能过滤,做一些安全验证。继续改造工程;

    @Component
    public class MyFilter extends ZuulFilter{
    
        private static Logger log = LoggerFactory.getLogger(MyFilter.class);
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
            Object accessToken = request.getParameter("token");
            if(accessToken == null) {
                log.warn("token is empty");
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                try {
                    ctx.getResponse().getWriter().write("token is empty");
                }catch (Exception e){}
    
                return null;
            }
            log.info("ok");
            return null;
        }
    }
    
    
    • filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
      • pre:路由之前
      • routing:路由之时
      • post: 路由之后
      • error:发送错误调用
    • filterOrder:过滤的顺序
    • shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
    • run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。

    这时访问:http://localhost:8769/api-a/hi?name=forezp ;网页显示:

    token is empty

    访问 http://localhost:8769/api-a/hi?name=forezp&token=22 ;
    网页显示:

    hi forezp,i am from port:8762

    本文源码下载:
    https://github.com/forezp/SpringCloudLearning/tree/master/chapter5

    更多阅读

    史上最简单的 SpringCloud 教程汇总

    SpringBoot教程汇总

    Java面试题系列汇总

    五、参考资料:

    router_and_filter_zuul

    优秀文章推荐:

    SouthEast
    扫码关注公众号有惊喜

    (转载本站文章请注明作者和出处 方志朋的博客

    展开全文
  • gateway配置路由主要有两种方式,一种是用yml配置文件,一种是写代码里,这两种方式都是不支持动态配置的。如: 下面就来看看gateway是如何加载这些配置信息的。 1 路由初始化 无论是yml还是代码,这些配置最终...

    gateway配置路由主要有两种方式,一种是用yml配置文件,一种是写代码里,这两种方式都是不支持动态配置的。如:

    下面就来看看gateway是如何加载这些配置信息的。

    1 路由初始化

    无论是yml还是代码,这些配置最终都是被封装到RouteDefinition对象中。

    一个RouteDefinition有个唯一的ID,如果不指定,就默认是UUID,多个RouteDefinition组成了gateway的路由系统。

    所有路由信息在系统启动时就被加载装配好了,并存到了内存里。我们从源码来看看。

    圆圈里就是装配yml文件的,它返回的是PropertiesRouteDefinitionLocator,该类继承了RouteDefinitionLocator,RouteDefinitionLocator就是路由的装载器,里面只有一个方法,就是获取路由信息的。该接口有多个实现类,分别对应不同方式配置的路由方式。

    通过这几个实现类,再结合上面的AutoConfiguration里面的Primary信息,就知道加载配置信息的顺序。

    PropertiesRouteDefinitionLocator-->|配置文件加载初始化| CompositeRouteDefinitionLocator
    RouteDefinitionRepository-->|存储器中加载初始化| CompositeRouteDefinitionLocator
    DiscoveryClientRouteDefinitionLocator-->|注册中心加载初始化| CompositeRouteDefinitionLocator

    参考:https://www.jianshu.com/p/b02c7495eb5e

    https://blog.csdn.net/X5fnncxzq4/article/details/80221488

    这是第一顺序,就是从CachingRouteLocator中获取路由信息,我们可以打开该类进行验证。

    不管发起什么请求,必然会走上面的断点处。请求一次,走一次。这是将路由信息缓存到了Map中。配置信息一旦请求过一次,就会被缓存到上图的CachingRouteLocator类中,再次发起请求后,会直接从map中读取。

    如果想动态刷新配置信息,就需要发起一个RefreshRoutesEvent的事件,上图的cache会监听该事件,并重新拉取路由配置信息。

    通过下图,可以看到如果没有RouteDefinitionRepository的实例,则默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。即通过自定义的RouteDefinitionRepository类,来提供路由配置信息。

    例如:

    在getRouteDefinitions方法返回你自定义的路由配置信息即可。这里可以用数据库、nosql等等任意你喜欢的方式来提供。而且配置信息修改后,发起一次RefreshRoutesEvent事件即可让配置生效。这就是动态配置路由的核心所在,下面来看具体代码实现。

    2 基于数据库、缓存的动态路由

    pom.xml如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.maimeng</groupId>
        <artifactId>apigateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>apigateway</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.51</version>
            </dependency>
            <!--<dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    
    </project>
    

    注意这里是SR1,经测试SR2有bug,会出问题。

    @Configuration
    public class RedisConfig {
    
        @Bean(name = {"redisTemplate", "stringRedisTemplate"})
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
            StringRedisTemplate redisTemplate = new StringRedisTemplate();
            redisTemplate.setConnectionFactory(factory);
            return redisTemplate;
        }
    
    }
    

    核心类:

    @Component
    public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
    
        public static final String GATEWAY_ROUTES = "geteway_routes";
    
        @Resource
        private StringRedisTemplate redisTemplate;
    
        @Override
        public Flux<RouteDefinition> getRouteDefinitions() {
            List<RouteDefinition> routeDefinitions = new ArrayList<>();
            redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
                    .forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class)));
            return Flux.fromIterable(routeDefinitions);
        }
    
        @Override
        public Mono<Void> save(Mono<RouteDefinition> route) {
            return null;
        }
    
        @Override
        public Mono<Void> delete(Mono<String> routeId) {
            return null;
        }
    
    }

    主要是在get方法里,此处从redis里获取配置好的Definition。

    然后我们的工作就是将配置信息,放到redis里即可。

    下面就是我模拟的一个配置,等同于在yml里

    spring:
      cloud:
        gateway:
          routes:
          - id: header
            uri: http://localhost:8888/header
            filters:
            - AddRequestHeader=header, addHeader
            - AddRequestParameter=param, addParam
            predicates:
            - Path=/jd
    @Resource
        private StringRedisTemplate redisTemplate;
        @PostConstruct
        public void main() {
            RouteDefinition definition = new RouteDefinition();
            definition.setId("id");
            URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
           // URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
            definition.setUri(uri);
    
            //定义第一个断言
            PredicateDefinition predicate = new PredicateDefinition();
            predicate.setName("Path");
    
            Map<String, String> predicateParams = new HashMap<>(8);
            predicateParams.put("pattern", "/jd");
            predicate.setArgs(predicateParams);
    
            //定义Filter
            FilterDefinition filter = new FilterDefinition();
            filter.setName("AddRequestHeader");
            Map<String, String> filterParams = new HashMap<>(8);
            //该_genkey_前缀是固定的,见org.springframework.cloud.gateway.support.NameUtils类
            filterParams.put("_genkey_0", "header");
            filterParams.put("_genkey_1", "addHeader");
            filter.setArgs(filterParams);
    
            FilterDefinition filter1 = new FilterDefinition();
            filter1.setName("AddRequestParameter");
            Map<String, String> filter1Params = new HashMap<>(8);
            filter1Params.put("_genkey_0", "param");
            filter1Params.put("_genkey_1", "addParam");
            filter1.setArgs(filter1Params);
    
            definition.setFilters(Arrays.asList(filter, filter1));
            definition.setPredicates(Arrays.asList(predicate));
    
            System.out.println("definition:" + JSON.toJSONString(definition));
            redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
        }

    定义好后,将其放到redis里,之后启动项目访问/jd,再启动后台的localhost:8888项目。即可进行验证。

    之后如果要动态修改配置,就可以通过类似于上面的方式,来获取json字符串,然后将字符串放到redis里进行替换。替换后,需要通知gateway主动刷新一下。

    刷新时,可以定义一个controller,然后调用一下notifyChanged()方法,就能完成新配置的替换了。

    3 通过REST接口

    gateway是自带接口能增删改查配置的,这个网上有比较多的教程,随便找个看看就明白了。譬如:

    http://springcloud.cn/view/368

    我发个类作为参考

    package com.maimeng.apigateway.route;
    
    import com.alibaba.fastjson.JSON;
    import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
    import org.springframework.cloud.gateway.filter.FilterDefinition;
    import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.web.util.UriComponentsBuilder;
    import reactor.core.publisher.Mono;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.Resource;
    import java.net.URI;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    import static com.maimeng.apigateway.repository.RedisRouteDefinitionRepository.GATEWAY_ROUTES;
    
    /**
     * @author wuweifeng wrote on 2018/10/25.
     */
    @Service
    public class DynamicRouteService implements ApplicationEventPublisherAware {
    
        @Resource
        private RouteDefinitionWriter routeDefinitionWriter;
    
        private ApplicationEventPublisher publisher;
    
        private void notifyChanged() {
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        }
    
    
        /**
         * 增加路由
         *
         */
        public String add(RouteDefinition definition) {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            notifyChanged();
            return "success";
        }
    
    
        /**
         * 更新路由
         */
        public String update(RouteDefinition definition) {
            try {
                this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
            } catch (Exception e) {
                return "update fail,not find route  routeId: " + definition.getId();
            }
            try {
                routeDefinitionWriter.save(Mono.just(definition)).subscribe();
                notifyChanged();
                return "success";
            } catch (Exception e) {
                return "update route  fail";
            }
    
    
        }
    
        /**
         * 删除路由
         *
         */
        public String delete(String id) {
            try {
                this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
    
                notifyChanged();
                return "delete success";
            } catch (Exception e) {
                e.printStackTrace();
                return "delete fail";
            }
    
        }
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.publisher = applicationEventPublisher;
        }
    
        @Resource
        private StringRedisTemplate redisTemplate;
        @PostConstruct
        public void main() {
            RouteDefinition definition = new RouteDefinition();
            definition.setId("id");
            URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
           // URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
            definition.setUri(uri);
    
            //定义第一个断言
            PredicateDefinition predicate = new PredicateDefinition();
            predicate.setName("Path");
    
            Map<String, String> predicateParams = new HashMap<>(8);
            predicateParams.put("pattern", "/jd");
            predicate.setArgs(predicateParams);
    
            //定义Filter
            FilterDefinition filter = new FilterDefinition();
            filter.setName("AddRequestHeader");
            Map<String, String> filterParams = new HashMap<>(8);
            //该_genkey_前缀是固定的,见org.springframework.cloud.gateway.support.NameUtils类
            filterParams.put("_genkey_0", "header");
            filterParams.put("_genkey_1", "addHeader");
            filter.setArgs(filterParams);
    
            FilterDefinition filter1 = new FilterDefinition();
            filter1.setName("AddRequestParameter");
            Map<String, String> filter1Params = new HashMap<>(8);
            filter1Params.put("_genkey_0", "param");
            filter1Params.put("_genkey_1", "addParam");
            filter1.setArgs(filter1Params);
    
            definition.setFilters(Arrays.asList(filter, filter1));
            definition.setPredicates(Arrays.asList(predicate));
    
            System.out.println("definition:" + JSON.toJSONString(definition));
            redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
        }
    }
    

     

    展开全文
  • (精华)2020年8月6日 Angular 路由的使用

    万次阅读 2020-08-06 23:45:25
    Angular 中的路由 一、 Angular 创建一个默认带路由的项目 命令创建项目 ng new ng-demo --skip-install [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qyw9k4u3-1596728634416)(/pic/...

    Angular 中的路由

    一、 Angular 创建一个默认带路由的项目

    1. 命令创建项目

    ng new ng-demo --skip-install

    在这里插入图片描述

    1. 创建需要的组件
    ng g component components/home
    ng g component components/news
    ng g component components/newscontent
    
    1. 找到 app-routing.module.ts 配置路由

    引入组件

    import { HomeComponent } from './components/home/home.component';
    import { NewsComponent } from './components/news/news.component';
    import { ProductComponent } from './components/product/product.component';
    

    配置路由

    const routes: Routes = [
    {path: 'home', component: HomeComponent},
    {path: 'news', component: NewsComponent},
    {path:'product', component:ProductComponent },
    {path: '*', redirectTo: '/home', pathMatch: 'full' }
    ];
    
    1. 找到 app.component.html 根组件模板,配置 router-outlet 显示动态加载的路由
    <h1>
        <a routerLink="/home">首页</a>
        <a routerLink="/news">新闻</a>
    </h1>
    <router-outlet></router-outlet>
    

    二、Angular routerLink 跳转页面默认路由

    
    <a routerLink="/home">首页</a>
    <a routerLink="/news">新闻</a>
    
    
    
    //匹配不到路由的时候加载的组件 或者跳转的路由
    {
        path: '**', /*任意的路由*/
        // component:HomeComponent
        redirectTo:'home'
    }
    
    

    三、Angular routerLinkActive 设置 routerLink 默认选中路由

    <h1>
      <a routerLink="/home" routerLinkActive="active">
        首页
      </a>
      <a routerLink="/news" routerLinkActive="active">
        新闻
      </a>
    </h1>
    
    <h1>
        <a [routerLink]="[ '/home' ]" routerLinkActive="active">首页</a>
        <a [routerLink]="[ '/news' ]" routerLinkActive="active">新闻</a>
    </h1>
    

    四、动态路由

    4.1.问号传参

    跳转方式,页面跳转或js跳转
    问号传参的url地址显示为 …/list-item?id=1

    queryParams属性是固定的

    <a [routerLink]="[’/list-item’]" [queryParams]="{id:item.id}">
    {{ item.name }}

    //js跳转
    //router为ActivatedRoute的实例

    
    import { Router } from '@angular/router';
    .
    constructor(private router: Router) {}
    .
    this.router.navigate(['/newscontent'],{
      queryParams:{
        name:'laney',
        id:id
      },
      skipLocationChange: true 
      //可以不写,默认为false,设为true时路由跳转浏览器中的url会保持不变,传入的参数依然有效
    });
    

    获取参数方式

    
    import { ActivatedRoute } from '@angular/router';
    
    constructor(public route:ActivatedRoute) { }
    ngOnInit() { 
        this.route.queryParams.subscribe((data)=>{
          console.log(data);
     })
    }
    
    

    4.2 路径传参

    路径传参的url地址显示为 …/list-item/1

    <a [routerLink]="[’/list-item’, item.id]">
    {{ item.name }}

    //js跳转
    //router为ActivatedRoute的实例

    this.router.navigate([’/list-item’, item.id]);

    路径配置:
    {path: ‘list-item/:id’, component: ListItemComponent}

    获取参数方式

    this.route.params.subscribe(
      param => {
          this.id= param['id'];
      }
    )
    

    五、父子路由

    1. 创建组件引入组件

    import { WelcomeComponent } from ‘./components/home/welcome/welcome.component’;
    import { SettingComponent } from ‘./components/home/setting/setting.component’;

    1. 配置路由
    {
        path:'home',
        component:HomeComponent,
        children:[{
          path:'welcome',
          component:WelcomeComponent
        },{
          path:'setting',
          component:SettingComponent
        },
        {path: '**', redirectTo: 'welcome'}
      ]
    },
    
    
    1. 父组件中定义router-outlet

    展开全文
  • UmiJS介绍--路由(三)

    万次阅读 2018-10-31 15:16:29
    umi 会根据 pages 目录自动生成路由配置。 1. 约定式路由 1.1 基础路由 假设 pages 目录结构如下: + pages/ + users/ - index.js - list.js - index.js 那么,umi 会自动生成路由配置如下: [ { path: '/', ...
  • 静态路由及默认路由——基本配置

    万次阅读 多人点赞 2019-05-11 11:51:22
    静态路由:是指用户或网络管理员手工配置的路由信息。当网络拓扑结构或链路状态发生改变时,需要网络管理员手工配置静态路由信息。 相比较动态路由协议,静态路由无需频繁的交换各自的路由表,配置简单,比较适合...
  • umiJS Umi 是蚂蚁金服的底层前端框架 中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备。同时有强大的插件扩展各种功能。 什么时候不用 ...
  • 整个动态路由的设置是我自己研究写的,现在项目中没发现什么问题。如发现有问题,欢迎前来纠正,谢谢!!! 链接:https://pan.baidu.com/s/1J9R2TkqJk0H9JMjqp2WcFw 提取码:n4rk 复制这段内容后打开百度网盘手机...
  • React-Umi3.0带参数路由跳转

    千次阅读 2020-05-26 11:46:10
    umi3.0开始使用history替换掉了2.0的router 1、跳转时携带参数: history.push({ pathname: "/list/profileadvanced", ...//or 监听路由 const unlisten = history.listen((location, action) =>
  • vue中缓存路由组件

    万次阅读 2020-06-14 19:25:37
    通过keep-alive的缓存路由组件,切换路由时原来的路由组件中的数据也会和数据一起保存下来。 这样就能做到切换路由,不用重新从后台获取数据,直接使用刚刚缓存的数据 <keep-alive> <router-view></...
  • 登录的路由分别是 /pc/login /phone/login 实现代码:先根据浏览器判断是电脑还是手机端的界面 然后通过正则截取/login进行字符串拼接,形成新的路由 在入口App.vue中的script中添加 export default { name: 'App...
  • 策略路由 路由策略 双点双向引入

    千次阅读 多人点赞 2020-07-10 01:33:17
    策略路由 路由策略 双点双向引入 一、策略路由 (一)策略路由–Policy-Based Routing—PBR 1、什么是策略路由: 对特定数据报文不按照路由表内的条目执行转发,根据需要按照某种策略改变数据报文转发路径。 2、...
  • vue-router(路由)详细教程

    万次阅读 多人点赞 2018-05-28 21:08:30
      由于Vue在开发时对路由支持的不足,于是官方补充了vue-router插件。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的...
  • 华为 等价路由

    千次阅读 多人点赞 2020-05-14 23:16:57
    切忌自我感动,觉得自己有多努力多可怜,事实上,比你努力的人多的是,比你矫情的没几个。 文章目录 ...等价路由(ECMP)即为到达同一个目的 IP 或者目的网段存在多条 Cost 值相等的不同路由路径。
  • windows如何添加静态路由

    万次阅读 2018-07-09 13:58:44
    一、静态路由含义说明 说明: 第一行表示: 所有到达192.168网段的都转送到192.268.242.65网关 第三行表示: 所有到达本机的数据包都被转送到x.x.213.1网关 第四行表示: 所有到达10.243网段的数据包都被...
  • 路由算法总结

    万次阅读 2016-09-02 14:50:54
    概述通信子网络源节点和目的节点提供了多条传输路径的可能性。网络节点在收到一个分组后,要确定向...首先是路由算法所基于的性能指标,一种是选择最短路由,一种是选择最优路由;其次要考虑通信子网是采用虚电路还是数
  • 配置静态路由,动态路由,默认路由

    千次阅读 2019-07-30 16:06:09
    一、什么是路由 路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程[1]。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持...
  • 静态路由介绍: 静态路由是指由用户或网络管理员手工配置的路由信息。当网络的拓扑结构或链路的状态发生变化时,网络管理员需要手工去修改路由表中相关的静态路由信息。静态路由信息在缺省情况下是私有的,不会传递...
  • react做路由跳转,路由传参

    万次阅读 2019-06-30 20:37:30
    如果你项目当中用的是react全家桶,react + react-router + redux + axios + antd类似这样的组合的话那么做路由的话就是react-router, 首先要配置路由,关于react-router路由配置请看: ...而后可通过 this.p...
  • 华为 BGP路由聚合

    千次阅读 多人点赞 2020-07-01 11:03:17
    1、减少路由表的明细路由(减少空间占用和维护每条明细路由带来压力) 2、减少因为某些明细路由的频繁更新导致网络波动 1、summary automatic 自动聚合 2、Aggregation 手动聚合 拓扑 基础配置 1、AS-200运行OSPF...
  • 静态路由和默认路由小结

    千次阅读 2018-09-16 09:33:59
    静态路由和默认路由 介绍: 路由器在转发数据时,需要现在路由表中查找相应的路由,有三种途径: (1)直连路由:路由器自动添加和自己直连的路由 (2)静态路由:管理员手动添加的路由 (3)动态路由:由路由...
  • 路由选择、路由协议与路由算法

    千次阅读 多人点赞 2017-05-20 15:52:43
    本文旨在区分清楚路由选择、路由协议和路由算法的关系。然后讲解常用路由协议和路由算法。什么是路由选择百科的说法: 路由选择是指选择通过互连网络从源节点向目的节点传输信息的通道,而且信息至少通过一个中间...
  • SpringCloudGateway+Nacos 集成应用 ...4. 动态路由 只需要修改对应的配置文件即可,nacos会发起刷新 附录 网关配置 TIMEOUT.IN.MILLISECONDS: 60000 hystrix: command: fallbackcmd: execution: isolation:
  • 静态路由 ISP路由 策略路由 OSPF路由

    千次阅读 2018-11-15 14:43:53
    静态路由(英语:Static routing),一种路由的方式,路由项(routing entry)由手动配置,而非动态决定。与动态路由不同,静态路由是固定的,不会改变,即使网络状况已经改变或是重新被组态。一般来说,静态路由是...
  • 虚拟机实现LEDE软路由最简单,最完整教程

    万次阅读 多人点赞 2019-05-05 20:38:55
    虚拟机实现LEDE软路由最简单,最完整教程 看了网上的一些lede的虚拟机搭建的教程,都是一些花里胡哨,没有都是问题的内容,B站youtube上的一些视频讲解说的云里雾里,下面我简单整理一下,单网口构建LEDE的教程。 ...
  • 路由交换技术

    万次阅读 多人点赞 2019-03-08 15:42:46
    路由交换技术 MAC地址表 在交换机中有一张记录着局域网主机MAC地址与交换机接口的对应关系的表,交换机就是根据这张表负责将数据帧传输到指定的主机上的,这张表就是MAC表。 交换机的工作原理 交换机在接收到...
  • 路由数据库之路由查询

    千次阅读 2019-08-07 01:54:09
    路由数据库的查询是通过调用路由表struct fib_table的tb_lookup回调完成的,该回调函数是在路由表创建的时候就指定的,对于哈希方式的AF_INET路由表,该接口是fn_hash_lookup(),这篇笔记就是来记录该查询过程的。...
  • 本文目录前言静态路由动态路由 前言 前面讲解过一些Flutter路由知识,比如讲解Hero动画的时候,就提到过路由的相关知识。其实路由是专业名词,就是界面切换,而跳转界面解释路由跳转。(下图为动态路由实现效果) ...
  • vue实现路由跳转和嵌套(快速入门)

    万次阅读 多人点赞 2018-06-20 12:39:26
    vue-router路由的原理是通过改变网址,来实现页面的局部刷新,相比a标签跳转的不同之处在于,路由跳转不需要刷新整个页面。 一、路由跳转 1、安装路由vue-router: npm install vue-router 2、vue项目引入vue...
  • Android路由实现

    万次阅读 多人点赞 2016-11-28 07:55:06
    前几个月有幸参加了CSDN组织的MDCC移动开发者大会, 一天下来我最大的收获就是了解到了模块化开发, 回来之后我就一直在思考模块化的一些优点, 不说别的, 提供一种可插拔的开发方式就足够我们兴奋一会了~ 接下来自己...

空空如也

1 2 3 4 5 ... 20
收藏数 715,033
精华内容 286,013
关键字:

umijs 参数 路由