從零開始學 Web 之 Vue.js(六)Vue的組件


大家好,這里是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......

在這里我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。現在就讓我們一起進入 Web 前端學習的冒險之旅吧!

一、Vue組件

什么是組件: 組件的出現,就是為了拆分 Vue 實例的代碼量的,能夠讓我們以不同的組件,來划分不同的功能模塊,將來我們需要什么樣的功能,就可以去調用對應的組件即可;

組件化和模塊化的不同:

  • 模塊化: 是從代碼邏輯的角度進行划分的;方便代碼分層開發,保證每個功能模塊的職能單一;
  • 組件化: 是從UI界面的角度進行划分的;前端的組件化,方便UI組件的重用;

二、定義組件

1、定義全局組件

定義全局組件有三種方式:

1、使用 Vue.extend 配合 Vue.component 方法:

// 1.使用 Vue.extend 來創建全局的Vue組件
var login = Vue.extend({
  // 通過 template 屬性,指定了組件要展示的HTML結構
  template: '<h1>登錄</h1>'
});

// 2.使用 Vue.component('組件的名稱', 創建出來的組件模板對象) 
Vue.component('login', login);

// 3.使用組件
<div id="app">
	<!-- 如果要使用組件,直接,把組件的名稱,以 HTML 標簽的形式,引入到頁面中即可 -->
	<login></login>
</div>

注意:

使用 Vue.component 定義全局組件的時候,組件名稱使用了 駝峰命名(如myLogin),則在引用組件的時候,需要把 大寫的駝峰改為小寫的字母,同時在兩個單詞之前,使用 - 鏈接(<my-login></my-login>);如果不使用駝峰,則直接拿名稱來使用即可;

當然,上面兩步可以合成一個步驟完成:

Vue.component('login', Vue.extend({
  template: '<h1>登錄</h1>'
}));

2、直接使用 Vue.component 方法:

Vue.component('login', {
  template: '<div><h3>注冊</h3><span>123</span></div>'
});

注意:不論是哪種方式創建出來的組件,組件的 template 屬性指向的模板內容,必須有且只能有唯一的一個根元素,否則會報錯。

3、將模板字符串,定義到 template 標簽中:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <!-- 3. 使用組件 -->
    <mycom></mycom>
  </div>
  <!-- 2.在 被控制的 #box 外面,使用 template 元素,定義組件的HTML模板結構  -->
  <template id="tmp1">
    <!-- 還是需要遵從template 模板內容,必須有且只能有唯一的一個根元素 -->
    <div>
      <h3>登錄</h3>
      <p>p標簽</p>
    </div>
  </template>

  <script>
    // 1.定義組件
    Vue.component('mycom', {
      template: '#tmp1'
    });

    var vm = new Vue({
      el: "#box",
      data: {},
      methods: {}
    });
  </script>
</body>

</html>

注意:

1、template: '#tmp1' 是定義模板標簽的 id ,# 別忘寫了。

2、被控制的 #box 外面,使用 template 標簽;

3、 template 標簽里面,還是遵從只能有唯一的一個根元素的原則。

2、定義私有組件

定義私有組件,就是再VM實例中定義組件。

如下,box中可以使用,box2不可以使用。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <mycom></mycom>
  </div>

  <div id="box2">
    <mycom></mycom>
  </div>

  <template id="temp">
    <h3>自定義私有屬性</h3>
  </template>

  <script>
    var vm = new Vue({
      el: "#box",
      data: {},
      methods: {},
      // 定義私有組件
      components: {
        mycom: {
          template: '#temp'
        }
      }
    });
    var vm2 = new Vue({
      el: "#box2",
      data: {},
      methods: {}
    });
  </script>
</body>

</html>

3、組件的data和methods屬性

組件中也可以有自己的data和methods屬性,可以傳入template中使用。

特點:

  • data屬性為一個匿名函數,其返回值為一個對象。
  • data 函數返回值為一個對象(最好是新開辟的對象,否則如果多次引用組件,不是新開辟的對象給的話,對象是同一份,而我們需要每一個組件有自己的對象),對象中可以放入數據。
  • 組件中 的data和methods,使用方式,和實例中的 data 和methods使用方式完全一樣
<div id="box2">
  <login></login>
</div>

<template id="temp2">
  <div>
    <input type="button" value="按鈕" @click="myclick">
    <h3>自定義私有屬性</h3>
    <p> {{msg}} </p>
  </div>
</template>

<script>
  Vue.component('login', {
    template: '#temp2',
    data: function () {
      return {
        msg: '這是組件中的data'
      }
    },
    methods: {
      myclick() {
        console.log("點擊按鈕");
      }
  }
  });
</script>

三、組件切換

我們在登錄注冊一個網站的時候,經常看到兩個按鈕,一個登錄,一個注冊,如果你沒有賬號的話,需要先注冊才能登錄。我們在點擊登錄和注冊的時候,網頁會相應的切換,登錄頁面就是登陸組件,注冊頁面就是注冊組件,那么點擊登錄和注冊,如何實現組件的切換呢?

