Vuejs——(8~13)组件(从不懂到精通)

简介: 本篇资料来于官方文档: http://cn.vuejs.org/guide/components.html 本文是在官方文档的基础上,更加细致的说明,代码更多更全。 简单来说,更适合新手阅读 (二十五)组件的定义 ①组件的作用: 【1】扩展HTML元素,封装可重用的代码; 【2】组

本篇资料来于官方文档:

http://cn.vuejs.org/guide/components.html

本文是在官方文档的基础上,更加细致的说明,代码更多更全。

简单来说,更适合新手阅读


(二十五)组件的定义

①组件的作用:

1】扩展HTML元素,封装可重用的代码;

2】组件是自定义元素,Vuejs的编译器可以为其添加特殊的功能;

3】某些情况下,组件可以是原生HTML元素的形式,以is的方式扩展。

 

②写一个标准的组件:

分为以下几步:

1】挂载组件的地方,需要是Vue实例所渲染的html元素,具体来说,比如上面的<div id=”app”></div>这样的html元素及他的子节点;

 

2】定义一个组件,用

var 变量名 = Vue.extend({template:”这里是html的模板内容”})

这样的形式创建,例如:

//定义一个组件
var btn = Vue.extend({
    
template: "<button>这是一个按钮</button>"
})

 

3】将定义的组件注册到Vue实例上,这会让指定标签,被组件的内容所替代。

如代码:

//注册他到Vue实例上
Vue.component("add-button", btn);

具体而言,每一个以下这样的标签(在Vue的根实例范围内的)

<add-button></add-button>

会被

<button>这是一个按钮</button>

所替代。

 

4】以上方法是全局注册(每个Vue实例的add-button标签都会被我们定义的所替代);

解决办法是局部注册。

 

如代码:(这是是设置了template属性,也可以在没有这个属性的时候,在<div id=”app”></div>标签内放置<add-button></add-button>标签

<div id="app">
</
div>
<
script>
   
//定义一个组件
   
var btn = Vue.extend({
       
template: "<button>这是一个按钮</button>"
   
})

   
Vue.component("add-button", btn);

   
//创建根实例,也就是说让Vue对这个根生效
   
var vm = new Vue({
       
el: '#app',
       
template: "<add-button></add-button>"
   
});
</script>

 

 

 

③局部注册组件:

简单来说,只对这一个Vue实例生效,具体做法是,在注册那一步,跳过;

然后在声明Vue实例的时候,将添加到components这个属性中(他是一个对象,以KV形式放置)(注意,这个单词多一个s

如代码:

<div id="app">
</
div>
<
script>
   
//定义一个组件
   
var btn = Vue.extend({
       
template: "<button>这是一个按钮</button>"
   
})

   
//创建根实例,也就是说让Vue对这个根生效
   
var vm = new Vue({
       
el: '#app',
       
template: "<add-button></add-button>",
        
components: {
           
"add-button": btn
       
}
    });
</script>

 

注:

根据官方教程,这种方法(指局部注册),也适用于其他资源,比如指令过滤器过渡

 

 

 

④步骤简化:

1定义组件注册组件结合起来一步完成:

//定义一个组件
Vue.component("add-button", {
   
template: "<button>这是一个按钮</button>"
});

 

2】局部注册时,定义和注册一步完成:

//创建根实例,也就是说让Vue对这个根生效
var vm = new Vue({
   
el: '#app',
   
template: "<add-button></add-button>",
   
components: {
       
"add-button": {
           
template: "<button>这是一个按钮</button>"
       
}
    }
});

 

 

 

data属性

直接给组件添加data属性是不可以的(无效);

 

原因在于,假如这么干,那么组件的data属性有可能是一个对象,而这个对象也有可能是外部传入的(例如先声明一个对象,然后这个对象作为data的值),可能导致这个组件的所有副本,都共享一个对象(那个外部传入的),这显然是不对的。

 

因此,data属性应该是一个函数,然后有一个返回值,这个返回值作为data属性的值。

 

且这个返回值应该是一个全新的对象(即深度复制的,避免多个组件共享一个对象);

 

如代码:

var vm = new Vue({
   
el: '#app',
   
template: "<add-button></add-button>",
   
components: {
       
"add-button": {
           
template: "<button>这是一个按钮{{btn}}</button>",
           
data: function () {
               
return {btn: "123"};
            }
        }
    }
});

 

 

另外,假如这样的话,btn的值是一样的(因为他们实际上还是共享了一个对象)

<div id="app">
</
div>
<
div id="app2">
</
div>
<
script>
   
var obj = {btn: "123"};
   
var vm = new Vue({
       
el: '#app',
       
template: "<add-button></add-button>",
       
components: {
           
"add-button": {
               
template: "<button>这是一个按钮{{btn}}</button>",
               
data: function () {
                   
return obj;
                }
            }
        }
    });

   
obj.btn = "456";
   
var vm2 = new Vue({
       
el: '#app2',
       
template: "<add-button></add-button>",
       
components: {
           
"add-button": {
               
template: "<button>这是一个按钮{{btn}}</button>",
               
data: function () {
                   
return obj;
                }
            }
        }
    });
</script>

 

 

1

el属性用在Vue.extend()中时,也须是一个函数。

 

2

给子组件添加methods无需使用函数返回值,直接如正常那样添加即可。

示例代码:

<div id="app">
   
子组件:
   
<test></test>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
components: {
           
test: {
               
props: ['test'],
               
template: "<input @keyup='findParent' v-model='test'/>",
               
methods: {
                   
findParent: function () {
                       
console.log("happened");
                    }
                }
            }
        }
    });
