Vue3.x全家桶之Vue组件化开发(二)

✍灬更新说明

  1. 更新时间:2022-1-03
  2. 更新内容:
    • Vue2.x 更新至 Vue3.x
    • Demo与文件、截图演示齐全
    • 保留了Coderwhy老师Vue2.x的精华部分,添加Vue3.x的内容
    • 在Vue2.x的基础上进行Vue3.x知识点的补全,所以不需具备Vue2.x的基础,可直接进行学习
  3. 阅前提示:
    • 希望您有HTML、CSS、JavaScript的基础知识
    • 熟悉JavaScript 的 ES6 语法
  4. 更新章节
    • 目前从Vue2.x到Vue3.x更新了基础指令、组件化开发部分,后面会逐渐更新专栏后续章节的3.x
  5. 支撑平台
    • Windows10
    • WebStrom

✍、目录脑图

Vue3.x全家桶之Vue组件化开发(二)_第1张图片

Vue

Vue全家桶 地址
Vue全家桶之Vue基础指令(一) https://blog.csdn.net/Augenstern_QXL/article/details/120117044
Vue全家桶之Vue组件化开发(二) https://blog.csdn.net/Augenstern_QXL/article/details/120117322
Vue全家桶之VueCLI 脚手架V2→V4版本(三) https://blog.csdn.net/Augenstern_QXL/article/details/120117453
Vue全家桶之webpack详解(四) https://blog.csdn.net/Augenstern_QXL/article/details/120297794
Vue全家桶之Vue-router路由(五) https://blog.csdn.net/Augenstern_QXL/article/details/120339146
Vue全家桶之VueX(六) https://blog.csdn.net/Augenstern_QXL/article/details/120339600

Vue3

1、组件化开发

如果将一整个应用的所有处理逻辑都放在一起,处理起来就会变得非常复杂,而且不利于后续的迭代和扩展。如果将一整个应用拆分成一个个页面,一个个页面内部又拆分成一个个独立的组件,每个组件完成属于自己内部独立的功能,组件汇集成页面,页面汇集成应用。那么整个应用的管理和维护则变得非常清晰。

1.1、组件化开发概念

我们来看下面的代码:

<body>
  <div>
    <header>头部header>
    <header>身体header>
    <header>尾部header>
  div>
  <div id="app">

  div>
  
  <script src="../js/vue.js">script>
  <script>
     // 1.创建Vue的实例对象
     const app = Vue.createApp({
      data(){
        return {
          msg: '你好Vue3!'
        }
      }
     }).mount('#app');
  script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第2张图片

如果我们要复用上述代码,那么我们只能复制 div 里面的代码,然后达到复用的效果,这样就太麻烦了,所以组件化开发就可以解决此类问题。

1.2、组件化开发步骤

  1. 注册组件:调用 Vue.component() 方法注册组件
  2. 使用组件:在 Vue 实例的作用范围内使用组件
<body>
  <div id="app">
    
    <button-counter>button-counter>
    <button-counter>button-counter>
    <button-counter>button-counter>
  div>

  <script src="../js/vue.js">script>
  <script>
     // 1.创建Vue的实例对象
     const app = Vue.createApp({
      data(){
        return {
          msg: '你好,Vue3!'
        }
      }
     });

     // 2.定义一个组件(全局组件)
     app.component('button-counter',{
       data() {
         return {
           count: 0
         }
       },
       template: `
        
       `
     })

     // 3. 挂载vue实例
     app.mount('#app');
  script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第3张图片

1.3、组件的命名规范

1.3.1、短横线式kebab-case

引用短横线式命名的组件,调用时

app.component('my-component-name',{
    data(){
        return {
            
        }
    },
    template: ``
})

1.3.2、驼峰式PascalCase

引用驼峰式命名的组件,调用时 都可以

但是我们直接在DOM(即非字符串的模板)中使用时只有短横线法是有效的。在后面CLI调用两种方法都可以

app.component('My-Component-Name',{
    data(){
        return {
            
        }
    },
    template: ``
})

