【前端Vue】04 - 組件開發(fā) [v-model + 父子組件通信]

1. 表單綁定 v-model

  1. 表單控件在實際開發(fā)中是非常常見的渊迁。特別是對于用戶信息的提交,需要大量的表單。

  2. Vue 中使用 v-model 指令來實現(xiàn)表單元素和數(shù)據(jù)的雙向綁定月趟。

1.1 案例分析

vue中v-model的雙向綁定
  1. 當(dāng)我們在輸入框輸入內(nèi)容時莉测。

  2. 因為input中的v-model綁定了message,所以會實時將輸入的內(nèi)容傳遞給message柔袁,message發(fā)生改變呆躲。

  3. 當(dāng)message發(fā)生改變時,因為上面我們使用Mustache語法捶索,將message的值插入到DOM中插掂,所以DOM會發(fā)生響應(yīng)的改變。

  4. 所以腥例,通過 v-model 實現(xiàn)了雙向的綁定辅甥。

<div id="app">
  <!-- v-model 是一個雙向綁定 -->
  <input type="text" v-model="message">
  <h2>{{message}}</h2>
</div>
<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            message: ''
        },
        methods: {}
    });

1.2 v-model 原理

  1. v-model 指令就是 v-click:inputv-bind:value 指令的結(jié)合,再使用 $eventtarget 獲取到當(dāng)前輸入框中輸入的值院崇。
<div id="app">
  <!--
     v-model = v-on:input + v-bind:value
  -->
  <input type="text" name="username" id="username" :value="message" @input="valueChange">
  <input type="text" name="email" id="email" :value="message" @input="message = $event.target.value">
  <h2>{{message}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            message: '您好啊!'
        },
        methods: {
            valueChange(event) {
                this.message = event.target.value;
            }
        }
    });

1.3 v-model 結(jié)合 radio 類型

  1. 單選框的實現(xiàn)需要多個input使用相同的name屬性肆氓。
<div id="app">
  <label for="male">
    <input type="radio" name="sex" id="male" value="1" v-model="sex">男
  </label>
  <label for="female">
    <input type="radio" name="sex" id="female" value="0" v-model="sex">女
  </label>
</div>

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            sex: 1
        }
    });

1.4 v-model 結(jié)合CheckBox

<div id="app">
  <input type="checkbox" value="籃球" name="basketball" id="basketball" v-model="hobbies">籃球
  <input type="checkbox" value="羽毛球" name="badminton" id="badminton" v-model="hobbies">羽毛球
  <input type="checkbox" value="毽子" name="shuttlecock" id="shuttlecock" v-model="hobbies">毽子
  <input type="checkbox" value="足球" name="football" id="football" v-model="hobbies">足球
  <h2>{{hobbies}}</h2>
</div>
<script src="../../js/vue.js"></script>
    const app = new Vue({
        el: '#app',
        data: {
            hobbies: []
        }
    });

1.5 v-model 結(jié)合select

  1. select可以實現(xiàn)單選和多選 。使用 multiple 屬性底瓣。
<div id="app">

  <!-- 1. 選擇一個 -->
  <select name="fruit" id="fruit" v-model="fruit">
    <option value="蘋果">蘋果</option>
    <option value="香蕉">香蕉</option>
    <option value="芒果">芒果</option>
    <option value="李子">李子</option>
  </select>

  <h2>{{fruit}}</h2>
  <br>
  <br>
  <br>
  <!-- 2. 選擇多個 multiple 屬性-->
  <select name="fruit" id="fruits" multiple v-model="fruits">
    <option value="蘋果">蘋果</option>
    <option value="香蕉">香蕉</option>
    <option value="芒果">芒果</option>
    <option value="李子">李子</option>
  </select>
  <h2>{{fruits}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            fruit: '香蕉',
            fruits: []
        }
    });

1.6 v-model 的值綁定

  1. 所謂的值綁定就是將數(shù)據(jù)動態(tài)的綁定到每一個操作項上谢揪。
<div id="app">
  <label v-for="hobby in hobbies" :for="hobby">
    <input type="checkbox" name="hobby" :id="hobby" :value="hobby" v-model="selected">{{hobby}}
  </label>
  <h2>您的愛好是 : {{selected}}</h2>
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            hobbies: ['籃球', '足球', '羽毛球', '乒乓球', '高爾夫球'],
            selected: []
        }
    });