</script>

 

 

 

 

is特性:

1】按照官方教程,一些HTML元素对什么元素可以放在它之中是有限制的;

简单来说,如果我要在table标签内复用某个组件,这个组件展开后是tr标签,但是展开前不是,那么就无法正常运行(被放置在table标签内);

 

如代码(错误写法,会渲染错误):

<div id="app">
    <
table>
        <
tr>
            <
td>索引</td>
            <
td>ID</td>
            <
td>说明</td>
        </
tr>
        <
thetr v-for="i in items" v-bind:id="i" :index="$index"></thetr>
    </
table>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
items: [1, 2, 3, 4]
        },
       
methods: {
           
toknowchildren: function () {   //切换组件显示
               
console.log(this.$children);
            }
        },
       
components: {
           
thetr: { //第一个子组件
               
template: "<tr>" +
               
"<td>{{index}}</td>" +
               
"<td>{{id}}</td>" +
               
"<td>这里是子组件</td>" +
               
"</tr>",
               
props: ['id', 'index']
            }
        }
    });
</script>

 

渲染结果如下:

<div id="app">
    <
tr><td>0</td><td>1</td><td>这里是子组件</td></tr>
    <
tr><td>1</td><td>2</td><td>这里是子组件</td></tr>
    <
tr><td>2</td><td>3</td><td>这里是子组件</td></tr>
    <
tr><td>3</td><td>4</td><td>这里是子组件</td></tr>
    <
table>
        <
tbody>
        <
tr>
            <
td>索引</td>
            <
td>ID</td>
            <
td>说明</td>
        </
tr>
        </
tbody>
    </
table>
</
div>

 

可以明显发现,内容没有被放在table之中。

 

正确写法如下:

<div id="app">
    <
button @click="toknowchildren">点击让子组件显示</button>
    <
table>
        <
tr>
            <
td>索引</td>
            <
td>ID</td>
            <
td>说明</td>
        </
tr>
        <
tr is="thetr" v-for="i in items" v-bind:id="i" :index="$index"></tr>
    </
table>
</
div>

 

 

 

2】更多冲突参照URL

http://cn.vuejs.org/guide/components.html#u6A21_u677F_u89E3_u6790

 

 

 

(二十六)props数据传递

①组件实例的作用域:

是孤立的,简单的来说,组件和组件之间,即使有同名属性,值也不共享。

<div id="app">
    <
add></add>
    <
del></del>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
components: {
           
"add": {
                
template: "<button>btn:{{btn}}</button>",
               
data: function () {
                   
return {btn: "123"};
                }
            },
           
del: {
               
template: "<button>btn:{{btn}}</button>",
               
data: function () {
                   
return {btn: "456"};
                }
            }
        }
    });
</script>

 

渲染结果是:

2个按钮,第一个的值是123,第二个的值是456(虽然他们都是btn

 

 

②使用props绑定静态数据:

1】这种方法用于传递字符串,且值是写在父组件自定义元素上的。

2】下面示例中的写法,不能传递父组件data属性中的值

3】会覆盖模板的data属性中,同名的值。

示例代码:

<div id="app">
    <
add btn="h"></add>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
h: "hello"
       
},
       
components: {
           
"add": {
               
props: ['btn'],
               
template: "<button>btn:{{btn}}</button>",
               
data: function () {
                   
return {btn: "123"};
                }
            }
        }
    });
</script>

 

这种写法下,btn的值是h,而不是123,或者是hello

 

4】驼峰写法

假如插值是驼峰式的,

而在html标签中,由于html的特性是不区分大小写(比如LIli是一样的),因此,html标签中要传递的值要写成短横线式的(如btn-test),以区分大小写。

而在props的数组中,应该和插值保持一致,写成驼峰式的(如btnTest)。

 

例如:

props: ['btnTest'],
template: "<button>btn:{{btnTest}}</button>",

 

正确的写法:

<add btn-test="h"></add>

 

假如插值写短横线式,或者是html标签写成驼峰式,都不能正常生效。(除非插值不写成驼峰式——跳过大小写的限制,才可以)

 

 

③利用props绑定动态数据:

简单来说,就是让子组件的某个插值,和父组件的数据保持一致。

 

标准写法是(利用v-bind):

<add v-bind:子组件的值="父组件的属性"></add>

 

如代码:

<div id="app">
    <
add v-bind:btn="h"></add>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
h: "hello"
        
},
       
components: {
           
"add": {
               
props: ['btn'],
               
template: "<button>btn:{{btn}}</button>",
               
data: function () {
                   
return {'btn': "123"};  //子组件同名的值被覆盖了
               
}
            }
        }
    });
</script>

 

说明:

1btn使用的父组件data h的值;

2】子组件的data的函数中返回值被覆盖了。

3】也就是说,使用v-bind的是使用父组件的值(根据属性名),没有使用v-bind的是将标签里的数值当做字符串来使用。

4】依然需要使用props,否则他会取用自己data里的btn的值

 

 

④字面量和动态语法:

1】简单来说,不加v-bind的,传递的是字面量,即当做字符串(例如1也是字符串,而不是number类型);

2】加上v-bind的,传递的是JS表达式(因此才能传递父组件的值);