1、方式一

使用flag標識符結合v-ifv-else切換組件

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <!-- 給a注冊點擊事件,切換flag狀態 -->
    <a href="javascript:;" @click.prevent="flag=true">登錄</a>
    <a href="javascript:;" @click.prevent="flag=false">注冊</a>
    <!-- 使用v-if v-else切換組件 -->
    <login v-if="flag">
    </login>
    <register v-else="flag">
    </register>
  </div>

  <script>
    Vue.component('login', {
      template: '<h3>登錄組件</h3>'
    });
    Vue.component('register', {
      template: '<h3>注冊組件</h3>'
    });

    var vm = new Vue({
      el: "#box",
      data: {
        flag: true
      },
      methods: {}
    });
  </script>
</body>

</html>

缺陷:由於flag的值只有true和false,所以只能用於兩個組件間 的切換,當大於兩個組件的切換就不行了。

2、方式二

使用 component元素的:is屬性來切換不同的子組件

使用 <component :is="componentId"></component> 來指定要切換的組件。

componentId:為需要顯示的組件名稱,為一個字符串,可以使用變量指定。

componentId: 'login' // 默認顯示登錄組件。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <!-- 給a注冊點擊事件,切換flag狀態 -->
    <a href="javascript:;" @click.prevent="componentId='login'">登錄</a>
    <a href="javascript:;" @click.prevent="componentId='register'">注冊</a>
    <component :is="componentId"></component>
  </div>

  <script>
    Vue.component('login', {
      template: '<h3>登錄組件</h3>'
    });
    Vue.component('register', {
      template: '<h3>注冊組件</h3>'
    });

    var vm = new Vue({
      el: "#box",
      data: {
        componentId: 'login'   // 默認顯示登錄
      },
      methods: {}
    });
  </script>
</body>

</html>

為組件切換添加過渡:

很簡單,只需要用 transition 將 component 包裹起來即可。

<transition>
  <component :is="componentId"></component>
</transition>

示例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
  <link rel="stylesheet" href="./lib/animate.css">

  <style>
    .loginDiv {
      width: 200px;
      height: 200px;
      background-color: red;
    }

    .registerDiv {
      width: 200px;
      height: 200px;
      background-color: blue;
    }
  </style>
</head>

<body>
  <div id="box">
    <!-- 給a注冊點擊事件,切換flag狀態 -->
    <a href="javascript:;" @click.prevent="componentId='login'">登錄</a>
    <a href="javascript:;" @click.prevent="componentId='register'">注冊</a>
    <transition mode="out-in" enter-active-class="animated bounceInRight" leave-active-class="animated bounceOutRight">
      <component :is="componentId"></component>
    </transition>
  </div>

  <template id="login">
    <div class="loginDiv">
    </div>
  </template>

  <template id="register">
    <div class="registerDiv">
    </div>
  </template>

  <script>
    Vue.component('login', {
      template: '#login'
    });
    Vue.component('register', {
      template: '#register'
    });

    var vm = new Vue({
      el: "#box",
      data: {
        componentId: 'login'
      },
      methods: {}
    });
  </script>
</body>

</html>

mode="out-in":可以設置切換組件的模式為先退出再進入。

四、組件傳值

1、父組件向子組件傳值

我們先通過一個例子看看子組件可不可以直接訪問父組件的數據:

<body>
  <div id="box">
    <mycom></mycom>
  </div>

  <template id="temp">
    <h3>子組件 --- {{msg}}</h3>
  </template>

  <script>
    var vm = new Vue({
      el: "#box",
      data: {
        msg: '父組件的msg'
      },
      methods: {},
      components: {
        mycom: {
          template: '#temp'
        }
      }
    });
  </script>
</body>

由於 components 定義的是私有組件,我們直接在子組件中調用父組件的msg會報錯。

那么,怎么讓子組件使用父組件的數據呢?

父組件可以在引用子組件的時候, 通過 屬性綁定(v-bind:) 的形式, 把需要傳遞給子組件的數據,以屬性綁定的形式,傳遞到子組件內部,供子組件使用 。

<body>
  <div id="box">
    <mycom v-bind:parentmsg="msg"></mycom>
  </div>

  <template id="temp">
    <h3>子組件 --- 父組件:{{parentmsg}}</h3>
  </template>

  <script>
    var vm = new Vue({
      el: "#box",
      data: {
        msg: '父組件的msg'
      },
      methods: {},
      components: {
        mycom: {
          template: "#temp",
          // 對傳遞給子組件的數據進行聲明,子組件才能使用 
          props: ['parentmsg']
        }
      }
    });
  </script>
</body>

注意:父組件綁定的屬性名稱不能有大寫字母,否則不會顯示,並且在命令行會有提示:

組件data數據和props數據的區別:

  • data數據是子組件私有的,可讀可寫;
  • props數據是父組件傳遞給子組件的,只能讀,不能寫。

案例:發表評論功能

