前端Vue高级知识分享

178 阅读3分钟

Vue高级用法


1.自定义指令

  • 在Vue的模版语法中我们学习过各种指令,比如v-show,v-for这些,但是除了这些指令之外其实Vue也提供了自定义的指令

    • 在通常情况下如果需要对DOM元素进行底层操作,就可以使用自定义指令
  • 自定义指令分为两种:

    • 自定义局部指令: 组件中通过directives选项,只能在当前组件中使用
    • 自定义全局指令: app中directive方法,可以在任意组件中被使用
  • 下面👇案例展示

<template>
  <input text="text" v-focus />
</template>
<script>
  export default {
    //局部自定义指令
    directives: {
      focus: {
        mounted(el,bindings,vnode,preVnode) {
          el.focus()
        }
      }
    }
  }
</script>
​
​
//全局自定义指令
app.directive("focus",{
  mounted(el,bindings,vnode,preVnode) {
    el.focus()
  }
})

2.指令的生命周期

  • 一个指令定义的对象,Vue提供了一些钩子函数:

    • created:在绑定元素的attribute或时事件监听器被应用之前调用;
    • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用;
    • mounted:在绑定元素的父组件被挂载后调用;
    • beforeUpdate:在更新包含组件的 VNode 之前调用;
    • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用;
    • beforeUnmount:在卸载绑定元素的父组件之前调用;
    • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次;
<template>
<!-- 自定义指令是可以有修饰符 -->
  <button v-if="counter < 2" @click="increment" v-lifeCircle.abc="'TimYang'">
    当前计数:{{counter}}
  </button>
</template><script>
  import {ref} from "vue"
  export default {
    directives: {
      lifeCircle: {
        created(el,bindings,vnode,preVnode) {
          console.log("lifeCircle created",el,bindings,vnode,preVnode)
          console.log(bindings.value) //TimYang
           console.log(bindings.modifiers);//修饰符对象
        },
        beforeMount() {
          console.log("lifeCircle beforeMount")
        },
        mounted() {
          console.log("lifeCircle mounted")
        },
        beforeUpdate() {
          console.log("lifeCircle beforeUpdate")
        },
        updated() {
          console.log("lifeCircle updated")
        },
        beforeUnmount() {
          console.log("lifeCircle beforeUnmount")
        },
        unmounted() {
          console.log("lifeCircle unmounted")
        },
      }
    }
    setup() {
      const counter = ref(0)
      const increment = () => {
        counter.value++
      }
      
      return {
        counter
        increment
      }
    }
  }
</script>

3.自定义指令案例

<template>
  <h2 v-format-time="'YYYY/MM/DD'">
    {{timestamp}}
  </h2>
</template>
<script>
  export default {
    setup() {
      const timestamp = 1624452193
      
      return {
        timestamp
      }
    }
  }
</script>
  • 把自定义指令抽离出来放入在directive文件夹中
//format-time.js
//记得下载一个dayjs库
import dayjs from "dayjs"
export default function(app) {
  let formatString = "YYYY-MM-DD HH:mm:ss"
  app.directive("format-time",{
    created(el,bindings) {
      if(bindings.value) {
        formatString = bindings.value
      }
    }
    mounted(el,bindings) {
      const textContent = el.textContent
      const timestamp = parseInt(textContent)
      if(textContent.length === 10) {
        timestamp = timestamp * 1000
      }
      el.textContent = dayjs(timestamp).format(formatString)
    }
  })
}
//index.js
import registerFormatTime from "./format-time"
export default function registerDirectives(app) {
  registerFormatTime(app)
}
//main.js
const app = createAPP(App)
//在入口文件把app传过去这样就可以有全局指令
registerDirectives(app)
​

4.Teleport

  • 在组件化开发中,我们封装一个组件A,在另外一个组件B中使用:

    • 那么组件A中template的元素,会被挂载到组件B中template的某个位置
    • 最终我们应用会形成一颗DOM树结构
  • 但是在某些情况下,是希望组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置

    • 比如移动到body元素上,或者有其他的div#app之外的元素上
    • 这个时候我们可以通过teleport来完成
  • Teleport是什么呢?

    • 它是Vue提供的内置组件,类似react的Portals;

    • 带有两个属性:

      • to:指定将其中的内容移动到目标元素,可以使用选择器
      • disabled:是否禁用teleport功能
    • App.vue

<template>
  <div class="app">
    <teleport to="#tele">
      <h2>当前计数</h2>
      <button>+1</button>
      <HelloWorld></HelloWorld>
    </teleport>
    <!-- 如果👇这个tele和上面的id都相同,也不会影响,因为到时候代码其实是会进行合并的 -->
    <!--<teleport to="#tele">
      <h2>当前计数</h2>
      <button>+1</button>
      <HelloWorld></HelloWorld>
    </teleport>-->
  </div>
</template>
<script>
  import HelloWorld from "./HelloWorld.vue"
  export default {
    components: {
      HelloWorld
    }
  }
</script>
<style scoped></style>
  • HelloWorld.vue
<template>
  <div>
    <h2>Hello World</h2>
  </div>
</template><script>
  export default {
    
  }
</script><style scoped></style>

然后这个#tele放在哪里呢,就放在对应的index里面


5.Vue插件

  • 通常我们向Vue全局添加一些功能时,会采用插件的模式,它有两种编写方式:

    • 对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行;
    • 函数类型:一个function,这个函数会在安装插件时自动执行;
  • 插件可以完成的功能没有限制,比如下面的几种都是可以的:

    • 添加全局方法或者 property,通过把它们添加config.globalProperties 上实现;
    • 添加全局资源:指令,过滤器,过渡等;
    • 通过全局 mixin 来添加一些组件选项;
    • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能;

创建一个plugins里面存放plugins_object.js

//plugins_object.js
export default {
  install(app) {
    console.log(app)
    app.config.globalProperties.$name = "Tim"
  }
}

在入口文件使用插件

import { createApp } from 'vue'
import registerDirectives from './directives'
import pluginObject from './plugins/plugins_object'
import pluginFunction from './plugins/plugins_function'const app = createApp(App);
​
registerDirectives(app);
//对象的使用:其实就是内部执行了install
app.use(pluginObject);
//函数的使用
app.use(pluginFunction);
​
​
app.mount('#app');
​

在👆的插件中写入全局的name那么在组件内部就可以获取到

//App.vue
<template></template>
<script>
  import {getCurrentInstance} from "vue"
  export default {
    mounted() {
      console.log(this.$name) //Tim
    }
    //也可以在setup中获取,比较麻烦
    setup() {
      const instance = getCurrentInstance();
      console.log(instance.appContext.config.globalProperties.$name
    }
  }
</script>
<style></style>

plugins_object.js里面写的也可以是一个函数不仅仅是对象

export default function(app) {
  console.log(app)
  app.component("")
  app.mixin();
  app.config.globalProperties.$name = "Tim"
}