Geeks_Z の Blog Geeks_Z の Blog
首页
  • 学习笔记

    • 《HTML》
    • 《CSS》
    • 《JavaWeb》
    • 《Vue》
  • 后端文章

    • Linux
    • Maven
    • 汇编语言
    • 软件工程
    • 计算机网络概述
    • Conda
    • Pip
    • Shell
    • SSH
    • Mac快捷键
    • Zotero
  • 学习笔记

    • 《数据结构与算法》
    • 《算法设计与分析》
    • 《Spring》
    • 《SpringMVC》
    • 《SpringBoot》
    • 《SpringCloud》
    • 《Nginx》
  • 深度学习文章
  • 学习笔记

    • 《PyTorch》
    • 《ReinforementLearning》
    • 《MetaLearning》
  • 学习笔记

    • 《高等数学》
    • 《线性代数》
    • 《概率论与数理统计》
  • 增量学习
  • 哈希学习
GitHub (opens new window)

Geeks_Z

AI小学生
首页
  • 学习笔记

    • 《HTML》
    • 《CSS》
    • 《JavaWeb》
    • 《Vue》
  • 后端文章

    • Linux
    • Maven
    • 汇编语言
    • 软件工程
    • 计算机网络概述
    • Conda
    • Pip
    • Shell
    • SSH
    • Mac快捷键
    • Zotero
  • 学习笔记

    • 《数据结构与算法》
    • 《算法设计与分析》
    • 《Spring》
    • 《SpringMVC》
    • 《SpringBoot》
    • 《SpringCloud》
    • 《Nginx》
  • 深度学习文章
  • 学习笔记

    • 《PyTorch》
    • 《ReinforementLearning》
    • 《MetaLearning》
  • 学习笔记

    • 《高等数学》
    • 《线性代数》
    • 《概率论与数理统计》
  • 增量学习
  • 哈希学习
GitHub (opens new window)
  • Linux

  • Java

  • 微服务笔记

  • MySQL

  • Nginx

  • HTML

  • CSS

  • JavaWeb

  • Vue

    • Start
    • Node和NPM
    • 快速入门
    • Vue实例
    • 组件化
      • 组件的基本使用
        • 创建组件构造器对象
        • 注册组件
        • 使用组件
      • 全局组件和局部组件
        • 全局组件
        • 局部组件
      • 父组件和子组件的区别
      • 注册组件的语法糖
      • 组件模板的分离写法
        • script 标签
        • template 标签
      • 组件的数据
        • 存放问题
        • 组件的 data 为什么必须要是函数
      • 父组件给子组件传递数据
        • 使用props属性,父组件向子组件传递数据
        • props 属性使用
        • props 的驼峰标识
      • 子传父$emit
        • 父子组件通信案例
      • 父访问子(children-ref)
      • slot-插槽的基本使用
        • slot-具名插槽的使用
        • 编译的作用域
        • 作用域插槽案例
    • 模块化
    • webpack
    • vue-cli
    • Promies
    • Vuex
    • Axios
    • vue-router
    • ref $refs(元素、组件引用 )
    • 生命周期
    • Mix(混入)配置项
    • 安装插件 use()
    • 功能样式
    • 项目开发配置
    • 样式绑定
    • 事件
  • Git

  • 开发规范

  • SpringCloud微服务权限系统

  • bug

  • Software

  • ProgramNotes
  • Vue
Geeks_Z
2022-10-24
目录

组件化

组件化

在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。

但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。

在 vue 里,所有的 vue 实例都是组件

https://gitee.com/geeks_z/upload_images/raw/master/202204051030276.png

组件的基本使用

简单的组件示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="app">
      <!-- 3.使用组件 -->
      <my-cpn></my-cpn>
      <my-cpn></my-cpn>
      <my-cpn></my-cpn>
      <cpnc></cpnc>
    </div>
    <script src="../js/vue.js"></script>
    <script>
      // 1.创建组件构造器对象
      const cpnc = Vue.extend({
        template: `
        <div>
          <h2>标题</h2>
          <p>内容1...<p>
          <p>内容2...<p>
        </div>`,
      })
      // 2.全局注册组件
      Vue.component('my-cpn', cpnc)
      const app = new Vue({
        el: '#app',
        data: {},
        components: {
          //局部组件创建
          cpnc: cpnc,
        },
      })
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 my-cpn。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用: <my-cpn></my-cpn>。