1.7 v-model 修飾符的使用

  1. lazy: 可以讓雙向綁定的元素蕉陋,減少實時渲染次數(shù),只有當(dāng)鼠標(biāo)失去焦點或者當(dāng)用戶按下回車的時候才渲染拨扶。

  2. number:可以將綁定的數(shù)據(jù)的數(shù)據(jù)類型轉(zhuǎn)換為 number 類型凳鬓,方便一些需要使用數(shù)字的地方。

  3. trim: 去除字符串兩邊的空格患民。

<div id="app">
  <!-- 1. 使用lazy修飾符的v-model 當(dāng)輸入完成之后敲回車或者是當(dāng)鼠標(biāo)失去焦點的時候更新數(shù)據(jù) -->
  <input type="text" name="username" id="username" v-model.lazy="username">
  <h2>您輸入的用戶名是 : {{username}}</h2>
  <!-- 2. 使用number修飾符修飾的v-model 文本框中的數(shù)據(jù)類型會轉(zhuǎn)換為string 后期不方便處理使用number修飾符的文本框文本類型是 number 的 -->
  <input type="number" name="phoneNum" id="phoneNum" v-model.number="phoneNum">
  <h2> 文本框的類型 : {{typeof phoneNum}} </h2>
  <!--3. 使用trim 去除字符串兩邊的空格-->
  <input type="text" name="content" id="content" v-model.trim="content">
  <h2>{{content}}</h2>
</div>
    const app = new Vue({
        el: '#app',
        data: {
            username: '',
            phoneNum: 0,
            content: ''
        }
    });

2. 組件化

  1. 組件化是Vue.js 中的重要思想,它提供了一種抽象缩举,讓我們可以開發(fā)出一個個獨立可復(fù)用的小組件來構(gòu)造我們的應(yīng)用。任何的應(yīng)用都會被抽象成一顆組件樹匹颤。
Vue組件樹

2.1 注冊組件的基本步驟

  1. 創(chuàng)建組件構(gòu)造器;

  2. 注冊組件;

  3. 使用組件仅孩。

注冊組件的基本步驟
創(chuàng)建組件的基本步驟
<div id="app">
  <cpn></cpn>
</div>
<script src="../../js/vue.js"></script>
// 1. 創(chuàng)建組件構(gòu)造器 使用Vue.extend 創(chuàng)建組件構(gòu)造器
    const cpnC = Vue.extend({
        template: `
                <div>
                  <h1>我是標(biāo)題</h1>
                  <p>我是段落一</p>
                  <p>我是段落二</p>
                </div>
              `
    });
    // 2. 注冊組件
    Vue.component('cpn', cpnC);
    // 3. 掛載 app
    const app = new Vue({
        el: '#app',
        data: {}
    });

2.2 全局組件和局部組件

  1. 全局組件是使用 Vue.component('組件名稱' , 組件構(gòu)造器) 注冊的組件;

  2. 局部組件時在 印蓖, Vue({}) 對象中使用 components屬性聲明的組件辽慕。

<div id="app">
  <cpn></cpn>
  <cpn></cpn>
  <cpna></cpna>
  <hr>
</div>

<div id="app2">
  <cpn></cpn>
  <!-- app注冊的局部組件在app中不能使用 <cpna></cpna>-->
</div>

<script src="../../js/vue.js"></script>
    /**
     * 全局組件可以在多個Vue的實例下面使用
     */
        // 1. 使用Vue.extend 創(chuàng)建一個組件構(gòu)造器
    const cpnC = Vue.extend({
            template: `
                  <div>
                    <h2>我是標(biāo)題</h2>
                    <p>我是段落1</p>
                    <p>我是段落2</p>
                  </div>>
                 `
        });


    /**
     * 局部組件構(gòu)造器聲明
     */
    const cpnCa = Vue.extend({
        template: `
                  <div>
                    <h2>我是局部組件的標(biāo)題</h2>
                    <p>我是段落1</p>
                    <p>我是段落2</p>
                  </div>>
                 `
    });

    // 2. 將組件注冊為全局組件
    Vue.component('cpn', cpnC);

    const app = new Vue({
        el: '#app',
        data: {},
        components: {
            cpna: cpnCa
        }
    });

    const app2 = new Vue({
        el: '#app2',
        data: {},
        components: {}
    });
