学习心得,
组件(二),
直接上代码了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Prop</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app-1">
<div is="child1" message="HI~~Vue.js"></div>
</div>
<hr>
<div id="app-2">
<!-- 在 HTML 中使用 kebab-case -->
<div is="child2" my-message="Hello~~2"></div>
</div>
<hr>
<div id="app-3">
<input v-model="parentMsg">
<br>
<div is="child3" v-bind:my-message="parentMsg" v-bind="todo"></div>
</div>
<hr>
<!--字面量语法 vs 动态语法-->
<!--初学者常犯的一个错误是使用字面量语法传递数值:-->
<!--<!– 传递了一个字符串 "1" –>-->
<!--<comp some-prop="1"></comp>-->
<!--因为它是一个字面量 prop,它的值是字符串 "1" 而不是一个数值。如果想传递一个真正的 JavaScript 数值,则需要使用 v-bind,从而让它的值被当作 JavaScript 表达式计算:-->
<!--<!– 传递真正的数值 –>-->
<!--<comp v-bind:some-prop="1"></comp>-->
<!--非 Prop 特性-->
<!--所谓非 prop 特性,就是指它可以直接传入组件,而不需要定义相应的 prop。-->
<!--尽管为组件定义明确的 prop 是推荐的传参方式,组件的作者却并不总能预见到组件被使用的场景。-->
<!--所以,组件可以接收任意传入的特性,这些特性都会被添加到组件的根元素上。-->
<!--例如,假设我们使用了第三方组件 bs-date-input,它包含一个 Bootstrap 插件,该插件需要在 input 上添加 data-3d-date-picker 这个特性。-->
<!--这时可以把特性直接添加到组件上 (不需要事先定义 prop):-->
<!--<bs-date-input data-3d-date-picker="true"></bs-date-input>-->
<!--添加属性 data-3d-date-picker="true" 之后,它会被自动添加到 bs-date-input 的根元素上。-->
<div id="app-4">
<span is="bs-date-input" data-3d-date-picker="true" class="date-picker-theme-dark"></span>
</div>
<hr>
<div id="app-5">
<p>{{totals}}</p>
<button-counter v-on:increments="incrementTotals"></button-counter>
<button-counter v-on:increments="incrementTotals"></button-counter>
</div>
<hr>
<!--.sync 修饰符-->
<!--<comp :foo.sync="bar"></comp>-->
<!--使用自定义事件的表单输入组件-->
<!--自定义事件可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。要牢记:-->
<!--<input v-model="something">-->
<!--这不过是以下示例的语法糖:-->
<!--<input-->
<!--v-bind:value="something"-->
<!--v-on:input="something = $event.target.value">-->
<!--所以在组件中使用时,它相当于下面的简写:-->
<!--<custom-input-->
<!--v-bind:value="something"-->
<!--v-on:input="something = arguments[0]">-->
<!--</custom-input>-->
<div id="app-6">
<currency-input v-model="price"></currency-input>
</div>
<hr>
<div id="app-7">
<my-checkbox v-model="foo" value="some value"></my-checkbox>
</div>
<hr>
<script>
//使用Prop传递数据
//组件实例的作用域是孤立的。
//这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。
//父组件的数据需要通过 prop 才能下发到子组件中。
//子组件要显式的用props选项声明它预期的数据:
Vue.component('child1',{
//声明 props
props:['message'],
//就像data一样,prop也可以用在模板中使用
//同样也可以在 vm 实例中通过 this.message 来使用
template:'<span>{{message}}</span>'
});
var app1 = new Vue({
el:"#app-1"
});
// camelCase vs. kebab-case
//
// HTML 特性是不区分大小写的。
// 所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名):
Vue.component('child2',{
// 在 JavaScript 中使用 camelCase
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
});
var app2 = new Vue({
el:"#app-2"
});
//动态Prop
//与绑定到任何普通的 HTML 特性相类似,
// 我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。
// 每当父组件的数据变化时,该变化也会传导给子组件:
// 如果你想把一个对象的所有属性作为 prop 进行传递,
// 可以使用不带任何参数的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。
// 例如,已知一个 todo 对象:
var app3 = new Vue({
el:"#app-3",
data:{
parentMsg:'',
todo:{
text:'Learn Vue',
isComplete:false
}
},
components:{
child3:{
props:['myMessage','text'],
template:'<span>{{myMessage}},{{text}}</span>'
}
}
});
//单项数据流
// Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会。
// 这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。
//另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。
// 这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。
//在两种情况下,我们很容易忍不住想去修改 prop 中数据:
//Prop 作为初始值传入后,子组件想把它当作局部数据来用;
//Prop 作为原始数据传入,由子组件处理成其它数据输出。
//对这两种情况,正确的应对方式是:
//1.定义一个局部变量,并用 prop 的值初始化它:
// props: ['initialCounter'],
// data: function () {
// return { counter: this.initialCounter }
// }
// 2.定义一个计算属性,处理 prop 的值并返回:
//props: ['size'],
// computed: {
// normalizedSize: function () {
// return this.size.trim().toLowerCase()
// }
// }
// 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,
// 如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
//Prop验证
//我们可以为组件的 prop 指定验证规则。
//如果传入的数据不符合要求,Vue 会发出警告。
//这对于开发给他人使用的组件非常有用。
//要指定验证规则,需要用对象的形式来定义 prop,而不能用字符串数组:
Vue.component('child3',{
props:{
//基础类型检测('null',指允许任何类型)
propA: Number,
//可能是多种类型
propB: [String, Number],
//必传且是字符串
propC:{
type:String,
required:true
},
//数值且有默认值
propD:{
type:'Number',
default:100
},
//数组/对象的默认值应当由一个工厂函数返回
propE:{
type:'Object',
defalut:function(){
return {message:'hello'}
}
},
//自定义验证函数
propF:{
validator:function(value){
return value > 10
}
}
}
});
// type 可以是下面原生构造器:
// String
// Number
// Boolean
// Function
// Object
// Array
// Symbol
// type 也可以是一个自定义构造器函数,使用 instanceof 检测。
// 当 prop 验证失败,Vue 会抛出警告 (如果使用的是开发版本)。
// 注意 prop 会在组件实例创建之前进行校验,
// 所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。
//非Prop特性
//替换/合并现有的特性
Vue.component('bs-date-input',{
template:'<input type="date" class="form-control">'
});
var app4 = new Vue({
el:"#app-4"
});
//自定义事件
// 我们知道,父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。
// 使用 v-on 绑定自定义事件
//
// 每个 Vue 实例都实现了事件接口,即:
// 使用 $on(eventName) 监听事件
// 使用 $emit(eventName) 触发事件
// Vue 的事件系统与浏览器的 EventTarget API 有所不同。
// 尽管它们的运行起来类似,但是 $on 和 $emit 并不是addEventListener 和 dispatchEvent 的别名。
//
// 另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
// 不能用 $on 侦听子组件释放的事件,而必须在模板里直接用 v-on 绑定,参见下面的例子。
//声明是子组件,调用是父组件
Vue.component('button-counter',{
template:'<button v-on:click="incrementTotals">{{counter}}</button>',
data:function(){
return {
counter:0
}
},
methods:{
incrementTotals:function () {
this.counter += 1;
this.$emit("increments");
}
}
});
var app5 = new Vue({
el:"#app-5",
data:{
totals:0
},
methods:{
incrementTotals:function () {
this.totals +=1 ;
}
}
});
// 给组件绑定原生事件
//
// 有时候,你可能想在某个组件的根元素上监听一个原生事件。
// 可以使用 v-on 的修饰符 .native。例如:
//所以要让组件的 v-model 生效,它应该 (从 2.2.0 起是可配置的):
//接受一个 value prop
// 在有新的值时触发 input 事件并将新值作为参数
// 我们来看一个非常简单的货币输入的自定义控件:
Vue.component('currency-input',{
template:'<span>$' +
'<input ref="inputr" v-bind:value="value" v-on:input="updateValue($event.target.value)">' +
'</span>',
props:['value'],
methods:{
//不是直接更新,而是使用此方法来对输入值进行改格式化和位数限制
updateValue:function(value){
var formattedValue = value
//删除两侧的空格符
.trim()
//保留2位小数
.slice(
0,
value.indexOf('.') === -1
? value.length
:value.indexOf('.') + 3
)
//如果值上不合规,则手动覆盖为合规的值
if(formattedValue !== value){
this.$refs.inputr.value = formattedValue;
}
//通过input 事件带出数值
this.$emit('input', Number(formattedValue));
}
}
});
var app6 = new Vue({
el:'#app-6',
data:{
price:''
}
//为什么price写在这里,哪里用到了v-model="price" ??
});
// 自定义组件的 v-model
// 默认情况下,一个组件的 v-model 会使用 value prop 和 input 事件。
// 但是诸如单选框、复选框之类的输入类型可能把 value 用作了别的目的。
// model 选项可以避免这样的冲突:
Vue.component('my-checkbox',{
template:'<input type="checkbox">',
model:{
prop:'checked',
event:'checked'
},
props:{
checked:Boolean,
value:String
}
});
var app7 = new Vue({
el:'#app-7',
data:{
foo:''
}
// 没调通,看不明白
});
// 非父子组件的通信
//
// 有时候,非父子关系的两个组件之间也需要通信。
// 在简单的场景下,可以使用一个空的 Vue 实例作为事件总线:
var bus = new Vue();
//触发组件A中的事件
bus.$emit('id-selected',1);
//在组件B创建的钩子中监听事件
bus.$on('id-selected',function(id){
//...
})
// 使用插槽分发内容
</script>
</body>
</html>