1.4、全局组件和局部组件

全局组件:在整个Vue实例中都可以被调用

局部组件:只能在当前组件中被使用

1.4.1、全局组件

如下代码,我们在 app 下注册了一个全局组件,这意味着该组件可以在app实例内部任意地方使用:我们可以在 app 实例下使用,也可以在 home 实例下使用,也可以在 message 实例下使用。

<body>
  <div id="app">
    <div id="home">
      <span>首页span>
      <button-counter>button-counter>
    div>
    <div id="message">
      <span>消息span>
      <button-counter>button-counter>
    div>
    
    <button-counter>button-counter>
  div>

  <script src="../js/vue.js">script>
  <script>
     // 1.创建Vue的实例对象
     const app = Vue.createApp({
      data(){
        return {
          msg: '你好,Vue3!'
        }
      }
     });

     // 2.定义一个组件(全局组件)
     app.component('button-counter',{
       data() {
         return {
           count: 0
         }
       },
       template: `
        
       `
     })

     // 3. 挂载vue实例
     app.mount('#app');
  script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第4张图片

当然我们也可以设置多个全局组件,代码如下:

<body>
<div id="app">
  <div id="home">
    <span>首页span>
    <button-counter>button-counter>
  div>
  <div id="message">
    <span>消息span>
    <button-counter>button-counter>
    
    <lk-box>lk-box>
  div>
  
  <button-counter>button-counter>
div>

<script src="../js/vue.js">script>
<script>
  // 1.创建Vue的实例对象
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好,Vue3!'
      }
    }
  });

  // 2.定义一个组件(全局组件)
  app.component('button-counter',{
    data() {
      return {
        count: 0
      }
    },
    template: `
        
       `
  })
  // 定义第二个全局组件
  app.component('lk-box',{
    template: `
        
盒子组件
`
}) // 3. 挂载vue实例 app.mount('#app');
script> body>

Vue3.x全家桶之Vue组件化开发(二)_第5张图片

当然我们全局组件之间可以相互使用,使用方式如下:我们在定义第二个全局组件,若向使用第一个全局组件,只需要将第一个全局组件的名称标签写入模板template中即可

 // 定义第二个全局组件
  app.component('lk-box',{
    template: `
        
盒子组件
`
})

Vue3.x全家桶之Vue组件化开发(二)_第6张图片

1.4.2、局部组件

如下代码,局部组件是使用一个常量来接收,我们将此局部组件通过 components 可以将其挂载在app实例中,这样我们在 app 实例里面就可以使用了

<body>
  <div id="app">
    <lk-count>lk-count>
  div>

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

    // 注册一个局部组件
    const Counter = {
      data() {
        return {
          count: 0
        }
      },
      template: `
        
       `
    }


     // 创建Vue的实例对象
     const app = Vue.createApp({
      data(){
        return {
          msg: '你好,Vue3!'
        }
      },
       // 组件选项
       components: {
        'lk-count': Counter
       }
     });


     // 挂载vue实例
     app.mount('#app');
  script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第7张图片

当然我们可以定义多个局部组件,然后通过 components 可以将其挂载在app实例中:

<body>
<div id="app">
  <lk-count>lk-count>
  <cc-count>cc-count>
div>

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

  // 注册一个局部组件
  const Counter = {
    data() {
      return {
        count: 0
      }
    },
    template: `
        
       `
  }

  // 注册第二个局部组件
  const Box = {
    template: `
        
盒子组件
`
} // 创建Vue的实例对象 const app = Vue.createApp({ data(){ return { msg: '你好,Vue3!' } }, // 组件选项 components: { 'lk-count': Counter, 'cc-count': Box } }); // 挂载vue实例 app.mount('#app');
script> body>

Vue3.x全家桶之Vue组件化开发(二)_第8张图片

若我们想要局部组件之间相互使用,不能像全局组件那样在注册时向模板template写入名称标签,比如在注册组件时通过components将其挂载在想使用的实例中,然后再如全局组件那样向模板template写入名称标签:

