漫漫学vue路(2)-- 组件

为什么组件的data属性会是一个函数呢?


一、组件化的基本使用

为了方便开发,我们将重复用到的代码可封装成一个组件,组件可在单个页面多次使用,也可在不同页面多次使用,甚至可在不同项目多次使用。
在这里插入图片描述

<body>
  <!--3.使用组件-->
  <div id="app">
    <my-cpn></my-cpn>
  </div>
</body>
<script>
  //1.创建组件构造器对象
  const mycpn = Vue.extend({
    template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容1</p>
        <p>我是内容2</p>  
      </div>
    `     
  });
  //2.注册组件
  Vue.component("my-cpn",mycpn);
  
  const app = new Vue({
    el: "#app",
    data: {
      
    },
    methods: {
      
    },
    computed: {
    }
  })
</script>

重点:

  • Vue.extend()方法传入的是一个对象,其里面的template属性是一个字符串,是组件的内容,是html代码。
  • 用到的ES6新语法`` 和 “” ‘’ 一样,是用来包裹字符串的。但是它包裹的字符串能换行,所以多用于包裹html代码,其他两个不能。
  • template属性中的html代码一定是只有一个根元素(官方文档说的),不然会报错,所以嵌套一个div是很有必要的。
  • Vue.component()方法接受两个参数,第一个是组件标签名称(字符串形式),第二个是已经用组件构造器创建好的组件。

二、全局组件和局部组件

概念:

  • 全局组件:可以在多个Vue实例下使用的组件
  • 局部组件:只可以在一个Vue实例下使用的组件

创建方法:

用组件构造器那一步是一样的,就是在注册组件那一步有所不同。

全局组件:

Vue.component("my-cpn",cpn);

局部组件

const app = new Vue({
  el: "#app",
  data: {
    
  },
  methods: {
    
  },
  computed: {
  },
  //组件属性
  components: {
    //标签名: 组件
    my-cpn: mycpn
  }
})

一般来讲,实际开发中只会有一个Vue实例,但是我们注册还是会注册局部组件,会用第二种方法在Vue的components属性中注册。

三、父组件和子组件

<div id="app">
  <mycpn2></mycpn2>
</div>
//创建第一个组件
const cpn1 = Vue.extend({
  template: `
    <div>
      <h2>我是组件1的标题</h2>
      <p>我是内容</p>
    </div>
  `     
});
//创建第二个组件
const cpn2 = Vue.extend({
  template: `
    <div>
      <h2>我是组件2的标题</h2>
      <p>我是内容</p>
      <!--在组件cpn2里面使用组件cpn1-->
      <mycpn1></mycpn1>
    </div>
  `,  
  //在组件cpn2里面注册组件cpn1  
  components: {
    mycpn1: cpn1
  }
});
const app = new Vue({
  el: "#app",
  data: {
    
  },
  methods: {
    
  },
  computed: {
  },
  //Vue实例里面注册组件cpn2
  components: {
    mycpn2: cpn2
  }
})

重点

  • 以上例子中,组件cpn2就是cpn1的父组件,cpn1是在cpn2的components属性中被注册的。
  • 组件cpn2的template属性可以调用组件cpn1的组件标签
  • 因为组件cpn1是在cpn2里面注册的,不是在Vue实例注册的,所以它不能脱离组件cpn2单独使用。
  • Vue实例和组件cpn2都有components属性,所以可将Vue实例看成root(根)组件,在上述例子中Vue是cpn2的父组件,cpn2是cpn1的父组件

四、注册组件的语法糖写法

其实就是创建,注册组件一体化。

全局组件

<body>
  <div id="app">
    <mycpn></mycpn>
  </div>
</body>
<script>
	Vue.component("mycpn",{
	  template: `
	    <div>
	      <h2>我是标题</h2>
	      <p>我是内容</p>
	    </div>
	  `      
	})  

局部组件

const app = new Vue({
  el: "#app",
  data: {
    
  },
  methods: {
    
  },
  computed: {
  },
  components: {
    mycpn: {
      template: `
        <div>
          <h2>我是标题</h2>
          <p>我是内容</p>
        </div>
      `      
    }
  }
})

五、组件模板(template)的抽离写法

以上写法,template属性里面有html代码,这样html,js语法在一起就会显得很混乱,所以要将组件模板(template)抽离出去

<div id="app">
  <mycpn></mycpn>
</div>
<template id="cpn">
  <div>
    <h2>标题</h2>
    <p>内容</p>
  </div>
</template>
const app = new Vue({
  el: "#app",
  components: {
    mycpn: {
      template: "#cpn"
    }
  }
})

六、组件中的数据存放问题

前面的例子中,我们写的组件,其template鼠性的标签里的内容都是写死了的,我们想用双大括号语法动态显示的话那肯定是要从数据里面获取再显示的。

但是其实组件是不能获取Vue实例的data属性里的变量的。

它自己有一个data属性,数据都存放在这个里面。

组件是个独立的空间,它有自己独立的data,methods,computed等等东西

但是组件的data属性是个函数,里面还必须有个return{ };

<div id="app">
  <mycpn></mycpn>
</div>
<template id="cpn">
  <div>
    <h2>{{title}}</h2>
    <p>内容</p>
  </div>
</template>
const app = new Vue({
  el: "#app",
  components: {
    //注册一个局部组件
    mycpn: {
      template: "#cpn",
      data() {
        return {
          title: "aaaa"
        };
      }
    }
  }
})

为什么组件的data属性会是一个函数呢?

我们发现,Vue实例的data是一个对象,它里面存放的变量其实就是对象的属性。但是组件的data属性是一个函数,这是为什么呢?

组件是用来在一个项目中进行多次使用的,但是虽然多次使用,我们也不希望这个页面使用的这个组件和另一个页面使用的组件有联系吧。比如一个计数器组件,+ -两个按钮可改变数的值,我们在第一个页面的组件点击按钮,假如第二个页面的组件的值改变了,那肯定就乱套了。。。。。。

所以组件的data属性必须是函数,它里面再return一个对象。当多个组件调用data这个函数时,其实return的对象是不同的,那么组件之间自然就不会有联系了。

七、父子组件通信

1.父传子——props

子组件不能使用父组件data属性里的变量,如果要使用,那就必须要用props将父组件变量的值传递给子组件

<div id="app">
  <!--将Vue中变量title的值传递给ctitle-->
  <mycpn :ctitle="title"></mycpn>
</div>
<!--组件cpn的模板-->
<template id="cpn">
  <div>
    <!--这里还是要用ctitle不能用title-->
    <h2>{{ctitle}}</h2>
    <p>内容</p>
  </div>
</template>
const app = new Vue({
  el: "#app",
  data: {
    title: "我是标题"
  },
  components: {
    //注册一个局部组件
    mycpn: {
      template: "#cpn",
      props: {
        //变量ctitle接受的数据是字符串
        ctitle: {
          type: String
        }
      }
    }
  }
})

注意:上述例子子组件模板中写的是ctitle而不是title,因为ctitle其实是获取了title的值,那么子组件模板引用的当然是ctitle而不是title

props还有很多种用法,我这里只是用了最简单的一种。

props更多请看 props视频props官方文档

  • props驼峰标识命名问题
    我们在子组件定义的用来接收父组件变量值的变量,假如用驼峰命名法命名,那在用v-bind绑定时要用-代替大写
<mycpn :c-title="title" :c-my-hobbies="myhobbies"></mycpn>
props: {
  cTitle: {
    type: String
  },
  cMyHobbies: {
    type: String
  }
}

2. 子传父——自定义事件( this.$emit() )

当父组件需要子组件的数据时,就需要通过自定义事件的方法来传递数据。

this.$emit(“事件名”,事件的参数/要传的数据)

现在有一个需求,子组件是一个加/减数器,父组件有一个数字,通过点击加/减数器的按钮可以改变数字的值。

逻辑 肯定是子组件内部有一个变量,点击按钮改变这个变量的值,再子组件将这个值传给父组件的数字,这就是子传父的一个小例子。

<div id="app">
  <!--increment,decrement事件发生后执行changenum-->
  <changenumber @increment="changenum"
                @decrement="changenum">
  </changenumber>
  <h2>数字为:{{num}}</h2>
</div>
<template id="changenumber">
  <div>
    <button @click="increment">+</button><button @click="decrement">-</button>
  </div>
</template>
const app = new Vue({
  el: "#app",
  data: {
    num: 0
  },
  methods: {
    changenum(result) {
      this.num = result;
    }
  },
  components: {
    //子组件
    changenumber: {
      template: "#changenumber",
      data() {
        return {
          result: 0
        }
      },
      methods: {
        increment() {
          this.result++;
          //将increment这个事件发送给父组件,并带有参数result
          this.$emit("increment", this.result);
        },
        decrement() {
          this.result--;
          //将decrement这个事件发送给父组件,并带有参数result
          this.$emit("decrement", this.result);
        }
      }
    }
  }
})

上面的基本逻辑是这样:

子组件执行点击事件increment,decrement时,将increment,decrement这两个事件作为自定义事件和result这个参数变量一起传给了父组件。

相当于现在父组件除了什么click事件,keyup事件啊,还多出了increment,decrement事件和result这个参数变量。

那么父组件模板就可以使用v-on监听这两个事件,这两个事件发生了就执行父组件的changenum事件,将result赋值给num。这样就实现了将子组件的数据传递给父组件。

当然changenum要写成changenum(result),要接受参数result,才能得到result的值。

八、父子组件访问

前面所讲的父子组件通信是父组件拿到子组件数据的值,或是子组件拿到父组件数据的值。
访问就是直接拿到组件这个对象(因为组件有很多属性,方法啊,所以也可看作对象。。。。万物皆对象嘛),拿到了对象那么就直接可以使用里面的属性啊,方法什么的了。。

1.父访问子

  • $refs(很常用,百分之90都用它)
<div id="app">
  <cpn></cpn>
  <button @click="btnClick">按钮</button>
</div>
<template id="cpnlist">
  <div>
  </div>
</template>
  const app = new Vue({
    el: "#app",
    data: {
      
    },
    methods: {
      btnClick() {
        //打印了this.$refs
        console.log(this.$refs)
      }
    },
    components: {
      cpn: {
        template: "#cpnlist",
        data() {
          return {
            name: "yuan"
          }
        },
        methods: {
          showName() {
            console.log("wo shi yuan");
          }
        }
      }
    }
  })

上述例子打印了this.$refs,所得结果如下
在这里插入图片描述
得到了一个空对象,所以不能这样使用,我们需要给组件加一个ref属性(一般就是这样使用)

<cpn ref="aaa"></cpn>
console.log(this.$refs.aaa)

这样打印的才是对应的子组件对象(这个跟前面的设置key有点相似),这样能够精准的访问想要访问的某个子组件

在这里插入图片描述
我们可以发现子组件定义的变量name,函数shouName都在里面,所以可以根据下面的代码调用组件的变量和函数

console.log(this.$refs.aaa.name);
console.log(this.$refs.aaa.showName);
console.log(this.$refs.aaa.showName());

但是调用函数的时候我发现一个问题,.showName会返回函数的代码,这个很容易理解。.showName()会返回以下东西。
在这里插入图片描述
wo shi yuan 是函数执行的结果,undefined据我个人猜测应该是浏览器将函数showName()执行后又将它当成一个属性在对象里面找,没找到就返回undefined了,,,(个人猜测,不权威)

  • $children(很少用,一般只在获得全部子组件时使用)
    为什么不常用呢?因为 $children得到的是一个子组件数组,每一项是一个子组件对象。所以你要得到具体的子组件对象,就要使用下标获取。但是我们实际开发中子组件不是写死的,你昨天写了四个子组件,你要获取第二个,那就是this. $children[1];今天你在第一个和第二个中又加了一个组件,那你就要改成this. $children[2];这不是很麻烦吗?完全没有$refs通过给子组件设置一个ref属性精准定位来的舒服。。。。

2. 子访问父

子组件访问父组件在实际开发中用处不大,因为组件的两大用途就是其复用性和独立性,它能在多个页面多次使用,极大简化了日常开发。但是子组件访问父组件后,其与父组件就加强了耦合作用,它的复用性和独立性就大大减弱了。例如它在一个父组件里调用了父组件的name变量,但它在另一个父组件里被使用时,另一个父组件没有name变量,那么数据就会乱套了,所以不建议使用子组件访问父组件。

  • $parent
    获得组件的父组件对象
  • $root
    获得组件的根组件对象,即Vue实例

九、编译作用域

前面提到过,子组件和父组件在没有进行通信和访问时,是不能使用除己之外的组件的变量的。

所以每个组件其实都是有属于自己的编译作用域,在不进行与其他组件通信和访问的前提下,它内部包含的变量、方法其他组件都不能使用。

<div id="app">
  <cpn v-show="isshow"></cpn>
</div>

以上例子是在Vue实例模板中调用了cpn组件。isshow这个变量还是会在Vue实例中去找它的值,因为它虽然是在cpn组件里,但它实际上还是在Vue的模板里的,在谁的模板里就是在谁的作用域里。

十、插槽(slot)

电脑上的插槽一个最具体的例子就是USB接口,可以连接键盘,鼠标,手机,耳机等等。。每连接一个东西,电脑的用途都会发生相应的变化。
在Vue中,组件的 slot 也是如此。它使组件有了更好的扩展性。
例如导航条组件,其中间部分在第一个页面需要是搜索框,在第二个页面需要是一段文字。所以可将中间部分定义为 slot ,个性化的定制组件的功能。

1. slot的基本使用

<div id="app">
  <cpn><h2>111</h2></cpn>
  <cpn></cpn>
  <cpn><i>222</i></cpn>
  <cpn></cpn>
</div>
<template id="cpnlist">
  <div>
    <h2>我是组件</h2>
    <slot><button>按钮</button></slot>
  </div>
</template>
const app = new Vue({
  el: "#app",
  components: {
    cpn: {
      template: "#cpnlist"
    }
  }
})

在这里插入图片描述
基本使用,就是在组件的模板(template)中添加一个 slot 标签即可。

  • 在模板的 slot 标签里添加的 html 代码是插槽的默认值。
  • 想个性化改变插槽内容,直接在 cpn 标签内写即可

2. 具名插槽的使用

一个组件使用多个插槽,有时我们需要只单独改变一个插槽,那就需要用到具名插槽了。

<div id="app">
  <!--给要替换插槽默认值的标签添加个slot属性-->
  <cpn><h2 slot="left">111</h2></cpn>
  <cpn><h2 slot="right">222</h2></cpn>
</div>
<template id="cpnlist">
  <div>
    <h2>我是组件</h2>
    <!--给插槽设置name-->
    <slot name="left"><button>按钮</button></slot>
    <slot name="center"><button>按钮</button></slot>
    <slot name="right"><button>按钮</button></slot>
  </div>
</template>

在这里插入图片描述
这样就可指定替换某个插槽的默认值。

2020/1/16更新:在vue2.6.0版本以后都不会像上面那样写了,在<cpn>里面写插槽的修改值时,会用template模板,给它一个v-slot指令来写,如下
在这里插入图片描述

3. 作用域插槽

前面的插槽的基本使用和具名插槽的使用都是为了解决一个需求:组件在不同环境下需要改变其部分内容

而作用域插槽解决的需求是:不改变其内容,改变内容的展示方式

例子:
组件中有一个数组变量,想将其按序列的方式展示出来。
简单,用v-for、大括号语法在组件中创建模板,再Vue实例的模板引用组件即可。
但是现在不想这个数组按序列方式展示出来了,而是想中间隔一个 - 展示出来。
在这里插入图片描述
组件里有个planguages这个数组,重点就是要Vue实例取到这个数组变量。

给组件插槽绑定个自定义属性(就是这里的data,属性名可以随便改变),这个属性其实此时就代表了planguages这个数组。
在父组件的模板引用的组件中加一个template标签(Vue2.5以后的版本可以改成其他任意标签,但是以前的版本必须用template),使用它的slot-scope属性,属性值随便写一个(最好是slot),这个slot便已经代表组件中的插槽了。
最后 slot.data 即是代表了 组件中的planguages数组。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`vue-beautiful-chat`是一个基于Vue2的聊天组件,支持实时聊天、多人聊天、消息记录等功能,使用起来非常方便。下面是使用`vue-beautiful-chat`的步骤: 1. 安装`vue-beautiful-chat` 可以使用npm命令进行安装:`npm install vue-beautiful-chat` 2. 引入并使用组件Vue组件中引入`vue-beautiful-chat`组件,并在`template`中使用: ```html <template> <div> <h1>聊天室</h1> <vue-beautiful-chat :messages="messages" @message="handleMessage" /> </div> </template> <script> import VueBeautifulChat from "vue-beautiful-chat"; export default { components: { VueBeautifulChat, }, data() { return { messages: [], }; }, methods: { handleMessage(message) { // 处理接收到的消息 console.log("Received message:", message); }, }, }; </script> ``` 在上面的代码中,`vue-beautiful-chat`组件的属性`messages`用来绑定消息数组,`@message`事件用来处理接收到的消息。 3. 发送消息 可以使用`vue-beautiful-chat`组件提供的`sendMessage`方法发送消息: ```javascript this.$refs.chat.sendMessage({ type: "text", content: "Hello, world!", sender: { name: "John Doe", avatar: "https://randomuser.me/api/portraits/men/1.jpg", }, }); ``` 其中,`type`表示消息类型(可以是文本、图片、音频等),`content`表示消息内容,`sender`表示消息发送者的信息。 4. 消息记录 `vue-beautiful-chat`组件提供了`loadMessages`方法用来加载历史消息记录: ```javascript this.$refs.chat.loadMessages([ { type: "text", content: "Hello, world!", sender: { name: "John Doe", avatar: "https://randomuser.me/api/portraits/men/1.jpg", }, }, // ... ]); ``` 以上是使用`vue-beautiful-chat`的基本步骤,具体的功能和使用方法可以参考`vue-beautiful-chat`的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值