Vue學習第七章-一篇搞定組件化開發

組件化開發

組件化開發是vue的一大特色,也是一個很重要的編程思想。
那組件化是什麼意思呢?
所謂組件化,就是把頁面拆分成多個組件,每個組件依賴的 CSS、JS、模板、圖片等資源放在一起開發和維護。 以函數作比喻,每個函數都有自己的功能,一個項目中可以多次調用相同的函數或者在別的類中可以導入然後使用此函數。那麼在組件化中,每個組件就相當於一個函數(不知道這樣比喻對不對,這是我的個人理解)。
在這裏插入圖片描述
那組件化有啥用?比如你在之前開發了一個項目,現在需要用到之前項目的一些東西,那麼使用之前寫好的組件進行復用就可以啦。
溫馨提示:這章有點長建議多次食用。。。。。


1.組件的基本使用

使用組件的三步驟 (組件構造要放在構造vue對象前面)
1.創建組件構造器對象
const cpnC = Vue.extend({template:'模板內容'}) 可以把 ’ ’ 換成 ` `(裏面可換行)
2.註冊組件
Vue.component('組件名',組件構造器對象) 使用此方法註冊的是全局組件,任何一個vue對象都可以使用
若要創建局部組件,需在vue實例裏面定義components屬性的template裏註冊,後面會提到。
3.使用組件
<組件名></組件名> 或 <組件名/> 如果不用添加什麼內容可以用單標籤
tips:使用多個組件時或元素時需要使用div或者p標籤包裹
組件的語法糖
Vue.component(‘組件名’,{template:‘模板內容’})

<div id="app">
  <!-- 使用全局組件 -->
  <cpn_quanju></cpn_quanju>
  <!-- 如果不用添加什麼內容可以用單標籤 -->
  <!-- 使用局部組件,因爲在app註冊的只能在app裏面用 -->
  <cpn_jubu/>
</div>

<div id="vm">
  <h2>我是第2個div,我不可以使用在app註冊的組件</h2>
  <cpn_quanju></cpn_quanju>
</div>

<script src="..\vue.js"></script>
<script>

  //  創建組件構造器對象
  const cpnC_quanju = Vue.extend({
    template:`
      <div><h2>我是全局組件</h2></div>
    `
    })
    
  //  註冊全局組件 
  Vue.component('cpn_quanju',cpnC_quanju)

  const cpnC_jubu = Vue.extend({
    template:`
      <div><h2>我是局部組件</h2></div>
    `
  })
  
  const app = new Vue({
  	el: '#app',
  	components:{
      // 註冊局部組件
      // 組件名:構造器對象
  	  cpn_jubu:cpnC_jubu
  	}
  })
  
  new Vue({
  	el: '#vm'
  })
</script>
</body>

看到這裏應該有人會想,可不可以在組件之間添加自己想添加的內容呢,比如<cpn_quanju><h1>啊杭是帥B</h1></cpn_quanju>,你試了你會發現,添加的內容好像沒有顯示在頁面中,並不是因爲啊杭不帥,而是因爲這要用到後面的知識,在最後組件化高級開發那裏會講插槽,那裏會解決你的困惑。

2.父子組件

組件之間也有爸爸跟兒子的關係?那誰是爸爸誰是兒子呢?
簡單來說:誰含住了誰,誰就是爸爸 。並且兒子構造器需定義在爸爸構造器的前面
在這裏插入圖片描述
Talk is cheap,Show your the code. --Linus

<div id="app">
  <cpn_fathor></cpn_fathor>
</div>

<script src="..\vue.js"></script>
<script>
  const cpnC_son = Vue.extend({
    template:`
      <div><h2>我是兒子組件</h2></div>
    `
    })

  const cpnC_father = Vue.extend({
    template:`
      <div>
        <h2>我是爸爸組件</h2>
        <cpnSon></cpnSon>
      </div>
    `,
    components:{
      cpnSon:cpnC_son
    }
  })

  const app = new Vue({
  	el: '#app',
  	components:{
  	  // 組件名:構造器對象
  	  cpn_fathor:cpnC_father
  	}
  })