3】加上v-bind后,如果能找到父组件的值,那么使用父组件的值;如果没有对应的,则将其看做一个js表达式(例如1+2看做3{a:1}看做是一个对象);

 

如代码:

<div id="app">
    <
add v-bind:btn="1+2"></add>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
            
h: "hello"
       
},
       
components: {
           
"add": {
               
props: ['btn'],
               
template: "<button>btn:{{btn}}</button>"
           
}
        }
    });
</script>

 

这里的btn的值是3(而不是没有加v-bind时,作为字符串的1+2

 

 

props的绑定类型:

1】简单来说,分为两种类型,即单向绑定(父组件能影响子组件,但相反不行)和双向绑定(子组件也能影响父组件);

 

2】单向绑定示例:(默认,或使用.once

<div id="app">
   
父组件:
   
<input v-model="val"><br/>
   
子组件:
   
<test v-bind:test-Val="val"></test>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
components: {
           
"test": {
               
props: ['testVal'],
               
template: "<input v-model='testVal'/>"
           
}
        }
    });
</script>

 

说明:

当父组件的值被更改后,子组件的值也随之更改;

当子组件的值被更改后,父组件的值不会变化,而假如再次修改父组件的值,子组件会再次同步。

另外需要注意的是,子组件如果要同步绑定,那么子组件的input需要是v-model,而不能是value属性(那样只能单项绑定,且修改子组件的值后会失去绑定)

 

 

3】双向绑定:

需要使用“.sync”作为修饰词

如示例:

<div id="app">
   
父组件:
   
<input v-model="val"><br/>
   
子组件:
   
<test :test.sync="val"></test>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
components: {
           
"test": {
               
props: ['test'],
               
template: "<input v-model='test'/>"
           
}
        }
    });
</script>

效果是无论你改哪一个的值,另外一个都会随之变动。

 

4props验证:

简单来说,当组件获取数据时,进行验证,只有符合条件的时候,才会使用之。

 

写法是将props变为一个对象,被验证是值是对象的key,验证条件是和key对应的value

例如:

props: {
   
test: {
       
twoWay: true
   
}
},

验证test这个变量是不是双向绑定,如果不是,则报错。(注意,这个不能用于验证单向绑定)。

 

示例代码如下:

<div id="app">
   
父组件:
    
<input v-model="val"><br/>
   
子组件:
   
<test :test="val"></test>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
components:{
           
test:{
               
props: {
                    
test: {
                       
twoWay: true
                   
}
                },
               
template: "<input v-model='test'/>"
           
}
        }
    });
</script>

 

更多验证查看官方教程:

http://cn.vuejs.org/guide/components.html#Prop__u9A8C_u8BC1

 

 

 

 

(二十七)父子组件通信

①访问子组件、父组件、根组件;

this.$parent    访问父组件

this.$children   访问子组件(是一个数组)

this.$root            根实例的后代访问根实例

示例代码:

<div id="app">
   
父组件:
   
<input v-model="val"><br/>
   
子组件:
   
<test :test="val"></test>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
components: {
           
test: {
               
props: ['test'],
               
template: "<input @keyup='findParent' v-model='test'/>",
               
methods: {
                   
findParent: function () {
                       
console.log(this.$parent);  //访问根组件
                       
console.log(this.$parent.val);  //访问根组件的val属性
                       
console.log(this.$parent.$children.indexOf(this));  //查看当前能否在其父组件的子组件中找到索引
                       
console.log(this.$parent === this.$root);   //查看父组件和根组件是不是全等的(因为他的父组件就是根组件)
                   
}
                }
            }
        }
    });
</script>

当在子组件的输入框按键弹起时,显示内容依次为:

父组件、父组件的输入框的值(默认情况是1)、0(表示是父组件的children属性中的第一个元素)、true(由于父组件就是根组件,所以是全等的);

 

通过这样的方法,可以在组件树中进行互动。

 

 

 

②自定义事件:

首先,事件需要放置在events属性之中,而不是放置在methods属性中(新手很容易犯的错误),只能触发events属性中的事件,而methods属性中的事件是无法触发的。

事件

说明

$on(事件名)

事件名的类型是字符串(下同),调用它可以通过this.$on()来调用;

$emit(事件名, 参数)

用于触发事件,参数是用于传递给事件的参数。这个用于触发同级事件(当前组件的)

$dispatch(事件名, 参数)

①向上派发事件,用于向父组件传播。

②会首先触发当前组件的同名事件(如果有);

③然后会向上冒泡,当遇到第一个符合的父组件的事件后触发并停止;

④当父组件的事件的返回值设为true会继续冒泡去找下一个。

$broadcast(事件名, 参数)

①向下广播事件,用于向所有子组件传播。

②默认情况是仅限子组件;

③子组件事件的返回值是true,才会继续向该子组件的孙组件派发;

④不会触发自身同名事件;

 

其次,向上派发和向下广播有所区别:向上派发会触发自身同名事件,而向下广播不会;

 

第三,向上派发和向下广播默认只会触发直系(子或者父,不包括祖先和孙)的事件,除非事件返回值为true,才会继续在这一条线上继续。

 

第四,事件不能显式的通过 this.事件名 来调用它。

 

示例代码:

<div id="app">
   
父组件:
   
<button @click="parentClick">点击向下传播broadcast</button>
    <
br/>
   
子组件1:
   
<children1></children1>
    <
br/>
   
另一个子组件1:
   
<another-children1></another-children1>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
methods: {
           
parentClick: function () {
               
this.$broadcast("parentClick", "abc");
            }
        },
       
events: {
           
childrenClick: function () {
               
console.log("childrenClick-Parent");
            },
           
parentClick: function () {
               
console.log("parentClick-Parent");
               
return true;
            }
        },
       
components: {
           
children1: {    //这个无返回值,不会继续派发
               
template: "<button>children1</button></br>子组件2:<children2></children2>",
               
events: {
                   
childrenClick: function () {
                       
console.log("childrenClick-children1");
                    },
                   
parentClick: function (msg) {
                       
console.log("parentClick-Children1");
                       
console.log("message:" + msg);
                    }
                },
               
components: {
                   
children2: {
                       
template: "<button @click='findParent'>children-Click</button>",
                       
methods: {
                           
findParent: function () {
                               
this.$dispatch('childrenClick');
                            }
                        },
                        
events: {
                           
childrenClick: function () {
                               
console.log("childrenClick-children2");
                            },
                           
parentClick: function (msg) {
                                
console.log("parentClick-Children2");
                               
console.log("message:" + msg);
                            }
                        }
                    }
                }
            },
           
anotherChildren1: { //这个是返回值为true,会继续向子组件的子组件派发
               
template: "<button>anotherChildren1</button></br>另一个子组件2:<another-children2></another-children2>",
               
events: {
                   
childrenClick: function () {
                       
console.log("childrenClick-anotherChildren1");
                       
return true;
                    },
                   
parentClick: function (msg) {
                       
console.log("parentClick-anotherChildren1");
                       
console.log("message:" + msg);
                       
return true;
                    }
                },
               
components: {
                   
anotherChildren2: {
                       
template: "<button @click='findParent'>anotherChildren2-Click</button>",
                       
methods: {
                           
findParent: function () {
                               
this.$dispatch('childrenClick');
                            }
                        },
                       
events: {
                            
childrenClick: function () {
                               
console.log("childrenClick-anotherChildren2");
                            },
                           
parentClick: function (msg) {
                               
console.log("parentClick-anotherChildren2");
                               
console.log("message:" + msg);
                            }
                        }
                    }
                }
            }

        }
    });
</script>

               },
                           
parentClick: function () {
                               
console.log("parentClick-anotherChildren2");
                            }
                        }
                    }
                }
            }

        }
    });
</script>

 

说明:

1】点击父组件的按钮,会向下广播,然后触发子组件1本身,另外一个子组件1,以及另一个子组件2

2】点击子组件2的按钮,会触发子组件2的事件和子组件1的事件,但不会触发父组件的按钮;

3】点击另一个子组件2的按钮,会触发另一个子组件2的事件,另一个子组件1的事件和父组件的事件(因为另一个子组件1的事件的返回值为true);

 

 

 

③使用v-on绑定自定义事件:

1】简单来说,子组件触发某个事件(events里的方法)时,父组件也会执行某个方法(父组件methods里的方法)。

 

2】触发的绑定写在模板之中(即被替换的那个template模板中),可以多个子组件的事件绑定一个父组件的方法,或者不同子组件的事情绑定不同父组件的方法,但是不能同一个子组件事件绑定多个父组件的方法。

 

3】子组件派发消息传递的参数,即使子组件的事件没有参数,也不影响将参数传递给父组件的方法(即父组件的方法可以接受到子组件方法获取的参数)

 

如示例:

<div id="app">
   
父组件:
   
<button>点击向下传播broadcast</button>
    <
br/>
   
子组件1:
   
<!--绑定写在这里,可以多个绑定同一个,或者不同绑定不同的,但不能一个绑定多个-->
   
<children v-on:test="parent" @test2="another"></children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
val: 1
       
},
       
methods: {
            
parent: function (arg) {
               
console.log(arg);
               
console.log("the first method with test event");
            },
           
another: function () {
               
console.log("another method");
            }
        },
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button @click='childClick'>children1</button></br><button @click='childClick2'>children1</button>",
               
methods: {
                   
childClick: function () {
                       
this.$emit("test", 'the argument for dispatch');
                    },
                   
childClick2: function () {
                       
this.$emit("test2");
                    }
                },
               
events: {
                   
test: function () {
                       
console.log("test");
                    },
                   
test2: function () {
                       
console.log("test2");
                    }
                }
            }
        }
    });
</script>

 

 

④子组件索引

简单来说:就是可以直接从索引获取到子组件,然后就可以调用各个子组件的方法了。

 

添加索引方法是:在标签里添加v-ref:索引名

调用组件方法是:vm.$ref.索引名

也可以直接在父组件中使用this.$ref.索引名

这个时候,就可以获得组件了,然后通过组件可以调用他的方法,或者是使用其数据。

 

示例代码:

<div id="app">
   
父组件:
   
<button @click="todo">触发子组件的事件</button>
    <
br/>
   
子组件1:
   
<!--绑定写在这里,可以多个绑定同一个,或者不同绑定不同的,但不能一个绑定多个-->
   
<children v-ref:child></children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
methods: {
           
todo: function () {
               
this.$refs.child.fromParent();  //通过索引调用子组件的fromParent方法
           
}
        },
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button>children1</button>",
               
methods: {
                   
fromParent: function () {
                        
console.log("happened fromParent by ref");
                    }
                }
            }
        }
    });
</script>

 

 

 

 

(二十八)Slot分发内容

①概述:

简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。

 

②默认情况下

父组件在子组件内套的内容,是不显示的。

例如代码:

<div id="app">
    <
children>
        <
span>12345</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button>为了明确作用范围,所以使用button标签</button>"
           
}
        }
    });
</script>

显示内容是一个button按钮,不包含span标签里面的内容;

 

 

③单个slot

简单来说,只使用这个标签的话,可以将父组件放在子组件的内容,放到想让他显示的地方。

<div id="app">
    <
children>
        <
span>12345</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button><slot></slot>为了明确作用范围,所以使用button标签</button>"
           
}
        }
    });
</script>

例如这样写的话,结果是:

<button><span>12345</span>为了明确作用范围,所以使用button标签</button>

 

即父组件放在子组件里的内容,插到了子组件的<slot></slot>位置;

注意,即使有多个标签,会一起被插入,相当于用父组件放在子组件里的标签,替换了<slot></slot>这个标签。

 

 

④具名slot

将放在子组件里的不同html标签放在不同的位置

父组件在要分发的标签里添加 slot=”name 属性

子组件在对应分发的位置的slot标签里,添加name=”name属性,

然后就会将对应的标签放在对应的位置了。

 

示例代码:

<div id="app">
    <
children>
        <
span slot="first">12345</span>
        <
span slot="second">56789</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
        
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button><slot name='first'></slot>为了明确作用范围,<slot name='second'></slot>所以使用button标签</button>"
           
}
        }
    });
</script>

 

显示结果为:(为了方便查看,已手动调整换行)

<button>

<span slot="first">12345</span>

为了明确作用范围,

<span slot="second">56789</span>

所以使用button标签

</button>

 

 

⑤分发内容的作用域:

被分发的内容的作用域,根据其所在模板决定,例如,以上标签,其在父组件的模板中(虽然其被子组件的children标签所包括,但由于他不在子组件的template属性中,因此不属于子组件),则受父组件所控制。

 

示例代码:

<div id="app">
    <
children>
        <
span slot="first" @click="tobeknow">12345</span>
        <
span slot="second">56789</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
methods: {
           
tobeknow: function () {
                
console.log("It is the parent's method");
            }
        },
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<button><slot name='first'></slot>为了明确作用范围,<slot name='second'></slot>所以使用button标签</button>"
           
}
        }
    });
</script>

 

当点击文字12345的区域时(而不是按钮全部),会触发父组件的tobeknow方法。

 

但是点击其他区域时则没有影响。

 

官方教程是这么说的:

父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译

 

 

 

⑥当没有分发内容时的提示:

假如父组件没有在子组件中放置有标签,或者是父组件在子组件中放置标签,但有slot属性,而子组件中没有该slot属性的标签。

 

那么,子组件的slot标签,将不会起到任何作用。

 

除非,该slot标签内有内容,那么在无分发内容的时候,会显示该slot标签内的内容。

 

如示例代码:

<div id="app">
    <
children>
        <
span slot="first">【12345】</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
components: {
            
children: {    //这个无返回值,不会继续派发
               
template: "<div><slot name='first'><button>【如果没有内容则显示我1】</button></slot>为了明确作用范围,<slot name='last'><button>【如果没有内容则显示我2】</button></slot>所以使用button标签</div>"
           
}
        }
    });
</script>

 

说明:

1name=’first’slot标签被父组件对应的标签所替换(slot标签内部的内容被舍弃);

2name=’last’slot标签,因为没有对应的内容,则显示该slot标签内部的内容。

 

 

 

⑦假如想控制子组件根标签的属性

1】首先,由于模板标签是属于父组件的,因此,将子组件的指令绑定在模板标签里,是不可以的(因为他归属于父组件);

 

2】假如需要通过父组件控制子组件是否显示(例如v-if或者v-show),那么这个指令显然是属于父组件的(例如放在父组件的data下面)。可以将标签写在子组件的模板上。

如代码:

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
children v-if="abc">
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
abc: false
       
},
       
methods: {
           
toshow: function () {
               
this.abc = !this.abc;
            }
        },
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<div>这里是子组件</div>"
           
}
        }
    });
</script>

 

说明:

通过父组件(点击按钮,切换v-if指令的值)控制子组件是否显示。

 

 

3】假如需要通过子组件,控制子组件是否显示(比如让他隐藏),那么这个指令显然是属于子组件的(会将值放在子组件的data属性下),那么就不能像上面这么写,而是必须放置在子组件的根标签中。

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
children>
        <
span slot="first">【12345】</span>
       
<!--上面这行不会显示-->
   
</children>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
methods: {
           
toshow: function () {
               
this.$children[0].tohidden = true;
            }
        },
       
components: {
           
children: {    //这个无返回值,不会继续派发
               
template: "<div v-if='tohidden' @click='tohide'>这里是子组件</div>",
               
data: function () {
                   
return {
                       
tohidden: true
                   
}
                },
               
methods: {
                   
tohide: function () {
                       
this.tohidden = !this.tohidden;
                    }
                }
            }
        }
    });
</script>

说明:

点击子组件会让子组件消失;

点击父组件的按钮,通过更改子组件的tohidden属性,让子组件重新显示。

子组件的指令绑定在子组件的模板之中(如此才能调用);

 

 

 

(二十九)组件——动态组件

①简单来说,就是几个组件放在一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示。

 

②动态切换:

在挂载点使用component标签,然后使用v-bind:is=”组件名,会自动去找匹配的组件名,如果没有,则不显示;

 

改变挂载的组件,只需要修改is指令的值即可。

 

如示例代码:

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
component v-bind:is="which_to_show"></component>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
which_to_show: "first"
       
},
       
methods: {
           
toshow: function () {   //切换组件显示
               
var arr = ["first", "second", "third", ""];
               
var index = arr.indexOf(this.which_to_show);
               
if (index < 3) {
                   
this.which_to_show = arr[index + 1];
                }
else {
                   
this.which_to_show = arr[0];
                }
            }
        },
       
components: {
           
first: { //第一个子组件
               
template: "<div>这里是子组件1</div>"
           
},
           
second: { //第二个子组件
               
template: "<div>这里是子组件2</div>"
           
},
            
third: { //第三个子组件
               
template: "<div>这里是子组件3</div>"
           
},
        }
    });
</script>

说明:

点击父组件的按钮,会自动切换显示某一个子组件(根据which_to_show这个变量的值来决定)。

 

 

 

keep-alive

简单来说,被切换掉(非当前显示)的组件,是直接被移除了。

在父组件中查看this.$children属性,可以发现,当子组件存在时,该属性的length1,而子组件不存在时,该属性的length0(无法获取到子组件);

 

假如需要子组件在切换后,依然需要他保留在内存中,避免下次出现的时候重新渲染。那么就应该在component标签中添加keep-alive属性。

 

如代码:

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
component v-bind:is="which_to_show" keep-alive></component>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
which_to_show: "first"
       
},
       
methods: {
           
toshow: function () {   //切换组件显示
               
var arr = ["first", "second", "third", ""];
               
var index = arr.indexOf(this.which_to_show);
               
if (index < 3) {
                   
this.which_to_show = arr[index + 1];
                }
else {
                   
this.which_to_show = arr[0];
                }
               
console.log(this.$children);
            }
        },
       
components: {
           
first: { //第一个子组件
               
template: "<div>这里是子组件1</div>"
           
},
           
second: { //第二个子组件
               
template: "<div>这里是子组件2</div>"
           
},
           
third: { //第三个子组件
               
template: "<div>这里是子组件3</div>"
           
},
        }
    });
</script>

 

说明:

初始情况下,vm.$children属性中只有一个元素(first组件),

点击按钮切换后,vm.$children属性中有两个元素,

再次切换后,则有三个元素(三个子组件都保留在内存中)。

之后无论如何切换,将一直保持有三个元素。

 

 

 

activate钩子

简单来说,他是延迟加载。

例如,在发起ajax请求时,会需要等待一些时间,假如我们需要在ajax请求完成后,再进行加载,那么就需要用到activate钩子了。

 

具体用法来说,activate是和templatedata等属性平级的一个属性,形式是一个函数,函数里默认有一个参数,而这个参数是一个函数,执行这个函数时,才会切换组件。

 

为了证明他的延迟加载性,在服务器端我设置当发起某个ajax请求时,会延迟2秒才返回内容,因此,第一次切换组件2时,需要等待2秒才会成功切换:

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
component v-bind:is="which_to_show"></component>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
which_to_show: "first"
       
},
       
methods: {
           
toshow: function () {   //切换组件显示
               
var arr = ["first", "second", "third", ""];
               
var index = arr.indexOf(this.which_to_show);
               
if (index < 3) {
                   
this.which_to_show = arr[index + 1];
                }
else {
                    
this.which_to_show = arr[0];
                }
               
console.log(this.$children);
            }
        },
       
components: {
           
first: { //第一个子组件
               
template: "<div>这里是子组件1</div>"
           
},
            
second: { //第二个子组件
               
template: "<div>这里是子组件2,这里是ajax后的内容:{{hello}}</div>",
               
data: function () {
                   
return {
                       
hello: ""
                   
}
                },
               
activate: function (done) { //执行这个参数时,才会切换组件
                   
var self = this;
                   
$.get("/test", function (data) {    //这个ajax我手动在服务器端设置延迟为2000ms,因此需要等待2秒后才会切换
                       
self.hello = data;
                       
done(); //ajax执行成功,切换组件
                   
})
                }
            },
           
third: { //第三个子组件
               
template: "<div>这里是子组件3</div>"
           
}
        }
    });
</script>

 

代码效果:

1】第一次切换到组件2时,需要等待2秒后才能显示(因为发起ajax);

 

2】在有keep-alive的情况下,第二次或之后切换到组件2时,无需等待;但ajax内容,需要在第一次发起ajax两秒后才会显示;

 

3】在无keep-alive的情况下(切换掉后没有保存在内存中),第二次切换到组件2时,依然需要等待。

 

4】等待时,不影响再次切换(即等待组件2的时候,再次点击切换,可以直接切换到组件3);

 

说明:

1】只有在第一次渲染组件时,才会执行activate,且该函数只会执行一次(在第一次组件出现的时候延迟组件出现)

2】没有keep-alive时,每次切换组件出现都是重新渲染(因为之前隐藏时执行了destroy过程),因此会执行activate方法。

 

 

 

transition-mode过渡模式

简单来说,动态组件切换时,让其出现动画效果。(还记不记得在过渡那一节的说明,过渡适用于动态组件)

默认是进入和退出一起完成;(可能造成进入的内容出现在退出内容的下方,这个下方指y轴方面偏下的,等退出完毕后,进入的才会出现在正确的位置);

transition-mode=”out-in”时,动画是先出后进;

transition-mode=”in-out”时,动画是先进后出(同默认情况容易出现的问题);

 

示例代码:(使用自定义过渡名和animate.css文件)

<div id="app">
    <
button @click="toshow">点击让子组件显示</button>
    <
component v-bind:is="which_to_show" class="animated" transition="bounce" transition-mode="out-in"></component>
</
div>
<
script>
   
Vue.transition("bounce", {
       
enterClass: 'bounceInLeft',
       
leaveClass: 'bounceOutRight'
   
})
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
which_to_show: "first"
       
},
       
methods: {
           
toshow: function () {   //切换组件显示
               
var arr = ["first", "second", "third", ""];
               
var index = arr.indexOf(this.which_to_show);
               
if (index < 3) {
                   
this.which_to_show = arr[index + 1];
                }
else {
                   
this.which_to_show = arr[0];
                }
            }
        },
       
components: {
           
first: { //第一个子组件
               
template: "<div>这里是子组件1</div>"
           
},
           
second: { //第二个子组件
               
template: "<div>这里是子组件2,这里是ajax后的内容:{{hello}}</div>",
               
data: function () {
                   
return {
                       
hello: ""
                   
}
                }
            },
           
third: { //第三个子组件
               
template: "<div>这里是子组件3</div>"
           
}
        }
    });
</script>

 

 

 

 

(三十)组件——杂项

①组件和v-for

简单来说,就是组件被多次复用;

 

例如表格里的某一行,又例如电商的商品橱窗展示(单个橱窗),都可以成为可以被复用的组件;

 

只要编写其中一个作为组件,然后使数据来源成为一个数组(或对象,但个人觉得最好是数组),通过v-for的遍历,组件的每个实例,都可以获取这个数组中的一项,从而生成全部的组件。

 

而数据传输,由于复用,所以需要使用props,将遍历结果i,和props绑定的数据绑定起来,绑定方法同普通的形式,在模板中绑定。

 

示例代码:

<div id="app">
    <
button @click="toknowchildren">点击让子组件显示</button>
    <
table>
        <
tr>
            <
td>索引</td>
            <
td>ID</td>
            <
td>说明</td>
        </
tr>
        <
tr is="the-tr" v-for="i in items" v-bind:id="i" :index="$index"></tr>
    </
table>
</
div>
<
script>
   
var vm = new Vue({
       
el: '#app',
       
data: {
           
items: [1, 2, 3, 4]
        },
       
methods: {
           
toknowchildren: function () {   //切换组件显示
               
console.log(this.$children);
            }
        },
       
components: {
           
theTr: { //第一个子组件
               
template: "<tr>" +
               
"<td>{{index}}</td>" +
               
"<td>{{id}}</td>" +
               
"<td>这里是子组件</td>" +
               
"</tr>",
               
props: ['id','index']
            }
        }
    });
</script>

 

说明:

1】记得将要传递的数据放在props里!

 

2】将index和索引$index绑定起来,因为索引从0开始,因此索引所在列是从0开始;id是和遍历itemsi绑定在一起的,因此id1开始。

 

3】可以在父组件中,通过this.$children来获取子组件(但是比较麻烦,特别是组件多的时候,比较难定位);

 

 

 

②编写可复用的组件:

简单来说,一次性组件(只用在这里,不会被复用的)跟其他组件紧密耦合是可以的,但是,可复用的组件应当定义一个清晰的公开接口。(不然别人怎么用?)

 

可复用的组件,基本都是要和外部交互的,而一个组件和外部公开的交互接口有:

1props:允许外部环境数据传递给组件;

2】事件:允许组件触发外部环境的action,就是说通过在挂载点添加v-on指令,让子组件的events触发时,同时触发父组件的methods

3slot:分发,允许将父组件的内容插入到子组件的视图结构中。

 

如代码:

<div id="app">
    <
p>这是第一个父组件</p>
    <
widget
           
:the-value="test"
           
@some="todo">
        <
span>【第一个父组件插入的内容】</span>
    </
widget>
</
div>
<
div id="app2">
    <
p>这是第二个父组件</p>
    <
widget @some="todo">
    </
widget>
</
div>
<
script>
   
Vue.component("widget", {
       
template: "<button @click='dosomething'><slot></slot>这是一个复用的组件,点击他{{theValue}}</button>",
       
methods: {
           
dosomething: function () {
               
this.$emit("some");
            }
        },
       
events: {
           
some: function () {
               
console.log("widget click");
            }
        },
       
props: ['theValue']
    })

   
var vm = new Vue({
       
el: '#app',
       
data: {
           
test: "test"
       
},
       
methods: {
            
todo: function () {
               
console.log("这是第一个父组件")
            }
        }
    });
   
var vm_other = new Vue({
       
el: '#app2',
       
data: {
           
name: "first"
       
},
       
methods: {
           
todo: function () {
                
console.log("这是另外一个父组件")
            }
        }
    });
</script>

说明:

1】在第一个父组件中使用了分发slot,使用了props来传递值(将test的值传到子组件的theValue之中);

 

2】在两个组件中,子组件在点击后,调用methods里的dosomething方法,然后执行了events里的some事件。又通过挂载点的@some=”todo”,将子组件的some事件和父组件的todo方法绑定在一起。

因此,点击子组件后,最终会执行父组件的todo方法。

 

3】更改父组件中,被传递到子组件的值,会同步更改子组件的值(即二者会数据绑定);

 

 

 

