vue几个实用技巧

298 阅读3分钟
extend使用技巧

这是一个全局API,详细可查看文档

使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。

经常使用Vue.extend来创建一个组件,

var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})

接下来封装一个简易的dialog,将其挂载到body下面。

//简易的dialog  alert.vue
<template>
  <div class="grid">
    <h1 class="head">这里是标题</h1>
    <div @click="close">{{ cancelText }}</div>
    <div @click="onSureClick">{{ sureText }}</div>
  </div>
</template>
<script>
export default {
  name:'alert',
  props: {
    close: {
      type: Function,
      default: () => {},
    },
    cancelText: {
      type: String,
      default: "取消",
    },
    sureText: {
      type: String,
      default: "确定",
    },
  },
  methods: {
    onSureClick() {
      // 其他逻辑
      console.log('onSureClick')
    },
  },
};
</script>

封装一个函数,用于挂载组件到目标标签上,说是挂载,实际上就是替换:

export default function extendComponents(component,callback,Vue){
  const Action = Vue.extend(component)
  const div = document.createElement('div')
  document.body.appendChild(div)
  const ele = new Action({
    propsData:{
      cancelText:'cancel1',
      sureText:'sure2',
      close:()=>{
        ele.$el.remove()
        callback&&callback()
      }
    }
  }).$mount(div)
}

来验证一下:

import extendComponents from './hooks/extendComponents.js'
import alert from './components/alert.vue'

extendComponents(alert,() => {
  console.log('入口挂载')
},Vue)

打开浏览器的element,即可以看到我们的组件被挂载到了body下面。

几个有用的自定义指令

在需要对dom元素进行更底层的操作时,就需要用到自定义指令,详细柯可看文档

有两种注册自定义指令的方式,全局注册和局部注册。

  • 全局注册

    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {
        // 聚焦元素
        el.focus()
      }
    })
    
  • 局部注册

    //在组件中定义
    directives: {
      focus: {
        // 指令的定义
        inserted: function (el) {
          el.focus()
        }
      }
    }
    

一个指定对象可以接受五个钩子函数

  • bind,只调用一次,指令第一次绑定到元素时调用
  • inserted,被绑定元素插入父节点时调用
  • update,所在组件的vnode更新时调用
  • componentUpdated,指令所在组件的vnode及其子vnode全部更新时调用
  • unbind,指令与元素解绑时调用

钩子函数会被传入以下参数

  • el,指令当前所绑定的对象
  • binding,一个对象,包含name,value等属性
  • vnode,vue编译生成的虚拟dom
  • oldVnode,上一个虚拟节点,只在update和componentUpdate中调用

接下里做几个有意思的自定义指令。

  • 点击复制

    这里主要通过bind钩子函数第二个参数的value属性拿的目标上的内容,然后通过动态创建一个textarea标签,将拿到的值赋值给textarea,调用textarea.select()选中内容,通过document.execCommand("Copy")进行复制。

    代码如下

    <template>
      <button v-copy="copyText">复制</button>
    </template>
    
    <script>
    export default {
      directives:{
        copy:{
          bind(el, { value }) {
          el.$value = value;
          el.handler = () => {
            if (!el.$value) {
              // 值为空的时候,给出提示。可根据项目UI仔细设计
              console.log("无复制内容");
              return;
            }
            // 动态创建 textarea 标签
            const textarea = document.createElement("textarea");
            // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
            textarea.readOnly = "readonly";
            textarea.style.position = "absolute";
            textarea.style.left = "-9999px";
            // 将要 copy 的值赋给 textarea 标签的 value 属性
            textarea.value = el.$value;
            // 将 textarea 插入到 body 中
            document.body.appendChild(textarea);
            // 选中值并复制
            textarea.select();
            const result = document.execCommand("Copy");
            if (result) {
              console.log("复制成功",value);
            }
            document.body.removeChild(textarea);
          };
          // 绑定点击事件,就是所谓的一键 copy 啦
          el.addEventListener("click", el.handler);
        },
        // 当传进来的值更新的时候触发
        componentUpdated(el, { value }) {
          el.$value = value;
        },
        // 指令与元素解绑的时候,移除事件绑定
        unbind(el) {
          el.removeEventListener("click", el.handler);
        },
        }
      },
      data() {
        return {
          copyText: "a copy directives",
        };
      },
    };
    </script>
    
  • 表单校验

    在使用到表单组件时,经常会对表单的输入内容进行校验,一般做法时在@change函数上添加判断:

    <template>
      <input type="text" v-model="note" @change="vaidateEmoji" />
    </template>
    
    <script>
      export default {
        methods: {
          vaidateEmoji() {
            var reg = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g
            this.note = this.note.replace(reg, '')
          },
        },
      }
    </script>
    

    如果表单过多,得一个个添加,可以使用自定义组件来解决这个问题。

    <template>
      <input type="text" v-model="note" v-validate />
    </template>
    
    <script>
    
    let findEle = (parent, type) => {
      return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type)
    }
    
    const trigger = (el, type) => {
      const e = document.createEvent('HTMLEvents')
      e.initEvent(type, true, true)
      el.dispatchEvent(e)
    }
    
    export default {
      data(){
        return{
          note:''
        }
      },
      directives: {
        validate: {
          bind: function (el, binding, vnode) {
            // 正则规则可根据需求自定义
            var regRule =
              /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g;
            let $inp = findEle(el, "input");
            el.$inp = $inp;
            $inp.handle = function () {
              let val = $inp.value;
              $inp.value = val.replace(regRule, "");
    
              trigger($inp, "input");
            };
            $inp.addEventListener("keyup", $inp.handle);
          },
          unbind: function (el) {
            el.$inp.removeEventListener("keyup", el.$inp.handle);
          },
        },
      },
      methods: {
        vaidateEmoji() {
          var reg =
            /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g;
          this.note = this.note.replace(reg, "");
        },
      },
    };
    </script>
    
@hooks

父组件监听子组件的生命周期,常用的方法可以在子组件的生命周期里用$emit向父组件抛出一个事件来监听,代码如下:

// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit("mounted");
}

如果子组件特别复杂,也可以通过hooks监听子组件事件

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
},
    
//  Child.vue
mounted(){
   console.log('子组件触发 mounted 钩子函数 ...');
},   

observable 创建跨组件状态存储器

使用vuex还是比较繁琐的,如果想简单点,可以使用observable来创建一个小型的vuex。

import Vue from 'vue'

// 通过Vue.observable创建一个可响应的对象
export const store = Vue.observable({
  userInfo: {},
  roleIds: []
})

// 定义 mutations, 修改属性
export const mutations = {
  setUserInfo(userInfo) {
    store.userInfo = userInfo
  },
  setRoleIds(roleIds) {
    store.roleIds = roleIds
  }
}
<template>
  <div>
    {{ userInfo.name }}
  </div>
</template>
<script>
import { store, mutations } from '../store'
export default {
  computed: {
    userInfo() {
      return store.userInfo
    }
  },
  created() {
    mutations.setUserInfo({
      name: 'nick'
    })
  }
}
</script>