IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Vue 2.x 复习总结 · 看不见我的美 · 是你瞎了眼

    馬腊咯稽发表于 2020-03-04 00:00:00
    love 0
    Vue 2.x 复习总结

    生命周期

    异步组件

    Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义;只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    const AsyncComponent = () => ({
     // 需要加载的组件 (应该是一个 Promise 对象)
     component: import('./MyComponent.vue'),
     // 异步组件加载时使用的组件
     loading: LoadingComponent,
     // 加载失败时使用的组件
     error: ErrorComponent,
     // 展示加载时组件的延时时间。默认值是 200 (毫秒)
     delay: 200,
     // 如果提供了超时时间且组件加载也超时了,则使用加载失败时使用的组件。默认值是:`Infinity`
     timeout: 3000
    });
    

    插件系统

    插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制:

    1. 添加全局方法或者属性;
    2. 添加全局资源:指令/过滤器/过渡等;
    3. 通过全局混入来添加一些组件选项;
    4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现;
    5. 提供自己的 API,同时提供上面提到的一个或多个功能。

    举个例子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    Example.install = (Vue, options) => {
     Vue.$shareConfig = configObj => {
     const { sharetitle, sharedesc, shareimg, shareurl } = configObj;
     const shareObj = {
     title: sharetitle,
     desc: sharedesc,
     link: shareurl,
     imgUrl: shareimg,
     success: function () {},
     cancel: function () {}
     };
     wx.config({
     appId: this.$store.state.appId,
     debug: false,
     jsApiList: [
     'onMenuShareTimeline',
     'onMenuShareAppMessage',
     'onMenuShareQQ',
     'onMenuShareWeibo'
     ],
     nonceStr: this.$store.state.nonceStr,
     signature: this.$store.state.signature,
     timestamp: this.$store.state.timestamp
     });
     wx.ready(function () {
     wx.onMenuShareTimeline(shareObj);
     wx.onMenuShareQQ(shareObj);
     wx.onMenuShareAppMessage(shareObj);
     wx.onMenuShareWeibo(shareObj);
     });
     };
    };
    // 使用插件并向插件传入可选选项
    Vue.use(Example, {});
    

    自定义指令

    自定义指令适用于需要对普通 DOM 元素进行底层操作的情况。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    // 全局注册
    Vue.directive('focus', {
     // 当被绑定的元素插入到 DOM 中时
     inserted: function (el) {
     el.focus(); // 聚焦元素
     }
    });
    // 局部注册
    new Vue({
     directives: {
     focus: {
     inserted(el) {
     el.focus();
     }
     }
     }
    });
    

    指令定义对象可以提供如下几个钩子函数:

    • bind:只调用一次,指令第一次绑定到元素时调用;在这里可以进行一次性的初始化设置;
    • inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中);
    • update:所在组件的 VNode 更新时调用,可以通过比较更新前后的值来忽略不必要的模板更新;
    • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用;
    • unbind:只调用一次,指令与元素解绑时调用。

    钩子函数参数:

    • el:指令所绑定的元素,可以用来直接操作 DOM;
    • binding:一个对象,包含以下属性:
      • name:指令名,不包括 v- 前缀;
      • value:指令的绑定值,例如:v-my=“1 + 1"中,绑定值为 2;
      • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用;
      • expression:字符串形式的指令表达式,例如 v-my=“1 + 1"中,表达式为 “1 + 1”;
      • arg:传给指令的参数,可选;例如 v-my:foo 中,参数为 “foo”;
      • modifiers:一个包含修饰符的对象;例如:v-my.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
    • vnode:编译生成的虚拟节点;
    • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

    举个例子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    <div id="hook-arguments" v-demo:foo.a.b="message"></div>
    <script>
     Vue.directive('demo', {
     bind(el, binding, vnode) {
     var s = JSON.stringify;
     el.innerHTML = `
     name: ${s(binding.name)},
     value: ${s(binding.value)},
     expression: ${s(binding.expression)},
     argument: ${s(binding.arg)},
     modifiers: ${s(binding.modifiers)},
     vnode keys: ${Object.keys(vnode).join(', ')}
     `;
     }
     });
     new Vue({
     el: '#hook-arguments',
     data: {
     message: 'hello'
     }
     });
     /**
     * name: "demo"
     * value: "hello"
     * expression: "message"
     * argument: "foo"
     * modifiers: {"a":true,"b":true}
     * vnode keys: tag, data, children, text...
     */
    </script>
    

    动态指令:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    <div id="dynamic">
     <h3>Scroll down inside this section...</h3>
     <p v-pin:[direction]="200">
     I am pinned onto the page at 200px to the left...
     </p>
    </div>
    <script>
     new Vue({
     el: '#dynamic',
     data() {
     return {
     direction: 'left'
     };
     },
     directives: {
     pin: {
     bind(el, binding, vnode) {
     el.style.position = 'fixed';
     el.style[binding.arg] = `${binding.value}px`;
     }
     }
     }
     });
    </script>
    

    响应式原理

    当把一个普通的 JS 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter;在内部它们让 Vue 能够追踪依赖,在属性被访问和修改时通知变更;每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖;之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

    Object.freeze 会阻止修改现有的属性,也意味着响应系统无法再追踪变化。

    Vue 不能检测以下数组的变动(因为性能问题):

    • 利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue;
    • 修改数组的长度时,例如:vm.items.length = newLength。

    举个例子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    var vm = new Vue({
     data: {
     items: ['a', 'b', 'c']
     }
    });
    vm.items[1] = 'x'; // 不是响应性的
    vm.$set(vm.items, indexOfItem, newValue); // 解决问题
    vm.items.splice(indexOfItem, 1, newValue); // 解决问题
    vm.items.length = 2; // 不是响应性的
    vm.items.splice(newLength); // 解决问题
    

    Vue 不能检测对象属性的添加或删除:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    var vm = new Vue({
     data: {
     a: 1,
     userProfile: {
     name: 'Anika'
     }
     }
    });
    // 对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性
    vm.b = 2; // vm.b 不是响应式的
    // 可以向嵌套对象添加响应式属性
    vm.$set(vm.userProfile, 'age', 27);
    vm.userProfile = Object.assign({}, vm.userProfile, {
     favoriteColor: 'Green'
    });
    

    Vue 在更新 DOM 时是异步执行的;只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更;如果同一个 watcher 被多次触发,只会被推入到队列中一次;然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。

    在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

    可以在数据变化之后立即使用 Vue.nextTick(callback);回调函数将在 DOM 更新完成后被调用;例如:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    var vm = new Vue({
     el: '#example',
     data: {
     message: '123'
     }
    });
    vm.message = 'new message'; // 更改数据
    vm.$el.textContent === 'new message'; // false
    vw.$nextTick(function () {
     vm.$el.textContent === 'new message'; // true
    });
    

    依赖注入

    Vue 中也有 context 机制:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    <google-map>
     <google-map-region v-bind:shape="cityBoundaries">
     <google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
     </google-map-region>
    </google-map>
    <script>
     Vue.component('google-map', {
     data() {
     return {
     sigma: 123
     };
     },
     provide() {
     return {
     sigma: this.sigma,
     getMap: this.getMap
     };
     },
     methods: {
     getMap() {
     // ...
     }
     }
     });
     Vue.component('google-map-markers', {
     // 可以跨越多个层级拿到值
     inject: ['sigma', 'getMap']
     });
    </script>
    

    事件监听器

    • 通过 $on(eventName, eventHandler) 侦听一个事件
    • 通过 $once(eventName, eventHandler) 一次性侦听一个事件
    • 通过 $off(eventName, eventHandler) 停止侦听一个事件

    举个例子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    new Vue({
     mounted() {
     this.picker = new Pikaday({
     field: this.$refs.input,
     format: 'YYYY-MM-DD'
     });
     },
     beforeDestroy() {
     this.picker.destroy();
     }
    });
    // 可以修改为
    new Vue({
     mounted() {
     var picker = new Pikaday({
     field: this.$refs.input,
     format: 'YYYY-MM-DD'
     });
    
     this.$once('hook:beforeDestroy', function () {
     picker.destroy();
     });
     }
    });
    

    混入

    混入提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能;一个混入对象可以包含任意组件选项;当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

    混入原则:

    • 数据对象在内部会进行递归合并,并在发生冲突时以『组件数据优先』;
    • 同名钩子函数将合并为一个数组,因此都将被调用;『混入对象』的钩子将在『组件自身』钩子『之前』调用;
    • 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象;『组件对象』优先级较高。

    Vue.extend 也使用同样的合并策略。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    var myMixin = {
     data() {
     return {
     message: 'hello',
     foo: 'abc'
     };
     },
     methods: {
     hi() {
     console.log('hi');
     },
     bye() {
     console.log('bye');
     }
     },
     mounted() {
     console.log('mixin mounted');
     }
    };
    var vm = new Vue({
     mixins: [myMixin],
     data() {
     return {
     message: "it's me",
     bar: 'def'
     };
     },
     methods: {
     hi() {
     console.log('hihihi');
     },
     do() {
     console.log('dododo');
     }
     },
     created() {
     console.log(this.$data); // { message: "it's me", foo: "abc", bar: "def" }
     },
     mounted() {
     console.log('component mounted'); // mixin mounted, component mounted
     }
    });
    vm.hi(); // hihihi
    vm.bye(); // bye
    vm.do(); // dododo
    // 全局混入,将影响每一个之后创建的 Vue 实例
    Vue.mixin(myMixin);
    

    computed vs watch vs methods

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    <template>
     <div>
     <div>{v1()}</div>
     <div>{v2}</div>
     <div>{v3}</div>
     </div>
    </template>
    <script>
     new Vue({
     data() {
     // data 选项必须是一个函数,每个实例可以维护一份被返回对象的独立的拷贝
     return {
     value: 123,
     v3: 0
     };
     },
     methods: {
     // 每当触发重新渲染时,方法将总会再次执行;无论 vm.value 是否变化
     v1() {
     return `v1: ${this.value}`;
     }
     },
     computed: {
     // 计算属性是基于响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值
     v2() {
     return `v2: ${this.value}`;
     }
     },
     watch: {
     // 在相关数据变化时,执行异步或开销较大的副作用有点像 React.useEffect
     v3() {
     this.v3 = `v3: ${this.value}`;
     }
     }
     });
    </script>
    

    v-model

    v-model 本质上是语法糖;它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理;在内部为不同的输入元素使用不同的属性并抛出不同的事件:

    1
    2
    3
    4
    5
    6
    
    <input v-model="searchText" />
    等价于
    <input
     v-bind:value="searchText"
     v-on:input="searchText = $event.target.value"
    />
    
    • text 和 textarea 使用 value 属性和 input 事件;
    • checkbox 和 radio 使用 checked 属性和 change 事件;
    • select 字段将 value 作为 prop 并将 change 作为事件。

    v-model 有配套的修饰符:

    1
    2
    3
    4
    5
    6
    
    <!-- 使用 change 事件替换 input 事件将输入框的值与数据进行同步 -->
    <input v-model.lazy="msg" />
    <!-- 将用户的输入值转为数值类型 -->
    <input v-model.number="age" type="number" />
    <!-- 过滤用户输入的首尾空白字符 -->
    <input v-model.trim="msg" />
    

    一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件;但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    Vue.component('base-checkbox', {
     model: {
     prop: 'checked',
     event: 'change'
     },
     props: {
     checked: Boolean
     },
     template: `
     <input
     type="checkbox"
     v-bind:checked="checked"
     v-on:change="$emit('change', $event.target.checked)"
     >`
    });
    // 这样使用
    <base-checkbox v-model="lovingVue"></base-checkbox>;
    

    props

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    new Vue({
     props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
    });
    new Vue({
     // 类型可以是:String、Number、Boolean、Array、Object、Date、Function、Symbol
     props: {
     title: String,
     likes: Number,
     isPublished: Boolean,
     commentIds: Array,
     author: Object,
     callback: Function,
     contactsPromise: Promise
     }
    });
    new Vue({
     props: {
     // 基础的类型检查(null 和 undefined 会通过任何类型验证)
     propA: Number,
     // 多个可能的类型
     propB: [String, Number],
     // 必填的字符串
     propC: {
     type: String,
     required: true
     },
     // 带有默认值的数字
     propD: {
     type: Number,
     default: 100
     },
     // 带有默认值的对象
     propE: {
     type: Object,
     // 对象或数组默认值必须从一个工厂函数获取
     default() {
     return { message: 'hello' };
     }
     },
     // 自定义验证函数
     propF: {
     validator(value) {
     // 这个值必须匹配下列字符串中的一个
     return ['success', 'warning', 'danger'].indexOf(value) !== -1;
     }
     }
     }
    });
    

    key

    • 管理可复用元素;
    • 表示元素是独立的,不要复用。

    举个例子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    <!--
     如果不设置独立的 key 属性,在切换 loginType 时,input 将被复用
     用户已经输入的内容将不会被清除,很明显是不合理的
    -->
    <template v-if="loginType === 'username'">
     <label>UserName</label>
     <input placeholder="Enter your username" key="username" />
    </template>
    <template v-else>
     <label>Email</label>
     <input placeholder="Enter your email" key="email" />
    </template>
    

    is

    is 用来将普通元素或组件指定渲染为指定的组件。

    有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的;而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    <!-- 这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错 -->
    <table>
     <blog-post-row></blog-post-row>
    </table>
    <!-- 可以这样解决 -->
    <table>
     <tr is="blog-post-row"></tr>
    </table>
    <!-- 通过将 currentTabComponent 设定为不同的组件来切换渲染结果-->
    <component v-bind:is="currentTabComponent"></component>
    

    slot

    具名插槽:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    <!-- <base-layout> 组件 -->
    <div class="container">
     <header>
     <slot name="header"></slot>
     </header>
     <main>
     <slot>后备用内容,name="default"</slot>
     </main>
     <footer>
     <slot name="footer"></slot>
     </footer>
    </div>
    <!-- 这样使用 -->
    <base-layout>
     <template v-slot:header>
     <h1>header 内容</h1>
     </template>
     <p>这两行会被插入到 main 标签</p>
     <p>这两行会被插入到 main 标签</p>
     <template v-slot:footer>
     <p>footer 内容</p>
     </template>
    </base-layout>
    <!-- 缩写 -->
    <base-layout>
     <template #header>
     <h1>header 内容</h1>
     </template>
     <p>这两行会被插入到 main 标签</p>
     <p>这两行会被插入到 main 标签</p>
     <template #footer>
     <p>footer 内容</p>
     </template>
    </base-layout>
    

    作用域插槽(让插槽内容能够访问子组件中才有的数据):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    <!-- <current-user> 组件 -->
    <span>
     <!-- user 存在于 current-user 组件内作用域中-->
     <slot>{{ user.lastName }}</slot>
    </span>
    <!-- 在父组件中 -->
    <current-user>
     <!-- 取不到 current-user 组件内作用域中的 user-->
     {{ user.firstName }}
    </current-user>
    

    为了让 user 在父级的插槽内容中可用,可以将 user 作为 <slot> 元素的一个属性绑定上去:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    <!-- <current-user> 组件 -->
    <span>
     <slot v-bind:user="user">{{ user.lastName }}</slot>
    </span>
    <!-- 在父组件中 -->
    <current-user>
     <template v-slot:default="slotProps">
     {{ slotProps.user.firstName }}
     <template v-slot:one="oneSlotProps">
     slotProps is NOT available here
     </template>
     </template>
     <template v-slot:two="twoSlotProps">slotProps is NOT available here</template>
    </current-user>
    <!-- 也可以酱 -->
    <current-user v-slot="{ user }">{{ user.firstName }}</current-user>
    <!-- 也可以酱 -->
    <current-user v-slot="{ user: person }">{{ person.firstName }}</current-user>
    <!-- 还可以定义后备内容 -->
    <current-user v-slot="{ user = { firstName: 'Guest' } }">
     {{ user.firstName }}
    </current-user>
    

    命名视图 & 滚动行为

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    <router-view class="view one"></router-view>
    <router-view class="view two" name="a"></router-view>
    <router-view class="view three" name="b"></router-view>
    <script>
     new VueRouter({
     routes: [
     {
     path: '/',
     components: {
     default: Foo,
     a: Bar,
     b: Baz
     }
     }
     ],
     scrollBehavior(to, from, savedPosition) {
     // return 期望滚动到哪个的位置
     if (savedPosition) {
     return savedPosition;
     } else {
     return { x: 0, y: 0 };
     }
     }
     });
    </script>
    

    路由组件传参

    有一个 User 组件,对于所有 ID 不同的用户,都要使用这个组件来渲染;通常的做法是“动态路由匹配”或者“Query 参数”,在组件中使用 vm.$route 来获取参数;在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

    组件与 $route 的耦合:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    <script>
     new Router({
     routes: [
     {
     path: '/user/:id',
     name: 'User',
     component: User
     }
     ]
     });
    </script>
    <template>
     <div class="user">
     <div>userId:{{$route.params.id}}</div>
     </div>
    </template>
    

    通过 props 与组件解耦:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    <script>
     // 布尔模式
     new Router({
     routes: [
     {
     path: '/user/:id',
     name: 'User',
     component: User,
     props: true
     }
     ]
     });
     // 对象模式
     new Router({
     routes: [
     {
     path: '/user',
     name: 'User',
     component: User,
     props: { id: 1 }
     }
     ]
     });
     // 函数模式
     new Router({
     routes: [
     {
     path: '/user',
     name: 'User',
     component: User,
     // /user?id=123 会将 {query: '123'} 作为属性传递给 user 组件
     props: route => ({ query: route.query.id })
     }
     ]
     });
    </script>
    <template>
     <div class="user">
     <div>userId:{{id}}</div>
     </div>
    </template>
    <script>
     export default {
     props: ['id']
     };
    </script>
    

    路由钩子

    参数或查询的改变并不会触发进入/离开的导航守卫;可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    
    const router = new VueRouter({
     routes: [
     {
     path: '/foo',
     component: Foo,
     // 路由独享守卫
     beforeEnter: (to, from, next) => {
     /**
     * to:Route 即将要进入的目标路由
     * from:Route 当前导航正要离开的路由
     * next:Function 调用该方法来 resolve 这个钩子
     * next()
     * 进行管道中的下一个钩子
     * next(false)
     * 中断当前的导航
     * next('/') 或者 next({ path: '/' })
     * 当前的导航被中断,然后进行一个新的导航;可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项
     * next(error)
     * 传入 Error 实例,导航会被终止且该错误会被传递给 router.onError
     */
     }
     }
     ]
    });
    // 全局前置守卫
    router.beforeEach((to, from, next) => {
     // 参数含义同上
    });
    // 全局解析守卫
    router.beforeResolve((to, from, next) => {
     // 参数含义同上,所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
    });
    // 全局后置钩子
    router.afterEach((to, from) => {
     // 参数含义同上
    });
    new Vue({
     // 用来在导航完成前获取数据
     beforeRouteEnter(to, from, next) {
     // 在渲染该组件的对应路由被 confirm 前调用
     // 不!能!获取组件实例 this
     // 守卫执行前,组件实例还没被创建
     next(vm => {
     // 通过 vm 访问实例
     });
     },
     beforeRouteUpdate(to, from, next) {
     // 在当前路由改变,但是该组件被复用时调用
     // 对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
     // 组件实例会被复用;而这个钩子就会在这个情况下被调用。
     // 可以访问组件实例 this
     },
     beforeRouteLeave(to, from, next) {
     // 导航离开该组件的对应路由时调用
     // 可以访问组件实例 this
     let isBye = window.confirm();
     if (isBye) {
     next();
     } else {
     next(false);
     }
     }
    });
    

    完整的导航解析流程:

    • 导航被触发;
    • 在失活的组件里调用 beforeRouteLeave;
    • 调用全局的 beforeEach;
    • 在重用的组件里调用 beforeRouteUpdate;
    • 在路由配置里调用 beforeEnter;
    • 解析异步路由组件;
    • 在被激活的组件里调用 beforeRouteEnter;
    • 调用全局的 beforeResolve;
    • 导航被确认;
    • 调用全局的 afterEach;
    • 触发 DOM 更新;
    • 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

    路由元信息

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    const router = new VueRouter({
     routes: [
     {
     path: '/foo',
     component: Foo,
     children: [
     {
     path: 'bar',
     component: Bar,
     meta: { requiresAuth: true }
     }
     ]
     }
     ]
    });
    router.beforeEach((to, from, next) => {
     if (to.matched.some(v => v.meta.requiresAuth)) {
     if (!auth.loggedIn()) {
     next({
     path: '/login',
     query: { redirect: to.fullPath }
     });
     } else {
     next();
     }
     } else {
     next();
     }
    });
    


沪ICP备19023445号-2号
友情链接