Vue 单文件组件

2,190 阅读2分钟

Vue 组件注册

  • 全局注册

    Vue.componet('my-component-name', {
      // 选项
    })
    

    全局注册的组件在注册之后,可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

  • 局部注册

    var ComponentA = { 选项 }
    
    new Vue({
      el: '#app',
      components: {
        'component-a': ComponentA
      }
    })
    

    局部注册的组件只在注册组件中可用,在其子组件中不可用。

    注意:不管哪种注册方式, data 选项都必须是函数

Vue.extend(options)

使用基础的 Vue 构造器,创建一个子类,参数是一个包含组件选项的对象。 data 选项是特例,需要注意 - 在 Vue.extend 中它必须是函数。

<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')

结果如下:

<p>Walter White aka Heisenberg</p>

vue-loader

我们在开发使用 vue-cli 搭建的 web 应用时写的最多的就是单文件组件(.vue),在 webpack 配置文件中我们会使用 vue-loader 处理 .vue 文件,配置如下:

// webpack.config.js
module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      ...
    ]
}

.vue 文件可以包含三种类型的顶级语言块 <template><script><style>,还允许添加可选的自定义块:

  • 模板块 template

    一个 .vue 中最多一个 <template> 块,其内容将被提取为字符串传递给 vue-template-compiler ,最终它被编译为 js 渲染函数,并注入到从 <script> 导出的组件中。

  • 脚本块 script 一个 .vue 最多一个 <script> 块,会被编译成一个模块封装函数。它的默认导出应该是一个 Vue.js 的组件选项对象,也可以导出由 Vue.extend() 创建的扩展对象。

  • 样式块 style

    一个 .vue 文件可以包含多个 <style> 标签,标签可以有 scoped 或者 module 属性。

  • 自定义语言块

    vue-loader 将会使用块名来查找对应的 loader 进行处理。

下面是一个典型的单组件文件 hello.vue

<template>
  <div class="address">{{ addr }}</div>
</template>
<style>
export default {
  name: 'component-a',
  data () {
    return {
      addr: 'shenzhen'
    }
  }
}
</style>
<style scoped>
  .address {
    color: 'red'
  }
</style>

假如我们在 App.vue 中引入这个组件,如下:

<template>
  <div id="app">
    <img src="./assets/logo.png" />
    <button @click="handleClick">Button</button>
  </div>
</template>

<script>
import Hello from './views/hello.vue'
import Vue from 'vue'

export default {
  name: "App",
  methods: {
    handleClick () {
      this.$toast('I am clicked');
    }
  },
  components: {
    hello: Hello
  },
  created() {
    console.log(Hello);
    
    const HelloComp = Vue.extend(Hello);
    const HelloComp1 = new HelloComp();
    const HelloComp2 = new HelloComp();
    console.log(HelloComp1.addr) // 'shenzhen'
    console.log(HelloComp2.addr) // 'shenzhen'
    
    HelloComp1.addr = 'shanghai'
    console.log(HelloComp1.addr) // 'shanghai'
    console.log(HelloComp2.addr) // 'shenzhen'
  }
};
</script>

我们在 created 中打印 Hello 来看看它是什么,可以看出就是一个普通的对象,并且 <template> 已经被编译成 render 函数注入到这个对象中。

当在 components 选项中注册 hello 时,vue 会调用 Vue.extend(hello),在其中还会检查 hello.data 是不是 function 类型,然后返回 Sub = function VueComponent(options) 函数(如下),并且 Sub.options 包含 hello(除此还添加了一些其他选项),最终生成一个 VNode 对象。

function VueComponent (options) {
  this._init(options)
}

当在 template 中使用 <hello /> 时,其实是使用 Sub 函数创建一个实例。

写一个通用组件

ModalVerifyPassword.vue

 <template>
  <el-dialog title="登陆验证" :visible.sync="isVisible">
  <el-form :model="form">
    <el-form-item label="密码" prop="pass">
      <el-input type="password" v-model="form.pass" autocomplete="off"></el-input>
    </el-form-item>
  </el-form>
  <div slot="footer" class="dialog-footer">
    <el-button @click="onCancel">取 消</el-button>
    <el-button type="primary" @click="onVerify">确 定</el-button>
  </div>
</el-dialog>
</template>

<script>
export default {
  name: 'modal-verify-password',
  data() {
    return {
      isVisible: false,
      form: {
        pass: '',
      },
      formLabelWidth: '120px'
    };
  },
  methods: {
    onCancel() {
      this.isVisible = false
    },
    onVerify() {
      this.isVisible = false
    }
  }
};
</script>

modal-verify-password.js

import Vue from 'vue';
import store from '@/store';
import ModalVerifyPassword from '@/components/modal-verify-password';

let instance = null;

const open = () => {
  if (!instance) {
    instance = new Vue({
      ...ModalVerifyPassword,
    });
    const component = instance.$mount();
    document.body.appendChild(component.$el);
  }
  instance.isVisible = true;
};

export default {
  open
};

最后全局注册,就能在所有组件中调用。