全局組件和局部組件

2.3 父組件和子組件

  1. 組件和組件之間存在層級關(guān)系,而其中一種非常重要的關(guān)系就是父子組件的關(guān)系。
父組件和子組件的關(guān)系

2.4 注冊組件的語法糖簡寫

注冊組件的語法糖寫法
<div id="app">
  <cpn1></cpn1>
  <cpn2></cpn2>
  <cpn3></cpn3>
  <cpn4></cpn4>
  <hr>
</div>

<div id="app2">
  <cpn1></cpn1>
  <cpn2></cpn2>
  <cpn3></cpn3>
  <cpn4></cpn4>
</div>
<script src="../../js/vue.js"></script>
/**
     *  (一) 傳統(tǒng)方式注冊組件
     */
        // 1. 創(chuàng)建組件構(gòu)造器
    const cpnC1 = Vue.extend({
            template: `
        <div>
          <h1>我是全局組件一</h1>
        </div>
       `
        });

    /**
   * 注冊一個全局組件
   * */
    Vue.component('cpn1', cpnC1);

    /**
     * (二) 使用語法糖的方式注冊組件 : 主要是省去調(diào)用Vue.extend的步驟赦肃,而是可以直接使用一個對象來代替
     */
    Vue.component('cpn2', {
        template: `
        <div>
          <h1>我是全局組件二(使用語法糖方式創(chuàng)建)</h1>
        </div>
       `
    });

    /**
     * 創(chuàng)建一個組件構(gòu)造器用于局部組件
     */
    const cpnC4 = Vue.extend({
        template: `
        <div>
          <h1>我是局部組件四</h1>
        </div>
       `
    });

    /**
     * 掛載app
     */
    const app = new Vue({
        el: '#app',
        data: {},
        components: {
            /* 創(chuàng)建局部組件 */
            cpn3: {
                template: `
        <div>
          <h1>我是局部組件三(使用語法糖方式創(chuàng)建)</h1>
        </div>
       `
            },

            cpn4: cpnC4
        }
    });

    /**
     * 掛載app2
     */
    const app2 = new Vue({
        el: '#app2',
        data: {}
    });

2.5 組件模板抽離

  1. 在編寫組件模板的時候可以使用溅蛉,script 或者 template標(biāo)簽定義標(biāo)簽?zāi)0濉⑵鋸膶ο笾谐殡x出來他宛。
組件模板抽離
<div id="app">
  <cpn></cpn>
  <cpn1></cpn1>
</div>

<!-- 1. 抽離template模板 -->
<script type="text/x-template" id="cpn">
  <div>
    <h1>我是抽離的標(biāo)簽的標(biāo)題</h1>
    <p>我是一個段落</p>
  </div>
</script>

<!-- 2. 使用template標(biāo)簽包裹-->
<template id="cpn1">
  <div>
    <h1>我是抽離的標(biāo)簽的標(biāo)題使用template標(biāo)簽</h1>
    <p>我是一個段落</p>
  </div>
</template>

<script src="../../js/vue.js"></script>
Vue.component('cpn', {
        template: '#cpn'
    });

    Vue.component('cpn1', {
        template: '#cpn1'
    });

    const app = new Vue({
        el: '#app',
        data: {}
    });

2.6 組件中可以訪問Vue實例中的數(shù)據(jù)嗎船侧?

  1. 組件是一個單獨功能模塊的封裝:這個模塊有屬于自己的HTML模板,也應(yīng)該有屬性自己的數(shù)據(jù)data厅各。

  2. 答案:不能镜撩。而且即使可以訪問,如果將所有的數(shù)據(jù)都放在Vue實例中讯检,Vue實例就會變的非常臃腫琐鲁。

  3. 結(jié)論:Vue組件應(yīng)該有自己保存數(shù)據(jù)的地方。

2.7 組件自己的數(shù)據(jù)存放在哪里呢?

  1. 組件對象也有一個data屬性(也可以有methods等屬性)人灼。

  2. 只是這個data屬性必須是一個函數(shù)围段。而且這個函數(shù)返回一個對象,對象內(nèi)部保存著數(shù)據(jù)投放。