</script>
</body>

3.組件模板抽離的寫法

看了前面兩節的代碼是不是覺得組件的代碼結構看上去有點複雜,一大坨代碼在那,其實我們可以把template那部分的代碼抽離出來
方法有兩種:
1.使用script標籤 type一定要寫text/x-template
2.使用template標籤。常用

<body>

<div id="app">
  <cpn></cpn>
</div>
<!-- 1.使用script標籤 type一定要寫text/x-template -->
<script type="text/x-template" id="isme1">
  <div><h2>是我1</h2></div>
</script>

<!-- 2.使用template標籤 常用 -->
<template id="isme2">
  <div><h2>是我2</h2></div>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn:{
        template:'#isme2'
      }
  	}
  })
</script>
</body>

4.組件的深入

接下來讓我們一起深入組件吧。
在這裏插入圖片描述組件是一個特殊的vue實例,但他不能引用vue實例的屬性及方法,所以呢他有自己的data(但必須是一個函數),methods等。

<body>
<div id="app">
  <cpn></cpn>
  <cpn></cpn>
</div>
<template id="isme2">
  <div id="add">
        <h2>當前計數:{{counter}}</h2>
        <button @click="aaa">+</button>
        <button @click="sub">-</button>
    </div>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn:{
  	    template:'#isme2',
  	    data(){  // 必須是一個函數
  	      return { // 返回一個對象
  	        counter:0
  	      }
  	    },
  	    methods:{
  	      aaa(){this.counter++},
          sub(){this.counter--}
  	    }
  	  }
  	}
  })
</script>
</body>

爲什麼組件的data要是一個函數呢?其實是爲了避免同個組件被調用多次時造成數據的同步修改。 上面這個例子調用了兩次組件,如果data不是一個函數,那麼這兩個組件會調用同一個對象(因爲函數每次執行的時候都地址都會改變),當一個組件修改counter時另一個組件的counter值也會隨着改變。
在這裏插入圖片描述
tips:return返回的對象不能是一個先前創建好的對象,否則會變成每個cpn都去引用此對象而不是每個組件都去創建一個對象了
在這裏插入圖片描述既然組件可以複用那就要考慮到數據會被共享的問題,vue在這方面做得很周到。
那組件深入就到這裏啦。

5.父子之間的通信

作爲父子,那麼兩父子之間有通信交流那是正常不過了。
在這裏插入圖片描述
父組件向子組件傳值:
父組件傳遞參數::定義的屬性名="要傳遞的值"
子組件接收參數的方式:

  1. props:[‘父組件中定義的屬性名’] 例: props:['cmessage']
  2. props:{ 父組件中定義的屬性名:指定類型} 例:props:{ cmessage:String}
  3. 父組件中定義的屬性名{
    type: 指定類型,
    default: 指定默認值,
    required: true|false 指定此屬性是否必須傳入
    }
    例cmessage:{ type:Array, default:[], required:true }
<body>
<div id="app">
 <!-- 父親傳遞數據 -->
  <cpn :cmessage="message"></cpn>
  <!-- 如果不動態綁定,只會當成字符串傳過去 -->
  <cpn cmessage="message"></cpn>
</div>

<template id="isme">
  <div>
    <h2>{{cmessage}}</h2>
  </div>
</template>

<script src="..\vue.js"></script>
<script>
  const cpn = {
  	template:'#isme',
    props:['cmessage'] // 接收數據
  }

  // vue實例可看成父組件
  const app = new Vue({
  	el: '#app',
  	data:{
  	  message:['hang']
  	},
  	components:{
  	  cpn
  	}
  })
</script>
</body>

