Vuejs的component的數據進行了沙箱隔離,除js全局變量如Math, Date之類外無法訪問用戶自定義的變量,所以使用component寫組件或嵌套組件時明白變量的訪問非常重要
編譯作用域
在看componnent的使用之前,來看下component編譯作用域,明白作用域范圍才能順利寫出想要的組件
假設我們有一個組件child-component,在父組件中代碼如下:
<child-component> {{ message }} </child-component>
編譯時message的作用域應該是父組件還是子組件呢,答案是父組件
父組件模板的內容在父組件作用域內編譯;子組件模板的內容在子組件作用域內編譯
Vue.component('child-component', { // 有效,因為是在正確的作用域內 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
slot在component中有單slot,具名slot和作用域slot之分,先來看看最簡單的單slot應用
單個slot
直接上代碼,其中的name字段會在父組件中初始化並賦值
父組件 <div id="test"> <test-slot> <h3>{{name}}</h3> <p>Something bad happened.</p> </test-slot> </div>
組件 Vue.component("test-slot",{ // 插槽允許有默認內容 template: `<div> <strong>Error!</strong> <slot></slot> </div> `, data:function () { return { name:"perry" } } }); new Vue({ el:"#test" data:{name:"500 error"} }); 結果: <div> <strong>Error!</strong> <h3>500 error</h3> <p>Something bad happened.</p> </div>
具名slot
具名插槽比較有意思,在模板制定時非常好用,比如我們要寫一個模板包含頭尾和內容部分,希望在模板中定義好一部分公共的東西
具名slot通過name來管理多個slot的解析,其中沒有name的slot會被歸為default slot放到沒有name的節點下面,default slot會無視散落在不同地方的html的位置,都將放到default slot的
模板位置中來
Vue.component("slot-name",{ template: `<div> <header> <slot name="header"></slot> </header> <main> <slot ></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> ` }); <slot-name> <h3>開始</h3>
<p>Default slot內容1</p> <template slot="header"> <ul> <li>主頁</li> <li>分診</li> <li>護理</li> <li>病歷</li> </ul> </template> <template slot="footer"> <p>結尾</p> </template> </slot-name>
運行結果:
作用域slot
作用域插槽在解決需要動態生成字符串模板時非常有用,特別針對控件編寫者
例如實現一個簡單的datagrid控件,控件在頁面component中相當於子控件,使用者希望只關注表頭和每行數據業務上,直接上代碼
控件代碼 Vue.component("datagrid",{ props:{ data:null }, template:` <table> <thead> <slot name="headslot"></slot> </thead> <tbody> <tr v-for="item in data"> <slot name="bodyslot" :item="item">{{item.text}</slot> </tr> </tbody> </table> ` }); 在父組件中(頁面上)使用如下: <datagrid :data="todos"> <template slot="headslot"> <tr> <td>id</td> <td>text</td> <td>isTrue</td> </tr> </template> <template slot="bodyslot" slot-scope="{item}"> <td>{{item.id}}</td> <td>{{item.text}}</td> <td>{{item.isTrue}}</td> </template> </datagrid>
如上代碼,簡單的datagrid就實現了,在父組件中只需要在head中指定table的head具體內容,對應的body中tr的每個td的字段綁定,其它交給控件處理
其中數據源是datagrid中的data屬性,與slot通信是通過slot-scope來實現數據域傳遞,這點非常關鍵
控件中 :item="item" 與父組件slot-scope="{item}" 完成數據訪問的傳遞,其中slot-scope="{item}"語句也可以通過"slot-scope="slotProps"來實現數據傳遞,slotProps對像相當於當slot對象上
所有props屬性的根,通過slotProps對象都能訪問到
在js調用如下:
var vm = new Vue({ el:"#app", data:{ todos:[ {text:"A",id:1,isTrue:true}, {text:"B",id:2,isTrue:true}, {text:"C",id:3,isTrue:false}, {text:"D",id:4,isTrue:true}, ] } });
在data中的todos屬性已經與頁面的table形成了關聯,只要todos屬性發生變化,頁面的table會自動更新tbody中的數據行,這就是數據驅動的精髓