• 14、Vue之#app|指令缩写|ref|实例和组件的异同|滚动加载、computed与methods的区别、5个内置组件、动态组件效果的实现方案、插槽slot、vue组件的七种写法、子组件向父组件传值、vuex、前后端路由拦截、vue-cli-4.0以上版本的安装(win10系统)、前端路由及触发条件


    一、#app、指令缩写、$refs、实例和组件的异同、滚动加载
    1、App.vue组件中的’#app’替换掉了index.html中的’#app’,成为最终的’#app’ 
    2、指令缩写
    (1)v-bind
    完整语法如下:<a v-bind:href="url">...</a>,href是指令v-bind的参数
    缩写语法如下:<a :href="url">...</a>2)v-on
    完整语法如下:<a v-on:click="doSomething">...</a>
    缩写语法如下:<a @click="doSomething">...</a>3)<template v-slot:head="slotProps"></template>
    3、$refs 获取DOM元素或组件的引用。
    (1)$refs 加在普通的元素上,用this.$refs.name 获取到的是dom元素
    (2)$refs 加在组件上,用this.$refs.name 获取到的是组件实例,可以使用组件的所有方法。
    4、实例和组件的异同
    (1)相同:两者接收相同的配置,例如 data、computed、watch、methods 以及生命周期钩子等。
    (2)不同点:A、自定义组件没有el配置项。B、自定义组件中的data 必须是一个函数。原因:如果data是对象,那么组件实例在不同的地方调用,data指向的是相同的地址,此处数据改变,它处数据也改变;如果data是函数,那么组件实例在不同的地方调用,data指向data此次的执行结果,是不同的地址,此处数据改变,它处数据不改变。
    5、滚动加载(vue-infinite-scroll)
    <div v-infinite-scroll="loadMore" //无限滚动的回调函数是loadMore。其内部判断:如果当前页大于总页数则return,否则再次执行loadMore
       infinite-scroll-throttle-delay="500" //下次检查和这次检查之间的间隔
       infinite-scroll-disabled="isBusy" //isBusy为false,执行无限滚动的回调函数loadMore,即不繁忙的时候执行。
       infinite-scroll-distance="10"> //这里10决定了页面滚动到离页尾多少像素的时候触发回调函数,10是像素值。通常我们会在页尾做一个几十像素高的“正在加载中...”,这样的话,可以把这个div的高度设为infinite-scroll-distance的值即可。
      <div v-for="item in data" :key="item.index">{{item.name}}</div>
    </div>
    export default {
      data(){
        return{
          data:[],
          isBusy:false,
          itemsNumber:5,
          pageIndex:1
        };
      },   
      methods:{
        loadMore:function () {
          var self = this;
          if(this.pageIndex>response.data.total_pages){
            this.isBusy = true;
          }else{
            this.ajax.get('https:Xxxx',{
              params:{
                page:self.pageIndex,
                page_size:self.itemsNumber
              }
            }).then(function (response) {
              this.data.push({name:response.data.list});
              this.pageIndex++;
            }).catch(function (error) {
              this.error = error;
            })
          }
        }
      }
    }
    
    二、computed与methods的区别
    1、正常情况下,二者没区别;
    2、computed是属性调用,methods是方法调用;
    3、computed在标签(非属性)上调用时不用加(),methods在标签(非属性)上调用时用加();
    4、computed在关联值发生改变时才调用,methods在不管关联值是否发生改变都调用;
    案例来源:https://www.jb51.net/article/137040.htm
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>computed的使用</title>
      <script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
    </head>
    <body>
      <div id="root">
      </div>
    </body>
    </html>
    <script>
      var vm = new Vue({
        el: "#root",
        data: {
          name: "老夫",
          age: 40,
          hobby: '测试computed和methods的区别',
          nameAgeStyle: {
            fontSize: "20px",
            color: "#0c8ac5"
          },
          inputStyle: {
            display: "block",
             "350px"
          }
        },
        template: `<div>
            <div v-bind:style="nameAgeStyle">computed方式渲染: {{computeNameAndAge}}</div>
            <div v-bind:style="nameAgeStyle">methods方式渲染: {{methodNameAndAge()}}</div>
            <br>
            <input type="text" v-model="hobby"  v-bind:style="inputStyle">
            <div>爱好: {{hobby}}</div>
            <div>{{testComputeAndMethod()}}</div>
            </div>`,
        computed: {
          computeNameAndAge() {
            console.log('computed在运行');
            return `${this.name} == ${this.age}岁`;
          }
        },
        methods: {
          methodNameAndAge() {
            console.log('methods在运行');
            return `${this.name} == ${this.age}岁`;
          },
          testComputeAndMethod() {
            console.log("testComputeAndMethod在运行");
          }
        }
      })
    </script>
    
    三、5个内置组件
    1、component
    渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。
    2、transition
    作为单个元素/组件的过渡效果。只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。
    3、transition-group
    作为多个元素/组件的过渡效果。它渲染一个真实的 DOM 元素。默认渲染 <span>,可以通过 tag attribute 配置哪个元素应该被渲染。它每个子节点必须有独立的 key,动画才能正常工作。
    4、keep-alive
    包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。它是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
    5、slot
    作为组件模板之中的内容分发插槽。它自身将被替换。
    (1)<base-layout>组件定义
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>2)<base-layout>组件使用
    <base-layout>
      <template v-slot:header>
        <h1>Here might be a page title</h1>
      </template>
      <p>A paragraph for the main content.</p>
      <p>And another one.</p>
      <template v-slot:footer>
        <p>Here's some contact info</p>
      </template>
    </base-layout>
    
    四、动态组件效果的实现方案
    1、在内置组件<component>里面使用 v-bind: is。没有keep-alive的配合,只能实现切换,不能实现缓存
    <div id="app">
      <component v-bind:is="whichcomp"></component>
      <button v-on:click="choosencomp('a')">a</button>
      <button v-on:click="choosencomp('b')">b</button>
      <button v-on:click="choosencomp('c')">c</button>
    </div>
    var app=new Vue({
      el: '#app',
      components:{
      acomp:{
        template:`<p>这里是组件A</p>`
      },
      bcomp:{
        template:`<p>这里是组件B</p>`
      },
      ccomp:{
        template:`<p>这里是组件C</p>`
      }},
      data:{whichcomp:""},
      methods:{
        choosencomp:function(x){
        this.whichcomp=x+"comp"}
      }
    })
    2、把组件作为子组件放在内置组件<keep-alive>里,后者包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
    (1)include:字符串或正则表达式。只有名称匹配的组件会被缓存。
    (2)在 2.2.0 及其更高版本中,activated 和 deactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。
    <keep-alive include="a,b" include='a' :include="/a|b/" :include="['a', 'b']">
    /* 字符串、正则、数组,也可以没有这些 */
      <component :is="view"></component>
    </keep-alive>
    <keep-alive>
      <comp-a v-if="a > 1"></comp-a>
      <comp-b v-else></comp-b>
    </keep-alive>
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <transition>
      <keep-alive>
        <component :is="view"></component>
      </keep-alive>
    </transition>
    
    五、插槽slot
    1、vue2.6.0及以后版本,
    使用组件<base-layout>并定义插槽
    <base-layout>
      <template v-slot:head="slotProps">
        {{ slotProps.user.firstName }}
      </template>
    </base-layout>
    定义组件<base-layout>并使用插槽
    <div>
      <slot name="head" v-bind:user="obj">
        {{ user.text }}<!-- 后备内容 -->
      </slot>
    </div>
    2、vue2.6.0以前版本,
    使用组件<base-layout>并定义插槽 
    <base-layout>
      <template slot="head" slot-scope="slotProps">
        {{ slotProps.user.firstName }}
      </template>
    </base-layout>
    定义组件<base-layout>并使用插槽 
    <div>
      <slot name="head" v-bind:user="obj">
        {{ user.text }}<!-- 后备内容 -->
      </slot>
    </div>
    3、插槽实例:
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title></title>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- Vue.js v2.6.10 -->
    </head>
    <style type="text/css">
    
    </style>
    <body>
      <div id="app">
        <this-div :list="list">
          <div>&lt;template v-slot:head(插槽名)&gt;&lt;/template&gt;</div>
          <template v-slot:head="slotProps">
            <strong v-if="slotProps.innerItem.id == 2">{{slotProps.innerItem.name}}</strong>
          </template>
          <div>&lt;template v-slot="slotProps(插槽所在的作用域)"&gt;&lt;/template&gt;</div>
        </this-div>
      </div>
    </body>
    </html>
    <script type="text/javascript">
      Vue.component('this-div', {
        props: ['list'],
        template: `<div>
                <slot/>
                <div style="margin:20px 0">以上是默认插槽内容。以下是具名插槽内容,每循环一次,插槽就渲染一次,如果插槽里没有内容,就用后备内容</div>
                <div>
                  <div :key='item.id' v-for='item in list'>
                    <slot name='head' :innerItem='item'>{{item.name+',这是后备(备用)内容'}}</slot>
                  </div>
                </div>
              </div>`,
      });
      var vm = new Vue({
        el: '#app', 
        data: {
          list: [{
            id: 1,
            name: 'apple'
          }, {
            id: 2,
            name: 'banane'
          }, {
            id: 3,
            name: 'orange'
          }]
        }
      });
    </script>
    
    六、vue组件的七种写法
    来源:https://www.cnblogs.com/hanguidong/p/9381830.html
    1. 字符串
    Vue.component('my-checkbox', {
        template: `<div class="checkbox-wrapper" @click="check"><div :class="{ checkbox: true, checked: checked }"></div><div class="title"></div></div>`,
        data() {
            return { checked: false, title: 'Check me' }
        },
        methods: {
            check() { this.checked = !this.checked; }
        }
    });
    2. 模板字面量
    Vue.component('my-checkbox', {
        template: `<div class="checkbox-wrapper" @click="check">
                <div :class="{ checkbox: true, checked: checked }"></div>
                <div class="title"></div>
              </div>`,
        data() {
            return { checked: false, title: 'Check me' }
        },
        methods: {
            check() { this.checked = !this.checked; }
        }
    });
    3. x-template
    <body>
      <div id="app">
        <my-component></my-component>
        <!-- 使用x-template -->
        <script type="text/x-template" id="my-component">
          <div>这是组件的内容</div>
        </script>
      </div>
      <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
      <script>
        Vue.component('my-component', {
          template: '#my-component',   //将id赋给template
          data() {
            return { checked: false, title: 'Check me' }
          },
          methods: {
            check() { this.checked = !this.checked; }
          }
        });
    
        var app = new Vue({
          el: '#app',
        })
      </script>
    </body>
    /* Vue.component('my-checkbox', {
        template: '#checkbox-template',
        data() {
            return { checked: false, title: 'Check me' }
        },
        methods: {
            check() { this.checked = !this.checked; }
        }
    });
    <script type="text/x-template" id="checkbox-template">
        <div class="checkbox-wrapper" @click="check">
            <div :class="{ checkbox: true, checked: checked }"></div>
            <div class="title"></div>
        </div>
    </script> */
    4. 内联模板
    内联模板不会把子组件的内容分发渲染到父组件中
    而是需要在父组件中实现其内容的渲染
    不使用父组件的数据
    不是slot
    (1)父组件
    <template>
      <div>
      <my-checkbox inline-template>
        <div class="checkbox-wrapper" @click="check">
          <div :class="{ checkbox: true, checked: checked }"></div>
          <div class="title"></div>
        </div>
      </my-checkbox>
      </div>
    </template>
    <script>
    import myCheckbox from './my-checkbox'
    export default {
      components: {  myCheckbox },
      data() {
        return {
          name:'父组件数据name'
        }
      },
    }
    </script>2)子组件
    Vue.component('my-checkbox', {
      data() {
        return { checked: false, title: 'Check me' }
      },
      methods: {
        check() { this.checked = !this.checked; }
      }
    });
    5. render 函数
    render 函数需要你将模板定义为 JavaScript 对象,这显然是最详细和抽象的模板选项。
    不过,优点是你的模板更接近编译器,并允许你使用完整的 JavaScript 功能,而不是指令提供的子集。
    Vue.component("my-checkbox", {
      data() {
        return { checked: false, title: "Check me" };
      },
      methods: {
        check() {
          this.checked = !this.checked;
        },
      },
      render(createElement) {
        return createElement(
          "div",
          {
            attrs: {
              class: "checkbox-wrapper",
            },
            on: {
              click: this.check,
            },
          },
          [
            createElement("div", {
              class: {
                checkbox: true,
                checked: this.checked,
              },
            }),
            createElement(
              "div",
              {
                attrs: {
                  class: "title",
                },
              },
              [this.title]
            ),
          ]
        );
      },
    });
    6. JSX
    Vue 中最有争议的模板选项是 JSX,一些开发者认为它丑陋、不直观,是对 Vue 精神的背叛。JSX 需要你先编译,因为它不能被浏览器运行。
    不过,如果你需要完整的 JavaScript 语言功能,又不太适应 render 函数过于抽象的写法,那么 JSX 是一种折衷的方式。
    Vue.component("my-checkbox", {
      data() {
        return { checked: false, title: "Check me" };
      },
      methods: {
        check() {
          this.checked = !this.checked;
        },
      },
      render() {
        return (
          <div class="checkbox-wrapper" onClick={this.check}>
            <div class={{ checkbox: true, checked: this.checked }}></div>
            <div class="title">{this.title}</div>
          </div>
        );
      },
    });
    7. 单文件组件(vue2.0之组件的es6写法)
    <template>
      <div class="checkbox-wrapper" @click="check">
        <div :class="{ checkbox: true, checked: checked }"></div>
        <div class="title"></div>
      </div>
    </template>
    import comTab from '@/components/ComTab/com-tab'//导入别处组件
    export default {
      components: {    
        comTab,//此组件依赖的组件
      },
      name: 'ComTpl',//组件名
      props: {   //用于接收父组件向子组件传递的数据
        tester: {
          type: Object
        }
      },
      data() {//本组件的数据
        return {
          tests: [],
          selectedTest: {}
        };
      },
      computed: {//计算属性,所有get,set的this上下文都被绑定到Vue实例
        方法名() {
          //.....
        }
      },
      created() {//生命周期之一。在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图
        //ajax请求放在created里,因为此时已经可以访问this和操作DOM了。
        this.classMap = ['a', 'b', 'c', 'd', 'e'];
        //如进行异步数据请求
        this.$http.get('/api/tests').then((response) => {
          response = response.body;
        if (response.errno === 0) {
          this.goods = response.data;
        }
      });
      },
      mounted() {   //在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作
        this.$nextTick(() => {
          this._initScroll();
        this._initPics();
      });
      },
      methods: {//定义方法
        方法名(参数) {
          //...
        }
      },
      filters: {  //过滤器,可用来写,比如格式化日期的代码
        //
        formatDate(time) {
          let date = new Date(time);
          return formatDate(date, 'yyyy-MM-dd hh:mm');
        }
      },
      watch: {//用来观察Vue实例的数据变动,后执行的方法
        //...
      }
    };
    
    七、子组件向父组件传值
    1、通过this
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Vue子组件给父组件传值</title>
        <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
      </head>
      <body>
        <div id="el">
          <p>{{total}}</p>
          <button-this v-bind:son-prop="parentAdd"></button-this>
        </div>
        <script>
        Vue.component('button-this', {
          template: '<button v-on:click="sonAdd">累加</button>',
          props: {       
            sonProp: {
              type: Function
            }
          },
          data: function() {
            return {
              counter: 0
            }
          },
          methods: { 
            sonAdd: function() {
              this.counter += 1;
              this.sonProp(this.counter);
            }                                 
          },
        })
        new Vue({
          el: '#el',
          data: {
            total: 0
          },
          methods: {
            parentAdd: function(counter) {
              this.total = counter
            }
          }
        })
        </script>
      </body>
    </html>
    2、通过$emit
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Vue子组件给父组件传值</title>
        <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
      </head>
      <body>
        <div id="el">
          <p>{{total}}</p>
          <button-counter v-on:son-event="parentAdd"></button-counter>
        </div>
        <script>
          Vue.component('button-counter', {
            template: '<button v-on:click="addCounter">clickButton</button>',
            props:['sonProp'],
            data: function() {
              return {
                counter: 0
              }
            },
            methods: { 
              addCounter: function() {
                this.counter += 1;
                this.$emit('son-event',this.counter);
              }                                  
            },
          })
          new Vue({
            el: '#el',
            data: {
              total: 0
            },
            methods: {
              parentAdd: function(counter) {
                this.total = counter
              }
            }
          })
        </script>
      </body>
    </html>
    
    八、vuex
    1、示例:
    注意:Vue实例是Vue应用的启动器,Vue组件是Vue实例的扩展。
    import Vue from 'vue'
    import router from './router'
    import store from './store'
    import '@/permission'//router.beforeEach
    <template>
     <div id="app">
       <router-view/>
     </div>
    </template>
    <script>
     export default {
       name: 'App'
     }
    </script>
    new Vue({ 
      el: '#app', 
      router, 
      store, 
      render: h => h(App) 
    }) 
    mutation:突变,改变 
    2、VUEX简介 
    (1)redux:处理同步用 dispatch action(对象类型),处理异步用 dispatch action(函数类型) 
    (2)Vuex:处理同步用 commit(mutations) ,处理异步用 dispatch(action),在action里执行 commit(mutations)
    (3)store:在普通.JS中,执行 store.commit(mutation) 或者 store.dispatch(action) 
    (4)$store:在.vue中,执行this.$store.commit(mutation) 或者 this.$store.dispatch(action) 
    (5)store 实例
    const store = new Vuex.Store({ 
      state: state, 
      mutations: mutations, 
      actions: actions, 
      getters: getters, 
      modules: { 
        app, 
        user, 
        permission 
      }
    })
    3、模块(默认)
    注意:Vuex允许我们将 store 分割成 module,每个模块拥有自己的 state、mutation、action、getter、module。
    (1)state
    state: {amount: 0}
    export default {
      computed:{
        ...mapState(['amount']) // 在html上用{{amount}}、在js中用this.amount 指向 store.state.amount。 
      }
    }; 
    (2)mutations
    mutations: {
      mutationA (state,playLoad) {
        state.amount = playLoad.amount
      }
    }
    store.commit('mutationA', {
      amount: 10
    })
    export default { 
      methods: { 
        ...mapMutations([ 
          'mutationA',// 在html上用{{mutationA({amount:10})}}、在js中用this.mutationA({amount:10}) 指向 this.$store.commit('mutationA',{amount:10})  
        ])
      } 
    }
    (3)actions
    actions: {
      actionA ({state, commit, rootState},playLoad) {
        var { amount } = playLoad;
        commit('mutationA', {
          amount: amount+10             
        })
      }
    }
    store.dispatch('actionA', {
      amount: 10
    })
    export default { 
      methods: { 
        ...mapActions([ 
          'actionA' // 在html上用{{actionA({amount:10})}}、在js中用this.actionA({amount:10}) 指向 this.$store.dispatch('actionA',{amount:10}) 
        ])
      } 
    }
    (4)getters
    getters: { 
      getterA: state => { 
        return state.todos.filter(todo => todo.done) 
      }
    }
    export default {
      computed: {
        ...mapGetters({getterA}) // 在html上用{{getterA}}、在js中用this.getterA 指向 `this.$store.getters.getterA`
      },
    }; 
    4、map
    (1)mapState:将store中的state映射到局部计算属性。 
    <p>mapState方式{{count}}</p> 
    computed: {
      //将 mapState 的每一项作为 computed 对象的每一项,  
      ...mapState([ 'count'
      // 把 this.count 指向 store.state.count。  
      ]), 
    } 
    computed: mapState({
      //用 mapState 的整体来取代 computed 对象的整体, 
      count: state => state.count, 
      countAlias: 'count',// 这里的 'count' 等同于 `state => state.count`  
      countPlusLocalState (state) { 
        return state.count + this.localCount// 为了能够使用 `this` 获取局部状态,必须使用常规函数  
      } 
    }) 
    (2)mapGetters:将store中的getter映射到局部计算属性。 
    <template> 
    <div> 
    <h4>测试1:Count is {{ count }}</h4> 
    <P>通过属性访问:{{ doneTodosCount }}</p> 
    <P>通过方法访问:{{ doneTodos }}</p> 
    </div> 
    </template> 
    <script> 
    import { mapGetters } from "vuex";
    export default {
     computed: {
       count() {
         return this.$store.state.count;
       },
       ...mapGetters(["doneTodos", "doneTodosCount"]),
       ...mapGetters({
         doneCount: "doneTodosCount", // 把 `this.doneCount` 指向 `this.$store.getters.doneTodosCount`
       }),
     },
    };
    </script> 
    (3)mapMutations:将store 中的 mutations 映射到局部方法属性。 
    export default { 
      methods: { 
        ...mapMutations([ 
          'increment', // 把 `this.increment()` 指向 `this.$store.commit('increment')`
          'incrementBy'// 把 `this.incrementBy(amount)` 指向 `this.$store.commit('incrementBy', amount)`  
        ]), 
        ...mapMutations({ 
          add: 'increment' // 把 `this.add()` 指向 `this.$store.commit('increment')`  
        }) 
      } 
    } 
    (4)mapActions:将store 中的 actions 映射到局部方法属性。 
    export default { 
      methods: { 
       ...mapActions([ 'increment', 
        // 把 `this.increment()` 指向 `this.$store.dispatch('increment')` 'incrementBy' 
        // 把 `this.incrementBy(amount)` 指向 `this.$store.dispatch('incrementBy', amount)`  
       ]), 
       ...mapActions({ add: 'increment' 
        // 把 `this.add()` 指向 `this.$store.dispatch('increment')`  
       }) 
      } 
    } 
    
    九、vue-router 
    1、示例: 
    注意:前端单页面应用,就是把组件和路由连起来 
    import Vue from 'vue' 
    import router from './router' 
    import store from './store' 
    import '@/permission'//router.beforeEach 
    new Vue({ 
     el: '#app', 
     router, 
     store, 
     render: createElement => createElement(App)//render执行,即createElement执行 
    }) 
    “导航”表示路由正在发生改变。
    2、一个路径可以匹配多个路由,先定义的路由,优先级高。 
    ===路径:this.$router.push('/user-admin') 
    (1)路由1:{ path: '/user-a*' } 
    (2)路由2:{ path: '/user-ad*' } 
    3、route(无)、router、$route、$router 
    (1)route:(无) 
    (2)router:在普通.JS中,关于全局路由的对象,router.beforeEach 
    (3)$router:在.vue子组件中,关于全局路由的对象,this.$router.push 
    (4)$route:在.vue子组件中,关于当前路由的对象,this.$route.path 
    4、vueRouter三大导航守卫 
    (1)全局导航守卫,主要包括beforeEach、beforeResolve和aftrEach, 
     router.beforeEach((to, from, next) => { 
       // to: Route: 即将要进入的目标 路由对象 
       // from: Route: 当前导航正要离开的路由 
       // next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 
       const route = ["index", "list"]; 
       let isLogin = store.state.token; // 是否登录 // 未登录状态;当路由到route指定页时,跳转至login 
       if (route.indexOf(to.name) >= 0) { 
         if (isLogin == null) { 
           router.push({ path: "/login" }); 
         } 
       } // 已登录状态;当路由到login时,跳转至home 
       localStorage.setItem("routerName", to.name); 
       if (to.name === "login") { 
         if (isLogin != null) { 
           router.push({ path: "/HomeMain" }); 
         } 
       } 
       next(); 
     }); 
    (2)路由导航守卫 
     { 
       path: '/dashboard', 
       component: resolve => require(['../components/page/Dashboard.vue'], resolve), 
       meta: { title: '系统首页'}, 
       beforeEnter: (to, from, next) => { } 
     }, 
    (3)组件导航守卫 
    const Foo = { 
      template: `...`, 
      beforeRouteEnter (to, from, next) { 
         //beforeRouteEnter 是支持给 next 传递回调的唯一守卫  
      }, 
      beforeRouteUpdate (to, from, next) { }, 
      beforeRouteLeave (to, from, next) { } 
    } 
    5、路由组件传参 
     const router = new VueRouter({ 
       routes: [ 
        { 
          //布尔模式 props 被设置为 true,route.params 将会被设置为组件属性。  
          path: '/user/:id', 
          component: User, 
          props: true 
        }, 
        { //对象模式 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:  
          path: '/user/:id', 
          components: { 
            default: User, 
            sidebar: Sidebar 
          }, 
          props: { 
            default: true, 
            sidebar: false 
          } 
        }, 
        { 
          //函数模式 URL /search?q=vue 会将 {q:'vue'} 作为属性传递给 SearchUser 组件 
          path: '/search', 
          component: SearchUser, 
          props: (route) => ({ 
            query: route.query.q 
          }) 
        } 
       ] 
     })
    6、VUE-Router嵌套与命名视图 
      (1)router-link
      <router-link to="bbb">Home</router-link>
      <router-link :to="'bbb'">Home</router-link>
      <router-link :to="{ path: 'bbb' }">Home</router-link>
      <router-link :to="{ name: 'BBB', params: { userId: 123 }}">User</router-link>
      const router = new VueRouter({
        routes: [
          {
            path: '/bbb',
            component: BBB
          }
        ];
      })
      (2)router-view 命名视图 
      <router-view></router-view>
      <router-view name="two"></router-view>
      <router-view name="three"></router-view>
      const router = new VueRouter({
        routes: [/* 多个视图就需要多个组件。确保正确使用components配置(带上s),如果 router-view 没有设置名字,那么默认为 default。 */
          {
            path: '/',
            components: {
              default: AAA,
              two: BBB,
              three: CCC
            }
          }
        ]
      })
      (3)router-view 嵌套与命名视图
      以下是总router
      const router = new VueRouter({
        routes: [
          {
            path: '/a',
            components: {
              default: A1,
              two: A2,
              three: A3
            },
            children: [
              {
                path: 'aa',
                components: {
                  default: AA1,
                  five: AA2,
                  six: AA3
                }
              }
            ]
          }
        ]
      })
      以下是总router的说明
      在/a路由下,把父组件A1、A2、A3渲染到页面的router-view标签里,如下。
      <router-view></router-view>
      <router-view name="two"></router-view>
      <router-view name="three"></router-view>/a/aa路由下,把子组件AA1、AA2、AA3渲染到A1、A2或A3的router-view标签里,如下。
      <router-view></router-view>
      <router-view name="five"></router-view>
      <router-view name="six"></router-view>
    7、完整的导航解析流程 
    (1)导航被触发。 
    (2)在失活的组件里调用 beforeRouteLeave 守卫。 
    (3)调用全局的 beforeEach 守卫。 
    (4)在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。 
    (5)在路由配置里调用 beforeEnter。 
    (6)解析异步路由组件。 
    (7)在被激活的组件里调用 beforeRouteEnter。 
    (8)调用全局的 beforeResolve 守卫 (2.5+)。 
    (9)导航被确认。 
    (10)调用全局的 afterEach 钩子。 
    (11)触发 DOM 更新。 
    (12)用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。 
     
    十、vue前后端路由拦截
    1、vue-router前端路由拦截
    (1)
    function getClientType(data) {
      return service({
        url: '/warn/getClientType',
        method: 'post',
        data: data,
        dataTranform: true
      })
    }
    (2)
    actions: {
      GenerateRoutes({
        commit
      }, data) {
        return new Promise(
          function (resolve) {
            const { roles } = data;
            const accessedRouters = asyncRouterMap.filter(function(v) {
              return makeChildRoute(roles, v)
            })
            makeRedirect(accessedRouters)
            commit('SET_ROUTERS', accessedRouters)
            resolve()
          })
      }
    }
    (3)
    router.beforeEach(function (to, from, next) {
      NProgress.start();
      if (getToken()) {//是否已登录
        if (store.getters.roles.length === 0) {//是否获取了权限列表
          store
            .dispatch("GetInfo") //执行GetInfo函数,返回Promise实例的过程中,向后台发送请求
            .then(function (allResult) {
              getSystemTime()
                .then(function (thisResult) {
                  store.commit("TIME", thisResult);
                  return getClientType({
                    ump: 0,
                  });
                })
                .then(function (thisResult) {
                  store.commit("GET_CLIENTS", thisResult);
                  return getClientType({
                    ump: 1,
                  });
                })
                .then(function (thisResult) {
                  store.commit("GET_UMPCLIENTS", thisResult);
                  store
                    .dispatch("GenerateRoutes", {
                      roles: allResult.resc,
                    })
                    .then(function () {//生成可访问的路由表
                      router.addRoutes(store.getters.addRouters); //动态添加可访问路由表
                      isLoginSucce(to, next);
                    });
                });
            })
            .catch(function () {
              store.dispatch("FedLogOut").then(function () {
                next({
                  path: "/",
                });
              });
            });
        } else {
          if (to.path === "/") {
            next({
              path: makeDefaultPageIndex(),
            });
            NProgress.done();
          } else {
            isLoginSucce(to, next, true);
          }
        }
      } else {
        if (to && whiteList.indexOf(to.path) !== -1) {
          next();
        } else {
          next(`/login`); // 否则全部重定向到登录页
          NProgress.done();
        }
      }
    });
    2、axios后端路由拦截
    (1)获取请求实例
    var service = axios.create({
      baseURL: process.env.BASE_API,
      timeout: 15000
    })
    (2)获取promise实例
    var instance = service({
      url: '/warn/licenceUpload',
      method: 'post',
      data: data
    })
    (3)添加请求拦截器
    service.interceptors.request.use(function (config) {
      // 在发送请求之前做些什么
      return config;
    }, function (error) {
      // 对请求错误做些什么
      return Promise.reject(error);
    });
    (4)添加响应拦截器
    axios.interceptors.response.use(function (response) {
      // 对响应数据做点什么
      return response;
    }, function (error) {
      // 对响应错误做点什么
      return Promise.reject(error);
    });
    
    十一、vue-cli-4.0以上版本的安装(win10系统)
    https://cli.vuejs.org/zh/guide/
    1、配置path(已配置的可跳过)
    (1)运行,npm config get prefix
    (2)得到,C:UsersxxxAppDataRoaming
    pm
    (3)前往,桌面>右键计算机>属性>高级系统设置>环境变量-系统变量-(双击)path>新建-粘贴(得到的内容)-确定
    2、安装
    (1)npm i npm -g
    (2)npm i @vue/cli -g 或 npm install @vue/cli -g
    3、package.json 
    "scripts": {
      "serve": "vue-cli-service serve",
      "build": "vue-cli-service build",
      "lint": "vue-cli-service lint"//代码检测
      "111": "vue-cli-service inspect",
    },
    运行 npm run 111,可得出以下结论,
    (1)config.module.rule('svg').exclude.add(resolve('src/icons')).end()
    对vue-cli-4.0里内置的'svg'模块规则进行修改
    (2)config.module.rule('icons').test(/.svg$/).include.add(resolve('src/icons')).end()
    定义并向vue-cli-4.0里注入名为'icons'的模块规则
    4、lintOnSave
    module.exports = {
      lintOnSave: process.env.NODE_ENV !== 'production'
    }
    (1)作用:是否在每次保存时 lint(应理解为"检查") 代码
    (2)取值
    (2-1)取值false:关闭每次保存都进行检测
    (2-2)取值true或'warning':开启每次保存都进行检测,lint 错误将显示到控制台命令行,而且编译并不会失败
    (2-3)取值'default''error':开启每次保存都进行检测,lint 错误将显示到浏览器页面上,且编译失败
     
    十二、前端路由:url有变化,但不向后台发送请求,
    1、它的作用是 
    (1)记录当前页面的状态; 
    (2)可以使用浏览器的前进后退功能; 
    2、hash模式帮我们实现这两个功能,因为它能做到: 
    (1)改变url且不让浏览器向服务器发出请求; 
    (2)监测 url 的变化; 
    (3)截获 url 地址,并解析出需要的信息来匹配路由规则。 
    3、history 模式改变 url 的方式会导致浏览器向服务器发送请求。
    4、vue前端路由触发条件
    (1)<router-link :to="'bbb'">Home</router-link>2this.$router.push
  • 相关阅读:
    springmvc学习笔记---idea创建springmvc项目
    Spring Boot中使用Swagger2构建RESTful API文档
    Dubbo原理实现之使用Javassist字节码结束构建代理对象
    Dubbo启动时服务检查
    Dubbo原理实现之代理接口的定义
    dubbo实现原理之动态编译
    Dubbo实现原理之基于SPI思想实现Dubbo内核
    dubbo实现原理之SPI简介
    Spring中使用StandardServletMultipartResolver进行文件上传
    SpringCloud之Feign
  • 原文地址:https://www.cnblogs.com/gushixianqiancheng/p/12988840.html
Copyright © 2020-2023  润新知