Vue学习笔记Vue组件

4次阅读

共计 8617 个字符,预计需要花费 22 分钟才能阅读完成。

全局组件注册语法

1. 全局组件注册语法

Vue.component('',{data: function(){
      // 组件所使用的数据
      ....
    },
    template: '...' // 组件定义的模板数据
  })

2. 全局组件使用示例

<body>
  <div id="app">
    <!-- 2.Vue 组件的使用 -->
    <!-- 2.1 组件的第一个实例 -->
    <button-handler></button-handler>
    <!-- 2.2 组件的第二个实例 -->
    <button-handler></button-handler>
  </div>
  <script>
    // 1. Vue 全局组件的声明
    Vue.component('button-handler', {data: function(){
        return {counter: 0}
      },
      template: '<button @click="countClick"> 点击了 {{counter}} 次 </button>',
      methods: {countClick() {this.counter ++;}
      },
    });

    var vm = new Vue({
      el: '#app',
      data: {}});
  </script>
</body>
  • Vue 可以根据组件的声明在 Vue 容器中创建多个组件实例,并且各个组件实例之间的数据和内容是相互独立互不影响的
  • Vue 组件中的 data 属性必须是一个函数并且返回一个 json 对象
  • Vue 组件中的 template 属性内容必须是由单个根元素包裹的 html 代码片段
  • Vue 组件中的 template 属性内容可以以 html 格式书写, 但是必须用 ’“’ 包裹

3. 组件的命名方式

  • 短横线方式
    Vue.component('my-component', { / ... /})
  • 驼峰方式
    Vue.component('MyComponent', { /* ... */})

笔者根据上述命名规则和 Vue 组件的定义语法做了几种尝试, 代码如下:

<body>
  <div id="app">
    <button-counter></button-counter>
    <h1> 直接在 vue 容器中分别用 '驼峰方式' 和 '短横线方式' 使用通过 '驼峰方式' 声明的组件 </h1>
    <div>
      <!-- 通过驼峰方式(不可行)
      <helloVue></helloVue> -->
      通过短横线方式
      <hello-vue></hello-vue>
    </div>
  </div>
  <script>
    // 定义第 1 个组件
    Vue.component('helloVue', {data: function() {
        return {msg: 'hello vue!'}
      },
      template: '<div> 驼峰命名规则声明的组件, 组件的模板内容是{{msg}}</div>'
    });

    // 定义第 2 个组件
    Vue.component('test-vue', {data: function() {
        return {testStr: 'test Vue Component!'}
      },
      template: '<div> 短横线命名规则声明的组件, 组件的模板内容是{{testStr}}</div>'
    });

    // 定义第 2 个组件
    Vue.component('button-counter', {data: function() {
        return {count: 0}
      },
      template: `<div>
        <button @click="count++"> 点击了 {{count}} 次 </button>
        <h1> 分别用 '驼峰方式' 和 '短横线方式' 在当前组件中使用通过 '驼峰方式' 声明的组件 </h1>
        <div>
          驼峰命名, 驼峰方式使用 <helloVue></helloVue>
        </div>
        <div>
          驼峰命名, 短横线方式使用 <hello-vue></hello-vue>
        </div>
        <h1> 分别用 '驼峰方式' 和 '短横线方式' 在当前组件中使用通过 '短横线方式' 声明的组件 </h1>
        <div>
          短横线命名, 短横线使用 <test-vue></test-vue>
        </div>
        <div>
          短横线命名, 驼峰方式使用 <testVue></testVue>
        </div>
      </div>`
    });
    var vm = new Vue({
      el: '#app',
      data: {}});
  </script>
</body>

上述代码, 分别声明了用 ’ 短横线方式 ’ 和 ’ 驼峰方式 ’ 声明了两个组件, 并且分别在组件中和 Vue 容器中使用了上述两种声明方式定义的组件。我们会发现:

  • 短横线方式 定义的组件在其他组件中可以用 ’ 短横线方式 ’ 和 ’ 驼峰方式 ’ 两种方式使用
  • 驼峰方式 定义的组件在其他组件中可以用 ’ 短横线方式 ’ 和 ’ 驼峰方式 ’ 两种方式使用
  • Vue 组件中 使用其他组件,无论被使用的组件使用声明方式,’ 短横线方式 ’ 和 ’ 驼峰方式 ’ 都能正确地使用到对应的组件
  • Vue 容器中 无论被使用的组件使用声明方式,都只能使用 ’ 短横线方式 ’

