文章

【若依】19、Vue3中自定义插件

自定义全局方法

Vue2 中定义全局方法

在 Vue2 中,自定义全局方法的思路如下:

1
Vue.prototype.post = $post

通过 Vue.prototype 将一个方法挂载为全局方法,这样,在具体的 .vue 文件中,我们就可以通过 this 来引用这个全局方法了:

1
2
3
this.$post('/login', this.loginForm).then(res => {
    console.log('---', res)
})

在 Vue2 中,我们可以将一个方法挂载为全局方法。 Vue3 这个写法则完全变了:

  1. 定义的方式变了,不再是 Vue.prototype。
  2. 引用的方式变了,因为在 Vue3 中,没法直接通过 this 去引用全局方法了。

    Vue3 中定义全局方法

    首先来看方法定义: ```javascript let app = createApp(App)

/**

  • Vue3 中定义全局方法 */ app.config.globalProperties.$sayHello = () => { console.log(‘hello yueyazhui’) }

app.use(store).use(router).mount(‘#app’)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
定义好之后,需要引用,方式如下:
```vue
<template>
  <div>
    <h1>Hello Vue3</h1>
    <div><button @click="handleClick">Click Me</button></div>
  </div>
</template>

<script setup>
// getCurrentInstance 方法可以获取到当前的 Vue 对象
import {getCurrentInstance} from 'vue'
// 来自 getCurrentInstance() 方法的 proxy 对象相当于 Vue2 中的 this
const {proxy} = getCurrentInstance()

const handleClick = () => {
  proxy.$sayHello()
}
</script>

<style lang='scss' scoped>

</style>
  1. 首先需要导入 getCurrentInstance() 方法。
  2. 从第一步导入的方法中,提取出 proxy 对象,这个 proxy 对象就类似于之前在 Vue2 中用的 this。
  3. 接下来,通过 proxy 对象就可以去引用全局方法了。

其他一些曾经在 Vue2 中使用 this 的地方,现在都可以通过 proxy 来代替了,如:router、store。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
  <div>
    <h1>Hello Vue3</h1>
    <h2>Test05</h2>
    <div><button @click="handleClick">Click Me</button></div>
    <div><button @click="handleGo">Router Demo(Go Test04)</button></div>
    <div><button @click="handleChange">Store Demo(Change name)</button></div>
  </div>
</template>

<script setup name="Test05">
  // getCurrentInstance 方法可以获取到当前的 Vue 对象
  import {getCurrentInstance} from 'vue'
  // 来自 getCurrentInstance() 方法的 proxy 对象相当于 Vue2 中的 this
  const {proxy} = getCurrentInstance()

  const handleClick = () => {
    proxy.$sayHello()
  }
  function handleGo() {
    proxy.$router.push('/test04')
  }
  function handleChange() {
    console.log(proxy.$store.state.name)
    proxy.$store.commit('SET_NAME', '月牙坠')
    console.log(proxy.$store.state.name)
  }
</script>

<style lang='scss' scoped>

</style>

自定义插件

一些工具方法可以定义为全局方法,如果这个全局的工具,不仅仅是一个工具方法,里边还包含了一些页面等,那么此时,全局方法就不适用了。这个时候我们需要定义插件。 全局方法定义,可以理解为是一个简单的插件。 Vue2 和 Vue3 中自定义插件的流程基本上都差不多,但是,插件内部的钩子函数不一样。

注册全局组件

首先定义一个组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
  <div>
    <div><a href="http://www.yueyazhui.top">月牙坠</a></div>
  </div>
</template>

<script setup name="Banner">

</script>

<style lang='scss' scoped>

</style>

接下来,就可以在插件中导入组件并注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在这里定义插件
// 在插件中,可以引入 vue 组件,并注册(这里的注册,就相当于全局注册)
import Banner from '@/components/Banner'

export default {
  /**
   * @param {*} app Vue 对象
   * @param {*} options 可选参数
   * 
   * 当项目启动的时候,插件方法就会自动执行
   */
  install: (app, options) => {
    console.log('这是我的第一个插件', app, options)
    // 在这里完成组件的注册,注意,这是一个全局注册
    app.component('banner', Banner)
  }
}

最后,就可以在项目的任意位置使用这个组件了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <!-- 这里就可以直接使用插件中注册的全局组件了 -->
  <banner/>
  <router-view/>
</template>

<style lang="scss">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>

注册全局指令

首先注册全局指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 在这里定义插件
// 在插件中,可以引入 vue 组件,并注册(这里的注册,就相当于全局注册)
import Banner from '@/components/Banner'

