目录
vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。
每个守卫接受三个参数:
(1)to:即将要进入的目标路由对象
(2)from:当前导航正要离开的路由
(3) next:一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参数。如果next函数没有执行或是传入了false等值,这个跳转就会被终止掉。
只要触发了路由就会触发路由前置和后置守卫
(1)全局前置守卫
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中。只有等所有守卫resolve之前一直处于等待中。
用途:可以用来判断进入的路由是否想让一开始就进入的,如果不是可以让它跳转过去。
- // 全局守卫,前置守卫 在路由跳转之前进行判断
- router.beforeEach(async (to, from, next) => {
- //to:可以获取到你要跳转到哪个路由信息
- // from::可以获取到你从哪个路由来的信息
- // next : 放行函数 可以放行到指定的路由
- let token = store.state.user.token
- let name = store.state.user.userinfo.name;
- if (token) {
- //用户已经登录了,去login(不让去,停留在首页)
- if (to.path == '/login') {
- next('/')
- } else {
- // 登录去的不是login,
- if (name) {
- next()
- } else {
- // 没有用户信息。派发action ,让仓库存储用户信息 进行跳转
- try {
- await store.dispatch('asyncUserInfo')
- next()
- } catch (e) {
- // token 失效,获取不到用户信息,清除token 重新登录,
- await store.dispatch('asyncUserOut')
- }
- }
-
- }
- } else {
-
- // 未登录不能去 支付 个人中心 去登录页
- let toPath = to.path
- console.log(toPath);
- if (toPath.indexOf('/myOrder') != -1 || toPath.indexOf('/ShopCart') != -1 || toPath.indexOf('/Center') != -1) {
- console.log('未登录');
- //未登录的时候想去而没有去成的信息,存储于地址栏中【路由】
- next('/login?redirect=' + toPath)
- } else {
- console.log('放行');
- next()
- }
-
- }
- })
写在routes中的,每个路由独有的,只在当前路由中生效。
- {
- path: '/Trade',
- component: Trade,
- meta: { show: true },
- beforeEnter: (to, from, next) => {
- //去交易页必须从购物车来
- if (from.path == '/ShopCart') {
- next()
- } else {
- //停留在当前页
- next(false)
- }
-
- }
- },
直接写在对应组件页面中。
beforeRouterEnter:进入该组件的对应路由后触发
beforeRouterUpdate:当同一个组件,path参数不同时,进行切换的时候触发。比如这种:path:/music/:id
beforeRouterLeave:要离开该组件的对应路由时触发。
在这里不能使用this,这时实例还没有被创建
如果要使用this。用下面这种办法:在next中通过vm来代替this
- //组件内守卫 组件实例还没有被创建
- beforeRouteEnter(to, from, next) {
- if (from.path == "/Pay") {
- next();
- } else {
- next(false);
- }
- },
- beforeRouteLeave(to,from,next){
- const answer = window.confirm("你确定要离开吗")
- if(answer){
- next()
- }else {
- next(false)
- }
- }
在当前路由改变,但是只有在该组件被复用时调用。
举例来说:对于一个带有动态参数的路径/foo/:id 在/foo/1与/foo/2之间进行切换跳转的时候会复用foo组件,所以才会触发这个钩子。
案例:当所在组件的路由要离开时,给个弹窗,是否退出,是就退出,next中值为false则不退出。
- beforeRouteLeave(to,from,next){
- const answer = window.confirm("你确定要离开吗")
- if(answer){
- next()
- }else {
- next(false)
- }
- }
插件通常用来为 Vue 添加全局功能,例如数据字典插件等。
| 1 2 3 |
|
通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:
| 1 2 3 4 5 6 |
|
也可以传入一个可选的选项对象:
| 1 |
|
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件,在像 CommonJS 这样的模块环境中,你应该始终显式地调用 Vue.use()。
当 install 方法被同一个插件多次调用,插件将只会被安装一次。
Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
- MyPlugin.install = function (Vue, options) {
- // 1. 添加全局资源
- Vue.directive('my-directive', {
- bind (el, binding, vnode, oldVnode) {
- // 逻辑...
- }
- ...
- })
-
- // 2. 注入组件选项
- Vue.mixin({
- created: function () {
- // 逻辑...
- }
- ...
- })
-
- // 3. 添加实例方法
- Vue.prototype.$myMethod = function (methodOptions) {
- // 逻辑...
- }
- }
注意:如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。
实例:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
main.js里面安装插件:
| 1 2 |
|
插件使用:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
注意:插件与组件的区别:
组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue。
插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身。
vue中除了核心功能内置的指令外,也允许注册自定义指令。有的情况下,对普通DOM元素进行底层操作,这时候就会用到自定义指令。
是通过Vue.directive(‘第一个参数是指令的名称’,{第二个参数是一个对象,这个对象上有钩子函数})
下边举例说明:
- Vue.directive('focus', {
- // el:指令所绑定的元素,可以用来直接操作 DOM。
- //binding:一个对象,包含以下 property:
- inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
- el.focus();
- }
- });
局部注册通过在组件options选项中设置directive属性
是定义在组件内部的,只能在当前组件中使用
下边举例说明:
- directives: {
- // 指令名称
- dir1: {
- inserted(el) {
- // 指令中第一个参数是当前使用指令的DOM
- console.log(el);
- console.log(arguments);
- // 对DOM进行操作
- el.style.width = '200px';
- el.style.height = '200px';
- el.style.background = '#000';
- }
- },
- color: { // 为元素设置指定的字体颜色
- bind(el, binding) {
- el.style.color = binding.value;
- }
- }
- }
自定义指令也像组件那样存在钩子函数:
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
项目中:拖拽
使用自定义组件组件可以满足我们日常一些场景,这里给出几个自定义组件的案例:
1.防抖
2.图片懒加载
3.一键 Copy的功能
使用自定义指令背景
代码复用和抽象的主要形式是组件。
当需要对普通 DOM 元素进行底层操作,此时就会用到自定义指令
但是,对于大幅度的 DOM 变动,还是应该使用组件
常用案例
1、 输入框自动聚焦
- // 注册一个全局自定义指令 `v-focus`
- Vue.directive('focus', {
- // 当被绑定的元素插入到 DOM 中时
- inserted: function (el) {
- // 聚焦元素
- el.focus()
- }
- })
- <input v-focus>
2 下拉菜单
点击下拉菜单本身不会隐藏菜单
点击下拉菜单以外的区域隐藏菜单
- Vue.directive('clickoutside', {
- bind(el, binding) {
- function documentHandler(e) {
- if (el.contains(e.target)) {
- return false
- }
-
- if (binding.expression) {
- binding.value(e)
- }
- }
-
- el.__vueMenuHandler__ = documentHandler
- document.addEventListener('click', el.__vueMenuHandler__)
- },
- unbind(el) {
- document.removeEventListener('click', el.__vueMenuHandler__)
- delete el.__vueMenuHandler__
- }
- })
-
- new Vue({
- el: '#app',
- data: {
- show: false
- },
- methods: {
- handleHide() {
- this.show = false
- }
- }
- })
- <div class="main" v-menu="handleHide">
- <button @click="show = !show">点击显示下拉菜单</button>
- <div class="dropdown" v-show="show">
- <div class="item"><a href="#">选项 1</a></div>
- <div class="item"><a href="#">选项 2</a></div>
- <div class="item"><a href="#">选项 3</a></div>
- </div>
- </div>
- <span v-relativeTime="time"></span>
- new Vue({
- el: '#app',
- data: {
- time: 1565753400000
- }
- })
-
- Vue.directive('relativeTime', {
- bind(el, binding) {
- // Time.getFormatTime() 方法,自行补充
- el.innerHTML = Time.getFormatTime(binding.value)
- el.__timeout__ = setInterval(() => {
- el.innerHTML = Time.getFormatTime(binding.value)
- }, 6000)
- },
- unbind(el) {
- clearInterval(el.innerHTML)
- delete el.__timeout__
- }
- })