props
的命名及使用HTML attribute 是大小写不敏感的,所以必须要注意 prop
的命名跟使用。
命名
可以使用 PascalCase (首字母大写) 或是 camelCase (驼峰命名法)的命名方法。
PascalCase : PostTitle
、 CartItem
、 TodoItem
,每个单字的开头都是大写。
camelCase : postTitle
、 cartItem
、 todoItem
,除了第一个单字以外,其余单字的开头都是大小。
使用
虽然 prop
的命名是使用 PascalCase (首字母大写) 或是 camelCase (驼峰命名法),但是在 HTML 中使用时必须使用 kebab-case (短横线分隔)且应该为小写。
像是 PostTitle
、 CartItem
、 TodoItem
等,在 HTML 中使用时就会变成 post-title
、 cart-item
、 todo-item
。
范例
<div id="vm">
<!--post-title 跟 post-content 都是props -->
<blog-post post-title="Blog1" post-content="I\'m content1"></blog-post>
</div>
<script>
Vue.component("blog-post", {
props: ["PostTitle", "postContent"],
template: `<div>
<h3>{{ PostTitle }}</h3>
<div>{{ postContent }}</div>
</div>`
});
</script>
props
值的方法<blog-post post-title="Blog1" post-content="I\'m content1" post-complete="true" post-total-num="500" post="{title:'Blog1'}"></blog-post>
只要是直接传递(静态传递)的都是字串,所以 prop
接收的值 Blog1
、I\'m content1
、true
、500
、{...}
等等都是字串,而非是数字、布林值、阵列、物件等型别。
要如何传递数字、布林值、阵列、物件等值给 prop
接收,这时候就要借助 Vue 的指令 v-bind
。
<blog-post post-title="动态传递" post-content="I\'m content1" v-bind:post-complete="true" v-bind:post-total-num="500" v-bind:post="{title:'动态传递'}"></blog-post>
透过 v-bind
(可用缩写 :
)来让 Vue 知道後面的值的型别不是字串,而是数字、布林值、阵列或物件等。
也可以通过给予变数来获得数字、布林值、阵列或物件等型别。
<blog-post :post-title="postTitle" :post-content="postContent" :post-complete="postComplete" :post-total-num="postTotalNum" :post="post"></blog-post>
<script>
const vm = new Vue({
el: "#vm",
data: {
postTitle: "动态传递",
postContent: "I'm content",
postComplete: true,
postTotalNum: 500,
post: { title: "动态传递" }
}
});
</script>
props
是为了接收从父模组传递进来的资料,而这些资料是单向绑定的,也就是说父模组资料的更新会影响子模组里的 prop
,但子模组里的 prop
值的改变并不能影响父模组。
测试:
<prop-change :counter=counter></prop-change>
<br/>
<span>外 {{counter}}</span>
<button type="button" @click="changeOuterCounter">改变外面数字</button>
<script>
Vue.component("prop-change", {
props: ["counter"],
template: `<div>
<span>component内的 {{counter}}</span>
<button type="button" @click="changeInnerCounter">改变component数字</button>
</div>`,
methods: {
changeInnerCounter() {
this.counter += 2;
}
}
});
const vm = new Vue({
el: "#vm",
data: {
counter: 1
},
methods: {
changeOuterCounter() {
this.counter += 1;
}
}
});
</script>
从上面的测试可以得知:
外面(父模组)的资料 counter
改变会影响子模组 prop
的 counter
值的改变。
子模组 prop
的 counter
值的改变仅影响内部 counter
值。
不论子模组 prop
的 counter
值是否有变动,只要父模组资料 counter
改变时,子模组 prop
的 counter
值一定会连动。
props
的值,使用以下几种方法:在 data
内创建一个值
赋予 data
跟 prop
初始值相同的值,且之後也是针对该 data
内的值做操作,并且不会再受到该 prop
的影响了。
Vue.component("one-way-data", {
props: ["counter"],
template: `<div>
<span>component内的 {{newCounter}}</span>
<button type="button" @click="changeNewCounter">改变component数字</button>
</div>`,
data () {
return {
newCounter: this.counter
}
},
methods:{
changeNewCounter(){
this.newCounter+=10
}
}
});
使用 computed
来做 prop
值的转换
透过 computed
取得 prop
值做复杂运算後的结果。
Vue.component("one-way-computed", {
props: ["counter"],
template: `<div>
<span>component内的 {{newCounter}}</span>
<button type="button">改变component数字</button>
</div>`,
computed: {
newCounter(){
return this.counter+100
}
}
});
有关於物件或阵列的传递,在子模组中更改值是会影响父模组的喔!因为物件或阵列的传递是透过传址(by reference)的方式,所以资料不管是在父模组还是子模组修改都会互相影响。
可以透过在 data
中创建 JSON.parse(JSON.stringify(物件))
的值,来使得内层跟外层的物件位址不同,而不会相互影响,但必须要注意到,使用 JSON.parse(JSON.stringify(物件))
所取得的 prop
值(物件),仅会取得初始的prop
值,之後不管 prop
值怎麽改变都不会再影响在 data
中新创建的值。
{
props:['post'],
data(){
return {
samePost:this.post,//跟props的post会相互影响
differentPost: JSON.parse(JSON.stringify(this.post))//取得props的post的初始值後,就跟props的post的位址不同而不会相互影响
}
}
}
props
的进阶使用之前看到的 props
都是阵列型别,阵列型态传递进来的 props
值不会有任何限制,传甚麽进来就接收甚麽。
props: ['PostTitle', 'postContent', 'postAuthor']
物件型别的 props
可以帮助我们指定每个 prop 的型别(type
)、默认值(default
)、是否必填(required
)或验证是否成功(validator
)。
范例
props: {
validationCounter: {
type: Number,
default: 2,
required: false,
validator(value) {
return value >= 0;
}
},
post: {
type: [Object,Array],//多种型别的可能
required: true
}
}
prop
的物件属性提醒:
type
: 可以是 String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、自定义建构函式或上述类型组成的阵列(该 prop
值可以拥有多种可能性)。
props:{
validationCounter:{
type: Number
}
}
validationCounter
传进来的值一定要是数字。
default
: 如果该 prop
没有任何值被传递进来,就会使用 default
的值作为预设值。
props:{
validationCounter:{
default: 2
}
}
validationCounter
如果没有任何值被传进来,那麽 validationCounter
的值就会是预设值数字 2
。
required
: 是否为必填项,如果是 true
则该 prop
没有任何值被传递进来的话,就会出现错误提示。
props:{
validationCounter:{
required: true
}
}
没有传值进去的话,会出现如下方的错误提示。
validator
: 自定义的验证函式,会将该 prop
的值当成唯一的引数带入验证该函式进行验证。
props:{
validationCounter:{
validator(value) {
console.log(this)//window
return value >= 2;
}
}
}
会将 validationCounter
的值作为引数带入 validator
参数进行验证。
验证结果失败的话,会出现如下方的提示。
以上所使用的验证方法,会是在该模组被创建以前就先进行验证,所以不能再 default
或 validator
中使用模组的 data
或 computed
等属性(因为模组还没被创建,所以会找不到 data
或 computed
等属性)。
有关 Vue 实例的创建可以参考DAY02 | 跟 Vue.js 认识的30天 - Vue 实体的生命周期(Lifecycle Hooks)及模板语法(Template Syntax)。
更多 props
的验证可以参考Vue.js - props。
没被模组定义的 Attribute,指的是在模组标签上出现的 HTML Attribute ,却没在模组内的 props
注册,出现这样的 HTML Attribute 的话,会出现以下几种可能性:
被添加到模板的根元素
<non-prop-attribute data-ride="carousel"></non-prop-attribute>
<script>
Vue.component("non-prop-attribute", {
template: `<div id="carouselExampleSlidesOnly" class="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img src="https://images.unsplash.com/photo-1593642531955-b62e17bdaa9c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" class="d-block w-25">
</div>
</div>
</div>`
});
</script>
模组标签的 data-ride="carousel"
会被加入到根元素 <div id="carouselExampleSlidesOnly" class="carousel">
里,在开发者工具可发现 DOM 会变成 <div id="carouselExampleSlidesOnly" class="carousel" data-ride="carousel">
。
class
或 style
与根元素的 class
或 style
相结合
<non-prop-attribute data-ride="carousel" class="slide"></non-prop-attribute>
<script>
Vue.component("non-prop-attribute", {
template: `<div id="carouselExampleSlidesOnly" class="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img src="https://images.unsplash.com/photo-1593642531955-b62e17bdaa9c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" class="d-block w-25">
</div>
</div>
</div>`
});
</script>
模组标签的 class="slide"
会与 <div id="carouselExampleSlidesOnly" class="carousel">
中的 class="carousel"
结合,变成 <div id="carouselExampleSlidesOnly" class="carousel slide">
。
取代根元素原有的 Attribute
<non-prop-attribute-input type="date"></non-prop-attribute-input>
<script>
Vue.component("non-prop-attribute-input", {
template: `<input type="text" class="form-control w-25" placeholder="我是input">`
});
</script>
模组标签的 type="date"
会取代掉根元素中相同的属性,所以 <input type="text" class="form-control w-25" placeholder="我是input">
中的 type="text"
会被 type="date"
取代,变成 <input type="date" class="form-control w-25" placeholder="我是input">
。
inheritAttrs: false
取消根元素继承未被定义的 prop<prop-inheritattrs label="我是label" placeholder="我是inheritAttrs後的input" required></prop-inheritattrs>
<script>
Vue.component("prop-inheritattrs", {
inheritAttrs: false,
props: ["label"],
template: `<label>
{{ label }}
<input type="text" class="form-control">
</label>`
});
</script>
被定义过的 prop label
不会受到任何影响,但是会发现未被定义的 HTML Attribute placeholder
跟 required
已经不会添加到根元素了。
加入 inheritAttrs: false
後:
未被定义的 HTML Attribute 除了不会被添加到根元素之外,也不会拥有取代的功能了,如上方(取代根元素原有的 Attribute)的例子, type="text"
不会被模组标签上的 type="date"
所取代。
class
跟 style
不会受到影响,仍会被合并到根元素的 class
跟 style
上。
inheritAttrs: false
跟 Vue 属性 $attrs
,改变未被定义的 HTML Attribute的添加位置<prop-inheritattrs label="我是label" placeholder="我是inheritAttrs後的input" required class="custom-control-label"></prop-inheritattrs>
<script>
Vue.component("prop-inheritattrs", {
inheritAttrs: false,
props: ["label"],
template: `<label>
{{ label }}
<input type="text" v-bind="$attrs" class="form-control">
</label>`
});
</script>
加入 inheritAttrs: false
後, placeholder="我是inheritAttrs後的input" required
的确不会被添加到根元素了,但透过 Vue 指令 v-bind
跟属性 $attrs
就可以将这些 Attribute 加入到 template
中的其他位置,如上方的 <input>
。但模组标签中的 class
跟 style
还是会合并或添加到根元素的 class
跟 style
中。
Demo:[DAY12]跟 Vue.js 认识的30天 - Vue 模组资料传递(props
)
参考资料:
>>: 如果要架设一个HTTPs File Server, 推荐用什麽软件呢?
在进行串接前,首先需要有定义串接的规格,例如:串接的协定 (HTTP、或走 FTP 档案交换等等)、...
如今,“秘密”(secret)是认证的基础。我们通常使用密码(您知道的东西)、令牌中的加密密钥(您拥...
突然发现日历事件被误删?我们曾丢失/误删过 iPhone 的行事历档案。那麽我们如何才能从 iPho...
1.前言 今天就不废话拉,直接进入主题(大家应该都去报复性出游了吧)。 2.html 其实HTML目...
在昨天的铁人贴文中制作了交易建立的画面,之前也有提到,透过批次,会於日档批次中,定期抓取历史缴费纪录...