<template id="cpn">
  <div>
    <h1>當(dāng)前計數(shù): {{counter}}</h1>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<template id="com">
  <div>
    <h1>{{title}}</h1>
  </div>
</template>

<div id="app">
  <!-- 組件實例 -->
  <cpn></cpn>
  <cpn></cpn>
  <cpn></cpn>
  <com></com>
</div>

<script src="../../js/vue.js"></script>
/**
     * 語法糖方式注冊組件
     */
    Vue.component('cpn', {
        template: '#cpn',
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            increment() {
                this.counter++
            },
            decrement() {
                this.counter--
            }
        }
    });

    /**
     * 使用語法糖方式注冊組件
     */
    Vue.component('com', {
        template: '#com',
        data() {
            return {
                title: '我是標(biāo)題'
            };
        }
    });

    const app = new Vue({
        el: '#app',
        data: {}
    });

2.8 為什么組件中的data是一個函數(shù)

  1. 如果不同組件他們共享一個共同的對象將會出大問題奈泪。
    const obj = {
        name: '張三',
        age: 23
    }

    /**
     * 每次調(diào)用函數(shù)返回一個對象
     */
    function data() {
        return obj;
    }

    let obj1 = data();
    obj1.name = '李四';
    let obj2 = data();
    console.log(obj1); // {name: "李四", age: 23}
    console.log(obj2); // {name: "李四", age: 23}
為什么組件中的data是一個函數(shù)

2.9 父子組件之間的通信

  1. 在開發(fā)中,往往一些數(shù)據(jù)確實需要從上層傳遞到下層灸芳,比如在一個頁面中涝桅,我們從服務(wù)器請求到了很多的數(shù)據(jù)。其中一部分?jǐn)?shù)據(jù)烙样,并非是我們整個頁面的大組件來展示的冯遂,而是需要下面的子組件進(jìn)行展示。這個時候谒获,并不會讓子組件再次發(fā)送一個網(wǎng)絡(luò)請求蛤肌,而是直接讓大組件(父組件)將數(shù)據(jù)傳遞給小組件(子組件)壁却。
如何進(jìn)行父子組件間的通信呢?Vue官方提到
  1. 父組件向子組件傳遞數(shù)據(jù) : 通過props向子組件傳遞數(shù)據(jù)裸准。

  2. 子組件向父組件傳遞數(shù)據(jù): 通過事件向父組件發(fā)送消息展东。

Vue父子組件之間的通信
props 的基本用法
  1. 在組件中,使用選項props 來聲明需要從父級接收到的數(shù)據(jù)炒俱。props的值有兩種方式:

方式一:字符串?dāng)?shù)組盐肃,數(shù)組中的字符串就是傳遞時的名稱。

方式二:對象权悟,對象可以設(shè)置傳遞時的類型砸王,也可以設(shè)置默認(rèn)值等。

我們先來看一個最簡單的props傳遞:
簡單的props傳遞
在下面的代碼中僵芹,我直接將Vue實例當(dāng)做父組件拇派,并且其中包含子組件來簡化代碼凿跳。
  1. 真實的開發(fā)中件豌,Vue 實例和子組件的通信和父組件和子組件的通信過程是一樣的控嗜。
實現(xiàn)父組件向子組件傳遞數(shù)據(jù)的步驟如下 :(使用字符串?dāng)?shù)組的方式)
  1. 第一步: 在子組件將props定義為一個字符串?dāng)?shù)組用于接收父組件傳遞的數(shù)據(jù)。

  2. 第二步:將父組件的值和子組件定義的屬性進(jìn)行綁定疆栏。

  3. 第三步:值已經(jīng)傳遞過來了曾掂,在子組件中使用父組件的數(shù)據(jù)珠洗。

父組件向子組件傳遞數(shù)據(jù)
<!-- 父子組件之間的通信
      1. 父組件向子組件傳通過props;
      2. 子組件向父組件傳通過自定義事件;
    在真實開發(fā)中许蓖,Vue實例和子組件的通信和父組件和子組件的通信過程是一樣的调衰。
  -->