③异步组件:

按照我的理解,简单来说,一个大型应用,他有多个组件,但有些组件无需立即加载,因此被分拆成多个组件(比如说需要立即加载的,不需要立即加载的);

 

需要立即加载的,显然放在同一个文件中比较好(或者同一批一起请求);

 

而不需要立即加载的,可以放在其他文件中,但需要的时候,再ajax向服务器请求;

 

这些后续请求的呢,就是异步组件;

 

做到这种异步功能的,就是Vue.js的功能——允许将组件定义为一个工厂函数,动态解析组件的定义。

 

可以配合webpack使用。

 

至于如何具体使用,我还不太明白,教程中写的不清,先搁置等需要的时候来研究。

 

链接:

http://cn.vuejs.org/guide/components.html#u5F02_u6B65_u7EC4_u4EF6

 

 

 

④资源命名的约定:

简单来说,html标签(比如divDIV是一样的)和特性(比如要写成v-on这样的指令而不是vOn)是不区分大小写的。

 

而资源名往往是写成驼峰式(比如camelCase驼峰式),或者单词首字母都大写的形式(比如PascalCase,我不知道该怎么称呼这个,不过这样写很少的说)。

Vue.component("myTemplate", {
   
//......
})

 

Vue.js可以自动识别这个并转换,

<my-template></my-template>

 

以上那个模板可以自动替换这个标签。

 

 

 

⑤递归组件:

简单来说,递归组件就是组件在自己里内嵌自己的模板。

 

组件想要递归,需要name属性,而Vue.component自带name属性。

 

大概样子是这样的,

<div id="app">
    <
my-template></my-template>
</
div>
<
script>
   
Vue.component("myTemplate", {
       
template: "<p><my-template></my-template></p>"
   
})

 

这种是无限递归,肯定是不行的。因此,需要控制他递归的层数,例如通过数据来控制递归,当数据为空时,则停止递归。

 

示例代码如下:

<ul id="app">
    <
li>
       
{{b}}
   
</li>
    <
my-template v-if="a" :a="a.a" :b="a.b"></my-template>
</
ul>
<
script>
   
Vue.component("myTemplate", {
       
template: '<ul><li>{{b}}</li><my-template v-if="a" :a="a.a" :b="a.b"></my-template></ul>',
       
props: ["a", "b"]
    })
   
var data = {
       
a: {
           
a: {
               
a: 0,
               
b: 3
           
},
           
b: 2
       
},
       
b: 1
   
}
   
var vm = new Vue({
       
el: '#app',
       
data: data,
       
methods: {
           
todo: function () {
               
this.test += "!";
               
console.log(this.test);
            }
        }
    });
</script>

 

说明:

1】向下传递时,通过props传递a的值和b的值,其中a的值作为递归后组件的ab的值的数据来源;

然后判断传递到递归后的组件的a的值是否存在,如果存在则继续递归;

如果a的值不存在,则停止递归。

 

 

 

⑥片断实例:

简单来说,所谓片断实例,就是组件的模板不是处于一个根节点之下:

 

片断实例代码:

Vue.component("myTemplate", {
   
template: '<div>1</div>' +
   
'<div>2</div>',
})

 

非片断实例:

Vue.component("myTemplate", {
   
template: '<div>' +
   
'<div>1</div>' +
   
'<div>2</div>' +
   
'</div>',
})

 

 

片断实例的以下特性被忽略:

1】组件元素上的非流程控制指令(例如写在挂载点上的,由父组件控制的v-show指令之类,但注意,v-if属于流程控制指令);

 

2】非props特性(注意,props不会被忽略,另外props是写在挂载点上的);

 

3】过渡(就是transition这个属性,将被忽略);

 

更多的参照官方文档:

http://cn.vuejs.org/guide/components.html#u7247_u65AD_u5B9E_u4F8B

 

 

 

⑦内联模板

参照:http://cn.vuejs.org/guide/components.html#u5185_u8054_u6A21_u677F

 

反正我试了下失败了,google也没搜到相关的内容,┑( ̄Д )

 

 

————————组件的基本知识到这里结束————————————

 

 

目录
相关文章
|
16天前
|
JavaScript
Vue 父传子组件传参 defineProps
Vue 父传子组件传参 defineProps
|
16天前
|
JavaScript 前端开发
Vue 创建组件
Vue 创建组件
|
20天前
|
JavaScript
vue给table组件添加合计
vue给table组件添加合计
12 0
|
1月前
|
存储 JavaScript BI
vue组件间通信的几个方法
vue组件间通信的几个方法
23 0
|
1月前
|
JavaScript
vue 异步加载组件
vue 异步加载组件
20 3
|
1月前
|
JSON JavaScript 前端开发
【form-generator在线表单生成---vue父组件调用vue弹框组件】
【form-generator在线表单生成---vue父组件调用vue弹框组件】
34 1
|
1月前
|
JavaScript
如何使用Vue的路由实现组件的懒加载和按需加载?
如何使用Vue的路由实现组件的懒加载和按需加载?
29 1
|
16天前
|
JavaScript
vue路由导航守卫(全局守卫、路由独享守卫、组件内守卫)
vue路由导航守卫(全局守卫、路由独享守卫、组件内守卫)
33 0
|
7天前
|
JavaScript
Vue Steps步骤组件用法
Vue Steps步骤组件用法
13 0
|
12天前
|
JavaScript
vue 组件注册
vue 组件注册