父組件為評論列表,子組件為ID,評論者,內容和按鈕的集合,在輸入ID,評論者等內容,然后點擊添加的時候,需要首先獲取子組件的list列表,然后再添加新的列表項到列表中。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <mycom :plist="list"></mycom>

    <ul>
      <li v-for="item in list" :key="item.id">
        ID:{{item.id}} --- 內容:{{item.content}} --- 評論人:{{item.user}}
      </li>
    </ul>
  </div>

  <template id="tmp1">
    <div>
      <label>
        ID:
        <input type="text" v-model="id">
      </label>
      <br>
      <label>
        評論者:
        <input type="text" v-model="user">
      </label>
      <br>
      <label>
        內容:
        <textarea v-model="content"></textarea>
      </label>
      <br>
      <!-- 把父組件的數據作為子組件的函數參數傳入 -->
      <input type="button" value="添加評論" @click="addContent(plist)">
    </div>
  </template>

  <script>
    var vm = new Vue({
      el: "#box",
      data: {
        list: [{
          id: Date.now(),
          user: 'user1',
          content: 'what'
        }, {
          id: Date.now(),
          user: 'user2',
          content: 'are'
        }]
      },
      methods: {},
      components: {
        mycom: {
          template: '#tmp1',
          data: function () {
            return {
              id: '',
              user: '',
              content: '',
            }
          },
          methods: {
            addContent(plist) {
              plist.unshift({
                id: this.id,
                user: this.user,
                content: this.content
              });
            }
          },
          props: ['plist']
        }
      }
    });
  </script>
</body>

</html>

把添加ID,評論人,內容作為子組件,把列表作為父組件,然后把添加的數據放到父組件列表上,由於要獲取到父組件列表的數據,所以必然涉及到父組件向子組件傳值的過程。這里還通過子組件方法參數來保存父組件的數據到子組件的數據中。

2、父組件向子組件傳方法

既然父組件可以向子組件傳遞數據,那么也可以向子組件傳遞方法。

<body>
  <div id="box">
    <mycom v-bind:parentmsg="msg" @parentfunc="show"></mycom>
  </div>

  <template id="temp">
    <div>
      <input type="button" value="調用父組件方法" @click="sonClick">
      <h3>子組件 --- 父組件:{{parentmsg}}</h3>
    </div>
  </template>

  <script>
    var vm = new Vue({
      el: "#box",
      data: {
        msg: '父組件的msg'
      },
      methods: {
        show(data1, data2) {
          console.log("這是父組件的show方法" + data1 + data2);
        }
      },
      components: {
        mycom: {
          template: "#temp",
          // 對傳遞給子組件的數據進行聲明,子組件才能使用 
          props: ['parentmsg'],
          methods: {
            sonClick() {
              // 調用父組件的show方法
              this.$emit("parentfunc", 111, 222);
            }
          }
        }
      }
    });
  </script>
</body>

1、@parentfunc="show" 綁定父組件的show方法。

2、<input type="button" value="調用父組件方法" @click="sonClick"> 點擊按鈕調用父組件的show方法

3、在 子組件的 sonClick 方法中使用 this.$emit("parentfunc"); 來調用父組件的show方法

4、父組件的show方法也可以傳參,在調用的時候,實參從 this.$emit 的第二個參數開始傳入。

5、如果 this.$emit 的第二個參數傳的是子組件的data數據,那么父組件的方法就可以獲得子組件的數據,這也是把子組件的數據傳遞給父組件的方式。

3、使用 ref 獲取DOM和組件的引用

我們知道Vue不推薦直接獲取DOM元素,那么在Vue里面怎么獲取DOM及組件元素呢?

我們呢可以在元素上使用 ref 屬性來獲取元素。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="./lib/vue-2.4.0.js"></script>
</head>

<body>
  <div id="box">
    <input type="button" value="獲取元素" @click="getrefs" ref="mybtn">
    <h3 ref="myh3">這是H3</h3>
    <mycom ref="mycom"></mycom>
  </div>

  <template id="tmp1">
  </template>

  <script>
    // 定義組件
    Vue.component('mycom', {
      template: '#tmp1',
      data: function () {
        return {
          msg: '子組件的msg',
          pmsg: ''
        }
      },
      methods: {
        show(data) {
          console.log('調用子組件的show');
          this.pmsg = data;
          console.log(this.pmsg);
        },

      }
    });

    var vm = new Vue({
      el: "#box",
      data: {
        parentmsg: '父組件的msg'
      },
      methods: {
        getrefs() {
          console.log(this.$refs.myh3);
          console.log(this.$refs.mycom.msg);
          this.$refs.mycom.show(this.parentmsg);
        }
      }
    });
  </script>
</body>

</html>

總結:

1、ref 屬性不僅可以獲取DOM元素,也可以獲取組件(無論全局還是私有組件)元素。

2、獲取到組件元素后,就可以獲取組件元素的data數據和methods方法。

3、獲取到組件中的方法后,可以傳入VM的data數據,就可以把VM的data數據傳入組件中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM