# Vue2.x全局挂载整理

> 基于`Vue2.x`与`@vue/cli`脚手架实现组件、工具类方法、混入、指令与样式常量的全局挂载

## 前言

* 全局挂载对于开发人员来说，是一个友好且非常方便的开发模式，在工程内合理地使用全局挂载能够有效的提高开发效率。
* 在Vue工程中使用全局挂载的同时，也是对工程内组件、工具类、混入等各类代码的整理与归类。使得目录结构更为明了。
* 随着产品开发的推进，不同类型内容的全局挂载量也会逐渐膨胀。产品组工作流程与组织架构也会随之调整，也因此对于全局挂载的内容必须要做好文档维护与同步。以保证新人能够迅速且正确上手，也保证不同开发之间做好沟通，防止项目组内的出现重复工作。
* 对于全局挂载的使用，需要根据产品本身架构、项目迭代周期长短、工程代码量大小等实际情况进行评估。一般来说，`Vue`框架多应用于单页面应用。全局挂载内容的增加无疑会逐渐加大首次打开页面的性能压力，资源包体会逐渐增大。所以如果对性能有比较高的要求，可以将全局挂载的代码进行评估，若使用范围比较小，按需引入将是更好的选择。另外，如果产品本身就是多页面应用，也同样更适合减少全局挂载内容量，在开发效率与界面性能之间做一个权衡后再作决定。

## 组件(components)

* 组件的全局挂载是最常用的挂载项，对于单页面应用来说，全局挂载组件一来能够提高开发效率，无需重复多次在界面内引用组件库。同时也能在首页加载时一次性加载到组件库的资源包，并渲染其中需要使用的部分。
* 也如上文前言所述，全局的组件挂载需要考虑到某些类型的组件库资源包会比较大，而且在对首页性能要求较高的情况下，或者本身并非单页面应用的情况下。减少全局挂载组件量，且按需`import`会是一个更折衷合理的方案。亦或是在业务代码中异步加载其中非首现的组件资源，比如弹窗、标签页等。
* 全局混入挂载配合`Vue`的`component()`api实现

  ```javascript
  // components/index.js
  import compA from "./compA"
  export default {
    compA,
    compB: () => import(/* webpackChunkName: "components/compB" */ './compB') // 异步引入
  }
  ```

  ```javascript
  // main.js
  import components from './components'
  import Vue from 'vue'
  Vue.use({
    install(Vue) {
      for(const key in components) {
        Vue.component(key, components[key])
      }
    }
  })
  ```
* 挂载第三方组件库也同理使用`use()`方法，不过主流的组件库多对组件资源进行了拆分，能够做到引入局部组件资源并挂载在我们自己的`Vue`实例上，以`ant-design-vue`为例

  ```javascript
  import Vue from 'vue';
  import { Button, message } from 'ant-design-vue';

  // 全局挂载按钮与弹窗
  Vue.component(Button.name, Button);
  Vue.component(Button.Group.name, Button.Group);
  Vue.use(Button);

  Vue.prototype.$message = message;
  ```
* 除去最基本的自定义组件与第三方组件库的挂载，再想象一下这样一个开发需求场景，如果对于某个文件夹下的自定义组件库都需要进行全局挂载，而又不想每新建一个都需要再次在入口文件中进行引入。则可以通过一下方法实现这个自动挂载功能。

  ```javascript
  import Vue from 'vue'
  import upperFirst from 'lodash/upperFirst'
  import camelCase from 'lodash/camelCase'

  // 匹配components目录(包括子目录)下的所有vue文件
  const requireComponent = require.context('../components', true, /\w+\.vue$/)

  requireComponent.keys().forEach(fileName => {
    // 获取所有的组件与文件名
    const componentConfig = requireComponent(fileName)
    const componentName = upperFirst(
      camelCase(
        // 获取和目录深度无关的文件名
        fileName
          .split('/')
          .pop()
          .replace(/\.\w+$/, '')
      )
    )

    // 全局挂载组件
    Vue.component(
      componentName,
      // 如果这个组件选项是通过 `export default` 导出的，那么就会优先使用前者。
      componentConfig.default || componentConfig
    )
  })
  ```