创建组件构造器对象

template中是组件的 DOM 元素内容。

注册组件

  1. 全局注册,通过 Vue.component。
  2. 局部注册,通过 components:{cpnc:cpnc}。

使用组件

像使用 html 标签一样使用。

<div id="app">
  <!-- 3.使用组件 -->
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
  <my-cpn></my-cpn>
  <cpnc></cpnc>
</div>
1
2
3
4
5
6
7

https://gitee.com/geeks_z/upload_images/raw/master/202204072145118.png

全局组件和局部组件

组件的注册方式有两种,一种是全局组件一种是局部组件。

<div id="app">
  <h2>全局组件</h2>
  <my-cpn></my-cpn>
  <h2>局部组件</h2>
  <cpnc></cpnc>
</div>
<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器对象
  const cpnc = Vue.extend({
    template: `
        <div>
          <h2>标题</h2>
          <p>内容1</p>
          <p>内容2</p>
        </div>`,
  })
  // 2.注册组件(全局组件,可以在多个vue实例中使用)
  Vue.component('my-cpn', cpnc)

  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpnc: cpnc,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

全局组件

全局组件,可以在多个 vue 实例中使用,类似于全局变量。

使用Vue.component('my-cpn', cpnc)方式注册,直接使用<my-cpn></my-cpn>调用。my-cpn是全局组件的名字,cpnc是定义的组件对象。

局部组件

局部组件,只能在当前 vue 实例挂载的对象中使用,类似于局部变量,有块级作用域。

注册方式

const app = new Vue({
  el: '#app',
  components: {
    //局部组件创建
    cpnc: cpnc,
  },
})
1
2
3
4
5
6
7

使用方式与全局变量一样,直接使用<cpnc></cpnc>调用。cpnc:cpnc第一个 cpnc 是给组件命名的名字,第二个是定义的组件对象。如果俩个同名也可以直接使用 es6 语法:

components: {
  //局部组件创建
  cpnc
}
1
2
3
4

父组件和子组件的区别

<div id="app">
  <cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
  // 1.创建组件构造器对象
  const cpn1 = Vue.extend({
    template: `
        <div>
          <h2>标题1</h2>
          <p>组件1</p>
        </div>`,
  })
  // 组件2中使用组件1
  const cpn2 = Vue.extend({
    template: `
        <div>
          <h2>标题2</h2>
          <p>组件2</p>
          <cpn1></cpn1>
        </div>`,
    components: {
      cpn1: cpn1,
    },
  })

  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpn2: cpn2,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

上述代码中定义了两个组件对象cpn1和cpn2,在组件cpn2中使用局部组件注册了cpn1,并在template中使用了注册的cpn1,然后在 vue 实例中使用注册了局部组件cpn2,在 vue 实例挂载的 div 中调用了cpn2,cpn2与cpn1形成父子组件关系。

注意:组件就是一个 vue 实例,vue 实例的属性,组件也可以有,例如 data、methods、computed 等。

注册组件的语法糖

<div id="app">
  <cpn1></cpn1>
  <cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
  // 1.注册全局组件语法糖
  Vue.component('cpn1', {
    template: `
        <div>
          <h2>全局组件语法糖</h2>
          <p>全局组件语法糖</p>
        </div>`,
  })

  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpn2: {
        template: `
        <div>
          <h2>局部组件语法糖</h2>
          <p>局部组件语法糖</p>
        </div>`,
      },
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

注册组件时候可以不实例化组件对象,直接在注册的时候实例化。{}就是一个组件对象。

组件模板的分离写法

script 标签

使用script标签定义组件的模板,script标签注意类型是text/x-template。

<!-- 1.script标签注意类型是text/x-template -->
<script type="text/x-template" id="cpn1">
  <div>
      <h2>组件模板的分离写法</h2>
      <p>script标签注意类型是text/x-template</p>
    </div>
</script>
1
2
3
4
5
6
7

template 标签

使用template标签,将内容写在标签内。

