什么是组件?
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。
使用组件
组件应该挂载到某个Vue实例下,否则它不会生效。
因而组件都是先注册,再挂载到Vue实例上。
一、全局注册
全局组件:先注册,再创建根实例。
Vue.component()方法内部会调用组件构造器,创建一个组件实例。
Vue.component(‘模板名字‘,‘template(组件构造器):要渲染的html‘);
例1:
<div id="app">
<mycon></mycon>
</div>
<script type="text/javascript">
// 注册 (注册必须在 创建根实例之前,否则失效)
Vue.component('mycon',{
template:'<div>adadasd</div>'
});
// 创建根实例
new Vue({
el:'#app',
});
</script>
html部分被渲染为:
<div id="app">
<div>adadasd</div>
</div>
输出:adadasd
例2:
反撇号(
)基础知识:
代替普通字符串的引号 ’ 或 ” 外,它们看起来与普通字符串并无二致。
ES6引入了一种新型的字符串字面量语法,我们称之为模板字符串(template strings)。除了使用反撇号字符
<div id="app">
<checkbox-test></checkbox-test>
</div>
<script type="text/javascript">
var content="content-test";
Vue.component('checkbox-test', {
//下面${content}改为{{content}}也行
template: `<div @click="check">
<span>{{ title }}</span><br>
<span>${content}</span><br>
<span>checked:{{checked}}</span>
</div>`,
data() {
return { checked: false, title: 'mytitle' }
},
methods: {
check() { this.checked = !this.checked; }
}
});
window.vm=new Vue({
el:"#app",
});
</script>
输出:
二、局部注册
用Vue 实例的 components选项,格式:
components:{
'模板名称':{template(组件构造器):'要渲染的html'}
}
实例:
<div id="app">
<mycon></mycon>
</div>
<script type="text/javascript">
//必须在创建实例之前创建template(组件构造器),否则失效
var Child = {template:'<div>adadasd</div>'};
var vm = new Vue({
el:'#app',
components:{
//或者'my-component':{template:'<div>局部注册</div>'}
'mycon':Child,
}
});
</script>
输出结果:adadasd
三、DOM 模板解析注意事项
当使用 DOM 作为模板时 (例如,使用 el 选项来把 Vue 实例挂载到一个已有内容的元素上),你会受到 HTML 本身的一些限制,因为 Vue 只有在浏览器解析、规范化模板之后才能获取其内容。
尤其要注意,像 < ul>、< ol>、< table>、< select> 这样的元素里允许包含的元素有限制,而另一些像 < option> 这样的元素只能出现在某些特定元素的内部。变通的方案是使用特殊的 is 特性。
应当注意,如果使用以下来源之一的字符串模板,则没有这些限制:
- < script type=”text/x-template”>
- JavaScript 内联模板字符串
- .vue 组件
请尽可能使用上面三个。https://sdk.cn/news/7244
意思就是:
这样不可以
<div id="app">
<select>
<option>please choose</option>
<!--组件<myoption>、<myoption2>会被当作无效的内容,导致错误的渲染-->
<myoption></myoption>
<myoption2></myoption2>
</select>
</div>
<script type="text/javascript">
var option1 = {template:'<option>math</option>'};
var option2 = {template:'<option>chinese</option>'};
var vm = new Vue({
el:'#app',
components:{
'myoption':option1,
'myoption2':option2,
}
});
</script>
但是用is特殊属性可以:
<div id="app">
<select>
<option>please choose</option>
<option is='myoption'></option> <!-- is='myoption'不加单双引号也行,但是不规范-->
<option is='myoption2'></option>
</select>
</div>
<script type="text/javascript">
//若为template:'<option>math</option><option>chinese</option>',则只显示第一个,即math
var option1 = {template:'<option>math</option>'};
var option2 = {template:'<option>chinese</option>'};
var vm = new Vue({
el:'#app',
components:{
'myoption':option1,
'myoption2':option2,
}
});
</script>
执行结果:
或者< script type=”text/x-template”>也可以
<div id="app">
<select>
<option>please choose</option>
<option is='myoption'></option>
<option is='myoption2'></option>
</select>
<!--模板内容存放区域,放在#app里是为了方便阅读-->
<script type="x-template" id="optemp">
<option>math</option>
</script>
<script type="x-template" id="optemp2">
<option>chinese</option>
</script>
</div>
<script type="text/javascript">
var option1 = {template:'#optemp'};
var option2 = {template:'#optemp2'};
var vm = new Vue({
el:'#app',
components:{
'myoption':option1,
'myoption2':option2,
}
});
</script>
执行结果:同上
或者 JavaScript 内联模板字符串 也行
即:局部模版 嵌套 全局模版。
注:不能 局部模版 嵌套 局部模版
<div id="app">
<myselect></myselect>
</div>
<script type="text/javascript">
var sel = {
//局部嵌套全局
template:
'<select>'+
'<option>please choose</option>'+
'<myoption></myoption>'+
'<myoption2></myoption2>'+
'</select>'
};
//全局模版
Vue.component('myoption',{template:'<option>math</option>'});
Vue.component('myoption2',{template:'<option>chinese</option>'});
var vm = new Vue({
el:'#app',
components:{
'myselect':sel,
}
});
</script>
执行结果:同上
最后 .vue 组件也可以,这里先省略。
当然直接这么完也行,(直接把select放模版里,规避掉上面掉问题)
但相比于上面的js内联模板字符串,JavaScript 内联模板字符串更便于维护。
<div id="app">
<myselect></myselect>
</div>
<script type="text/javascript">
var child = {
template:
//在编译器中,一个字符串若不在一行会报错,所以用"+"连接。
'<select>'+
'<option>please choose</option>'+
'<option>math</option>'+
'<option>chinese</option>'+
'</select>'
};
var vm = new Vue({
el:'#app',
components:{
'myselect':child,
}
});
</script>
执行结果:同上
四、data 必须是函数
构造 Vue 实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:data 必须是函数。
意思就是:这样不行
Vue.component('my-component', {
template: '<span>{{ message }}</span>',
data: {
message: 'hello'
}
})
但这样可以
Vue.component('my-component', {
template: '<span>{{ message }}</span>',
data: function(){
return XXXX;
}
})
一个错误的例子:
<div id="app">
<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>
</div>
<script>
var mydata = { counter: 0 };
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// 技术上 data 的确是一个函数了,因此 Vue 不会警告,
// 但是我们却给每个组件实例返回了同一个对象的引用
data: function () {
return mydata;
}
})
vm=new Vue({
el:"#app",
});
</script>
执行结果:
点击任意一个,你会发现所有的都加1 这个bug.
这是由于这三个组件实例共享了同一个 data 对象,因此递增一个 counter 会影响所有组件。
我们可以通过为每个组件返回全新的数据对象来修复上面的bug
# 组件全局注册使用data写法:
<div id="app">
<mycount></mycount>
<mycount></mycount>
<mycount></mycount>
</div>
<script type="text/javascript">
Vue.component('mycount',{
template:'<button @click=counter++>{{counter}}</button>',
data:function(){
return {
counter:0,
};
}
});
var vm = new Vue({
el:'#app',
});
</script>
执行结果:
点击后:
将上面代码改成局部注册
# 组件局部注册使用data写法:
<div id="app">
<mycount></mycount>
<mycount></mycount>
<mycount></mycount>
</div>
<script type="text/javascript">
var clkcount = {
template:'<button @click=counter++>{{counter}}</button>',
data:function(){
return {
counter:0,
};
}
};
var vm = new Vue({
el:'#app',
components:{
'mycount':clkcount,
}
});
</script>
执行结果功能:同上
五、组件组合
组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。然而,通过一个良好定义的接口来尽可能将父子组件解耦也是很重要的。这保证了每个组件的代码可以在相对隔离的环境中书写和理解,从而提高了其可维护性和复用性。
在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。
Prop
一、用 Prop 传递数据 ( 父 -> 子 )
组件实例的作用域是孤立的。这意味着不能在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中 ( 父组件 -> 子组件 )。
子组件要显式地用 props 选项声明它预期的数据:
父组件通过props向子组件传递数据。
<!--子组件-->
<child id="app" message="hello boy"></child>
<script type="text/javascript">
//父组件:全局注册
Vue.component('child',{
// 声明 props
props:['message'],
// 就像 data 一样,prop 也可以在模板中使用
// 同样也可以在 vm 实例中通过 this.message 来使用
template:'<span>{{message}}</span>'
});
var vm = new Vue({
el:'#app',
});
</script>
二、命名:驼峰式 vs. 短横线分隔式
驼峰式命名:camelCase
短横线分隔式命名:kebab-case
HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名):
记住:
1、在注册组件时要用camelCase(用kebab-case会出错)。
2、在html中将camelCase 转换为 kebab-case
(因为HTML不区分大小写的)
3、如果你使用字符串模板,则没有这些限制。
<!-- 在 HTML 中使用 kebab-case -->
<child id="app" my-message="hello!"></child>
<script type="text/javascript">
//js变量名 用kebab-case,不能使用camelCase命名
Vue.component('child',{
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
});
var vm = new Vue({
el:'#app',
});
</script>
三、动态 Prop
与绑定到任何普通的 HTML 特性相类似,我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:
<div id="app">
<input v-model="parentMsg"></input><br>
<child :my-message="parentMsg"></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
});
var vm = new Vue({
el:'#app',
data:{
parentMsg:''
}
});
</script>
输入前:
输入后:
最近事比较多,晚点写完,
待续·····