因此在声明组件时,推荐使用 ’ 短横线方式 ’ 命名

Vue 组件之局部组件

1. 局部组件注册语法

Vue 在实例的构造函数中提供了 ’components’ 属性,用来定义局部组件,语法规则:

// 组件声明方式一之组件定义
var componentA = {data: function() {...},
  ...,
  template: '...'
}

var vm = new Vue({
  el: '...',
  data: {...},
  ...,
  components: {
    // 组件声明方式一之组件声明
    'component-1': componentA,
    // 组件声明方式二
    'component-2': {data: function() {return {...}
      },
      ...,
      template: '...'
    }
  }
});

2. 局部组件使用示例

<body>
  <div id="app">
    <hello-world></hello-world>
    <hello-vue></hello-vue>
    <test-component></test-component>
  </div>
  <script>
    Vue.component('test-global-component', {data: function() {
        return {msg: '这是全局组件中的内容'}
      },
      template: '<div>{{msg}}</div>'
    });

    var helloWorld = {data: function() {
        return {msg: 'hello world'}
      },
      template: '<div> 这是局部组件 helloWorld 中的内容 </div>'
    }

    var testComponent = {data: function() {
        return {testStr: 'test vue component'}
      },
      template: `<div>
                  {{testStr}}
                  <!-- 这是其他局部组件的内容 -->
                  <!--<hello-world></hello-world>-->
                  这是全局组的内容
                  <test-global-component></test-global-component>
                </div>`
    }

    var vm = new Vue({
      el: '#app',
      data: {},
      components: {
        'hello-world': helloWorld,
        'hello-vue': {data: function() {
            return {msg: 'hello vue'}
          },
          template: '<div> 这是局部组件 helloVue 中的内容 </div>'
        },
        'test-component': testComponent
      }
    });
  </script>
</body>

以上代码是局部组件的示例,其中我还加了局部组件使用全局组件的代码,还有局部组件中使用其他局部组件的代码,测试后发现:

  • 全局组件内部可以使用其他全局组件
  • 局部组件内部不能使用其他组件
  • 局部组件内部可以使用全局组件
  • 全局组件内部不能使用局部组件

Vue 组件间数据交互

1. 父组件向子组件传值

Vue 组件之间实现数据交互需要两个步骤来完成,

1-1. 首先,需要在组件声明处通过 props 属性来接受从父组件传递过来的数据变量名称

 Vue.component('menu-item', {props: ['title'], // 此处定义接收组件传递过来的便令的名称
      template: '<div>{{title}}</div>' // 此处要与 props 定义的变量名保持一致,才能获取到对应的变量数据
    });

1-2. 然后,再父组件处添加传值的代码

<!-- 在父组件中使用子组件,子组件的名称标签上添加对应的 props 中的元素名,即可实现数据传输 -->
    <menu-item title='传给子组件的数据'></menu-item>
    <!-- 也可以通过数据绑定质量 'v-bind(:)' 将 Vue 实例中的数据传递给子组件,实现动态数据传输 -->
    <menu-item :title='msg'></menu-item>

代码示例:

<body>
 <div id="app">
      {{msg}}
      // 向组件传递常量值
      <menu-item title="这是一个向子组件传递的消息"></menu-item>
      // 通过 'v-bind(:)' 指令向组件传递动态数据
      <menu-item :title="msg"></menu-item>
  </div>
  <script>
    Vue.component('menu-item', {props: ['title'],
      template: '<div>{{title}}</div>'
    });

    var vm = new Vue({
      el: '#app',
      data: {msg: 'Vue 实例容器也算是一个组件'}
    });
  </script>
</body>

组件中的 ’props’ 属性是用来向顶的组件传递参数的,该属性对应的是一个数组,可以实现多个变量的同时接收

2. props 关联的属性名命名规则

  1. 如果关联的属性名使用了 驼峰命名规则 ,那么再模板中对应的属性要使用 短横线方式 来传递数据
  2. 但是如果是组件 template 属性里是以字符串的形式在 html 中来使用 props 关联的属性,那么就可以使用 驼峰方式 来传递数据