// 注册第二个局部组件
  const Box = {
    components: {
      'lk-count': Counter,
    },
    template: `
        
盒子组件
`
}

Vue3.x全家桶之Vue组件化开发(二)_第9张图片

1.4.3、总结

全局组件:在整个Vue实例中都可以被调用,若想要全局组件之间相互使用,只需将想使用全局组件的名称写入 template

局部组件:只能在当前组件中被使用,若想在其他组件中使用,必须使用 components 将其挂载在想使用的组件中,然后再如全局组件那样向模板template写入名称标签

我们之后其实对局部组件用的更多一些。

1.5、组件标签化

template 模块写法不够清晰,如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰

Vue 提供了两种方案来定义HTML模板内容

  • 使用 < script > 标签
  • 使用 < template > 标签

1.5.1、使用script标签

下面代码是我们之前注册局部组件的代码,我们可以看到 templates 里面有一个 button 按钮

<body>
<div id="app">
  <lk-count>lk-count>
div>

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

  // 注册一个局部组件
  const Counter = {
    data() {
      return {
        count: 0
      }
    },
    template: `
        
       `
  }


  // 创建Vue的实例对象
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好,Vue3!'
      }
    },
    // 组件选项
    components: {
      'lk-count': Counter,
    }
  });


  // 挂载vue实例
  app.mount('#app');
script>
body>

我们使用 script 标签将其抽离

<body>
<div id="app">
  <lk-count>lk-count>
div>


<script type="text/x-template" id="mycount">
  <button @click="count++">你点击了{{count}}</button>
script>

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

  // 注册一个局部组件
  const Counter = {
    data() {
      return {
        count: 0
      }
    },
    // 使用#id
    template: '#mycount'
  }


  // 创建Vue的实例对象
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好,Vue3!'
      }
    },
    // 组件选项
    components: {
      'lk-count': Counter,
    }
  });


  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第10张图片

1.5.2、使用template标签

我们使用 template 标签将其抽离

<body>
<div id="app">
  <lk-count>lk-count>
div>

<template id="mycount">
  <button @click="count++">你点击了{{count}}次button>
template>

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

  // 注册一个局部组件
  const Counter = {
    data() {
      return {
        count: 0
      }
    },
    template: '#mycount'
  }


  // 创建Vue的实例对象
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好,Vue3!'
      }
    },
    // 组件选项
    components: {
      'lk-count': Counter,
    }
  });


  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第11张图片

1.6、组件数据存放

问题:组件可以访问Vue实例数据吗?

结论:组件不能直接访问Vue实例中的 data

组件是一个单独功能模块的封装:

  • 这个模块有属于自己的 HTML 模板,也应该有属于自己的数据 data

组件自己的数据存放在哪呢?

  • 组件对象也有一个 data 属性(也可以有 methods 属性)

  • 只是这个 data 属性必须是一个函数

  • 而且这个函数返回一个对象,对象内部保存着数据

// 注册一个局部组件
const Counter = {
  data() {
    return {
      count: 0
    }
  },
  template: `
    
   `,
  methods: {}
}

为什么 data 在组件中必须是一个函数呢?

我这里来写一个简单介绍:我们设立两个对象,虽然都是空对象,但是两者不相等。

let obj1 = {};
let obj2 = {};
console.log(obj1 === obj2); // false

上述代码在内存中的示意图如下:

Vue3.x全家桶之Vue组件化开发(二)_第12张图片

我们创建一个对象

let obj = {
	name: '秦晓'
}

let p1 = obj;
let p2 = obj;
let p3 = obj;

console.log(p1,p2,p3);
console.log(p1 === p2);	// true

Vue3.x全家桶之Vue组件化开发(二)_第13张图片

let obj = {
	name: '秦晓'
}

let p1 = obj;
let p2 = obj;
let p3 = obj;

// 我们修改p1的name
p1.name = '大林';

console.log(p1,p2,p3);
console.log(p1 === p2);