<!-- 2.template标签 -->
<template id="cpn2">
  <div>
    <h2>组件模板的分离写法</h2>
    <p>template标签</p>
  </div>
</template>
1
2
3
4
5
6
7

调用分离的模板,使用 template:'#cpn1'

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpn1: {
        template: '#cpn1',
      },
      cpn2: {
        template: '#cpn2',
      },
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

组件的数据

存放问题

前面说过 vue 组件就是一个 vue 实例,相应的 vue 组件也有data属性来存放数据。

<div id="app">
  <cpn1></cpn1>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpn1: {
        template: '<div>{{msg}}</div>',
        data() {
          return {
            msg: '组件的数据存放必须要是一个函数',
          }
        },
      },
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

在template中使用组件内部的数据msg。

https://gitee.com/geeks_z/MacImages/raw/master/202204062127959.png

组件的 data 为什么必须要是函数

组件的思想是复用,定义组件当然是把通用的公共的东西抽出来复用。

<div id="app">
  <h2>data不使用函数</h2>
  <cpn1></cpn1>
  <cpn1></cpn1>
  <hr />
  <h2>data使用函数</h2>
  <cpn2></cpn2>
  <cpn2></cpn2>
  <hr />
</div>
<script src="../js/vue.js"></script>
<template id="cpn1">
  <div>
    <button @click="count--">-</button>
    当前计数:{{count}}
    <button @click="count++">+</button>
  </div>
</template>
<template id="cpn2">
  <div>
    <button @click="count--">-</button>
    当前计数:{{count}}
    <button @click="count++">+</button>
  </div>
