你体会到Vue 组件化思想了吗?
组件化思想
组件的使用必须在 Vue 实例挂载的 div 之内
三大步:
创建组件构造器
<script>
const cpnC = Vue.extend({
template: ' //这里的template属性是html 模板的 意思
<div>
<h2>iiiiiii<h2>
<h3>sdfjsefi<h3>
</div>'
})
</script>
注册组件
Vue.component('my-cpn',cpnC)
//第一个参数是创建的标签名,
//第二个参数是创建组件构造器的方法名
//这样的注册方式注册的是全局组件</pre>
使用组件
组件实例对象
<body>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</body></pre>
简化
简化注册组件的方式:在 Vue 实例或其他组件构造器中添加 component 属性
<script>
const app = new Vue({
el:'#app',
components:{
cpn1:cpnC1, //cpn1是指标签名,cpnC1是指构造组件方法名
cpn2:cpnC2 //可以注册多个组件
}
})
</script>
组件的创建、注册可以在任意其他组件中,或者在 Vue 实例中
官方文档的注册方式
Vue.component('my-component-name',{ })
//该组件名就是 Vue.component 的第一个参数
全局注册
直接通过 Vue 实例注册创建的组件意味着注册方式是全局的。也就是说他们再注册之后可以用在任何新创建的 Vue 根实例的(new Vue)的模板中
Vue.component('my-component-name',{
//..选项})
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
父组件与子组件的联系
如果是组件之间进行注册的话,那么最高级的组件其注册位置在 Vue 实例中,最高级不包括 Vue 实例,其实 Vue 实例是根组件
组件在哪里注册,就在哪里使用;在 Vue 实例中注册可以在全局中使用 、在其他组件中注册只能在其 他组件内使用
<script>
const cpnC = Vue.extend({
components:{
cpn1:cpnC1,
cpn2:cpnC2
}
})
</script>
组件的语法糖注册方式(创建组件的构造方法和注册一齐简写)
<script> //省去组件的构造器方法(但是仍会调用extend),直接在注册组件中添加模板属性 template
Vue.component('cpn1',{ //con1 是标签名
template:{
`
<div>
<h2>iiiiiii<h2>
<h3>sdfjsefi<h3>
</div>
`
}
})
</script>
疑问在于,这样注册的组件算是全局组件还是局部组件呢?是算局部组件的,因为在组件内进行注册的,但是可以直接使用该组件
组件的 template 模板属性的抽离
1.把模版写在 <script type = 'text/x-template'></script> 中,通过添加 id 属性和组件相关联
在组件的template属性中添加模板的id即可
<template id="cpn">
<div>
<h2>
我是内容
</h2>
</div>
</template>
<script>
Vue.component('cpn',{
template:'#cpn'
})
</script>
2.使用 template 标签 ,同样的通过添加 id 属性 和组件相关联
template 标签必须放在挂载区内
<div id = "app">
<template id = "cpn">
<div>
<h2>
我是标题
</h2>
</div>
</template>
</div>
<script>
Vue.component('cpn',{
template:'#cpn'
})
const app = new Vue({
el:'#app'
})
</script>
组件中的数据存放
data()非对象类型
组件是一个单独功能模块 的封装:这个模块有属于自己的HTML模板,也应该有属于自己的数据 data(因为组件不能访问 Vue 实例中的data属性中的值,就算能访问,多个组件叠加的值不利于代码编写),组件拥有属于自己可以保存数据的地方
组件中的data属性为什么是一个函数
因为在重复使用同一个组件时,并不希望在修改其中一个组件的数据时其他组件的值也同时被修改,使用函数是为了让其中的每个组件的内存地址不同从而做到互不影响
<script>
//注册组件
const obj = {
counter:0
}
Vue.component('cpn',{
template:'#cpn',
//data(){
//return{
// counter:0
//}
//}
data(){
return {
obj
}
}
})
</script>
这种情况就是多个组件的值会同时被修改
父子组件的通信
子组件是不能引用父组件或者 Vue 实例的数据的,但是在开发中,一些数据确实需要从上层传递到下层,比如在一个页面中从服务器中请求到很多数据,其中一部分数据并非由整个页面的大组件展示的,而是需要下面的小组件进行展示,这时我们不会让小组件再次发送网络请求,而是直接让大组件将数据传递给小组件
途径
-
通过props向子组件传递数据
-
通过事件向父组件发送消息
props 基本用法(父组件向子组件传递数据)
使用选项 props 来声明需要从父级接受到的数据
方法一:字符串数组,数组中的字符串就是传递时的名称
方法二:对象、对象可以设置传递时的类型,也可以设置默认值等
<body>
<div id = "app">
<cpn v-bind:cmovies = "movies" :cmessage = "message"></cpn> :相当于 v-bind
</div>
<template id = "cpn">
<div>
<h1>sdfsdf</h1>
<h3>
{{cmovies}}
</h3>
<h3>
{{cmessage}} 检测是否接收到父组件的值
</h3>
</div>
</template>
<script>
const app = new.Vue({
el:'#app',
data:{
message:'nihao',
movies:['还望','海贼','海尔'],
component:{
cpn //这里的子组件的注册放在了 Vue 实例中
}
}
})
const cpn = {
template:"#cpn",
props:['cmovies','cmessage'], //利用props属性通过数组的方式来接受父组件的值(变量)
data(){
return{} //data 方法中必须设置返回值
},
methods:{
}
}
</script>
</body>
props 属性还可以提供类型限制或一定的默认值
<script>
const cpn = {
template:"#cpn",
props:{
//1.类型限制
cmovies:Array,
cmessage:String,
//2.提供默认值
cmessage:{
type:String,
default:'aaaaa',
required:true //当调用这个组件时必须使用这个属性'cmessage',required是必传值
},
cmovies:{ //类型是对象或者数组时,默认值必须是个函数
type:Array,
default(){
return[]
}
}
}
}
</script>
<script>
Vue.component('my-component',{
pros:{ //基础的类型检查 ('null'匹配任何类型)
propA:Number
//多个可能的类型
propB:[String,Number],
//必填的字符串
propC: {
type: String,
required: true
},
//带有默认值的数字
propD: {
type: Number,
default:100
},
//带有默认值的对象
propE: {
type: Object,
default: function(){
return: {message: 'hello'}
}
},
propF: {
validator: function (value) {
//这个值必须匹配下列字符串中的一个
return ['success','warning','danger'].indexof(vaule) !== -1
}
}
}
})
</script>
props驼峰标识 的转换
使用组件时,接受父组件的值通常会有 利用驼峰标识定义的属性,这时我们需要利用 c- / 或者用 - 来连接词语
暂时标签内传值时不支持驼峰命名
<body>
<div id = "app">
<cpn :c-info = "info" :child-my-message = "message"></cpn>
</div>
</body>
<script>
const cpn = {
template:'#cpn',
props:{
cInfo:{ }
}
}
</script>
<body>
<div>
<h2>
{{cInfo}}
</h2>
<h2>
{{childMyMessage}}
</h2>
</div>
</body>
当模板中的标签过多时,需要使用一个总标签将其括起来,否则会报错
<template id = "cpn">
<div>
<h2>
{{cInfo}}
</h2>
</div>
</template>
子传父(自定义事件)
当子组件需要向父组件传递数据时就需要用到自定义事件
流程:在子组件中,通过$emit()来触发事件;在父组件中,通过v-on 来监听子组件事件(v-on不仅可以监听DOM事件,也可以监听自定义事件)
<body>
<!-- 挂载区 父组件模板-->
<div id="app">
<cpn @item-click="cpn-click"></cpn>
</div>
<!-- //子组件 模板-->
<template id="cpn1">
<div>
<!-- 向每个按钮添加点击事件 -->
<button v-for="item in categories" @click='btnClick(item)'>{{item.name}}</button>
</div>
</template>
<script>
const cpn = {
template: '#cpn1',
data() {
return {.
categories: [
{ id: 'aaa', name: 'aaareinasdaif' },
{ id: 'bbb', name: 'fffreiasdanaif' },
{ id: 'ccc', name: 'gggreinasdaaif' },
{ id: 'ddd', name: 'hhhhhhhh' },
]
}
},
methods: {
btnClick(item) {
//发射
this.$emit('item-click',item)
}
}
}
const app = new Vue({
el: '#app',
components: {
cpn:cpn
},
methods: {
cpnClick(item) {
console.log('cpnClick',item);
}
}
})
</script>
<script>
const cpn = {
template: '#cpn1',
data() {
return {.
categories: [
{ id: 'aaa', name: 'aaareinasdaif' },
{ id: 'bbb', name: 'fffreiasdanaif' },
{ id: 'ccc', name: 'gggreinasdaaif' },
{ id: 'ddd', name: 'hhhhhhhh' },
]
}
},
methods: {
btnClick(item) {
//发射
this.$emit('item-click',item)
}
}
}
const app = new Vue({
el: '#app',
components: {
cpn:cpn
},
methods: {
cpnClick(item) {
console.log('cpnClick',item);
}
}
})
</script></pre>
错误思维
正确思维