Vue.extend动态注册子组件

写本篇文章以前其实也关注过vue中的一个关于加载动态组件is的API,最开始研究它只是用来实现一个tab切换的功能,使用起来也蛮不错的。javascript

ishtml

预期:string | Object (组件的选项对象)
用于动态组件且基于 DOM 内模板的限制来工做。前端

示例:vue

<!-- 当 `currentView` 改变时,组件也跟着改变 -->
<component v-bind:is="currentView"></component>

详见vue API中关于is的定义和用法java

至于用在tab切换中,大概就是:api

<template>
    <div>
      <div>#动态组件实现tab切换效果#</div><br><br><br>
        <nav>
              <a href="javascript:void(0);" @click="toggleTabs('first');">{{first}}</a>
              <a href="javascript:void(0);" @click="toggleTabs('second');">{{second}}</a>
              <a href="javascript:void(0);" @click="toggleTabs('third');">{{third}}</a>
        </nav>

     <first :is="currentView" keep-alive></first>
      </div>
</template>

<script>
import first from 'components/first';
import second from 'components/second';
import third from 'components/third';

export default {
        data () {
             return {
                 first: "first",
                second: "second",
                third: "third",
                currentView: 'first',
             };
         },
         components: { 
             first,
             second,
             third
         },
         methods: {
             toggleTabs (tabText) {
                 this.currentView = tabText;
             }
         }
    }
</script>

可是今天,一个前端同行在群里问我“若是当前页面是根据传进来的参数的不一样而显示不一样的组件,并且当前页面中可能会import进来几十个子组件,而我又不想挨个去import这些组件,同时这些组件又是按需加载的,该咋实现?” 说实话,一开始我也懵了。
我在想,实在不行就用const demo = () => import ( './demo.vue'),或者在组件的components中按需引入:函数

components: {
   demo: () => import ( './demo.vue')
}

可是我一想,也不对啊,这样虽然能实现按需加载,可是仍是要挨个import这些组件,仍是没有解决实际的问题。this

通过查阅资料发现,vue有一个extend的方法能够实现。那么这个extend方法究竟是干吗的?


Vue.extend( options )url

Vue.extend 返回的是一个“扩展实例构造器”,也就是预设了部分选项的Vue实例构造器。常常服务于Vue.component用来生成组件,能够简单理解为当在模板中遇到该组件名称做为标签的自定义元素时,会自动调用“扩展实例构造器”来生成组件实例,并挂载到自定义元素上。spa

只是,extend建立的是一个组件构造器,而不是一个具体的组件实例,因此他不能直接在new Vue中使用。

使用Vue.extend建立的组件构造器最终是能够经过Vue.component注册成全局组件或new实例化后注册为局部组件。

接下来就来实现一下使用Vue.extend和Vue.component注册全局组件:

import Vue from 'vue';

const globalComponent = Vue.extend({
  template:"<p><a :href='url'>{{nama}}</a></p>",
  data:function(){
      return{
          nama:'某度',
          url:'http://www.moudu.com'
      }
  }
});

Vue.component('globalComponent', globalComponent);

使用这个全局注册的组件:

<template>
  <globalComponent />
</template>

也能够传入一个选项对象,会自动调用Vue.extend:

const globalComponent = {
  template:"<p><a :href='url'>{{nama}}</a></p>",
  data(){
      return{
          nama:'某度',
          url:'http://www.moudu.com'
      }
  }
};

Vue.component('globalComponent', globalComponent);

注册全局组件仍是很简单的,可是有一个容易报错的问题须要说明一下。

报错的缘由不是本文的重点,详情可参考:
http://www.javashuo.com/article/p-xpkvgdev-hd.html
http://www.javashuo.com/article/p-gxacsqmg-kp.html

接下来就来实现根据传参的不一样加载不一样组件的方法:

<template>
  <button type="button" @click="toggle('test')">动态注册组件<button>
  <p><div ref="currentView"></div>
</template>

<script>
import Vue from 'vue'

export default {
  data(){
    return {}
  },
  methods: {
    toggle(componentName){
      this.registerComponent(componentName).then(Component => {
        // new Component().$mount(this.$refs.currentView)

        new Component({
          el: this.$refs.currentView,
          data: {
              msg: "动态组件传值"
          }
        })
      })
    },
    registerComponent(componentName) {
      return import(`@/views/${componentName}.vue`).then(component => {
        return Vue.extend(component.default);
      });
    }
  },
}
</script>

这样,咱们就能够根据动态传入的参数,经过import(@/views/${componentName}.vue)来加载不一样的组件,注意,import返回一个Promise对象,在Promise的then函数中就可使用 Vue.extend(component.default)来建立一个组件的构造器,而后经过new关键字就能够实现局部注册组件了。

还有一点须要说明:在实例化Vue.extend时,不光能够绑定el到具体的DOM节点,还能够定义要传给子组件的参数data,在子组件能够直接经过this来拿到父组件传过来的参数,如:

new Component({
   el: this.$refs.currentView,
   data: {
        msg: "动态组件传值"
   }
})

子组件就能够经过this.msg拿到父组件传过来的值,若是子组件自己在data中就已经定义了一个msg字段,那么父组件中定义的msg字段会覆盖子组件中定义的msg字段。

本文参考:
Vue2.0 - 构造器的延伸 Vue.extend
vue extend 的基本使用

相关文章
相关标签/搜索