<div id="app">
  <!-- 第二步 將父組件的值和子組件定義的屬性進(jìn)行綁定 -->
  <cpn :cmovies="movies" :cmessage="message"></cpn>
</div>

<template id="cpn">
  <div>
    <ul>
      <!-- 第三步 使用子組件中的屬性 -->
      <li v-for="item in cmovies">{{item}}</li>
    </ul>

    <h2>{{cmessage}}</h2>
  </div>
</template>

<script src="../../js/vue.js"></script>
 /* 父傳子通過 props */
  const cpn = {
    template:'#cpn',
    data() {
        return{

      }
    },
    // 字符串?dāng)?shù)組寫法
    // 第一步
    props:['cmovies','cmessage']
  }
    const app = new Vue({
        el: '#app',
        data: {
            message:'我是父組件的message',
      movies:['陸小鳳傳奇之決戰(zhàn)紫禁之巔','陸小鳳傳奇之劍神一笑','陸小鳳傳奇之鳳舞九天']
    },
        components: {
            /* 使用屬性的增強(qiáng)寫法 */
            cpn
        }
    });
實現(xiàn)父組件向子組件傳遞數(shù)據(jù)的步驟如下 :(使用對象的方式)
  1. props除了使用數(shù)組的方式之外還能使用對象的方式接收父組件傳遞的數(shù)據(jù)嚎莉。當(dāng)需要對props進(jìn)行類型驗證的時候趋箩,就需要使用對象的寫法琼懊。

  2. 對象中屬性非自定義的數(shù)據(jù)類型可以使用 type屬性驗證該屬性的數(shù)據(jù)類型哼丈,使用required標(biāo)識該屬性是否為必須傳遞的醉旦,使用default屬性設(shè)置默認(rèn)值桨啃。

props中的屬性
驗證都支持如下類型:
  1. String车胡;

  2. Number匈棘;

  3. Boolean主卫;

  4. Array:當(dāng)是數(shù)組類型的時候鹃愤,在設(shè)置默認(rèn)值時需要注意软吐,在某些版本之后不能使用 default:[]這樣的默認(rèn)值凹耙,而是需要一個函數(shù)肖抱,在函數(shù)中返回數(shù)組的默認(rèn)值 ,如 : default:() => { return []; }default:() => [] 或者 default() {return [];}虐沥;

  5. Object欲险;

  6. Date

  7. Function槐壳;

  8. Symbol务唐;

  9. 當(dāng)我們有自定義構(gòu)造函數(shù)時枫笛,驗證也支持自定義的類型刑巧。

peops為對象類型
父組件傳遞值給子組件props屬性駝峰標(biāo)識
  1. 在進(jìn)行父組件傳遞數(shù)據(jù)給子組件的時候啊楚,在props中的屬性如果使用了駝峰標(biāo)識恭理,那么在子組件標(biāo)簽中使用該屬性的時候就需要使用 橫線分隔的形式颜价,綁定父組件傳遞的數(shù)據(jù)拍嵌。
子組件中props的屬性使用駝峰標(biāo)識

2.10 子組件向父組件傳遞數(shù)據(jù)

子組件向父組件傳遞數(shù)據(jù)的步驟
  1. 第一步 : 定義一個,比如按鈕事件 將子組件的數(shù)據(jù)傳遞給父組件横辆。
    <!-- 第一步 : 定義一個,比如按鈕事件 將子組件的數(shù)據(jù)傳遞給父組件 -->
    <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
  1. 第二步 : 將子組件的自定義事件發(fā)射給父組件 并傳遞攜帶的參數(shù)狈蚤。
  btnClick(item) {
                /* 第二步 : 將子組件的自定義事件發(fā)射給父組件 并傳遞攜帶的參數(shù) */
                this.$emit('customevent', item);
            }
  1. 第三步 : 在父組件中處理子組件自定義的事件脆侮。
<!-- 第三步 : 在父組件中處理子組件自定義的事件  -->
  <cpn @customevent="getSonCpnData"></cpn>
  1. 第四步 : 處理觸發(fā)子組件自定義事件之后需要執(zhí)行的方法靖避。
  /* 第四步 : 處理觸發(fā)子組件自定義事件之后需要執(zhí)行的方法 */
            getSonCpnData(item) {
                console.log(item);
            }
