【Vue】options构造选项(三)

214 阅读5分钟

directives指令--减少DOM操作的重复

指令的作用:主要用于DOM操作

  1. Vue实例/组件用于数据绑定、事件监听、DOM更新
  2. Vue指令主要目的就是原生DOM操作
  3. 减少重复
  • ★ 如果某个DOM操作你经常使用,就可以封装为指令。比如事件绑定经常做,那就写成v-on指令。
  • ★如果某个DOM操作比较复杂,也可以封装为指令

Vue自带的指令

请看上一篇博客

自己造一个指令(官方文档

两种声明方式

方法一:声明一个全局指令

在main.js里 Vue.directives("x",directiveOptions)

方法二:声明一个局部指令

在options里写,只能被那个Vue实例/组件使用

new Vue({
    ...,
    directives:{
        "x":directiveOptions
    }
})

关于directiveOptions

directiveOptions是个对象,里面有五个函数属性

  1. bind(el, info, vnode, oldVnode)
  • 类似created,只调用一次,指令第一次绑定到元素时调用。

  • 参数都是vue给我们的

    el:绑定指令的那个元素

    info:是个对象,我们想要的信息基本都在里面

    vnode:虚拟节点

    oldVnode:之前的虚拟节点

  1. inserted(参数同上)
  • 类似mounted,被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  1. update(参数同上)
  • 类似 updated
  1. componentUpdated(参数同上)
  • 用得不多,见文档
  1. unbind(参数同上)
  • 类似destroyed,当元素要消亡时调用。

例子:写一个v-on的简单的v-on2

new Vue({
  directives: {
    on2: {
      //当元素出现在页面时,会调用bind函数,我把bind函数写成添加事件监听
      //bind和inserted都行
      bind(el, info) {
        //console.log(info); //打印出info,看看我们需要他的哪些信息
        el.addEventListener(info.arg, info.value);
      },
      //添加了事件监听,那就想办法在一定的时机删掉,不然越累积越多
      //当元素要消亡时,会调用unbind函数,我把unbind函数写成删除事件监听
      unbind(el, info) {
        el.removeEventListener(info.arg, info.value);
      }
    }
  },
  template: `
    <button v-on2:click="f1">点我</button>  //button使用了我们写的指令
  `,
  methods: {
    f1() {
      console.log("Hi");
    }
  }
}).$mount("#app");

Mixins混入,就是复制---减少options构造选项的重复

就是把共同的options构造选项复制到需要用的Vue实例/组件里

作用

  • 减少重复
  • directives的作用是减少DOM操作的重复
  • mixins的作用是减少data、methods、钩子的重复
  • options里的构造选项都可以先放到一个js文件,之后哪个实例/组件需要就导入并且用mixins使用就行。

智能合并

写在了共同东西里的东西被组件引用了之后,组件还可以覆盖他们,Vue会智能合并

全局的mixins:不推荐

在main.js里写

Vue.mixins({公用的的options选项})

这样所有的组件都会用这个,所以不推荐。

例子

场景描述

  • 非完整版Vue,App.vue引用了五个组件
  • 假设我们需要在每个组件上添加name和time
  • 在这五个组件createddestroyed时,打出提示,并报出存活时间
  • 请问你怎么做? 给每个组件添加data和钩子,共五次 或者使用mixins减少重复

先写组件Child1

  1. 当child1组件出生就打印出“child1出生了”
  • 那就需要个data里面name:child1
  • created函数
  • 还需要个时间。那就data里面time为出生时间
  • 在出生时记录时间(new Date),给time赋值
  1. child1组件死亡了就打印出“child1死亡了,共生存了多少ms”
  • beforeDestroy
  • 获得当前时间
  • 那么存活时间就是当前时间-出生时间
//Child.vue
<template>
<div>Child1</div>
</template>

<script>
export default {
  data(){
    return {
      name:"Child1",
      time:undefined  //time即将用来表示出生时间
      }
  },
  //当这个组件出生了就执行created函数。
  created(){
    //把出生时间记录下来
    this.time=new Date()
    console.log(`${this.name}出生了`)
  },
  //当组件死掉之前,执行这个函数。注意不是死掉之后,死掉之后数据都没了!
  beforeDestroy(){
    //把死亡时间记录下来
    const now = new Date()
    console.log(`${this.name}死亡了,共生存了${now-this.time}ms`)
  }
}
</script>
  1. Child1组件出生是当然的,所以created函数自动执行了。那Child1组件怎么消亡呢?
  • Child1组件是被App.vue所使用的。所以用App.vue来控制这个组件消亡
  • 消亡就是把这个组件不被App.vue所用,就是把组件从DOM树里弄消失
  • 在data里Child1Visible:true(默认不消亡)
  • 使用这个组件的时候判断一下,如果Child1Visible是false就不出现在DOM树里,就是消亡了。所以点击按钮的时候让Child1Visible变成false就可以控制消亡了。
//App.vue
<template>
  <div id="app">
    <Child1 v-if="Child1Visible"/>  //用Child1Visible判断是否从DOM树里移走也就是死亡
    <button @click="Child1Visible=false">x</button>  //点击按钮就会让Child1Visible变成false
    <Child2/>
    <button>x</button>
    <Child3/>
    <button>x</button>
    <Child4/>
    <button>x</button>
    <Child5/>
    <button>x</button>
  </div>
</template>

<script>
import Child1 from "./components/Child1.vue";
import Child2 from "./components/Child2.vue";
import Child3 from "./components/Child3.vue";
import Child4 from "./components/Child4.vue";
import Child5 from "./components/Child5.vue";
export default {
  name: "App",
  data() {
    return {
      Child1Visible:true   //添加Child1Visible,默认不消亡
    };
  },
  components: {
    Child1,
    Child2,
    Child3,
    Child4,
    Child5
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Child1组件写好了,那剩下的四个组件呢?

你不会想把上面的操作在做四遍吧?重复就是罪过啊。

关于每个(子)组件的共同操作,可以用mixins

  1. 新建src/mixins/log.js,把公共的东西(Child1.vue的options)剪切到log.js里面,在导出。
  • 但是注意,以前的name:Child1被写死了,可是其他的组件不能用这个名字啊,所以把name:undefined;之后每个组件在自己里面写name:Childx,就会智能覆盖undefined
const log = {}
export default log
  1. 每个组件如何使用(就是复制就是复制)?先引入,再放到mixins里(就是复制就是复制)。别忘了写name:Childx
//Child1.vue
<template>
<div>Child1</div>
</template>

<script>
import log from "../mixins/log.js";
export default {
  data(){
    return {
      name:"Child1"  //智能覆盖log中的name
    }
  },
    mixins:[log]  //把公共的复制到我身体里了
}
</script>

关于(父组件)App.vue的操作

  1. 在App.vue里还是得把每个子组件的操作再做一遍的
<template>
  <div id="app">
    <Child1 v-if="Child1Visible"/>
    <button @click="Child1Visible=false">x</button>
    <Child2 v-if="Child2Visible"/>
    <button @click="Child2Visible=false">x</button>
    <Child3 v-if="Child3Visible"/>
    <button @click="Child3Visible=false">x</button>
    <Child4 v-if="Child4Visible"/>
    <button @click="Child4Visible=false">x</button>
    <Child5 v-if="Child5Visible"/>
    <button @click="Child5Visible=false">x</button>
  </div>
</template>

<script>
import Child1 from "./components/Child1.vue";
import Child2 from "./components/Child2.vue";
import Child3 from "./components/Child3.vue";
import Child4 from "./components/Child4.vue";
import Child5 from "./components/Child5.vue";
export default {
  name: "App",
  data() {
    return {
      Child1Visible: true,
      Child2Visible: true,
      Child3Visible: true,
      Child4Visible: true,
      Child5Visible: true,
    };
  },
  components: {
    Child1,
    Child2,
    Child3,
    Child4,
    Child5
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Extends 继承、扩展

  • extends是比mixins更抽象一点的封装
  • 如果你嫌写五次mixins麻烦,可以考虑extends一次
  • 不过实际工作中用得很少
  • 你可以使用Vue.extend或options.extends
const MyVue = Vue . extend({
data(){ return {name: “' ,time:undefined} },
created(){
if( !this . name){ console. error('
no name! ')}
this. time = new
Date( )
},
beforeDestroy(){
const duration = (new Date()) - this. time 
console . log( ${ this . name}存活时间$ { duration}^ )
})

然后我们就可以使用new Vue(options)

provide | inject提供和注入

  • 祖先提供东西,后代注入东西
  • 作用是大范围、隔N代共享信息(data、methods等)

例子:一键换肤(代码,代码

  • 点击换肤按钮会切换class:${themeName}(blue|red)来切换css从而改变颜色
  • 每个子组件都要有换肤按钮
  • 那就把换肤按钮也写成一个组件,其他组件导入使用就行了
  • 可是themeName是App.vue的data,换肤按钮组件怎么拿到祖先的东西?
  • 那就让(祖先)App.vue提供themeName;

(子孙代)换肤按钮组件把themeName注入自己就行

  • 但是祖先传过来的themeName到我们这只是我们复制的一个字符串。我们改了我们的字符串themeName并不会改祖先的themeName。所以祖先得写一个可以修改祖先自己的themeName的函数提供给换肤按钮组件,这样换肤按钮组件才可以真的改themeName

例子:选择字体大小

  • 点击字体按钮会切换class:${fontSizeName}(normal|big|small)来切换css从而改变字体大小
  • 写css:当.app.fontsize-normal ;当.app.fontsize-big ; 当.app.fontsize-small
  • App.vue得提供可以修改自己的fontSizeName的函数给按钮组件

总结

directives指令

  • 全局用Vue.directive('x', {...})
  • 局部用options.directives
  • 作用是减少DOM操作相关重复代码

mixins混入

  • 全局用Vue.mixin({..})
  • 局部用options.mixins: [mixin1, mixin2]
  • 作用是减少options里的重复

extends继承/扩展

  • 全局用Vue.extend({.})
  • 局部用options.extends: {...}
  • 作用跟mixins差不多,只是形式不同

provide | inject提供和注入

  • 祖先提供东西,后代注入东西 *作用是大范围、隔N代共享信息