</template>
<script>
  const obj = {
    count: 0,
  }
  const app = new Vue({
    el: '#app',
    components: {
      //局部组件创建
      cpn1: {
        template: '#cpn1',
        data() {
          return obj
        },
      },
      cpn2: {
        template: '#cpn2',
        data() {
          return {
            count: 0,
          }
        },
      },
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

上述代码中定义了两个组件cpn1和cpn2,都是定义了两个计数器,con1的 data 虽然使用了函数,但是为了模拟data:{count:0},使用了常量obj来返回 count。

https://gitee.com/geeks_z/MacImages/raw/master/202204062127246.gif

图中可以看到,不使用函数data的好像共用一个count属性,而使用函数的data的 count 是各自用各自的,像局部变量一样有块级作用域,这个块级就是 vue 组件的作用域。

我们在复用组件的时候肯定希望,各自组件用各自的变量,如果确实需要都用一样的,可以全局组件注册,也可以是用 vuex 来进行状态管理。

父组件给子组件传递数据

使用props属性,父组件向子组件传递数据

使用组件的 props 属性

const cpn = {
  template: '#cpn',
  props: {
    cmessage: {
      type: String,
      default: 'zzzzz',
      required: true, //在使用组件必传值
    },
  },
}
1
2
3
4
5
6
7
8
9
10

向 cmessage 对象传值

<div id="app">
  <cpn :cMessage="message"></cpn>
</div>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好',
      movies: ['复仇者联盟', '钢铁侠', '星际穿越', '哪吒传奇'],
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

props 属性使用

数组写法

props: ['cmovies', 'cmessage']
1

对象写法

props: {
          cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用组件必传值
          }
  }
1
2
3
4
5
6
7

props 属性的类型限制

//1.类型限制(多个类使用数组)
cmovies:Array,//限制为数组类型
cmessage:String,//限制为Strin类型
cmessage:['String','Number']//限制为String或Number类型
1
2
3
4

props 属性的默认值

// 2.提供一些默认值,以及必传值
        cmessage: {
          type: String,
          default: 'zzzzz',//默认值
        }
1
2
3
4
5

props 属性的必传值

cmessage: {
          type: String,
          default: 'zzzzz',
          required: true //在使用组件必传值
        }
1
2
3
4
5

类型是 Object/Array,默认值必须是一个函数

//类型是Object/Array,默认值必须是一个函数
cmovies: {
	type: Array,
	default () {
		return [1, 2, 3, 4]
	}
},
1
2
3
4
5
6
7

自定义验证函数

vaildator: function (value) {
	//这个传递的值必须匹配下列字符串中的一个
	return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
}
1
2
3
4

自定义类型

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}
cmessage: Person //限定了cmeessage必须是Person类型
1
2
3
4
5

综合使用

<div id="app">
  <cpn :cMovies="movies" :cMessage="message"></cpn>
</div>
<template id="cpn">
  <div>
    <ul>
      <li v-for="(item, index) in cmovies" :key="index">{{item}}</li>
    </ul>
    <h2>{{cmessage}}</h2>
  </div>
</template>
<script src="../js/vue.js"></script>

<script>
  function Person(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
  // 父传子:props
  const cpn = {
    template: '#cpn',
    // props: ['cmovies', 'cmessage'],//数组写法
    props: {
      //对象写法
      // 1.类型限制(多个类使用数组)
      // cmovies:Array,
      // cmessage:String,
      // cmessage:['String','Number'],
      // 2.提供一些默认值,以及必传值
      cmessage: {
        type: String,
        default: 'zzzzz',
        required: true, //在使用组件必传值
      },
      //类型是Object/Array,默认值必须是一个函数
      cmovies: {
        type: Array,
        default() {
          return [1, 2, 3, 4]
        },
      },
      // 3.自定义验证函数
      // vaildator: function (value) {
      //   //这个传递的值必须匹配下列字符串中的一个
      //   return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
      // }
      // 4.自定义类型
      // cmessage:Person,
    },
    data() {
      return {}
    },
    methods: {},
  }
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好',
      movies: ['复仇者联盟', '钢铁侠', '星际穿越', '哪吒传奇'],
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

props 的驼峰标识

v-bind 是不支持使用驼峰标识的,例如cUser要改成c-User。

<div id="app">
  <!-- v-bind不支持驼峰 :cUser改成 :c-User-->
  <!-- <cpn :cUser="user"></cpn> -->
  <cpn :c-User="user"></cpn>
  <cpn :cuser="user"></cpn>
</div>
<template id="cpn">
  <div>
    <!-- 使用驼峰 -->
    <h2>{{cUser}}</h2>
    <!-- 不使用 -->
    <h2>{{cuser}}</h2>
  </div>
</template>
<script src="../js/vue.js"></script>
<script>
  // 父传子:props
  const cpn = {
    template: '#cpn',
    props: {
      //对象写法
      //驼峰
      cUser: Object,
      //未使用驼峰
      cuser: Object,
    },
    data() {
      return {}
    },
    methods: {},
  }
  const app = new Vue({
    el: '#app',
    data: {
      user: {
        name: 'zzz',
        age: 18,
        height: 175,
      },
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

子传父$emit

子组件向父组件传值,使用自定义事件$emit。

<!-- 父组件 -->
<div id="app">
  <!-- 不写参数默认传递btnClick的item -->
  <cpn @itemclick="cpnClcik"></cpn>
</div>

<!-- 子组件 -->
<template id="cpn">
  <div>
    <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

<script src="../js/vue.js"></script>

<script>
  const cpn = {
    template: '#cpn',
    data() {
      return {
        categoties: [
          {
            id: 'aaa',
            name: '热门推荐',
          },
          {
            id: 'bbb',
            name: '手机数码',
          },
          {
            id: 'ccc',
            name: '家用家电',
          },
          {
            id: 'ddd',
            name: '电脑办公',
          },
        ],
      }
    },
    methods: {
      btnClick(item) {
        this.$emit('itemclick', item)
      },
    },
  }
  const app = new Vue({
    el: '#app',
    data() {
      return {}
    },
    methods: {
      cpnClcik(item) {
        console.log('cpnClick' + item.name)
      },
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

1.在子组件中定义一个方法btnClick(item),使用$emit,’itemclick’是事件名,item是传过去的值。

methods: {
        btnClick(item) {
          this.$emit('itemclick', item)
        }
      },
1
2
3
4
5

2.在子组件中监听点击事件并回调此方法

<div>
  <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">
    {{item.name}}
  </button>
</div>
1
2
3
4
5

3.在父组件中定义一个方法 cpnClcik(item)

methods: {
	cpnClcik(item) {
		console.log('cpnClick'+item.name);
	}
},
1
2
3
4
5

4.并在父组件(vue 实例)中调用<cpn @itemclick="cpnClcik"></cpn>(不写参数默认传递 btnClick 的 item ),父组件监听事件名为itemclick的子组件传过来的事件。

<cpn @itemclick="cpnClcik"></cpn>
1

https://gitee.com/geeks_z/MacImages/raw/master/202204062127379.gif

父子组件通信案例

实现父子组件的值双向绑定。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>组件通信-父子通信案例</title>
  </head>

  <body>
    <!-- 父组件 -->
    <div id="app">
      <h2>子组件</h2>
      <cpn :number1="num1" :number2="num2" @num1change="num1Change" @num2change="num2Change"></cpn>
      <h2>--------------</h2>

      <h2>父组件{{num1}}</h2>
      <input type="text" v-model="num1" />
      <h2>父组件{{num2}}</h2>
      <input type="text" v-model="num2" />
    </div>

    <!-- 子组件 -->
    <template id="cpn">
      <div>
        <h2>number1:{{number1}}</h2>
        <h2>dnumber1:{{dnumber1}}</h2>
        <input type="text" :value="dnumber1" @input="num1input" />
        <h2>number2:{{number2}}</h2>
        <h2>dnumber2:{{dnumber2}}</h2>
        <input type="text" :value="dnumber2" @input="num2input" />
      </div>
    </template>

    <script src="../js/vue.js"></script>

    <script>
      // 父传子:props
      const cpn = {
        template: '#cpn',
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2,
          }
        },
        props: {
          number1: [Number, String],
          number2: [Number, String],
        },
        methods: {
          num1input(event) {
            this.dnumber1 = event.target.value
            this.$emit('num1change', this.dnumber1)
          },
          num2input(event) {
            this.dnumber2 = event.target.value
            this.$emit('num2change', this.dnumber2)
          },
        },
      }
      const app = new Vue({
        el: '#app',
        data: {
          num1: 1,
          num2: 2,
        },
        methods: {
          num1Change(value) {
            this.num1 = value
          },
          num2Change(value) {
            this.num1 = value
          },
        },
        components: {
          cpn,
        },
      })
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

使用 watch 实现。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>组件通信-父子通信案例(watch实现)</title>
  </head>

  <body>
    <!-- 父组件 -->
    <div id="app">
      <cpn :number1="num1" :number2="num2" @num1change="num1Change" @num2change="num2Change"></cpn>

      <h2>父组件{{num1}}</h2>
      <input type="text" v-model="num1" />
      <h2>父组件{{num2}}</h2>
      <input type="text" v-model="num2" />
    </div>

    <!-- 子组件 -->
    <template id="cpn">
      <div>
        <h2>{{number1}}</h2>
        <input type="text" v-model="dnumber1" />
        <h2>{{number2}}</h2>
        <input type="text" v-model="dnumber2" />
      </div>
    </template>

    <script src="../js/vue.js"></script>

    <script>
      // 父传子:props
      const cpn = {
        template: '#cpn',
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2,
          }
        },
        props: {
          number1: [Number, String],
          number2: [Number, String],
        },
        watch: {
          dnumber1(newValue) {
            this.dnumber1 = newValue * 100
            this.$emit('num1change', newValue)
          },
          dnumber2(newValue) {
            this.dnumber1 = newValue * 100
            this.$emit('num2change', newValue)
          },
        },
      }
      const app = new Vue({
        el: '#app',
        data() {
          return {
            num1: 1,
            num2: 2,
          }
        },
        methods: {
          num1Change(value) {
            this.num1 = value
          },
          num2Change(value) {
            this.num1 = value
          },
        },
        components: {
          cpn,
        },
      })
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

父访问子(children-ref)

父组件访问子组件,有时候需要直接操作子组件的方法,或是属性,此时需要用到$children和$ref。

<!-- 父组件 -->
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpn ref="aaa"></cpn>
  <button @click="btnClick">按钮</button>
</div>
<!-- 子组件 -->
<template id="cpn">
  <div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
  // 父传子:props
  const cpn = {
    template: '#cpn',
    data() {
      return {
        name: '我是子组件的name',
      }
    },
    methods: {
      showMessage() {
        console.log('showMessage')
      },
    },
  }
  const app = new Vue({
    el: '#app',
    data() {
      return {
        message: 'hello',
      }
    },
    methods: {
      btnClick() {
        // 1.children
        // console.log(this.$children[0].showMessage)
        // for (let cpn of this.$children) {
        //   console.log(cpn.showMessage)
        // }
        // 2.$ref
        console.log(this.$refs.aaa.name)
      },
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

$children 方式

// 1.children
console.log(this.$children[0].showMessage)
for (let cpn of this.$children) {
  console.log(cpn.showMessage)
}
1
2
3
4
5

使用this.$children直接获取**当前实例的直接子组件,需要注意 $children 并不保证顺序,也不是响应式的。**如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

$refs 方式

先定义子组件

<cpn ref="aaa"></cpn>
1

直接调用

slot-插槽的基本使用

我们在使用组件的时候有时候希望,在组件内部定制化内容,例如京东这样。

https://gitee.com/geeks_z/MacImages/raw/master/202204071003232.png

https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516083549.png

这两个都是导航栏,组件的思想是可以复用的,把这个导航栏看做一个组件。

这个组件都可以分成三个部分,左边中间右边,如果可以分割组件,就可以定制化组件内容了。

<!-- 父组件 -->
<div id="app">
  <cpn></cpn>
  <cpn>
    <span style="color:red;">这是插槽内容222</span>
  </cpn>
  <cpn>
    <i style="color:red;">这是插槽内容333</i>
  </cpn>
  <cpn></cpn>
</div>

<!-- 插槽的基本使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
  <div>
    <div>{{message}}</div>
    <!-- 插槽默认值 -->
    <slot><button>button</button></slot>
  </div>
</template>

<script src="../js/vue.js"></script>

<script>
  const cpn = {
    template: '#cpn',
    data() {
      return {
        message: '我是子组件',
      }
    },
  }
  const app = new Vue({
    el: '#app',
    data() {
      return {
        message: '我是父组件消息',
      }
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

简单使用插槽,定义 template 时候使用 slot

<!-- 子组件 -->
<template id="cpn">
  <div>
    <div>{{message}}</div>
    <!-- 插槽默认值 -->
    <slot><button>button</button></slot>
  </div>
</template>
1
2
3
4
5
6
7
8

插槽可以使用默认值,就是插槽的默认值。

<cpn></cpn>
<cpn><span style="color:red;">这是插槽内容222</span></cpn>
1
2

使用插槽,这是插槽内容 222将替换插槽的默认值

上述代码结果如图所示

https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516083936.png

替换了两次插槽,两次未替换显示默认的 button。

如果想实现组件分成三部分就可以使用三个<slot></slot>来填充插槽了。

slot-具名插槽的使用

具名插槽,就是可以让插槽按指定的顺序填充,而没有具名的插槽是按照你填充的顺序排列的,而具名插槽可以自定义排列。

<!-- 父组件 -->
<div id="app">
  <cpn>
    <span>具名插槽</span>
    <span slot="left">这是左边具名插槽</span>
    <!-- 新语法 -->
    <template v-slot:center>这是中间具名插槽</template>
    <!-- 新语法缩写 -->
    <template #right>这是右边具名插槽</template>
  </cpn>
</div>

<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件 -->
<template id="cpn">
  <div>
    <slot name="left">左边</slot>
    <slot name="center">中间</slot>
    <slot name="right">右边</slot>
    <slot>没有具名的插槽</slot>
  </div>
</template>

<script src="../js/vue.js"></script>

<script>
  const cpn = {
    template: '#cpn',
    data() {
      return {
        message: '我是子组件',
      }
    },
  }
  const app = new Vue({
    el: '#app',
    data() {
      return {
        message: '我是父组件消息',
      }
    },
    components: {
      cpn,
    },
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

如图所示

https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516084157.png

没有具名的插槽排在最后,因为在定义组件的时候,排在了最后,如果有多个按顺序排列。具名插槽按照自定义的顺序排列。

定义具名插槽,使用 name 属性,给插槽定义一个名字。

<!-- 插槽的基本使用使用<slot></slot> -->
<!-- 子组件模板 -->
<template id="cpn">
  <div>
    <slot name="left">左边</slot>
    <slot name="center">中间</slot>
    <slot name="right">右边</slot>
    <slot>没有具名的插槽</slot>
  </div>
</template>
1
2
3
4
5
6
7
8
9
10

使用具名插槽,在自定义组件标签内使用 slot="left",插入指定插槽

<!-- 父组件 -->
<div id="app">
  <cpn>
    <span>具名插槽</span>
    <span slot="left">这是左边具名插槽</span>
    <!-- 新语法 -->
    <template v-slot:center>这是中间具名插槽</template>
    <!-- 新语法缩写 -->
    <template #right>这是右边具名插槽</template>
  </cpn>
</div>
1
2
3
4
5
6
7
8
9
10
11

注意:此处有是三种写法,获取指定插槽。

编译的作用域

前面说过组件都有自己的作用域,自己组件的作用在自己组件内。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>编译的作用域</title>
  </head>
  <body>
    <!-- 父组件 -->
    <div id="app">
      <!-- 使用的vue实例作用域的isShow -->
      <cpn v-show="isShow"></cpn>
    </div>
    <!-- 子组件 -->
    <template id="cpn">
      <div>
        <h2>我是子组件</h2>
        <p>哈哈哈</p>
        <!-- 组件作用域,使用的子组件的作用域 -->
        <button v-show="isShow"></button>
      </div>
    </template>

    <script src="../js/vue.js"></script>

    <script>
      const cpn = {
        template: '#cpn',
        data() {
          return {
            isShow: false,
          }
        },
      }
      const app = new Vue({
        el: '#app',
        data() {
          return {
            message: '我是父组件消息',
            isShow: true,
          }
        },
        components: {
          cpn,
        },
      })
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

结果如下

https://gitee.com/geeks_z/MacImages/raw/master/202204071003628.png

子组件使用的是子组件的 isShow,子组件为 false,所以 button 没显示,被隐藏。

作用域插槽案例

父组件替换插槽的标签,但是内容是由子组件来提供。

当组件需要在多个父组件多个界面展示的时候,将内容放在子组件插槽中,父组件只需要告诉子组件使用什么方式展示界面。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>作用域插槽案例</title>
  </head>

  <body>
    <!-- 父组件 -->
    <div id="app">
      <cpn></cpn>
      <!-- 目的是获取子组件数据 -->
      <cpn>
        <!-- 2.5以下必须使用template -->
        <template slot-scope="slot">
          <!-- <span v-for="(item, index) in slot.data" :key="index">{{item}}-</span> -->
          <span>{{slot.data.join(' - ')}}</span>
        </template>
      </cpn>
      <cpn>
        <!-- 2.5以下必须使用template -->
        <template slot-scope="slot">
          <!-- <span v-for="(item, index) in slot.data" :key="index">{{item}}*</span> -->
          <span>{{slot.data.join(' * ')}}</span>
        </template>
      </cpn>
    </div>

    <!-- 插槽的基本使用使用<slot></slot> -->
    <!-- 子组件 -->
    <template id="cpn">
      <div>
        <slot :data="pLanguage">
          <ul>
            <li v-for="(item, index) in pLanguage" :key="index">{{item}}</li>
          </ul>
        </slot>
      </div>
    </template>

    <script src="../js/vue.js"></script>

    <script>
      const cpn = {
        template: '#cpn',
        data() {
          return {
            isShwo: false,
            pLanguage: ['JavaScript', 'Java', 'C++', 'C'],
          }
        },
      }
      const app = new Vue({
        el: '#app',
        data() {
          return {
            isShow: true,
          }
        },
        components: {
          cpn,
        },
      })
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

组件中使用 slot-scope="slot"(2.6.0 已经废弃)给插槽属性命名,在通过 slot 调用绑定在插槽上的属性。也可以使用 v-slot="slot"。

https://cdn.jsdelivr.net/gh/krislinzhao/IMGcloud/img/20200516084957.png

#Vue
上次更新: 2025/02/26, 08:57:57
Vue实例
模块化

← Vue实例 模块化→

最近更新
01
RAIL
02-26
02
IOCTF
02-25
03
DGM
02-25
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Geeks_Z | MIT License
京公网安备 11010802040735号 | 京ICP备2022029989号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式