编写基础和全局混入

混入(mixin)就是一个包含任意组件选项(如 createdmethodsmounted 等)的 JavaScript 对象,可用于实现这些选项的复用。我们可以通过将混入导入组件,并与该组件的其他选项 "混合" 来使用它们。

在某些场景下使用混入非常实用,例如第 2 章《Nuxt 入门》中的案例。我们知道当 Vue Loader 编译单文件组件中的 <template> 块时,会将所有遇到的资源 URL 转换为 webpack 模块请求,例如:

<img src="~/assets/sample-1.jpg">

上述图片会被转换成如下 JavaScript 代码:

createElement('img', {
  attrs: {
    src: require('~/assets/sample-1.jpg') // 这里变成了模块请求
  }
})

如果是手动插入图片,这并不复杂。但大多数情况下,我们需要动态加载图片:

<!-- pages/about.vue -->
<template>
  <img :src="'~/assets/images' + post.image.src" :alt="post.image.alt">
</template>
const post = {
  title: 'About',
  image: {
    src: '/about.jpg',
    alt: 'Sample alt 1'
  }
}
export default {
  data () {
    return { post }
  }
}

这个例子中,控制台会出现图片 404 错误,因为当图片与 :src 指令配合使用时,Vue Loader 不会进行编译,导致 webpack 在构建过程中没有处理该图片。要解决这个问题,需要手动在 :src 指令中插入模块请求:

<img :src="require('~/assets/images/about.jpg')" :alt="post.image.alt">

但这样又失去了动态加载图片的优势。因此最佳解决方案是:

<img :src="loadAssetImage(post.image.src)" :alt="post.image.alt">

通过编写可复用的 loadAssetImage 函数,就能在任何需要的 Vue 组件中调用。这正是混入的用武之地。混入有几种常见的使用方式,我们将在后续章节具体探讨。

创建基础混入/非全局混入

在非单文件组件的 Vue 应用中,我们可以这样定义一个混入对象:

var myMixin = {
  created () {
    this.hello()
  },
  methods: {
    hello () { console.log('hello from mixin!') }
  }
}

然后通过 Vue.extend() 将其 "附加" 到组件:

const Foo = Vue.extend({
  mixins: [myMixin],
  template: '<div>foo</div>'
})

在这个例子中,我们只将这个混入应用到了 Foo 组件,所以你只会在调用这个组件时看到那条 console.log 消息。

您可以在本书 GitHub 仓库的 /chapter-5/vue/mixins/basic.html 中找到此示例。

对于 Nuxt 应用,我们需要在 /plugins/ 目录的 .js 文件中创建和维护混入对象。具体操作如下:

  1. /plugins/ 目录创建 mixin-basic.js 文件,包含一个在 Vue 实例创建时向浏览器控制台输出消息的函数:

    // plugins/mixin-basic.js
    export default {
      created () {
        this.hello()
      },
      methods: {
        hello () {
          console.log('hello from mixin!')
        }
      }
    }
  2. 在需要的地方导入使用:

    // pages/about.vue
    import Mixin from '~/plugins/mixin-basic.js'
    export default {
      mixins: [Mixin]
    }

在这个例子中,你只有在 /about 路由下才会收到 console.log 的消息。这就是我们创建和使用非全局混入的方式。但在某些情况下,我们需要在应用的所有组件中使用全局混入。让我们来看看如何实现这一点。

您可以在本书 GitHub 仓库的 /chapter-5/nuxt-universal/mixins/basic/ 目录找到此示例。

创建全局混入

我们可以通过 Vue.mixin() 创建并全局应用混入:

Vue.mixin({
  mounted () {
    console.log('来自混入的问候!')
  }
})

全局混入必须在实例化 Vue 之前定义:

const app = new Vue({
  //...
}).$mount('#app')

现在,你创建的每个组件都会受到影响并显示该消息。你可以在本书 GitHub 仓库的 /chapter-5/vue/mixins/global.html 中找到这个例子。如果你在浏览器中运行它,你会看到控制台的 console.log 消息出现在每个路由上,因为它会传播到所有的路由组件。通过这种方式,我们可以看到如果滥用它可能会造成的潜在危害。在 Nuxt 中,我们以相同的方式创建全局混入;也就是说,通过使用 Vue.mixin()。让我们来看一下:

  1. /plugins/ 目录创建 mixin-utils.js 文件,包含从 /assets/ 目录加载图片的函数:

    // plugins/mixin-utils.js
    import Vue from 'vue'
    
    Vue.mixin({
      methods: {
        loadAssetImage (src) {
          return require('~/assets/images' + src)
        }
      }
    })
  2. Nuxt 配置文件中引入该全局混入:

    // nuxt.config.js
    module.exports = {
      plugins: [
        '~/plugins/mixin-utils.js'
      ]
    }
  3. 现在您可以在任意组件中使用 loadAssetImage 函数:

    // pages/about.vue
    <img :src="loadAssetImage(post.image.src)" :alt="post.image.alt">

    注意:与基础混入不同,我们不需要手动导入全局混入,因为它们已通过 nuxt.config.js 完成全局注入。但请再次注意,应当谨慎且节制地使用它们。

    您可以在本书 GitHub 仓库的 /chapter-5/nuxt-universal/mixins/global/ 目录找到此混入示例。

混入虽然实用,但像全局 Vue 组件一样,过多的全局混入会使应用难以管理和调试,导致不可预测的行为。因此请明智且节制地使用它们。希望您现在已了解 Vue 组件的工作原理及编写方法。然而,仅知道如何编写还不够——我们还需要遵守标准规范来保证代码的可读性和可维护性。在结束本章前,我们将探讨一些相关规范。