子組件向父組件傳值:
子組件:

  1. methods:{this.$emit(‘自定義事件名稱’, 數據)}
  2. 子組件標籤上綁定@自定義事件名稱=‘回調函數’

父組件:methods: { 回調函數() { //邏輯處理 } }

<body>
<div id="app">
  <!-- 因爲事件對象是自定義的所以就不會默認傳event -->
  <cpn @father_click="click"></cpn>
</div>

<template id="isme">
  <div>
    <button v-for="book in books" @click="Son_click(book)">
      {{book}}
    </button>
  </div>
</template>

<script src="..\vue.js"></script>
<script>
  const cpn = {
  	template:'#isme',
  	data(){
  	  return {
  	    books:['C','JAVA','PYTHON']
  	  }
  	},
  	methods:{
  	  Son_click(book){
  	    console.log('子組件----點擊了',book)
  	    this.$emit('father_click',book)
  	  }
  	}
  }
  
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn
  	},
  	methods:{
  	  click(item){
  	    console.log('我是爸爸,我的兒子點擊了'+item);
  	  }
  	}
  })
</script>
</body>

6.父子組件互相訪問內容(瞭解)

以下介紹的方法儘量避免使用,因爲父子關係一旦搞複雜了就難以debug了。就好比如父子倆老是侵入對方的隱私,這不太好,每個人有權利保留點祕密。
在這裏插入圖片描述
父組件訪問子組件的東西

  1. this.$children[index] 不常用
  2. this.$refs.子組件標籤的ref名字
<body>
<div id="app">
  <div>
    <cpn ref="son_ref"></cpn>
    <button @click="btnclick">按鈕</button>
  </div>
</div>

<template id="isme">
  <div>我是子組件</div>
</template>

<script src="..\vue.js"></script>
<script>
  const cpn = {
  	template:'#isme',
  	data(){
  	  return {
  	    name:'兒子的名字'
  	  }
  	},
    methods:{
      show(){
        console.log('兒子是我')
      }
    }
  }

  const app = new Vue({
  	el: '#app',
  	methods:{
  	  btnclick(){
        //	 1.$children  
        //   	 console.log(this.$children);
        //   	 console.log(this.$children[0].show());
        //   	 console.log(this.$children[0].name);

        // 2.$refs 需在組件上定義ref='' 不然默認空對象
        console.log(this.$refs)
        console.log(this.$refs.son_ref)
        console.log(this.$refs.son_ref.show())
        console.log(this.$refs.son_ref.name)
  	  }
  	},
  	components:{
  	  cpn
  	}
  })
</script>
</body>

子組件訪問父組件的東西
訪問父組件:this.$parent.屬性/方法
訪問根:this.$root.屬性/方法

<body>
<div id="app">
  <div>
    <cpn></cpn>
  </div>
</div>

<template id="isme">
  <div>
    我是子組件
    <button @click="btnclick">按鈕</button>
  </div>

</template>

<script src="..\vue.js"></script>
<script>
  const cpn = {
  	template:'#isme',
  	methods:{
  	  btnclick(){
  	    console.log(this.$parent.show())
  	    console.log(this.$root.show())
  	  }
  	}
  }
  
  const app = new Vue({
  	el: '#app',
  	methods:{
  	  show(){
  	    console.log('我是爸爸');
  	  }
  	},
  	components:{
  	  cpn
  	}
  })
</script>
</body>

組件化高級開發-插槽

在這裏插入圖片描述
就快完了。。。。再堅持一會。。。
前面留有困惑的小夥伴,現在終於可以釋然了!
首先了解一下什麼是插槽。插槽就是一些方便我們需要添加新功能新東西的時候方便我們添加的東西。相比如usb插槽,充電插槽等,想用的時候在就能方便的時候。

1.插槽的基本使用

插槽的作用就是在引用組件的時候可以在組件標籤內部添加自己想要的東西。
語法:<v-slot>不添加東西時,slot的默認值</v-slot>