<body>
  <div id="app">
    <h1> 组件测试一:</h1>
    <menu-item menu-title="只能使用短横线的形式传递数据,不能使用驼峰形式"></menu-item>
    <h1> 组件测试二:</h1>
    <menu-test></menu-test>
    <h1> 组件测试三:</h1>
    <menu-test1></menu-test1>
  </div>
  <script>
    Vue.component('menu-item', {props: ['menuTitle'],
      template: '<div>{{menuTitle}}</div>'
    });

    Vue.component('menu-test', {data: function() {
        return {msg: '声明一个新组件,在组件的 template 属性中以字符串的形式使用 menu-item 组件,可以通过驼峰的形式来传值'}
      },
      template: '<div><menu-item :menuTitle="msg"></menu-item> 下面是用的短横线的形式:<menu-item :menu-title="msg"></menu-item></div>'
    });

    Vue.component('menu-test1', {data: function() {
        return {content: '这次是以 `` 形式来定义 template 中的内容,也是字符串定义的 html 内容因此可以使用驼峰规则'}
      },
      template: `<div>
        <menu-item :menuTitle="content"></menu-item>
        这次用短横线的方式来测试:<menu-item :menu-title="content"></menu-item>
      </div>`
    });

    var vm = new Vue({
      el: '#app',
      data: {}});
  </script>
</body>

Vue 对驼峰命名规则做上述处理的原因是,js 是区分大小写的,但 html 不区分大小写,因此需要做上述处理保证 js 和 html 中的变量名称一致,实现准确的数据交互。

2-1. 验证命名规则

以上是以 Vue 的全局组件来测试 props 属性变量的命名规则,下面我们使用局部组件来测试一下,代码是基于上一个代码示例之上改写的,本示例中重复部份已省略

<body>
  <div id="app">
    ...
    <h1> 组件测试四:</h1>
    <local-com local-title="这是向局部组件传递数据的内容"></local-com>
  </div>
  <script>
    ...
    var vm = new Vue({
      el: '#app',
      data: {},
      components: {
        'local-com': {data: function() {
            return {content: '局部组件'}
          },
          props: ['localTitle'],
          template: '<div><menu-item :menuTitle="content"></menu-item>{{localTitle}}</div>'
        }
      }
    });
  </script>
</body>

3. 子组件向父组件传值

子组件也需要向父组件传递数据,具体示例代码:

<body>
  <div id="app">
    <div :style="{fontSize: fontSize +'px'}"> 子组件向父组件传数据 </div>
    <menu-item :parrs="menus"@enlarge-text="handleText"></menu-item>
  </div>
  <script>
    Vue.component('menu-item', {props: ['parrs'],
      template: `<div>
        <div>
          <ul>
            <li :key="index" v-for="(item, index) in parrs">{{item}}</li>
            <button @click='parrs.push("lemon")'> 点击添加 menu</button>
            <button @click='$emit("enlarge-text")'> 点击扩大字体 </button>
          </ul>
        </div>
      </div>`
    });
    var vm = new Vue({
      el: '#app',
      data: {menus: ['apple', 'orange', 'banana'],
        fontSize: 10
      },
      methods: {handleText() {this.fontSize = this.fontSize + 5;}
      },
    });
  </script>
</body>

3-1. 在子组件中操作父组件传递的数据

上述代码示例,子组件的定义中,处理父组件传递给子组件的数据代码 <button @click='parrs.push("lemon")'> 点击添加 menu</button>,Vue 不建议这么做,处于对代码之间的解耦,父组件中的数据应该在父组件的代码逻辑中进行处理,子组件只是用来触发相应的处理逻辑或是传递相应的数据。

3-2. 子组件通过事件处理函数传递数据

上述代码示例,子组件的定义中,<button @click='$emit("enlarge-text")'> 点击扩大字体 </button>。具体说明如图所示:

3-3. 子组件通过事件函数向父组件传递参数

4. 非父子组件间的数据交互

非父子组件之间进行数据交互的方式,如图:

通过eventHub.$off('事件名称'),来关闭对应名称的事件处理函数,执行以后,对应的函数代码将不再执行。

5. Vue 插槽

