new vue()、vue.extend、 component、vue.use、render的理解

493 阅读3分钟

new vue()、vue.extend、 component、vue.use、render的理解

new Vue({}), Vue.extend, 和 Vue.component 是 Vue.js 框架中用于创建和管理组件的核心方法,它们在构建 Vue 应用时扮演着不同的角色。下面分别解释这三个方法的用途和用法:

1. new Vue({})

  • 作用: 创建并返回一个Vue实例。这是启动Vue应用的常见方式,通过指定选项如eldatamethods等配置Vue实例的行为。
  • 使用场景: 作为应用程序的主要入口点,负责挂载Vue实例到DOM中的某个元素,并管理数据、模板、方法等

这是创建一个 Vue 实例的最直接方式。当你初始化一个 Vue 应用时,基本上就是在创建一个根 Vue 实例。这个实例是整个应用的入口点,可以挂载到 DOM 中的一个元素上,并且可以包含数据、计算属性、方法、生命周期钩子等。

用法示例:

var app = new Vue({
  el: '#app', // 指定挂载元素
  data: { // 数据对象
    message: 'Hello Vue!'
  },
  methods: { // 方法集合
    reverseMessage: function() {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

2. Vue.extend()

  • 作用: 创建一个Vue构造函数的子类,用于扩展Vue的基本功能,为创建可复用的组件构造器提供基础。通过扩展,可以向组件添加默认属性、方法等。
  • 使用场景: 当你需要定义具有特定配置的组件模板,并希望在多个地方复用时,可以先使用Vue.extend定义组件构造器,再实例化使用。

Vue.extend() 方法用于创建一个 Vue 的子类,即扩展一个基础的 Vue 构造器。这个方法主要用于定义可复用的组件构造器。通过扩展 Vue 构造器,你可以添加自定义的选项和默认值,然后使用这个扩展过的构造器来创建组件实例。

用法示例:

// 定义一个基组件构造器
var MyComponent = Vue.extend({
  template: '<div>我是基础组件内容</div>',
  data: function() {
    return {
      customData: '自定义数据'
    }
  }
});

// 使用扩展后的构造器创建组件实例
var myComponentInstance = new MyComponent().$mount('#my-component');

3. Vue.component()

  • 作用: 全局注册组件。允许你在应用的任何地方使用该组件,无需再次导入或局部注册。
  • 使用场景: 当一个组件需要被频繁复用,并且希望在整个应用中都能直接通过标签名访问时,使用此方法进行全局注册。

Vue.component() 是全局注册组件的方法。它接受两个参数:第一个是组件的名称(字符串),第二个是组件的选项对象或扩展构造器。这个方法使你可以在整个 Vue 应用中使用该组件,而不需要每次使用时都手动导入和注册。

用法示例:

Vue.component('my-component', {
  template: `<div>我是全局注册的组件</div>`
});

// 然后在任何 Vue 模板中直接使用 <my-component></my-component>
new Vue({
  el: '#app'
});

4. Vue.use()

  • 作用: 注册或安装Vue插件。Vue插件可以为Vue增加全局功能或改变Vue的行为。

  • 使用场景: 当你需要在应用中使用第三方库或自定义插件(如Vuex、Vue Router)时,使用Vue.use(Plugin)来安装并配置插件。

    // myPlugin.js
    const MyPlugin = {
      install(Vue, options) {
        // 1. 添加全局方法或属性
        Vue.myGlobalMethod = function() {
          console.log('Hello from my global method');
        };
    
        // 2. 添加全局资源 (指令/过滤器等)
        Vue.directive('my-directive', {
          bind(el, binding, vnode) {
            // 在指令第一次绑定到元素时调用
            el.textContent = `Custom directive bound with value: ${binding.value}`;
          }
        });
    
        // 3. 添加实例方法
        Vue.prototype.$myMethod = function() {
          console.log('Called myMethod from instance');
        };
        
        // 可以根据options进行一些初始化设置
        if (options && options.someOption) {
          console.log('Options someOption:', options.someOption);
        }
      }
    };
    
    export default MyPlugin;
    
    Vue.use(MyPlugin)
    

5. render函数

  • 作用: 提供一个更灵活的方式来生成Vue实例的虚拟DOM结构。相比于模板字符串,render函数提供了程序化的DOM生成方式,适用于复杂的动态渲染逻辑或与第三方库集成的场景。

  • 使用场景: 当模板表达能力不足以满足需求,或者需要根据条件动态渲染完全不同的DOM结构时,可以使用render函数来自定义渲染逻辑。

案例使用:

简单使用

    let TempConstructor = Vue.extend({
      template: "<div @click='handleClick'>我是extend组件内容</div>",
      data: function () {
        return {
          customData: "自定义数据",
        };
      },
      methods: {
        handleClick: function () {
          console.log(this.customData, "customData");
        },
      },
    });

    let instance = new TempConstructor({
      data: function () {
        return {
          customData1: "自定义数据",
        };
      },
    });
    instance.vm = instance.$mount();

    document.body.appendChild(instance.vm.$el);
    console.log(instance, "instance"); 
Vue.extend 命令式弹窗
<!--
  Messagebox弹框,基于element-Dialog
  全局居中显示
  中间内容为文字

  【传参说明】
  见data


  【使用示例】
    this.$MessageBox({
        type: 'info', //类型,默认info
        title: '提示', //必填项
        content: '此操作将永久删除该文件, 是否继续?' //非必填项
        btnText: ['取消', '去编辑'] //非必填项
      })
        .then(() => {
          setTimeout(() => {
            console.log('删除成功啦!')
          }, 2000)
        })
        .catch(() => {
          console.log('取消删除啦')
        })
-->
<template>
  <el-dialog
    :append-to-body="true"
    custom-class="custom-messagebox"
    class="custom-messagebox-wrapper"
    :visible="dialogVisible"
    :width="width"
    @close="close"
  >
    <div class="message-title font16 bold">
      <svg-icon
        :icon-class="iconObj.icon"
        class-name="icon-size-24"
        :style="{'color': iconObj.color}"
      />
      <span class="title-text ml8">{{ title }}</span>
    </div>
    <div class="mt16 pl32 font14" v-if="content">
      <span v-html="htmlContent"></span>
      {{ content }}
    </div>
    <span slot="footer" class="dialog-footer">
      <el-button @click.stop="handleClose" size="small">{{
        btnText[0]
      }}</el-button>
      <el-button v-if="btnText[1]"
        :loading="comfirmLoading"
        type="primary"
        @click.stop="handleEvent"
        size="small"
        >{{ btnText[1] }}</el-button
      >
    </span>
  </el-dialog>
</template>

<script>
export default {
  name: "Messagebox",
  model: {
    prop: "visible",
    event: "change",
  },
  props: {
    // 显示,在子组件v-model传入
    visible: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    visible(val) {
      this.dialogVisible = val;
    },
  },
  data() {
    return {
      type: 'info', //提示类型,可选值:info/warning/error/ask
      dialogVisible: false,
      title: "", // 标题,String类型,必传
      content: "", // message的内容
      width: "480px", //  弹框宽度,默认480px
      btnText: ["取消", "确认"], // footer的两个按钮文字,数组类型,默认为['取消','确认']
      comfirmLoading: false,
      htmlContent: '',//自定义内容节点
    };
  },
  created() {
    this.iconMap = {
      info: { //提示
        icon: 'icon_tips_dark',
        color: '#397BFD'
      },
      warning: { //警告
        icon: 'icon_tips_dark',
        color: '#ffba00'
      },
      error: { //错误
        icon: 'icon_close_dark',
        color: '#ff4949'
      },
      ask: { //询问
        icon: 'icon_help_dark',
        color: '#397BFD'
      }
    }
  },
  computed: {
    iconObj() {
      return this.iconMap[this.type]
    }
  },
  methods: {
    // 关闭
    close() {
      this.comfirmLoading = false
      this.dialogVisible = false;
    },
    // 取消按钮
    handleClose() {},
    // 确认按钮
    handleEvent() {},
  },
};
</script>
<style lang="scss">
.custom-messagebox-wrapper {
  .custom-messagebox {
    margin-top: 0 !important;
    top: 50%;
    transform: translateY(-50%);
    .el-dialog__body {
      padding: 24px 40px 24px 32px;
    }
    .message-title {
      display: flex;
      color: $font-main;
      line-height: 24px;
      .title-text {
        width: 0;
        flex: 1;
      }
    }
    .message-content {
      color: $font-minor;
      line-height: 22px;
    }

    .el-dialog__footer{
      border: 0;
    }
  }
}
</style>

// Vue.extend
import MessageBox from './index.vue'

let instance = null
let showToast = false
const mMessageBox = {
  install(Vue, options = {}) {
    let opt = MessageBox.data()
    Object.assign(opt, options)
    Vue.prototype.$MessageBox = (message) => {
      return new Promise((resolve, reject) => {
        if (message) {
          opt = {
            ...message,
          }
        }

        // 判断当前实例是否已存在
        if (!instance) {
          let TempConstructor = Vue.extend(MessageBox)

          instance = new TempConstructor({
            data: opt,
          })
          instance.vm = instance.$mount()

          document.body.appendChild(instance.vm.$el)
        } else {
          Object.assign(instance, MessageBox.data(), opt)
        }

        instance.vm.dialogVisible = showToast = true

        instance.handleClose = function () {
          reject()
          instance.vm.dialogVisible = showToast = false
        }

        instance.handleEvent = function () {
          instance.vm.comfirmLoading = true
          resolve()
          setTimeout(() => {
            instance.vm.dialogVisible = showToast = false
          })
        }
      })
    }
  },
}
export default mMessageBox


new Vue创建弹窗
<template>
  <el-dialog
    title="提示"
    :visible.sync="dialogVisible"
    width="30%"
    :before-close="handleClose"
  >
    <span>这是一段信息</span>
    <span slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleSubmit">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script>
export default {
  model: {
    prop: 'visible',
    event: 'change',
  },
  props: {
    // modal的显示,在父组件v-model传入
    visible: {
      type: Boolean,
      default: false,
    },
    // 认领id
    id: {
      type: [Number, String],
      default: '',
    },
  },
  data() {
    return {
      dialogVisible: false,
    }
  },
  watch: {
    visible(val) {
      this.dialogVisible = val
    },
  },
  methods: {
    handleClose() {
      this.$emit('handleClose', false)
    },
    handleSubmit() {
      this.$emit('handleSubmit', this.id)
    },
  },
}
</script>



<template>
  <el-button @click="handleShow">点击开启弹窗</el-button>
</template>
<script>
import myDialog from './myDialog.vue'
import Vue from 'vue'
import store from '@/store'
export default {
  data() {
    return {
      visible: false,
    }
  },
  methods: {
    // 发起弹窗
    handleShow() {
      // 弹窗已经存在,不再挂载
      if (this.divName) {
        this.visible = true
        return
      }
      this.divName = 'main-mount-div'
      // 创建
      let instance = new Vue({
        el: document.createElement('div'),
        store,
        render: (h) =>
          h(myDialog, {
            class: this.divName,
            props: {
              id: '111',
              visible: this.visible,
            },
            on: {
              handleSubmit: this.submitEdit,
              handleClose: this.closeEditDialog,
            },
          }),
      })
      document.body.appendChild(instance.$el)
      this.$nextTick(() => {
        this.visible = true
      })
    },
    submitEdit(params) {
      console.log(params)
      this.divName = ''
      this.visible = false
    },
    closeEditDialog(value) {
      console.log(value)
      this.divName = ''
      this.visible = false
    },
  },
}
</script>