这是局部的,全局的需要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
- include:定义缓存白名单
- exclude:定义缓存黑名单
- max:最多能缓存多少组件
- activated 和 deactivate 生命周期钩子
-
实现步骤
1.a,b,c会被keep-alive
2.
同时给router设置meta:
export default{
path:'',
component:'',
meta:{
keepAlive:true
}
}
- 第一步:获取keep-alive包裹着的第一个子组件对象及其组件名;
- 第二步:根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
- 第三步:根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
- 第四步:在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key);
- 第五步:最后并且很重要,将该组件实例的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. 不能写道模板里,因为他们不是响应式的
- 在created及beforecreated之前,$refs获取不到。
三大作用:
1. 父组件的vue js代码中获得子组件的data、method、computed
子组件.vue
data:{
msg:'son'
},
methods:{
test_son:function(){ ... }
}
父组件.vue
methods:{
test_father:function(){
this.$refs.com.test_son()
console.log(this.$refs.com.msg)
}
}
2.子组件获得父组件的方法:通过this.$emit('触发父组件methods的v-on名')
点击button
父组件.vue
methods:{
get_son:function(){
this.$refs.com.test_son()
},
test_father:function(){
console.log('output father')
}
}
子组件.vue
methods:{
test_son:function(){
this.$emit('test2')
}
}
$ref 和 v-for一起使用:(ref=变量,前别忘记加:)
$refs 和 v-for一起用的时候比较特殊,(和v-for一起使用时,返回的一定是一个 数组 );$refs 会受到 v-if、v-for、v-show 这些语句的影响
- {
{item.name}}
(这里注意一下,如果是:ref='item.key',下面就是undefined了。因为$refs只接收字符串)
第一个console返回是数组。
另一种写法:(有几个博客是这么写的)
- {
{item.name}}
(这里有用模板匹配的方法,我没看:https://blog.csdn.net/LzzMandy/article/details/91372406)
$nextTick()
(和前面的event loop一起食用更加)
nextTick(function(){...}),是将其内部的回调函数function延迟在下一次dom更新数据后调用。同一事件循环DOM 更新会引起nextTick回调函数触发
nextTick()的用处:
- Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳。
- 当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中
- 一些插件
nextTick()原理:
Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个event loo的数据变化的推送进这个队列。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你修改 vm.msg = '新值',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。
简单的流程:同步代码执行 -> 查找异步队列,推入执行栈,执行Vue.nextTick[事件循环1] ->查找异步队列,推入执行栈,执行Vue.nextTick[事件循环2]...
主线程完成同步环境执行,查询任务队列,提取队首的任务,放入主线程中执行;执行完毕,再重复该操作,该过程称为事件循环。而主线程的每次读取任务队列操作,是一个事件循环的开始。