摘自:https://div.io/topic/1880,並加入一點本身實際練習的demo,記錄下學習歷程,並方便後續翻閱。html
關於is的妙用,參考另外一篇本身寫的demo文章:https://juejin.im/editor/drafts/5c863924e51d4561a0778dd5vue
對於一些有規律的 dom 結構,咱們能夠經過遞歸方式來生成這個結構,在 vue 的模板中遞歸生成dom。
git
官方介紹:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8github
demo詳見:https://github.com/elainema/elaine/tree/master/VUE/vue-components/src/views/component-communication和https://github.com/elainema/elaine/tree/master/VUE/vue-components/src/views/recursive-componentsvuex
首先爲了使用遞歸組件須要準備一份數據,由於此次是生成一個菜單,因此準備一個菜單書數據,新建一個testdata.js 文件代碼以下:api
var demoData = [ { 'id': '1', 'menuName': '基礎管理', 'menuCode': '10', 'children': [ { 'menuName': '用戶管理', 'menuCode': '11' }, { 'menuName': '角色管理', 'menuCode': '12', 'children': [ { 'menuName': '管理員', 'menuCode': '121' }, { 'menuName': 'CEO', 'menuCode': '122' } ] }, { 'menuName': '權限管理', 'menuCode': '13' } ] }, { 'id': '2', 'menuName': '商品管理', 'menuCode': '' } ]; export default demoData;複製代碼
如今創建樹形組件,首先新建一個文件treeMenu,代碼以下瀏覽器
<template><div> <li> <span @click="toggle"> <i v-if="hasChild" class="icon" v-bind:class="[open ? 'folder-open': 'folder' ]"></i> <i v-if="!hasChild" class="icon file-text"></i> {{model.menuName}} </span> <ul v-show="open" v-if="hasChild"> <tree-menu v-for="(item,index) in model.children" v-bind:model="item" v-bind:key="index"></tree-menu> </ul> </li></div></template><script> export default { name: "TreeMenu", inheritAttrs:false, props: ['model'], data(){ return { open:false } }, computed:{ hasChild(){ return this.model.children && this.model.children.length } }, methods:{ toggle(){ if(this.hasChild){ this.open = !this.open } } } }</script><style> ul { list-style: none; margin: 10px 0; } li { padding: 3px 0; } li > span { cursor: pointer; font-size: 14px; line-height: 20px; } i.icon { display: inline-block; width: 20px; height: 20px; margin-right: 5px; background-repeat: no-repeat; vertical-align: middle; } .icon.folder { background-image: url(/src/assets/folder.png); } .icon.folder-open { background-image: url(/src/assets/folder-open.png); } .icon.file-text { background-image: url(/src/assets/file-text.png); } .tree-menu li { line-height: 1.5; }</style>複製代碼
上述代碼中咱們須要注意,這個組件必須含有 name 這個屬性,由於沒有 name 這個屬性會形成控件自身不能調用自身,自身調用的時候最好有綁定 key ,由於這個 key 是惟一的標識,對於 vue 更新控件比較好.除非控件很是簡單就不用 key.bash
咱們知道,v-model
是在表單類元素上進行雙向綁定時使用的,好比:
iview
<template>
<input type="text" v-model="data">
{{ data }}
</template>
<script>
export default {
data () {
return {
data: ''
}
}
}
</script>複製代碼
這時
data
就是雙向綁定的,輸入的內容會實時顯示在頁面上。在 Vue 1.x 中,自定義組件可使用 props 的
.sync
雙向綁定,好比:
<my-component :data.sync="data"></my-component>複製代碼
在 Vue 2.x 中,能夠直接在自定義組件上使用 v-model
了,好比:
dom
<my-component v-model="data"></my-component>複製代碼
在組件my-component
中,經過this.$emit('input')
就能夠改變data的值了。
當咱們在書寫 vue
組件的時候,常常會用到數據傳遞;將父組件的數據傳遞給子組件,有時候也須要經過子組件去事件去觸發父組件的事件;總結一下比較經常使用的三種解決辦法:
經過 props
的方式向子組件傳遞(父子組件)
vuex
進行狀態管理(父子組件和非父子組件)
父組件經過this.$refs[子組件ref] 可直接調用子組件的方法
// 父組件
<template> <child ref="child"></child></template><script>export default { name:'parent', mounted() { this.refs.child.childSubmit() }}</script>
// 子組件
<template> <div> 這是一個子組件 </div></template><script>export default { name:'parent', methods:{ childSubmit() { alert("trigger") } }}</script>複製代碼
inheritAttrs
+
$attrs
+
$listeners
基本是大部分的公司或者項目都是用前面兩種,我也不例外......初次看到第四種寫法時甚至有些驚訝,原來還有這種寫法,而後去API看才發現其實很早就有,只是沒有仔細看文檔......故整理一下,若是有需求能夠嘗試用一用,官方api地址(英文看到很懵,轉中文文檔先看。。。):https://cn.vuejs.org/v2/api/index.html#inheritAttrs
vue中一個比較使人煩惱的事情是屬性只能從父組件傳遞給子組件。這也就意味着當你想向嵌套層級比較深組件數據傳遞,只能由父組件傳遞給子組件,子組件再傳遞給孫子組件...像下面這樣:
<parent-component :passdown="passdown">
<child-component :passdown="passdown">
<grand-child-component :passdown="passdown">
....
就這樣一層一層的往下傳遞passdown這個變量,最後纔會用{{passdown}}。複製代碼
說實話,官方的解釋開始看了幾遍也是雲裏霧裏的,忽略個人理解力。。。
父組件ComponentCommunication.vue
<template> <div class=""> <MyTest :title="title" :massgae="massgae"></MyTest> </div></template><script>import MyTest from './MyTest.vue'export default { name:'componentCommunication', data () { return { title:'定義在父組件的title', massgae:'message111' } }, components:{ MyTest }, created:function(){ }}</script>複製代碼
子組件MyTest.vue
<template><section> <div>這裏是標題,父組件經過prop傳遞給子組件的:{{title}}</div> <div> 注意這裏:this.$attrs{{$attrs}}</div></section></template><script>export default { props:['title'], data(){ return{ } }, created:function(){ console.log(this.$attrs)//注意這裏 }}</script>複製代碼
上邊的代碼,父組件傳遞了兩個參數給子組件title和message,在子組件裏只註冊並使用了title,massgae並無註冊和使用,那麼下瀏覽器渲染出來是什麼樣呢?以下圖:
在Vue2.4.0,能夠在組件定義中添加inheritAttrs:false,組件將不會把未被註冊的props呈現爲普通的HTML屬性。
關於Props
的一個使人討厭的事情是,他們只能從父母傳給孩子。 這意味着若是您有深刻的嵌套組件,您須要傳遞數據,則必須將數據做爲Props
綁定到每一箇中間組件中:
Props
來講還好,可是在一個真正的項目中,你可能會有許多更多的東西要傳下去。
inheritAttrs
的組件的標誌,其次是一個實例屬性
$attrs
。 在組件裏咱們能夠經過其$attrs能夠獲取到父組件傳遞給子組件,但子組件沒有使用和註冊的數據。
咱們在子組件裏設置 inheritAttrs: false // 默認是true, 渲染效果以下,能夠看到父組件傳遞給組件的參數,但子組件未註冊和使用的,不會做爲普通html元素被渲染
個人理解就是:子組件能夠觸發父組件的事件(不須要用什麼那些麻煩的vuex或者一個空的 Vue
實例做爲事件總線,或者又是什麼vm.$on
)