子組件向父組件傳遞數(shù)據(jù)
<div id="app">
  <!-- 第三步 : 在父組件中處理子組件自定義的事件  -->
  <cpn @customevent="getSonCpnData"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>我是子組件</h2>
    <!-- 第一步 : 定義一個,比如按鈕事件 將子組件的數(shù)據(jù)傳遞給父組件 -->
    <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
  </div>
</template>

<script src="../../js/vue.js"></script>
const cpn = {
        template: '#cpn',
        data() {
            return {
                categories: [
                    {id: 1, name: '手機(jī)數(shù)碼'},
                    {id: 2, name: '家用電器'},
                    {id: 3, name: '電腦辦公'},
                    {id: 4, name: '生鮮蔬菜'}
                ]
            }
        },
        methods: {
            btnClick(item) {
                /* 第二步 : 將子組件的自定義事件發(fā)射給父組件 并傳遞攜帶的參數(shù) */
                this.$emit('customevent', item);
            }
        }
    };

    /* 子組件產(chǎn)生了一些事件希望父組件知道 */
    const app = new Vue({
        el: '#app',
        components: {
            cpn
        },
        methods: {
            /* 第四步 : 處理觸發(fā)子組件自定義事件之后需要執(zhí)行的方法 */
            getSonCpnData(item) {
                console.log(item);
            }
        }
    });

2.11 父子組件通信-結(jié)合雙向綁定

  1. 需求1: 當(dāng)子組件中的 data 數(shù)據(jù) dNumber1 dNumber2 改變的時候 子組件 props 中的 number1 number2 也需要發(fā)生改變,利用的是 v-model的原理 是由 v-on:inputv-bind:value 組合篡九。

  2. 需求2: 當(dāng)number1 的值改變的時候 將 number2 的值變?yōu)?number1100 倍榛臼。

Vue父子組件通信
簡單圖解
<div id="app">

  <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>

</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dNumber1}}</h2>
    <input type="number" @input="dnumber1change" :value="dNumber1">

    <h2>props:{{number2}}</h2>
    <h2>data:{{dNumber2}}</h2>
    <input type="number" @input="dnumber2change" :value="dNumber2">
  </div>
</template>

<!--
  1. 需求1: 當(dāng)子組件中的 data數(shù)據(jù) dNumber1 dNumber2 改變的時候 子組件 props 中的  number1 number2 也需要發(fā)生改變
      1.1 利用的是 v-model的原理 是由 v-on:input 和 v-bind:value 組合

  2. 需求2: 當(dāng) number1 的值改變的時候 將 number2 的值變?yōu)?number1 的 100 倍
 -->

<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            num1: 0,
            num2: 0
        },
        methods: {
            num1change(value) {
                this.num1 = parseFloat(value);
            },
            num2change(value) {
                this.num2 = parseFloat(value);
            }
        },
        components: {
            'cpn': {
                template: '#cpn',
                props: {
                    /* 這里的值有父組件的 num1 或 num2 決定 */
                    number1: Number,
                    number2: Number
                },
                data() {
                    return {
                        dNumber1: this.number1,
                        dNumber2: this.number2
                    };
                },
                methods: {
                    dnumber1change(event) {
                        // 從event中獲取當(dāng)前改變的值
                        this.dNumber1 = event.target.value;
                        // 發(fā)送一個自定義事件給父組件讓父組件的值發(fā)生改變
                        this.$emit('num1change', this.dNumber1);
                        this.dNumber2 = this.dNumber1 * 100;
                        this.$emit('num2change', this.dNumber2);
                    },
                    dnumber2change(event) {
                        this.dNumber2 = event.target.value;
                        this.$emit('num2change', this.dNumber2);
                        this.dNumber1 = this.dNumber2 / 100;
                        this.$emit('num1change', this.dNumber1);
                    }
                }
            }
        }
    });

2.12 父子組件通信-結(jié)合雙向綁定 (watch實現(xiàn))

<div id="app">
  <cpn
    :number1="num1"
    :number2="num2"
    @num1change="num1Change"
    @num2change="num2Change"></cpn>
