组件定义与使用

Vue.component 原理

  1. 内部会调用 Vue.extend 返回一个组件的构造函数,通过 new 这个构造函数产生实例,将该实例挂载到 Vue.options.components 上,这也说明所有的全局组件最终都会挂载到这个变量上;
  2. 在组件中调用全局组件:组件 options 会和全局 options 进行合并拿到全局组件的实例,组件的合并策略是 通过原型链找到全局组件

创建全局组件

<body>
  <div id="app">
    <my-button></my-button>
    <my-button></my-button>
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script type="text/javascript">
  window.onload = function () {
    Vue.component('myButton', {
      template: '<div>{{msg}}</div>',
      data() {
        return {
          msg: '你好'
        }
      }
    })
    var app = new Vue({
      el: '#app',
      data: { }
    })
  }
</script>

创建局部组件

<body>
  <div id="app">
    <my-button></my-button>
    <my-button></my-button>
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script type="text/javascript">
  window.onload = function () {
    var app = new Vue({
      el: '#app',
      data: {
      },
      components: {
        'my-button': {
          template: '<div>{{msg}}</div>',
          data() {
            return {
              msg: 'aaa'
            }
          }
        }
      }
    })
  }
</script>

定义组件名的方式

  1. 使用 kebab-case (短横线分隔命名)

  2. 使用 PascalCase (首字母大写命名:驼峰)

组件的好处

  1. 可复用,提高开发效率;

  2. 方便后期的维护和修改;

  3. 可以减少渲染;

组件缓存

  1. keep-alive 的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是 activateddeactivated,它们分别在组件激活和失活时触发;

  2. 第一次 activated 触发是在 mounted 之后;

  3. 控制缓存的刷新可以控制 componentkey 来实现;

非缓存组件

在来回切换组件后,组件状态会丢失,即会重新渲染组件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <button @click="currentComp='component1'">组件一</button>
    <button @click="currentComp='component2'">组件二</button>
    <br>
    <br>
    <component :is="currentComp"></component>
  </div>
</body>
<script>
let component1 = {
  data: function () {
    return {
      count: 1,
    }
  },
  template: '<button style="background-color: #13E8E9" @click="count++">累加{{count}}</button>',
};

let component2 = {
  data: function () {
    return {
      count: 1,
    }
  },
  template: '<button style="background-color: red" @click="count++">累加{{count}}</button>',
};

new Vue({
  el: '#app',
  data: {
    currentComp: component1,
  },
  methods: {},
  //局部注册组件
  components: {
    'component1': component1,
    'component2': component2,
  }
});
</script>
</html>

缓存组件

来回切换不会丢失状态

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
  <div id="app">
    <button @click="currentComp=component1">组件一</button>
    <button @click="currentComp=component2">组件二</button>
    <br>
    <br>
      <!-- 
        可以通过 include 和 exclude prop 来定制该行为;
        这两个 prop 的值都可以是一个以英文逗号分隔的字符串、一个正则表达式,或是包含这两种类型的一个数组 
      -->
      <!-- 
        我们可以通过传入 max prop 来限制可被缓存的最大组件实例数;
        <KeepAlive> 的行为在指定了 max 后类似一个 LRU 缓存:如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间 
      -->

      <keep-alive>
        <component :is="currentComp"></component>
      </keep-alive>
  </div>
</body>
<script>
let component1 = {
  data: function () {
    return {
      count: 1,
    }
  },
  template: '<button style="background-color: #13E8E9" @click="count++">累加{{count}}</button>',
};

let component2 = {
  data: function () {
    return {
      count: 1,
    }
  },
  template: '<button style="background-color: red" @click="count++">累加{{count}}</button>',
};

new Vue({
  el: '#app',
  data: {
    currentComp: component1,
  },
  methods: {},
  //局部注册组件
  components: {
    component1,
    component2,
  }
});
</script>
</html>

设置白名单 include

<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>

<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
  <component :is="view" />
</KeepAlive>

<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>

设置黑名单 exclude

<!-- 以英文逗号分隔的字符串 -->
<KeepAlive exclude="a,b">
  <component :is="view" />
</KeepAlive>

<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :exclude="/a|b/">
  <component :is="view" />
</KeepAlive>

<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :exclude="['a', 'b']">
  <component :is="view" />
</KeepAlive>

设置最大缓存数 max

通过 max 可以设置最大缓存数,当缓存的实例超过该数时,vue 会移除最久没有使用的组件缓存

<KeepAlive :max="10">
  <component :is="view" />
</KeepAlive>

组件扩展

  1. 逻辑扩展:mixinsextendsComposition API

  2. 内容扩展:slots

mixins

  1. mixins:当用得比较多的时候,可能会出现多个 mixins 变量名一样时的冲突,而且溯源麻烦,不好维护;

  2. mixinsdatamethods 优先级低于组件里 datamethods

  3. mixins 的生命周期钩子函数优先级高于组件里面的生命周期钩子函数;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>mixins 扩展选项</title>
    <script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
    <h1>mixins 扩展选项</h1>
    <hr>
    <div id="app">
        <p>{{num}}</p>
        <p><button @click="add">add</button></p>
    </div>
