在 Vue.js 中,每个组件实例都有自己的隔离作用域,这意味着如果父组件有子组件——子组件有自己的隔离作用域,父组件也有自己的隔离作用域。
对于任何大中型应用程序,遵循最佳实践惯例可以防止在开发阶段和维护之后出现很多麻烦。要遵循的其中一件事情是避免直接从子组件引用/改变父数据。那么我们如何从子组件中引用父数据呢?
子组件中需要的任何父数据都应该props从父组件传递给子组件。
用例:假设我们有一个包含两个表users和addresses以下字段的用户数据库:
users表
名称 | 电话 | 电子邮件 |
---|---|---|
John Mclane | (1) 234 5678 9012 | john@dirhard.com |
占士邦 | (44) 777 0007 0077 | bond@mi6.com |
addresses 桌子
堵塞 | 街道 | 城市 |
---|---|---|
中富塔 | 百老汇 | New York |
米6之家 | 白金汉路 | 伦敦 |
并且我们希望有三个组件来在我们的应用程序中的任何位置显示相应的用户信息
用户组件.js
export default{ template:`<div class="user-component"> <label for="name" class="form-control">Name: </label> <input class="form-control input-sm" name="name" v-model="name"> <contact-details :phone="phone" :email="email"></contact-details> </div>`, data(){ return{ name:'', phone:'', email:'' } }, }
联系人详细信息.js
import Address from './address'; export default{ template:`<div class="contact-details-component> <h4>Contact Details:</h4> <label for="phone" class="form-control">Phone: </label> <input class="form-control input-sm" name="phone" v-model="phone"> <label for="email" class="form-control">Email: </label> <input class="form-control input-sm" name="email" v-model="email"> <h4>Address:</h4> <address :address-type="addressType"></address> //请参阅下面的骆驼案例与烤肉串案例的解释 </div>`, props:['phone', 'email'], data:(){ return:{ addressType:'Office' } }, components:{Address} }
地址.js
export default{ template:`<div class="address-component"> <h6>{{addressType}}</h6> <label for="block" class="form-control">Block: </label> <input class="form-control input-sm" name="block" v-model="block"> <label for="street" class="form-control">Street: </label> <input class="form-control input-sm" name="street" v-model="street"> <label for="city" class="form-control">City: </label> <input class="form-control input-sm" name="city" v-model="city"> </div>`, props:{ addressType:{ required:true, type:String, default:'Office' }, data(){ return{ block:'', street:'', city:'' } } }
主文件
import Vue from 'vue'; Vue.component('user-component', require'./user-component'); Vue.component('contact-details', require'./contact-details'); new Vue({ el:'body' });
索引.html
... <body> <user-component></user-component> ... </body>
我们显示phone和email数据,这些数据的特性user-component在contact-details其中没有电话或电子邮件数据。
将数据作为道具传递
所以内user-component.js的模板特性,其中包括我们的<contact-details>组件,我们传递的电话和电子邮件,从数据<user-component>(父组件)到<contact-details>由(子组件),动态地将其绑定到道具-:phone="phone"和:email="email这一样v-bind:phone="phone"和v-bind:email="email"
道具 - 动态绑定
由于我们动态绑定 props ,因此父组件 ie中电话或电子邮件的任何更改<user-component>都会立即反映在子组件 ie 中<contact-details>。
道具 - 作为文字
但是,如果我们将phone和email的值作为字符串文字值传递,phone="(44) 777 0007 0077" email="bond@mi6.com"那么它将不会反映父组件中发生的任何数据更改。
单向绑定
默认情况下,更改方向是从上到下,即父组件中动态绑定的 props 的任何更改都将传播到子组件,但子组件中 prop 值的任何更改都不会传播到父组件。
例如:如果从内部<contact-details>我们将电子邮件从 更改bond@mi6.com为jamesbond@mi6.com,则父数据即电话数据属性中<user-component>仍将包含值bond@mi6.com。
但是,如果我们在父组件(在我们的用例中)中将email 的值从 更改bond@mi6.com为jamesbond@mi6.co,那么子组件(<user-component>在我们的用例中)中的 email 值<contact-details>将jamesbond@mi6.com自动更改为- 父组件的更改会立即传播到子组件.
双向绑定
如果我们想要双向绑定,那么我们必须明确指定双向绑定 as:email.sync="email"而不是:email="email"。现在,如果我们更改子组件中 prop 的值,更改也会反映在父组件中。
在中型到大型应用程序中,从子状态更改父状态将非常难以检测和跟踪,尤其是在调试时 -小心。
Vue.js2.0中将没有任何 .sync 选项可用。props 的双向绑定在Vue.js2.0 中被弃用。
一次性绑定
也可以将显式一次性绑定定义为:email.once="email,它或多或少类似于传递文字,因为父属性值的任何后续更改都不会传播到子属性。
CAVEAT
当Object 或 Array作为 prop 传递时,它们总是通过 REFERENCE传递,这意味着无论显式定义的绑定类型:email.sync="email"或:email="email"或:email.once="email",如果电子邮件是父级中的对象或数组,则无论绑定类型如何,在子组件中的 prop 值也会影响父组件中的值。
作为数组的道具
在contact-details.js文件中,我们定义props:['phone', 'email']了一个数组,如果我们不想用 props 进行细粒度控制,这很好。
道具作为对象
如果我们想要对 props 进行更细粒度的控制,比如
如果我们想定义什么类型的值可以作为 prop
道具的默认值应该是什么
一个值是必须(必须)传递给 prop 还是可选的
然后我们需要使用对象符号来定义道具,就像我们在address.js.
如果我们正在创作可能被团队中的其他开发人员使用的可重用组件,那么将 props 定义为对象是一个很好的做法,以便使用该组件的任何人都清楚应该是什么类型的数据以及是否它是强制性的或可选的。
它也称为道具验证。该类型可以是以下默认构造函数中的任何一个:
细绳
数字
布尔值
大批
目的
功能
或自定义构造函数
取自http://vuejs.org/guide/components.html#Props 的一些 prop 验证示例
Vue.component('example', { props: { // 基本类型检查(`null` 表示接受任何类型) propA: Number, // 多种可能的类型(1.0.21+) propM: [String, Number], // 一个必需的字符串 propB: { type: String, required: true }, // 具有默认值的数字 propC: { type: Number, default: 100 }, // 对象/数组默认值应该从 // 工厂函数 propD: { type: Object, default: function () { return { msg: 'hello' } } }, //表明此道具需要双向绑定。将要 // 如果绑定类型不匹配,则发出警告。 propE: { twoWay: true }, // 自定义验证器功能 propF: { validator: function (value) { return value > 10 } }, // 强制功能(1.0.12 中的新功能) // 在组件上设置值之前强制转换值 propG: { coerce: function (val) { return val + '' // 将值转换为字符串 } }, propH: { coerce: function (val) { return JSON.parse(val) // 将值转换为 Object } } } });
骆驼壳 vs 烤肉串
HTML 属性不区分大小写,这意味着它不能区分addresstype和addressType,因此当使用驼峰命名法属性名称作为属性时,我们需要使用它们的 kebab-case(hyphen-delimited) 等价物:
addressType应该像address-type在 HTML 属性中那样编写。