vue中指令分为全局指令和局部指令

先来看全局自定义指令:

第一个参数是指令的名字,第二个参数可以是函数也可以是对象,先来看函数:

<body>
  <div id="app">
    <input type="text" v-slice="content" v-model="content">
  </div>
  <script>
    Vue.directive('sice', (el, bindings, vnode) => {
      console.log(el); // el是指令所在的dom元素
      console.log(bindings); // 存放绑定的信息
      console.log(vnode); // 虚拟节点,指令绑定元素1的节点
    });
    const vm = new Vue({
      el: '#app',
      data: {
        content: ''
      }
    })
  </script>
</body>

函数中接收三个参数:

el在这里是input元素

bindings里是一些绑定信息:

其中expression是指绑定的变量名字,这里是content

name是指令名字

value是绑定数据的值,上面content为空,所以目前value为空

v-slice上还可以写修饰符

<div id="app">
    <input type="text" v-slice.number="content" v-model="content">
</div>

还可以传参:

<div id="app">
    <input type="text" v-slice:9.number="content" v-model="content">
</div>

vnode虚拟节点:

vnode中contex是vue实例,其中的content是vue中的content值,即通过vnode.context拿到vue实例

小demo:规定input框中只能输入5位

<body>
  <div id="app">
    <input type="text" v-slice:9.number="content" v-model="content">
    {{ content }}
  </div>
  <script>
    Vue.directive('sice', (el, bindings, vnode) => {
      const val = bindings.value.slice(0, 5);
      vnode.context.content = val;
    });
    const vm = new Vue({
      el: '#app',
      data: {
        content: ''
      }
    })
  </script>
</body>


再利用对象的形式实现:

<script>
    // Vue.directive('sice', (el, bindings, vnode) => {
    //   const val = bindings.value.slice(0, 5);
    //   vnode.context.content = val;
    // });
    Vue.directive('sice',{
      bind(el, bindings, vnode) {
        // 将指令绑定给dom元素时执行,只执行一次
      },
      update(el, bindings, vnode) {
        // 虚拟dom渲染时执行
        const val = bindings.value.slice(0, 5);
        vnode.context.content = val;
      }
    })
    const vm = new Vue({
      el: '#app',
      data: {
        content: ''
      }
    })
  </script>

bind + update 等于之前的函数

第三个对象:(不常用)

insert(el) {
    // 当元素插入到页面中执行
    el.focus();// 实现聚焦效果
}

完善一下:在这个demo中用到两个指令,v-slice和v-model ,不太合适,所以要v-slice自己实现双向数据绑定:

<div id="app">
    <input type="text" v-slice:9.number="content" v-model="content">
    {{ content }}
  </div>
  <script>
    Vue.directive('sice',{
      bind(el, bindings, vnode) {
        // 第一步,数据显示在input框中
        const context = vnode.context;
        // el.value = context; (content时动态的,不能写死, 下面形式)
        el.value = context[bindings.expression];
        // 第二步,input变化,content也要跟着变化,所以要坚挺input
        el.oninput = e => {
          const value = e.target.value;
          const val = value.slice(0, 5);
          context[bindings.expression] = val;
          // content内容被约束了,input中也要被需要被约束
          el.value = val;
        }
      },
      update(el, bindings, vnode) {

      }

此功能实现了,但是当data中content值被提前设置后,出现此情况

const vm = new Vue({
      el: '#app',
      data: {
        content: 'dsfgfhghgf'
      }
    })

所以取value值时应先对其进行截取

Vue.directive('sice',{
      bind(el, bindings, vnode) {
        // 第一步,数据显示在input框中
        const context = vnode.context;
        // el.value = context; (content时动态的,不能写死, 下面形式)
        el.value = context[bindings.expression].slice(0, 5);

又发现content值很长

Vue.directive('sice',{
      bind(el, bindings, vnode) {
        const context = vnode.context;
        let initVal = context[bindings.expression].slice(0, 5);
        el.value = initVal;
        context[bindings.expression] = initVal;

截取想要是长度,可根据

v-slice:9.number这个属性

<body>
  <div id="app">
    <input type="text" v-slice:9.number="content">
    {{ content }}
  </div>
  <script>
    Vue.directive('sice',{
      bind(el, bindings, vnode) {
        const context = vnode.context;
        const length = bindings.arg || 5;// arg时参数,没有则是5
        let initVal = context[bindings.expression].slice(0, length);
        el.value = initVal;
        context[bindings.expression] = initVal;
        
        el.oninput = e => {
          const value = e.target.value;
          const val = value.slice(0, length);
          context[bindings.expression] = val;
          el.value = val;
        }
      },


这里限制9位数

在控制台更改content值:

发现input中的值没变。要想同步更新,需写在update中

el.oninput = e => {
  const value = e.target.value;
  const val = value.slice(0, length);
  context[bindings.expression] = val;
  el.value = val;
}

增加功能:.number时不能有字母出现

Vue.directive('sice',{
  bind(el, bindings, vnode) {
    const context = vnode.context;
    const length = bindings.arg || 5;
    const numberFlag = bindings.modifiers.number;
    let initVal = context[bindings.expression];
    if (numberFlag) {
      initVal = initVal.replace(/[^0-9]/g, '');
    }
    el.value = initVal.slice(0, length);
    el.value = initVal;
    context[bindings.expression] = initVal;

    el.oninput = e => {
      let value = e.target.value;
      if (numberFlag) {
        value = value.replace(/[^0-9]/g, '');
      }
      const val = value.slice(0, length);
      context[bindings.expression] = val;
      el.value = val;
    }
  },

update中也要做处理

update(el, bindings, vnode) {
    let context = vnode.context;
    const numberFlag = bindings.modifiers.number;
    let value = context[bindings.expression];
    if (numberFlag) {
      value = value.replace(/[^0-9]/g, '')
    }
    el.value = value;
}

局部指令:

<div id="app">
    <input type="text" v-slice:9.number="content">
    {{ content }}
  </div>
  <script>
    
    const vm = new Vue({
      el: '#app',
      data: {
        content: 'dsfgfhghgf'
      },
      directives: {
        slice: (el, bindings, vnode) => {
          
        }
      }
    })
  </script>