## 工具类方法(utils)

* 工具类方法说的通俗点就是全局函数，在上文组件的全局挂载中其实已经涉及到了全局函数的挂载方式。弹窗本身为一个函数式的组件，在实例上完成全局挂载后，即可直接调用`this.$message()`这个方法使用弹窗组件。其余工具类方法也可以通过同样的方式实现挂载。

  ```javascript
  // main.js
  Vue.prototype.$message = message;
  ```
* 实现的本质，就是在`Vue`实例(对象)的原型对象上注册全局方法，而对于业务代码而言，其中的`this`均指向`Vue`实例本身，所以可以直接通过`this.$apiName()`的形式调用。一般常用的全局挂载方法有如深拷贝(deepCopy)、精确的数字计算方法、封装过的请求方法等。

## 指令(directives)

* 关于指令已在[Vue2.x指令](https://github.com/fff455/tech-share/blob/master/Vue/Vue2.x%E6%8C%87%E4%BB%A4.md)一文中有过详细的使用介绍，其中也包括了指令的全局挂载，这里不过多赘述。

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

## 混入(mixins)

* 在Vue项目中，混入相对来说是一个具有争议的功能，理想情况下，混入可以是通用业务功能，也可以是通用的工具方法，合理使用混入能够有效减少不必要的代码量。
* 但如果在缺乏使用经验情况下使用，混入就会在代码迭代与增加中逐渐趋于混乱，繁杂的混入文件一来会大大提高开发人员的学习成本，二来各个业务界面的使用需求也是各不相同，容易产生大量冗余的代码。一份混入文件会因为归类不清而不断膨胀，也可能因为过于在意分类而使文件数量过多。
* 非全局的混入已是如此，全局混入的使用更需要谨慎处理。一般使用于优化整个工程的代码当中，比如按需引入vuex，就需要配合全局混入功能实现。
* 全局混入挂载配合`Vue`的`install()`与`use()`实现

  ```javascript
  // global-mixins.js
  module.exports = {
    // install方法用于全局注册
    install: (Vue) => {
      // 全局混入
      Vue.mixin({
        // 入参为Vue实例对象
        data() {
          return {
            // 混入内的参数
            key: 'value'
          }
        }
        methods: {
          // 全局混入的方法
        }
        beforeCreate() {
          // 全局混入中对于某个生命周期进行处理
        },
      });
    },
  };
  ```

  ```javascript
  // main.js
  import mixins from './global-mixins.js';
  Vue.use(mixins); // 全局挂载动态加载的混入
  new Vue({
    el: '#app',
    router,
    components: { App },
    template: '<App/>'
  })
  ```

## 样式

* 如果产品组的UI比较多变，或者逢年过节需要出一个新皮肤来优化用户体验。此时就需要一份样式常量来降低样式的成本。而如果样式代码没有单独维护在一个文件夹下，非常正常合理的存在于`<style>`标签内，那么样式常量就需要在各个标签下都进行引入才能统一使用。这就有了全局样式文件挂载的需求。需要样式常量挂载在所有样式之前。
* `Vue`实例提供的api并没有提供样式挂载的方式，所以样式的全局挂载本身是基于`sass-loader`与`less-loader`等`webpack`插件配置实现的。在`@vue/cli`脚手架中，已经为开发人员封装好了样式预处理器的配置，样式文件、样式常量引入的语句直接配置即可。

  ```javascript
  // vue.config.js
  module.exports = {
    css: {
      loaderOptions: {
        // sass-loader, sass与scss通用, 区别在于scss语法需要分号
        sass: {
          addtionalData: `@import "~@/variables.sass"`
        },
        scss: {
          addtionalData: `@import "~@/variables.scss";`
        },
        // less-loader
        less: {
          // `primary` 即为全局变量名
          globalVars: {
            primary: '#fff'
          }
        }
      }
    }
  }
  ```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaobaiha.gitbook.io/tech-share/vue/vue-quan-ju-gua-zai-zheng-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