</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{pNumber1}}</h2>
    <input type="number" v-model="pNumber1">
    <!--<input type="text" :value="pNumber1" @input="number1Change">-->

    <h2>props:{{number2}}</h2>
    <h2>data:{{pNumber2}}</h2>
    <input type="number" v-model="pNumber2">
    <!--<input type="text" :value="pNumber2" @input="number2Change">-->
  </div>
</template>

<script src="../../js/vue.js">
const app = new Vue({
        el: '#app',
        data: {
            num1: 0,
            num2: 0
        },
        methods: {
            num1Change(number1) {
                this.num1 = parseFloat(number1);
            },
            num2Change(number2) {
                this.num2 = parseFloat(number2);
            }
        },
        components: {
            'cpn': {
                template: '#cpn',
                props: {
                    number1: Number,
                    number2: Number
                },
                data() {
                    return {
                        pNumber1: this.number1,
                        pNumber2: this.number2
                    }
                },
                methods: {},
                /* 用于監(jiān)視一個屬性的改變 */
                watch: {
                    pNumber1(newValue, oldValue) {
                        this.pNumber2 = newValue * 100
                        // 子組件發(fā)送一個自定義事件給父組件
                        this.$emit('num1change', newValue);
                    },
                    pNumber2(newValue, oldValue) {
                        this.pNumber1 = newValue / 100
                        this.$emit('num2change', newValue);
                    }
                }
            }
        }
    });
watch 的使用
  1. watch : 是屬于Vue實例的一個 options金刁。參考文檔,它可以監(jiān)控一個屬性的變化胀葱,在watch中可以將屬性名稱作為方法名稱 屬性名稱(newValue,oldValue) 抵屿,newValue參數(shù)就是該屬性改變之后的值轧葛。watch可以監(jiān)控data中屬性的變化。
<div id="app">
  <input type="text" v-model="number">
</div>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {
            number: 123
        },
        watch: {
            number(newValue, oldValue) {
                console.log('number從' + oldValue + '變?yōu)榱? + newValue);
            }
        }
    });

2.13 父組件直接訪問子組件使用 $children$refs

父組件訪問子組件
  1. $children 獲取到的是父組件中的所有子組件,結(jié)果是一個數(shù)組芳杏。包含所有子組件對象辟宗。$children的缺陷:通過$children 訪問子組件時泊脐,是一個數(shù)組類型容客,訪問其中的子組件必須通過索引值缩挑。但是當(dāng)子組件過多,我們需要拿到其中一個時镜遣,往往不能確定它的索引值悲关,甚至還可能會發(fā)生變化寓辱。
父組件通過this.$children獲取到子組件對象
  1. $refs 獲取到的是一個類似Map集合的對象秫筏。但是使用$refs獲取之前需要在組件標(biāo)簽上使用ref屬性指定这敬。$refsref 指令通常是一起使用的崔涂。首先缭保,我們通過ref給某一個子組件綁定一個特定的ID艺骂。其次隆夯,通過this.$refs.ID就可以訪問到該組件了吮廉。
<cpn ref="aaa"></cpn>
<cpn ref="bbb"></cpn>
父組件通過this.$refs獲取子組件的對象
<div id="app">
  <cpn ref="aaa"></cpn>
  <cpn ref="bbb"></cpn>
  <button @click="btnClick">點我</button>
</div>

<template id="cpn">
  <div>
    <h2>{{message}}</h2>
  </div>
</template>

<script src="../../js/vue.js"></script>
/* $children 可以拿到所有的子組件 */
    /* $refs 拿到指定 ref 的組件 */
    const app = new Vue({
        el: '#app',
        data: {},
        methods: {
            btnClick() {
                // console.log(this.$children);
                // console.log(this.$children[0].message);
                // this.$children[0].showMessage();
                console.log(this.$refs);
                console.log(typeof this.$refs);
                // let map = new Map();
                // map.set('aaa', {name: '張三'});
                // console.log(map);
                console.log(this.$refs.aaa.message);
                this.$refs.bbb.showMessage();
            }
        },
        components: {
            'cpn':
                {
                    template: '#cpn',
                    data() {
                        return {
                            message: '我是子組件'
                        }
                    },
                    methods: {
                        showMessage() {
                            console.log(this.message);
                        }
                    }
                }
        }
    });