Vue3.x全家桶之Vue组件化开发(二)_第14张图片

从上述例子中就可以看出,如果我们在使用对象的时候,很容易造成值拷贝。现在我们来回答一下为什么 data 在组件中必须是一个函数呢?

  • 首先,如果不算是一个函数,Vue 直接就会报错
  • 其次,原因是在于 Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象,组件在多次使用后会相互影响

1.7、组件通信props

组件通信的常用方式有4种:

  1. props
  2. 自定义事件
  3. 消息订阅与发布
  4. vuex

在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的

但是,在开发中,往往一些数据确实需要从上层传递到下层

  • 比如在一个页面中,我们从服务器请求到了很多的数据
  • 其中的一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件) 将数据传递给小组件(子组件)

组件中,使用选项 props 来声明需要从父级接收到的数据(properties)

props 的值有两种方式:

  • 方式一:字符串数组,数组中的字符串就是传递时的名称
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

1.7.1、props传数组

我们先来看方式一:先看如下代码,我们使用局部组件展示数据

<body>
<div id="app">
  <lk-box>lk-box>
div>

<template id="box">
  <div>
    <h1>技能掌握h1>
    <li>Webli>
    <li>Pythonli>
    <li>Javali>
  div>
template>

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

  // 注册一个局部组件
  const Box = {
    template: '#box'
  };
  
  // 创建Vue的实例对象
  const app = Vue.createApp({
    data(){
      return {
          
      }
    },
    components: {
      'lk-box': Box
    }
  });


  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第15张图片

如果我们的数据是从服务器端返回的,那么我们就需要动态绑定数据,代码如下:

<body>
<div id="app">
  
  <lk-box :brand="msg1" :colleges="msg2">lk-box>
div>

<template id="box">
  <div>
    <h1>{{brand}}h1>
    <ul>
      <li v-for="item in colleges">{{item}}li>
    ul>

  div>
template>

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

  // 1.注册一个局部组件(子组件)
  const Box = {
    // 子组件接收数据
    props: ['brand','colleges'],
    template: '#box'
  };

  // 2.将子组件在父组件里面注册
  const app = Vue.createApp({
    data(){
      return {
        msg1: '技能掌握',
        msg2: ['Web','Python','Java']
      }
    },
    components: {
      'lk-box': Box
    }
  });


  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第16张图片

1.7.2、props传对象

  • 在前面,我们的 props 选项是使用一个数组
  • 除了数组之外,我们也可以使用对象,当需要对props 进行类型等验证时,就需要对象写法了

1、类型限制

我们可以在 props 里面限制父组件给子组件传递的数据类型

<body>

<div id="app">
  <cpn :cmessage="message" :cmovies="movies">cpn>
div>