Vue 在组件的 template 内容中提供了一个 ’slot’ 指令,类似一个占位符会把组件的开始标签和结束标签之间的文本内容填充到 slot 指令所在的位置,代码示例:

<body>
  <div id="app">
    <slot-test></slot-test>
    <slot-test> 这是添加的内容 </slot-test>
    <slot-test> 可以添加自定义的内容 </slot-test>
    <slot-test><font color='red'> 警告出现异常 </font></slot-test>
  </div>
  <script>
    Vue.component('slot-test', {data: function() {
        return {msg: 'ERROR'}  
      },
      template:`
        <div>
          <strong>{{msg}}</strong>
          <slot> 插槽的默认内容 </slot>
          <slot> 第二个插槽位置 </slot>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {}});
  </script>
</body>

在上述代码中我们会发现:

  1. Vue 会把每个组件标签包含的文本内容自动替换到 slot 指令所在的位置
  2. 如果组件标签没有包含任何内容,则会显示 slot 指令指定的默认内容;如果没有指定默认内容那么就不显示
  3. 如果组件模板中制定了多个 slot 指令的位置,那么在上述代码的情况下,Vue 会统一全部替换成组件标签包含的文本内容

5-1. 具名插槽

slot 指令提供了 ’name’ 属性,对不同的 slot 位置进行命名,在填充 slot 时,指定对应的 slot 名称即可把指定的内容填写到对应的 slot 指令所在的位置处,代码示例:

<body>
  <div id="app">
    <named-slot>
      <h1 slot="header"> 这是标题 1 </h1>
      <h1 slot="header"> 这是标题 2 </h1>
      <div> 这是主体 1 </div>
      <div> 这是主体 2 </div>
      <h1 slot="header"> 这是标题 3 </h1>
      <div> 这是主体 3 </div>
      <p slot="footer"> 这是底部 1 </p>
      <p slot="footer"> 这是底部 2 </p>
    </named-slot>
  </div>
  <script>
    Vue.component('named-slot', {data: function() {
        return {
          headerName: 'header',
          bodyName: 'main',
          footerName: 'footer'
        }
      },
      template: `
        <div>
          <header>
            <slot name="header"></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
            <slot name="footer"></slot>
          </footer>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {}});
  </script>
</body>

还可以通过 template 指令来统一组织和管理 slot 指令的填充内容,代码示例

<named-slot>
    <template slot="header">
        <h1> 这是标题 1 </h1>
        <h1> 这是标题 2 </h1>
    </template>
    <template>
        <div> 这是主体 1 </div>
    </template>
    <template slot="header">
        <h1> 这是标题 3 </h1>
    </template>
    <template slot="footer">
        <p> 这是底部 1 </p>
    </template>
    <template slot="footer">
        <p> 这是底部 2 </p>
    </template>
    <template>
        <div> 这是主体 2 </div>
    </template>
</named-slot>

上述 html 代码在页面显示的结果与,前一个示例显示的结果一致,以上是 Vue 具名插槽的两种使用形式。

5-2. 作用域插槽

组件标签包含的内容需要组件中的数据来组成,此时就需要通过作用域插槽来实现,示例代码:

<body>
  <div id="app">
    <menu-com :menu-list="menus">
      <template v-slot:menu-item="slotProps">
        <span v-if="slotProps.menuItem.id == 2" style="color: orange; font-weight: bold;">
            {{slotProps.menuItem.name}}
        </span>
        <span v-else>{{slotProps.menuItem.name}}</span>
      </template>
    </menu-com>
  </div>
  <script>
    Vue.component('menu-com', {props: ['menuList'],
      template: `
      <ul>
        <li :key="item.id" v-for="item in menuList">
          <slot name="menu-item" :menuItem="item">{{item.name}}</slot>
        </li>
      </ul>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        menus: [
          {
            id: 1,
            name: 'm1'
          },
          {
            id: 2,
            name: 'm2'
          },
          {
            id: 3,
            name: 'm3'
          },
        ]
      }
    });
  </script>
</body>

上述代码示例是需要对遍历出来的列表数据进行变色,此时父组件就需要获取子组件中的数据,并根据子组件的数据进行判断以确定要对哪些数据进行处理。此时就是用到了 作用域插槽,具体说明如图:

正文完
 0