<body>
<div id="app">
  <cpn></cpn>
  <cpn><button>我是擴展的按鈕</button></cpn>
  <cpn><p>我是擴展的p</p></cpn>
</div>
<template id="isme2">
  <div id="add">
    <h2>我是基礎的</h2>
    <slot></slot>
  </div>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn:{
  	    template:'#isme2',
  	  }
  	}
  })
</script>
</body>

2.具名插槽的使用

顧名思義,具名插槽就是有名字的插槽,當需要向組件標籤添加多個功能的時候,可以指定每個功能添加到哪一個具體的插槽上。
語法:<v-slot name=''>默認值</v-slot>
使用方式(vue2.6的版本及以上):

<template v-slot:插槽的名字>要添加的內容</template>
語法糖:<template #插槽的名字>要添加的內容</template>
2.6以下的版本可自行百度

<div id="app">
  <cpn>
    <template><h2>沒有名字的我修改的是默認值</h2></template>
    <!-- <template v-slot:middle><h2>我來修改中間的啦</h2></template> -->
    <template #middle>
          <h2>我來修改中間的啦</h2>
          <h2>我是來幫忙修改中間的</h2>
    </template>
    <template #left>
      <h2>我來修改左邊的啦</h2>
    </template>
  </cpn>
</div>

<template id="isme2">
  <div id="add">
    <slot>我是個默認值</slot>
    <slot name="left">左邊</slot>
    <slot name="middle">中間</slot>
    <slot name="right">右邊</slot>
  </div>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn:{
  	    template:'#isme2',
  	  }
  	}
  })
</script>
</body>

3.編譯作用域

插槽是有作用域的,即在插槽中能使用父容器裏數據,但是不能使用子組件裏的(數據)。

<div id="app">
	<!-- 這裏的isShow屬於vue的 -->
  <cpn v-show="isShow"></cpn>
</div>

<template id="me">
	<h2>我是第一行,第二行不會顯示</h2>
	<!-- 這裏的isShow屬於組件的 -->
  <h2 v-show="isShow">我是第二行,第一行纔會顯示</h2>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	data:{
      // 這裏的isShow只會在vue實例的模板中才有效
  		 isShow:true
  	},
  	components:{
  	    cpn:{
  	      template:'#isme',
  	      data(){
  	        return{
  	          // 這裏的isShow只會在id爲me的組件模板中才有效
  	          isShow:false
  	        }
  	      }
  	    }
  	}
  })
</script>
</body>

以上是定義slot時使用組件的數據。那麼現在有一個需求,就是使用slot時需要用到組件的屬性時呢?

  1. 需要在插槽定義的時候把數據作爲實參傳出去,跟父傳子的方法差不多。
  2. 在使用插槽的時候創建一個插槽對象(我是這麼理解的)
<div id="app">
  <cpn></cpn>
  <h2>以下部分是作用域插槽的展示</h2>
  <cpn>
    <!--  相當於創建一個slot對象,然後用這個對象去訪問傳遞過來的參數 -->
    <template v-slot="myslot">
      <i>我是在使用slot時調用組件的屬性</i>
      <i>{{myslot.cmessage}}</i>
    </template>
  </cpn>
</div>

<template id="isme">
  <div>
    <h2>我是組件部分</h2>
    <!--  相當於把cpn的message作爲實參傳遞給slot對象的data形參
          並對其進行動態綁定  -->
    <slot :cmessage="message">
      我是在定義時使用message(cpn裏定義的數據)
      <br>
      {{message}}
    </slot>
  </div>
</template>

<script src="..\vue.js"></script>
<script>
  const app = new Vue({
  	el: '#app',
  	components:{
  	  cpn:{
        template:'#isme',
        data(){
          return {
            message:'由組件傳給slot的數據'
          }
        }
      }
  	}
  })
</script>
</body>

好啦,本章結束啦!!
希望對你們有幫助!
同步學習代碼可以在我的guthub項目中查看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章