<template id="cpn">
  <div>
    <h1>{{cmovies}}}h1>
    <h1>{{cmessage}}h1>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 父传子: props
  const cpn = {
    template: '#cpn',
    props: {
      // 1.类型限制
      cmovies: Array,		// 限制父组件传的是数组类型
      cmessage: String,		// 限制父组件传的是字符串类型
    }

  }
  // root组件,我们当作父组件
  const app = Vue.createApp({
    data(){
      return {
        message: '你好啊',
        movies: ['海王', '海贼王', '海尔兄弟']
      }
    },
    components: {
      //对象字面量增强写法的属性增强写法
      cpn
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第17张图片

类型一般支持:String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义类型

2、默认值和必传值

  • type : 限制的类型
  • default : 如果没有传值,给一个默认值
    • 注意:类型是对象或者数组时, 默认值必须是一个函数
  • required : 必须的,即意味着这个值是必须要传递的,不传就报错
<body>

<div id="app">
  <cpn :cmessage="message" :cmovies="movies">cpn>
div>


<template id="cpn">
  <div>
    <h1>{{cmovies}}}h1>
    <h1>{{cmessage}}h1>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 父传子: props
  const cpn = {
    template: '#cpn',
    props: {
      // 2.提供一些默认值, 以及必传值
      cmessage: {
        type: String,         // 类型限制为 String
        default: 'aaaaaaaa',  // 如果没有传值,则给一个默认值
        required: true        // required 必须的,即意味着这个值是必须要传递的,不传就报错
      },
      // 类型是对象或者数组时, 默认值必须是一个函数
      cmovies: {
        type: Array,
        default() {
          return []
        }
      }
    }
  }
  // root组件,我们当作父组件
  const app = Vue.createApp({
    data(){
      return {
        message: '你好啊',
        movies: ['海王', '海贼王', '海尔兄弟']
      }
    },
    components: {
      //对象字面量增强写法的属性增强写法
      cpn
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

3、props驼峰标识

当我们 props 里面的属性是驼峰写法的时,在传入值时需要进行 - 连接

<div id="app">
  
  <cpn :c-info="info" :child-my-message="message" >cpn>
div>

<template id="cpn">
  <div>
    <h2>{{cInfo}}h2>
    <h2>{{childMyMessage}}h2>
  div>
template>

<script src="../js/vue.js">script>
<script>
  const cpn = {
    template: '#cpn',
    props: {
      // 驼峰写法cInfo
      cInfo: {
        //类型是对象或者数组时, 默认值必须是一个函数
        type: Object,
        default() {
          return {}
        }
      },
      childMyMessage: {
        type: String,
        default: ''
      }
    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      },
      message: 'aaaaaa'
    },
    components: {
      cpn
    }
  })
script>

1.7.3、子级向父级传递

props 用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件去。这个时候,我们需要使用自定义事件来完成

自定义事件的流程:

  • 在子组件中,通过$emit() 来发射事件
  • 在父组件中,通过v-on 来监听子组件事件

我们来看下方代码,我们在子组件中放置一个按钮,在父组件中对子组件进行注册:

<body>

<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
  <lk-box>lk-box>
div>


<template id="box">
  <div style="background-color:red;width: 200px;height: 200px;">
    <button @click="btnClick">点我button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 子组件
  const Box = {
    template: '#box',
    methods: {
      btnClick(){
        alert('点击了');
      }
    },
  };

  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': Box
    },
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第18张图片

如果我们点击了子组件,希望让父组件知道,那么子组件就要发射事件到父组件:例如如下代码,我们使用子组件发射事件来触发父组件的 boxFunc 函数:

<body>

<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
  	
    <lk-box @box-click="boxFunc">lk-box>
div>


<template id="box">
  <div style="background-color:red;width: 200px;height: 200px;">
    <button @click="btnClick">点我button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 子组件
  const Box = {
    template: '#box',
    methods: {
      btnClick(){
        alert('点击了');
        // 发射事件
        // 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数
        this.$emit('box-click');
      }
    },
  };

  // 父组件
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': Box
    },
    methods: {
      boxFunc(){
        console.log('子组件中的按钮发生了点击')
      }
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第19张图片

那如果我们点击子组件按钮,同时还想要传递参数给父组件,代码如下:

  • 我们给$emit()事件传递参数
  • 在触发函数中接收参数
<body>

<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
  <lk-box @box-click="boxFunc">lk-box>
div>


<template id="box">
  <div style="background-color:red;width: 200px;height: 200px;">
    <button @click="btnClick">点我button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 子组件
  const Box = {
    template: '#box',
    methods: {
      btnClick(){
        alert('点击了');
        // 发射事件
        // 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数
        this.$emit('boxClick','秦晓');
      }
    },
  };

  // 父组件
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': Box
    },
    methods: {
      boxFunc(item){
        console.log('子组件中的按钮发生了点击');
        console.log(item);
      }
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第20张图片

如果我们想传递多个参数,我们可以以一个对象或者数组的形式传递,例如我们传递一个对象:

<body>

<div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;">
  <lk-box @box-click="boxFunc">lk-box>
div>


<template id="box">
  <div style="background-color:red;width: 200px;height: 200px;">
    <button @click="btnClick">点我button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 子组件
  const Box = {
    template: '#box',
    methods: {
      btnClick(){
        alert('点击了');
        const dataObj = {
          name: '秦晓',
          age: 20
        }
        // 发射事件
        this.$emit('boxClick',dataObj);
      }
    },
  };

  // 父组件
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': Box
    },
    methods: {
      boxFunc(item){
        console.log('子组件中的按钮发生了点击');
        console.log(item);
      }
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第21张图片

1.7.4、父子组件相互访问

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。

  • 父组件访问子组件:使用 $children(Vue3.x已经废弃)$refs
  • 子组件访问父组件:使用$parent

1、父组件访问子组件

$refs 的使用

  • $refs 和 ref 指令通常是一起使用的
  • 首先,我们通过 ref 给某一个子组件绑定一个特定的 ID
  • 其次,通过 this.$refs.ID 就可以访问到该组件了
    • 接着使用 this.$refs.ID.xx 就可以拿到该组件里面的属性数据了

例如下方代码,我们给子组件使用 ref="box1" 绑定ID,在父组件里面使用 this.$refs.box1 就可以拿到该组件,接着使用this.$refs.box1.msg 拿到该组件的 msg 属性数据

<body>

<div id="app">
  <lk-box ref="box1">lk-box>
  <button @click="getChildComponent">获取子组件button>
div>


<template id="box">
  <div style="background-color:red;width: 200px;height: 200px;">
    <button @click="btnClick">点我button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 定义局部组件
  const Box = {
   data(){
     return {
       msg: '春风十里'
     }
   },
    methods: {
     btnClick(){
       alert('点击了按钮')
     }
    },
    template: '#box'
  };

  // 父组件
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': Box
    },
    methods: {
      // 获取子组件
      getChildComponent(){
        // this.$refs.box1 相当于拿到了子组件
        // this.$refs.box1.msg 就是拿到了子组件里面的 msg 数据
        // this.$refs.box1.btnClick 就是拿到了子组件里面的 btnClick 方法
        console.log(this.$refs.box1.msg);

      }
    }
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第22张图片

2、子组件访问父组件

如果我们想在子组件中直接访问父组件,可以通过 $parent ,如果想访问根组件,可以通过 $root

  • 尽管在 Vue 开发中,我们允许通过 $parent 来访问父组件,但是在真实开发中尽量不要这要做

  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了

  • 因为子组件一般会复用,如果我们将子组件放入另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题

例如如下代码,我们做了三层嵌套,最外层是根组件Vue实例,第二层是div盒子,最里面一层是 button 按钮。我们现在想点击 最里面的 button 来拿到div父组件或者root根组件。

<body>

<div id="app">
  <lk-box>lk-box>
div>


<template id="box1">
  <button @click="btnClick">点击了按钮{{count}}次button>
template>


<template id="box2">
  <div style="background-color:red; width: 200px; height: 200px">
    <lk-button>lk-button>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 定义局部组件1
  const LKButton = {
   data(){
     return {
       count: 0
     }
   },
    template: '#box1',
    methods: {
      btnClick(){
        // 子组件访问父组件
        // this.$parent 可以拿到该组件的父组件,也就是button子组件的父组件div
        // console.log(this.$parent);
        this.count++;
        console.log(this.$parent.count);


        // 子组件访问根组件
        // this.$root 可以拿到根组件,也就是vue实例
        // console.log(this.$root);
        console.log(this.$root.msg);
      }
    }
  };

  // 定义局部组件2
  const LKBox = {
    data(){
      return {
        count: 0
      }
    },
    template: '#box2',
    components: {
      'lk-button': LKButton
    }
  };


  // 父组件
  const app = Vue.createApp({
    data(){
      return {
        msg: '你好Vue3!'
      }
    },
    components: {
      'lk-box': LKBox
    },
  });
  // 挂载vue实例
  app.mount('#app');
script>
body>

Vue3.x全家桶之Vue组件化开发(二)_第23张图片

你可能感兴趣的:(Vuejs,vue.js,javascript)