Vue2.extend编写全局组件

209 阅读2分钟

elementUI的Loading组件就是extend的最好实践

那么我们来实现一个全局组件

新建文件

<template>
  <div>

  </div>
</template>

<script>
export default {
  name: "Test",
  props:{
    test:String
  }
}
</script>

<style scoped lang="scss">

</style>

导入文件

<template>
  <div>

  </div>
</template>

<script>
import Test from './Test'
import Vue from "vue";
export default {
  name: 'EmptyHome',
  created() {
    const instant = Vue.extend(Test)
    const vm = new instant()
    console.log(vm);
  }
}
</script>

<style scoped lang="scss">

</style>

image.png

这样就打印出来了

给全局组件传入props

const instant = Vue.extend(Test)
const vm = new instant()
vm.test = "5555" 
console.log(vm);

image.png

挂载节点

vm.test = "5555"
this.$refs.rootRef.append(vm.$mount().$el)
console.log(vm);
<template>
  <div>
    {{test}}
  </div>
</template>

<script>
export default {
  name: "Test",
  props:{
    test:String
  }
}
</script>

<style scoped lang="scss">

</style>

image.png

在全局组件里面使用路由

created() {
  console.log(this.$route);
}

image.png

打印出来的是一个空,无法读取到

打开vue的开发工具查看一下

image.png

这个组件并不在vue的节点树中

使用定位,可以定位到,但是节点树也是没有的

image.png

那么我们添加父级

const instant = Vue.extend(Test)
const vm = new instant({
  parent: this
})
vm.test = "5555"
this.$refs.rootRef.append(vm.$mount().$el)
console.log(vm);

image.png

image.png

路由打印出来了,树里面也可以找到,但是给props赋值报警告,这个警告其实就是我们不能直接修改props的警告

那么我们就不要父级,然后路由直接导入使用

import Test from './Test'
import Vue from "vue";
import router from "@/router";
export default {
  name: 'EmptyHome',
  mounted() {
    const instant = Vue.extend(Test)
    const vm = new instant({
      router: router
    })
    vm.test = "5555"
    this.$refs.rootRef.append(vm.$mount().$el)
    console.log(vm);
  }
}

image.png

正常打印

也就可以正常使用路由

使用vuex

同路由的使用

我们直接导入

import Test from './Test'
import Vue from "vue";
import store from "@/store";
export default {
  name: 'EmptyHome',
  mounted() {
    const instant = Vue.extend(Test)
    const vm = new instant({
      store
    })
    vm.test = "5555"
    this.$refs.rootRef.append(vm.$mount().$el)
    console.log(vm);
  }
}
<template>
  <div>
    {{test}}
    <div>
      vuex: {{$store.getters.getTest}}
    </div>
  </div>
</template>

<script>
export default {
  name: "Test",
  props:{
    test:String
  },
  created() {
  }
}
</script>

<style scoped lang="scss">

</style>

image.png

页面正常使用

销毁全局组件

<template>
  <div>
    <div>
      <el-button @click="addCom">添加组件</el-button>
      <el-button @click="removeCom">销毁组件</el-button>
    </div>
    <div ref="rootRef">

    </div>
  </div>
</template>

<script>
import Test from './Test'
import Vue from "vue";
import store from "@/store";
export default {
  name: 'EmptyHome',
  data(){
    return{
      vms:[]
    }
  },
  methods:{
    addCom(){
      const instant = Vue.extend(Test)
      const vm = new instant({
        store
      })
      vm.test = "5555"
      this.$refs.rootRef.append(vm.$mount().$el)
      console.log('添加组件',vm);
      this.vms.push(vm)
    },
    removeCom(){
      const vm = this.vms.shift()
      vm.$destroy()
      console.log('销毁组件',vm);
    }
  }
}
</script>

<style scoped lang="scss">

</style>

image.png

打印了但是dom没有消失

image.png

v2.cn.vuejs.org/v2/api/#vm-…

官网有解释,不会消除dom元素,所以dom我们自己消除

removeCom(){
  const vm = this.vms.shift()
  const el = vm.$el
  el.remove() // 移除dom
  vm.$destroy()
  console.log('销毁组件',vm);
}

image.png

正确销毁

当然有的时候组件销毁是自己发出通知----比如全局ELementUI的loading

那么就是要监听事件然后处理,当然也可以写在组件内部处理

<template>
  <div>
    <div>
      <el-button @click="addCom">添加组件</el-button>
    </div>
    <div ref="rootRef">

    </div>
  </div>
</template>

<script>
import Test from './Test'
import Vue from "vue";
import store from "@/store";
export default {
  name: 'EmptyHome',
  data(){
    return{
      data: 1
    }
  },
  methods:{
    addCom(){
      const instant = Vue.extend(Test)
      const vm = new instant({
        store
      })
      // vm.$on('removeCom',this.removeCom) // 监听事件
      vm.test = "5555" + this.data++
      this.$refs.rootRef.append(vm.$mount().$el)
      console.log('添加组件',vm);
    },
    removeCom(vm){
      const el = vm.$el
      el.remove() // 移除dom
      vm.$destroy()
      console.log('销毁组件',vm);
    }
  }
}
</script>

<style scoped lang="scss">

</style>
<template>
  <div>
    {{test}}
    <el-button @click="removeCom">销毁组件</el-button>
    <div>
      vuex: {{$store.getters.getTest}}
    </div>
  </div>
</template>

<script>
export default {
  name: "Test",
  props:{
    test:String
  },
  methods:{
    removeCom(){
      this.$emit('removeCom',this)
      this.$el.remove() // 自行处理
      this.$destroy()
    }
  }
}
</script>

<style scoped lang="scss">

</style>

总结

这样我们就可以自己做全局组件,适配一些改动大,到处都要用,跨模块使用的组件,这样包装出来的组件就可以直接放在一个函数上面,在挂载到Vue的原型链上面,就到处都可以调用和使用,最后就变成一行代码就可以搞定

最后

这个东西肯定很少用到的,不过会比不会好\(^o^)/~