2.14 子組件中訪問父組件 $parent$root

  1. $parent : 可以獲取組件的父組件轴脐。

  2. $root : 訪問的是根組件,Vue實例恬涧。

子組件訪問父組件
<div id="app">
  <cpn></cpn>
  <hr>
</div>

<template id="cpn">
  <div>
    <h2>我是cpn組件</h2>
    <button @click="btnClick"> cpn的按鈕</button>
    <hr>
    <!-- 這是定義的一個局部子組件 -->
    <ccpn></ccpn>
  </div>
</template>

<template id="ccpn">
  <div>
    <h2>我是cpn的子組件ccpn</h2>
    <button @click="btnCcpnClick">ccpn的按鈕</button>
  </div>
</template>
<script src="../../js/vue.js"></script>
const app = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: {
            'cpn': {
                template: '#cpn',
                data() {
                    return {}
                },
                methods: {
                    btnClick() {
                        console.log(this.$parent); //
                    }
                },
                components: {
                    'ccpn': {
                        template: '#ccpn',
                        methods: {
                            btnCcpnClick() {
                                console.log(this.$parent); // 獲取的是其父組件對象
                                console.log(this.$root); // 獲取的是根組件對象
                            }
                        }
                    }
                }
            }
        }
    });
子組件通過$parent訪問父組件注意事項
  1. 盡管在Vue開發(fā)中,我們允許通過$parent 來訪問父組件提揍,但是在真實開發(fā)中盡量不要這樣做劳跃。

  2. 子組件應(yīng)該盡量避免直接訪問父組件的數(shù)據(jù)刨仑,因為這樣耦合度太高了杉武。

  3. 如果我們將子組件放在另外一個組件之內(nèi),很可能該父組件沒有對應(yīng)的屬性飞涂,往往會引起問題封拧。

  4. 另外泽西,更不好做的是通過$parent 直接修改父組件的狀態(tài)捧杉,那么父組件中的狀態(tài)將變得飄忽不定味抖,很不利于我的調(diào)試和維護(hù)灰粮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熔脂,一起剝皮案震驚了整個濱河市霞揉,隨后出現(xiàn)的幾起案子晰骑,更是在濱河造成了極大的恐慌,老刑警劉巖骤公,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淋样,死亡現(xiàn)場離奇詭異趁猴,居然都是意外死亡儡司,警方通過查閱死者的電腦和手機(jī)捕犬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門碉碉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垢粮,“玉大人蜡吧,你說我怎么就攤上這事占键∨弦遥” “怎么了牲距?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵嗅虏,是天一觀的道長皮服。 經(jīng)常有香客問我龄广,道長择同,這世上最難降的妖魔是什么敲才? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任紧武,我火速辦了婚禮阻星,結(jié)果婚禮上妥箕,老公的妹妹穿的比我還像新娘畦幢。我一直安慰自己,他們只是感情好禾怠,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弦讽,像睡著了一般膀哲。 火紅的嫁衣襯著肌膚如雪某宪。 梳的紋絲不亂的頭發(fā)上兴喂,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音酱酬,去河邊找鬼膳沽。 笑死挑社,一個胖子當(dāng)著我的面吹牛滔灶,可吹牛的內(nèi)容都是我干的录平。 我是一名探鬼主播斗这,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼表箭,長吁一口氣:“原來是場噩夢啊……” “哼免钻!你這毒婦竟也來了极舔?” 一聲冷哼從身側(cè)響起拆魏,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤渤刃,失蹤者是張志新(化名)和其女友劉穎卖子,沒想到半個月后揪胃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喊递,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡骚勘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泽疆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殉疼。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓢娜,死狀恐怖眠砾,靈堂內(nèi)的尸體忽然破棺而出褒颈,到底是詐尸還是另有隱情谷丸,我是刑警寧澤念秧,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布币狠,位于F島的核電站漩绵,受9級特大地震影響止吐,放射性物質(zhì)發(fā)生泄漏碍扔。R本人自食惡果不足惜不同,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望二拐。 院中可真熱鬧服鹅,春花似錦、人聲如沸百新。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饭望。三九已至仗哨,卻和暖如春杰妓,著一層夾襖步出監(jiān)牢的瞬間藻治,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工巷挥, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留桩卵,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓倍宾,卻偏偏與公主長得像雏节,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子高职,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容