export default {
  /**
   * @param {*} app Vue 对象
   * @param {*} options 可选参数
   * 
   * 当项目启动的时候,插件方法就会自动执行
   */
  install: (app, options) => {
    console.log('这是我的第一个插件', app, options)
    // 在这里完成组件的注册,注意,这是一个全局注册
    app.component('banner', Banner)
    // 自定义指令
    // el 表示添加这个自定义指令的节点
    // binding 中包含了自定义指令的参数
    app.directive('font-size', (el, binding, vnode) => {
      console.log(el, binding, vnode)
      let size = 16
      // binding.arg 获取到的就是 small 或 large
      switch (binding.arg) {
        case 'small':
          size = 12
          break
        case 'large':
          size = 20
          break
        default:
          break
      }
      // 为使用 v-font-size 指令的标签设置 font-size 的大小
      el.style.fontSize = size + 'px'
    })
  }
}

然后就可以在任意地方去使用这个全局指令了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
  <div>
    <div>
      <!-- 使用自定义指令,去指定文本的大小,指令的名字就是 font-size -->
      <a href="http://www.yueyazhui.top" v-font-size:small>月牙坠</a>
    </div>
    <div>
      <a href="http://www.yueyazhui.top" v-font-size:large></a>
    </div>
  </div>
</template>

<script setup name="Banner">

</script>

<style lang="scss" scoped>

</style>

参数传递

自定义插件的时候,可以通过 options 传递参数到插件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 在这里定义插件
// 在插件中,可以引入 vue 组件,并注册(这里的注册,就相当于全局注册)
import Banner from '@/components/Banner'

export default {
  /**
   * @param {*} app Vue 对象
   * @param {*} options 可选参数
   * 
   * 当项目启动的时候,插件方法就会自动执行
   */
  install: (app, options) => {
    console.log('这是我的第一个插件', app, options)
    // 在这里完成组件的注册,注意,这是一个全局注册
    app.component('banner', Banner)
    // 自定义指令
    // el 表示添加这个自定义指令的节点
    // binding 中包含了自定义指令的参数
    app.directive('font-size', (el, binding, vnode) => {
      console.log(el, binding, vnode)
      let size = 16
      // binding.arg 获取到的就是 small 或 large
      switch (binding.arg) {
        case 'small':
          size = options.fontSize.small
          break
        case 'large':
          size = options.fontSize.large
          break
        default:
          break
      }
      // 为使用 v-font-size 指令的标签设置 font-size 的大小
      el.style.fontSize = size + 'px'
    })
  }
}

options.fontSize.small 就是插件在引用的时候传入的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// 导入插件
import plugins from './plugins'

createApp(App)
.use(plugins, {
  fontSize: {
    small: 12,
    large: 20
  }
})
.use(store)
.use(router)
.mount('#app')

provide 和 inject

可以通过 provide 去定义一个方法,然后在需要使用的使用,通过 inject 去注入这个方法然后使用。 方法定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 在这里定义插件
// 在插件中,可以引入 vue 组件,并注册(这里的注册,就相当于全局注册)
import Banner from '@/components/Banner'

export default {
  /**
   * @param {*} app Vue 对象
   * @param {*} options 可选参数
   * 
   * 当项目启动的时候,插件方法就会自动执行
   */
  install: (app, options) => {
    console.log('这是我的第一个插件', app, options)
    // 在这里完成组件的注册,注意,这是一个全局注册
    app.component('banner', Banner)
    // 自定义指令
    // el 表示添加这个自定义指令的节点
    // binding 中包含了自定义指令的参数
    app.directive('font-size', (el, binding, vnode) => {
      console.log(el, binding, vnode)
      let size = 16
      // binding.arg 获取到的就是 small 或 large
      switch (binding.arg) {
        case 'small':
          size = options.fontSize.small
          break
        case 'large':
          size = options.fontSize.large
          break
        default:
          break
      }
      // 为使用 v-font-size 指令的标签设置 font-size 的大小
      el.style.fontSize = size + 'px'
    })

    const clickMe = () => {
      console.log('------Click Me------')
    }
    // 提供
    app.provide('clickMe', clickMe)
  }
}

注意定义的时候,方法要写在 install 中。 方法使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
  <div>
    <div>
      <!-- 使用自定义指令,去指定文本的大小,指令的名字就是 font-size -->
      <a href="http://www.yueyazhui.top" v-font-size:small>月牙坠</a>
    </div>
    <div>
      <a href="http://www.yueyazhui.top" v-font-size:large></a>
    </div>
  </div>
</template>

<script setup name="Banner">
import {inject, onMounted} from 'vue'

onMounted(() => {
  // 注入
  const clickMe = inject('clickMe')
  clickMe()
})
</script>

<style lang="scss" scoped>

</style>
本文由作者按照 CC BY 4.0 进行授权