组件
创建方式一:
HTML: <div id="app"><c1></c1></div>
/*要注意,局部模板的作用范围 只在 相对的vue对象 范围内, 比如此例子 <haha></haha>的作用范围 只在 <div id="app"></div>内*/
let vm = new Vue({ ...
components:{"c1":{ template:"<div>i am c1<div>" }}
});
创建方式二:Vue.component 全局组件 --- 全局模板 顾名思义 肯定是 放在哪里都能用
HTML: <div id="app"><c2></c22></div>
//这里使用Vue.component原型方法来创造。注意,全局组件注册位置必须在父实例的模块上面(即在初始化组件根实例之前,注册好组件)
Vue.component(
"c2",
template:"<div>i am c2<div>"
);
let vm = new Vue({...,components:{ 这里则不需要添加 } });
创建方式三:局部注册
不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用:
HTML: <div id="app"><c3></c3></div>
let c3={ template:"<div>嘻嘻嘻<div>" }
let vm = new Vue({ ...,components: { c3} });
防止有些浏览器不认识标签报错,用 is 属性判断
![](https://i-blog.csdnimg.cn/blog_migrate/c4dcce2f83ee072ac761da3333ae1af8.png)
组件中的data必须返回object类型
父、子组件
在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。
使用Props传递数据
组件实例(父实例)的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
注:把父实例的引用类型的数据传给子组件,子组件也能反向修改这个引用类型的数据。这种操作方式有两面性,看个人怎么用了,官方是不推荐这么操作,如上是建议用props把数据传给子组件,通过event或emit返给父实例/父组件
例如:
prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props
选项 声明 “prop”:
Vue.component('child', { // 声明 props props: ['message'], //prop 是父组件用来传递数据的一个自定义属性, props: ['属性名'] // 就像data 一样,prop可用在模板内. 同样也可在 vm实例中像"this.message"这样使用 template: '<span>{{ message }}</span>' }) |
然后向它传入一个普通字符串:
<child message="hello!"></child> //子组件需要显式地用 |
动态 Props
v-bind
类似于用 v-bind
绑定 HTML 特性到一个表达式,组件也可以使用 v-bind
动态绑定 props 的值 到 父组件的数据中。
每当父组件的数据变化时,该变化也会传给子组件:
<div> //[父组件的数据中,每当父组件的数据变化时,该变化也会传导给子组件] <input v-model="parentMsg"> //双向绑定 <br> <child v-bind:my-message="parentMsg"></child> //子组件 </div> |
使用 v-bind
的缩写语法通常更简单:<child :my-message="parentMsg"></child>
字面量语法 vs 动态语法
初学者常犯的一个错误是使用字面量语法传递数值:
<!-- 传递了一个字符串"1" -->
<comp some-prop="1"></comp>
因为它是一个字面 prop ,它的值以字符串 "1"
而不是以实际的数字传下去。
如果想传递一个实际的 JS数字,需要使用 v-bind
,从而让它的值被当作JS表达式计算:
<!-- 传递实际的数字 -->
<comp v-bind:some-prop="1"></comp>
注: 测试发现,在组件的prop中, prop-name="..." 和 v-bind:prop-name="..." 有区别
单向数据流
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态。
另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。(个人测试没有警告!!!)
通常有两种改变 prop 的情况:(父影响子,子不影响父)
1. prop 作为初始值传入子组件之后只是将它的初始值作为本地数据的初始值使用. 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值
:
例如,定义一个局部 data 属性,并将 prop 的初始值作为局部数据的初始值:
props: ['initialCounter'], //子组件 data: function () { //子组件 return { counter: this.initialCounter }//counter子组件的局部数据 } |
父控件的变量值作为子组件的属性值,如:
2. 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性
例如,在 computed 定义一个属性,此属性从 prop 的值计算得出。
<div id="div8">
<input type='text' v-model="msg">
<child v-bind:message='msg'></child>
</div>
<script type="text/javascript">
Vue.component('child', {
props: ['message'],
template: '<p>message:{{ normalizedSize}} </p>',
computed: {
normalizedSize: function() {
return this.message += "_str"
}
}
})
new Vue({
el: '#div8',
data: {
msg: '我是msg'
}
})
</script>
执行结果:
再如:从父控件向子组件传值
Prop 验证
组件可以为 props 指定验证要求。如果未指定验证要求,Vue 会发出警告。当组件给其他人使用时这很有用。
prop 是一个对象而不是字符串数组时,它包含验证要求:
type
可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
type
也可以是一个自定义构造器,使用 instanceof
检测。
当 prop 验证失败了, Vue 将拒绝在子组件上设置此值,如果使用的是开发版本会抛出一条警告
#自定义事件
我们知道,父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,应该怎样做?那就是自定义事件!
使用 v-on 绑定自定义事件
每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
另外,父组件可以在使用子组件的地方直接用 v-on
来监听子组件触发的事件。
下面是一个例子,在本例中,子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件:
给组件绑定原生事件
有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 .native
修饰 v-on
。例如:
<my-component v-on:click.native="doTheThing"></my-component>
使用自定义事件的表单输入组件
自定义事件也可以用来创建自定义的表单输入组件,使用 v-model
来进行数据双向绑定。牢记:
<input v-model="something">
仅仅是一个语法糖,他也相当于:<input v-bind:value="something" v-on:input="something = $event.target.value">
所以在组件中使用时,它相当于下面的简写:<input v-bind:value="something" v-on:input="something = arguments[0]">
所以要让组件的 v-model
生效,它必须:
- 接受一个
value
属性 - 在有新的 value 时触发
input
事件
例如,货币过滤器:
(模板的继承)全局模板 顾名思义 肯定是 放在哪里都能用
/*
我们来讲个最简单的 父亲,儿子 ,孙子的 例子
要注意以下几点:
1.先实例化对象vm
2.造出parent模板,并挂载在 vm的 conponents 下
3.造出 son 模板 并 挂载 在 父级 parent 的conponents下,并在 父级template属性中 包裹住自己的 模板名标签 ;同理 造出孙子标签
4.在HTML节点中添加 目标 根节点
(节点的顺序一定要书写正确)
*/
<div id="app">
<parent></parent>
</div>
//创建 孙子 模板
let grendson = {
template:"<div>孙子</div>"
}
//创建 儿子 模板
let son= {
template: "<son>儿子<grendson >孙子</grendson ></son>"
components:{
grendson
}
}
//创建 父亲 模板
let parent = {
template:"<div>父亲<son></son></div>"
components:{
son
}
}
//实例化vm对象
let vm = new Vue({
el: "#app",
data:{
},
components:{
parent
}
});
实例一
css:
<style type="text/css">
.tab-button {
padding: 6px 10px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #ccc;
cursor: pointer;
background: #f0f0f0;
margin-bottom: -1px;
margin-right: -1px;
}
.tab-button:hover {
background: #e0e0e0;
}
.tab-button-active {
background: #e0e0e0;
}
.tab {
border: 1px solid #ccc;
padding: 10px;
}
</style>
html:
<div id="app">
<div v-for="(item, index) in buttons">
<button @click="change($event)" :id="item.id" class="tab-button"
:class="{'tab-button-active': item.isActive}">{{item.name}}</button>
<div class="tab">
<component v-bind:is="currentTabComponent"></component>
</div>
</div>
</div>
script:
var vm = new Vue({
el: '#app',
data:{
currentTab: 'Home',
tabs:['Home','Posts','Archive'],
buttons: [
{id: 0, name: 'Home', isActive:true},
{id: 1, name: 'Posts', isActive:false},
{id: 2, name: 'Archive', isActive:false}
],
isActive: false,
prevBtn: 0
},
computed: {
// 计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。
// 这里当 this.index 有改变时,currentView()会重新求值
currentTabComponent(){
return 'tab-' + this.currentTab.toLowerCase()
}
},
methods: {
change( $event ){
var index = $event.target.id;
Vue.set(vm.buttons[vm.prevBtn], 'isActive', false);
Vue.set(vm.buttons[index], 'isActive', true);
this.currentTab = this.tabs[index%3];
vm.prevBtn = index;
}
}
})