</body>
<script>
    const mymixin1 = {
        created() {
            console.log('我是mixin1中的created')
        },
        methods: {
            // 如果方法名一样,只触发构造器里的方法,扩展不触发,混入选项也一样
            add: function () {
                this.num++
                console.log('我是 mixin1 中的方法 add')
            }
        }
    }

    const mymixin2 = {
        created() {
            console.log('我是 mixin2 中的 created')
        }
    }

    var app = new Vue({
        el: '#app',
        data: {
            num: 1
        },
        created() {
            console.log('我是构造器中的 created')
        },
        mounted() {
            console.log('我是构造器 mounted')
        },
        updated: function () {
            console.log('我是构造器触发的 updated')
        },
        methods: {
            add: function () {
                this.num++
                console.log('我是原生的方法 add')
            }
        },
        mixins: [mymixin1, mymixin2]
    });
</script>
</html>

extends

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

  2. data 选项是特例,需要注意,在 Vue.extend() 中它必须是函数;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue Extends 扩展选项</title>
    <script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
    <h1>Vue Extends 扩展选项</h1>
    <hr>
    <div id="app">
        <p>{{num}}</p>
        <p><button @click="add">add</button></p>
    </div>
</body>
<script>
    var extendObj = {
        updated: function () {
            console.log('我是扩展的 Update')
        },
        mounted() {
            console.log('我是扩展的 mounted')
        },
        methods: {
            // 如果方法名一样,只触发构造器里的方法,扩展不触发,混入选项也一样
            add: function () {
                this.num++
                console.log('我是扩展出来的方法 add')
            }
        }
    };

    var app = new Vue({
        el: '#app',
        data: {
            num: 1
        },
        created() {
            console.log('我是构造器中的 created')
        },
        mounted() {
            console.log('我是构造器 mounted')
        },
        updated: function () {
            console.log('我是构造器触发的 updated')
        },
        methods: {
            add: function () {
                this.num++
                console.log('我是原生的方法 add')
            }
        },
        // 扩展不能为数组,混入是数组,所以扩展只能一个
        extends: extendObj,
    });
</script>
</html>

Composition API

面试题

vue 子组件和父组件创建和挂载顺序?

  1. 创建过程自上而下,挂载过程自下而上,即:

    1. parent created
    2. child created
    3. child mounted
    4. parent mounted
  2. 之所以会这样是因为 vue 创建过程是一个递归过程,先创建父组件,有子组件就会创建子组件,因此创建时先有父组件再有子组件;子组件首次创建时会添加 mounted 钩子到队列,等到 patch 结束再执行它们,可见子组件的 mounted 钩子是先进入到队列中的,因此等到 patch 结束执行这些钩子也先执行;

怎么缓存当前的组件?缓存后怎么更新?

  1. 开发中缓存组件使用 keep-alive 组件,keep-alivevue 内置组件,keep-alive 包裹动态组件 component 时,会缓存不活动的组件实例,而不是销毁它们,这样在组件切换过程中将状态保留在内存中,防止重复渲染 DOM

  2. 结合属性 includeexclude 可以明确指定缓存哪些组件或排除缓存指定组件,vue3 中结合 vue-router 时变化较大,之前是 keep-alive 包裹 router-view,现在需要反过来用 router-view 包裹 keep-alive

    <router-view v-slot="{ Component }">
      <keep-alive>
        <component :is="Component"></component>
      </keep-alive>
    </router-view>
    
  3. 缓存后如果要获取数据,解决方案可以有以下两种:

    JavaScript
    JavaScript
    // 第一种 beforeRouteEnter:在有 vue-router 的项目,每次进入路由的时候,都会执行 beforeRouteEnter;
    beforeRouteEnter(to, from, next){
      next(vm => {
        console.log(vm)
        // 每次进入路由执行
        vm.getData()  // 获取数据
      })
    }
    
    // 第二种 actived:在 keep-alive 缓存的组件被激活的时候,都会执行 actived 钩子;
    activated(){
      this.getData() // 获取数据
    }
    
  4. keep-alive 是一个通用组件,它内部定义了一个 map 缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的 component 组件对应组件的 vnode,如果该组件在 map 中存在就直接返回它,由于 componentis 属性 是个响应式数据,因此只要它变化,keep-aliverender 函数就会重新执行;

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 组件定义与使用
    1. 1.1. Vue.component 原理
    2. 1.2. 创建全局组件
    3. 1.3. 创建局部组件
    4. 1.4. 定义组件名的方式
    5. 1.5. 组件的好处
  2. 2. 组件缓存
    1. 2.1. 非缓存组件
    2. 2.2. 缓存组件
    3. 2.3. 设置白名单 include
    4. 2.4. 设置黑名单 exclude
    5. 2.5. 设置最大缓存数 max
  3. 3. 组件扩展
    1. 3.1. mixins
    2. 3.2. extends
    3. 3.3. Composition API
  4. 4. 面试题
    1. 4.1. vue 子组件和父组件创建和挂载顺序?
    2. 4.2. 怎么缓存当前的组件?缓存后怎么更新?