【vue】4、directives、keep alive、vue实现双向绑定原理、$refs、$nextTick

自定义指令directives

这是局部的,全局的需要Vue.
directives:{
    color:{ //自定义指令名
        bind:function(el,binding,vnode){ //bind是钩子函数
            el.style.color=binging.value
            ...
            el:是当前元素,默认传进来的
            binding:是自定义指令的参数,一般在function中使用binding.value;可有可没有
            vnode:虚拟dom节点
        }
}

abc

1. 钩子函数

  • bind: 当每次将该自定义指令绑定时自动调用;且只调用一次。
  • unbind:当元素与该指令解绑时
  • inserted:当该元素被渲染到(添加到)html页面的Dom中时
  • update:所有组件的vnode更新时 (vnode就是虚拟dom节点)
  • componentUpdated:该组件的vnode及子vnode全部全部更新后调用

2.钩子函数的一些参数

  • el:指令所绑定的元素,可以用来直接操作 DOM 。
  • binding:一个对象,包含以下属性:
    • name:指令名,不包括 v- 前缀。binding.name
    • value:指令的绑定值,例如:

      中,绑定值为2。binding.value。同时也可以

      这样的话传入的是一个对象,那么就是binding.value.text。

    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数(:),可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符(.)的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

3. 钩子函数能够动态定义指令的参数:arg

example 注意引号是否存在

data:{ arg:'left', value:'200px' }

4. 简写方法

上面的那个自定义指令例子是全写,简写指的是不注明钩子函数,此时默认绑定bind和update

directive:{ //指令:v-color
    color:function(...){....} //默认绑定bind和update
}

Keep-alive

https://www.jianshu.com/p/9523bb439950

  • 定义

在开发中经常有从列表跳到详情页,然后返回详情页的时候需要缓存列表页的状态(比如滚动位置信息),这个时候就需要保存状态,要缓存状态。

keep-alive不仅仅是能够保存页面/组件的状态这么简单,它还可以避免组件反复创建和渲染,有效提升系统性能。总的来说,keep-alive用于保存组件的渲染状态。


     或者 
  • 属性 props

  1. include:定义缓存白名单
  2. exclude:定义缓存黑名单
  3. max:最多能缓存多少组件
  4. activated 和 deactivate 生命周期钩子
  • 实现步骤

1.a,b,c会被keep-alive

    


2.

    

同时给router设置meta:
export default{
    path:'',
    component:'',
    meta:{
        keepAlive:true
    }
}
  1. 第一步:获取keep-alive包裹着的第一个子组件对象及其组件名;
  2. 第二步:根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
  3. 第三步:根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
  4. 第四步:在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key);
  5. 第五步:最后并且很重要,将该组件实例的keepAlive属性值设置为true。(keepAlive在meta中)
  • 生命周期过程

         设置了keep-alive的组件:
             第一次进入: beforeRouteEnter => created => ... => activated => ... => deactivated
             后续进入:beforeRouteEnter => activated => deactivated,
             只有第一次进入该组件时,才会走created钩子,需要缓存的组件中activated时每次都会走的钩子函数

  • 页面滚动问题

https://juejin.im/post/5b2ce07ce51d45588a7dbf76

在router实例里面添加scrollBehavior方法

const router=new VueRouter({
        routes:[
            {
                path:"/",
                component:Home
            }
        ],
        scrollBehavior(to,form,savedPosition){
         if(savedPosition){
            点击按钮时返回的页面会滚动过到之前按钮
            return savedPosition
         }else{
           设置返回值来指定页面的滚动位置,表示在用户切换路由时让是所有页面都返回到顶部位置
           return {x:0,y:0}
         }
     }
})

如果是通过vue路由进行的页面切换。例如a前往b,首先判断a是不是通过keep-alive缓存的组件,如果是,则在a路由的meta中添加一个savedPosition字段,并且值为a的滚动位置。最后return的是页面需要回滚的位置。如此一来,如果打开一个页面,该页面的组件路由中meta.savedPosition为undefined的话,则页面滚动到(0,0)的位置。那么如果打开一个页面,它的路由的meta.savedPosition有值的话,则滚动到上次浏览的位置,因为meta.savedPosition保存的就是上次浏览的位置。


Vue实现双向绑定

1.首先背下来什么是MVVM

2.实现步骤

首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

并且实现一个订阅者管理器。这个管理器中有两个方法:添加订阅者方法和通知订阅者方法。还有一个target属性和一个subs数组。target是当前的订阅者。subs数组是订阅者数组。

下面的代码最好背下来。

function defineReactive(data, key, val) {
    observe(val); 
    var dep = new Dep(); // 背下来:定义一个订阅器
    Object.defineProperty(data, key, {
        enumerable: true,  // 背下来
        configurable: true,  // 背下来
        get: function() { // 背下来
            if (dep.target) {  // 背下来
                dep.addSub(dep.target); // // 背下来:在这里添加一个订阅者
            }
            return val;// 背下来
        },
        set: function(newVal) {
            if (val === newVal) {
                return;
            }
            val = newVal;
            dep.notify(); // // 背下来:如果数据变化,通知所有订阅者
        }
    });
}
 
function Dep () {
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub) { // 背下来:添加订阅者
        this.subs.push(sub);
    },
    notify: function() {
        this.subs.forEach(function(sub) { // 背下来:通知更新订阅者
            sub.update();
        });
    }
};

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。


$refs和ref

ref可以加到dom上也可以加到组件上,ref相当于name的作用,返回一个节点。

              
div test
this.$refs.com 返回一个com组件对象 com:{data,methods,computed ...} this.$refs.div1 返回一个节点
div test
  • $refs是当前组件的ref的集合。但是this.$refs. 不能写道模板里