簡述在Vue腳手架中,組件以及父子組件(非父子組件)之間的傳值


1、組件的定義

  • 組成:
    • template:包裹HTML模板片段(反映了數據與最終呈現給用戶視圖之間的映射關系)
      • 只支持單個template標簽;
      • 支持lang配置多種模板語法;
    • script:配置Vue和載入其他組件或者依賴庫
      • 只支持單個script標簽;
      • 支持通過import方式載入其他.vue后綴的組件文件;
    • style:設置樣式
      • 支持多個style標簽;
      • 支持scoped屬性,css只應用於當前組件的元素中;
      • 支持lang配置多種預編譯語法;
  • 局部組件:這里是一個三級地址組件
    • 首先看一下目錄:在./src/components/下面新建立一個文件夾,此時命名為chinaAddress,在里面建立入口文件index.js,將你寫好的組件通過import導入

                     

    • 在index.js里面進行全局注冊組件;install是一個默認的方法
      import chinaAddress from './src/main';
      
      /* istanbul ignore next */
      chinaAddress.install = function(Vue) {
          Vue.component(chinaAddress.name, chinaAddress);
      };
      
      export default chinaAddress;
      View Code

      src里面的main.vue代碼如下:

      <template>
          <div class="area">
              <el-select v-model="provinceCode" :placeholder="$t('message.common.province')" class="select" clearable @clear="clear">
                  <el-option v-for="item in provinceList" :key="item.value" :label="item.name" :value="item.value">
                  </el-option>
              </el-select>
              <div>&nbsp;{{$t('message.common.provinceOrDirectly')}}&nbsp;</div>
              <el-select v-model="cityCode" :placeholder="$t('message.common.city')" class="select">
                  <el-option v-for="item in cityList" :key="item.value" :label="item.name" :value="item.value">
                  </el-option>
              </el-select>
              <div>&nbsp;{{$t('message.common.city')}}&nbsp;</div>
              <el-select v-model="countryCode" :placeholder="$t('message.common.country')" class="select">
                  <el-option v-for="item in countryList" :key="item.value" :label="item.name" :value="item.value">
                  </el-option>
              </el-select>
              <div>&nbsp;{{$t('message.common.districtOrCountry')}}&nbsp;</div>
          </div>
      </template>
      <script>
      import chinaAddressData from './chinaAddressData'
      export default {
          name: 'chinaAddress',
          data() {
              return {
                  provinceCode: '', //省code
                  cityCode: '', //市code
                  countryCode: '', //縣code
                  provinceList: [], //省表數據
                  cityList: [], //具體省下面市列表數據
                  countryList: [], //具體市下面縣列表數據
                  addressCode: [], //組件內部省市縣code數組
              }
          },
          props: {
              value: {
                  type: Array,
                  default () {
                      return []
                  }
              },
          },
          computed: {
      
          },
          components: {
      
          },
          watch: {
              value(newVal, oldVal) {
                  if (newVal.length) {
                      const [provinceCode, cityCode, countryCode] = newVal
                      this.provinceCode = provinceCode ? provinceCode : ''
                      this.cityCode = cityCode ? cityCode : ''
                      this.countryCode = countryCode ? countryCode : ''
                  }
              },
              provinceCode(newVal, oldVal) {
                  if (newVal) {
                      this.cityList = chinaAddressData.filter(val => val.parent === newVal)
                      this.cityCode = this.cityList[0].value
                  }
                  this.emitValueChange()
              },
              cityCode(newVal, oldVal) {
                  if (newVal) {
                      this.countryList = chinaAddressData.filter(val => val.parent === newVal)
                      this.countryCode = this.countryList[0].value
      
                  }
                  this.emitValueChange()
              },
              countryCode(newVal, oldVal) {
                  this.emitValueChange()
              }
          },
          mounted() {
      
          },
          created() {
              this.provinceList = chinaAddressData.filter(val => !val.parent)
          },
          methods: {
              emitValueChange() {
                  this.$nextTick(() => {
                      this.addressCode = [].concat(this.provinceCode, this.cityCode, this.countryCode).filter(val => val)
                      this.$emit('input', this.addressCode)
                      const addressInfo = {
                          provinceCode: '',
                          provinceName: '',
                          cityCode: '',
                          cityName: '',
                          countryCode: '',
                          countryName: '',
                      }
                      if (this.addressCode.length) {
                          const [provinceCode, cityCode, countryCode] = this.addressCode
                          addressInfo.provinceCode = provinceCode
                          addressInfo.cityCode = cityCode
                          addressInfo.countryCode = countryCode
                          addressInfo.provinceName = chinaAddressData.find(val => val.value === provinceCode)['name']
                          addressInfo.cityName = chinaAddressData.find(val => val.value === cityCode)['name']
                          addressInfo.countryName = chinaAddressData.find(val => val.value === countryCode)['name']
                      }
                      this.$emit('change', addressInfo)
                  })
              },
              clear() {
                  this.provinceCode = ''
                  this.cityCode = ''
                  this.countryCode = ''
                  this.emitValueChange()
              }
          },
      
      }
      </script>
      <style lang="scss" scoped>
      .area {
          width: 100%;
          display: flex;
         .select {
             width: 26%;
         }
      }
      </style>
      View Code

      js文件為:

    • 在main.js里面進行全局注冊:(沒有這一步,這是全局組件)
      import   chinaAddress from 'url';
      Vue.use(chinaAddress)
    • 在組件里面使用:
      <china-address v-model="addressCodeList" @change="addressChange"></china-address>
    • 局部組件的步驟如下:
      • 定義:
        var com = {
        //局部組件的定義,當名字為駝峰式命名myCom   寫成<my-com></my-com>
           template: "<div>局部組件com{{msg}}</div>",
           data() {//每個組件的實例都有獨立的數據
             return {
                msg: "hello",
                n: this.v
             };
           },
            methods: {}
        };
      • 注冊:(在父組件中注冊)
        components: {
            Header,
            com
         },
  • 全局組件:
    • 定義:
      Vue.component("組件的名字",{
          template:"模板的內容"(換行時用模板字符串``)//配置項
      })

2、數據傳遞方式(組件通信)

  • props傳值($parent以及$children)這兩種方式只能用於父子組件之間的通信
    • 父子組件之間的傳值   
      • 父組件  -----> 子組件(參考資料https://www.cnblogs.com/Sky-Ice/p/9267192.html
        • 子組件不能修改父組件傳遞過來的值,因為數據時單向的(參考資料https://www.cnblogs.com/Sky-Ice/p/10456533.html
          在 Vue 中,父子組件的關系可以總結為 prop 向下傳遞,事件向上傳遞。父組件通過 prop 給子組件下發數據,子組件通過事件給父組件發送消息。
          Prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,但是反過來不會。這是為了防止子組件無意間修改了父組件的狀態,來避免應用的數據流變得
          難以理解。 解決辦法:  原理:將要更改的值,傳遞給父組件,在父組件中更改,再傳遞給子組件 步驟:  先將值傳遞給子組件,子組件 props 接收並使用,然后通過 $emit 廣播一個事件給父組件,並將值一並傳遞,父組件 @子組件廣播過來的事件,
          並定義一個方法,在該方法中,改變傳遞過來的值,父組件又會將值傳遞給子組件,這樣就形成了一個閉環,問題得以解決
        • 傳值:
          • 在父組件上用v-bind綁定要傳遞的屬性,若沒有v-bind,則屬性的值始終是 字符串     v-bind:v="msg"
            <template>
              <div id="example" style="margin-bottom:200px">
                我是首頁
                <hr />這里是分界線
                <Header v-bind:v="msg"></Header>
              </div>
            </template>
            <script>
            import Header from "./header";
            export default {
              name: "example",
              data() {
                return {
                  msg: "我是你爸爸"
                };
              },
              components: {
                Header,//子組件
              },
              methods: {},
              mounted() {}
            };
            </script>
            View Code
          • 在子組件上用props屬性進行接收:
            • 對象方式:(驗證)
              props:{
                  v : {
                    type : String,//null表示可以為任意類型,Number為數值型,Object為一個對象,
                    default : 'hello',//不傳之時指定默認值
                    required : true,//不傳值則產生錯誤
              twoWay : true,//如果綁定類型不對將發出警告 } },
            • 數組方式:props : ["v"]
              <template>
                <div id="header">
                  <p>我是父組件傳遞過來的數據:{{v}}</p>
                </div>
              </template>
              <script>
              export default {
                props:["v"],
                name: "thesisHeader",
                data() {
                  return {
                    search: "", //搜索框內容
                  };
                },
                methods: {},
              };
              View Code
        • 傳方法:
          • 在使用子組件時,根據傳值的方式將方法進行綁定,:run="run";
          • 在子組件中,依舊使用props進行接收,this.run()運行;  
          • 子組件還可以用這種方式調用父組件的   this.$parent.方法/屬性
        • 傳父組件本身:
          • 在父組件中,使用子組件時,動態綁定this,即:home="this";
          • 子組件接收props:["home"];
          • 子組件使用:this.home.父組件的屬性或者方法
      • 子組件 -----> 父組件(參考資料https://www.cnblogs.com/Sky-Ice/p/9289922.html
        • 步驟:
          • 依舊是父組件Home
            <template>
              <div>
                <div
                  id="example"
                  style="width:200px;height:200px;background:#ff0;font-size:25px;line-height:200px;text-align:center;margin:20px"
                >我是父組件</div>
                <Header></Header>
              </div>
            </template>
            <script>
            import Header from "./header";
            export default {
              name: "example",
              data() {
                return {
                  msg: "我是你爸爸"
                };
              },
              components: {
                Header //子組件
              },
              methods: {},
              mounted() {}
            };
            </script>
            View Code
            與子組件Header
            <template>
              <div id="header">
                <div
                  style="width:200px;height:200px;background:pink;font-size:25px;padding-top:100px;text-align:center;margin:20px"
                >我是子組件</div>
                <button @click="give">給父組件傳值</button>
              </div>
            </template>
            <script>
            export default {
              name: "thesisHeader",
              data() {
                return {
                  msg: "爸爸,我是您的寶貝女兒啊!"
                };
              },
              methods: {
                give(){
                  
                }
              },
              mounted() {}
            };
            View Code
          • 子組件發送數據
             give() {
                  this.$emit("receive", { msg: this.msg });//$emit用來觸發自定義事件,第一個參數是事件的名稱,第二個參數是傳遞給監聽事件的數據
             }
          • 父組件在使用子組件的地方進行監聽
            <Header @receive="getData"></Header>
          • 接收數據,並進行處理
            getData(data){
                console.log(data)//{msg: "爸爸,我是您的寶貝女兒啊!"}
            }
        • 同理:傳方法與自身都可以,像父組件給子組件的形式   當然,父組件還可以這么調用   this.$refs.名字.屬性/方法
          //傳方法 
          myMethod() { alert("我是屬於子組件的方法"); }, give() { this.$emit("receive", { msg: this.msg, myMethod: this.myMethod }); }
          結果:{msg: "爸爸,我是您的寶貝女兒啊!", myMethod: ƒ} data.myMethod()執行
          //傳組件本身
          give() { this.$emit("receive", { header: this }); }
          //父組件的接收處理方法
            getData(data){
               console.log(data.header)//子組件本身
               console.log(data.header.msg)//子組件屬性
               data.header.myMethod()//子組件方法
            }
    • 父子組件通信的練習:
      • 父組件
        <template>
          <div>
            <div
              id="example"
              style="width:200px;height:200px;background:#ff0;font-size:25px;line-height:200px;text-align:center;margin:20px"
            >我是父組件</div>
            <Header @receive="getData" :message="msg"></Header>
          </div>
        </template>
        <script>
        import Header from "./header";
        export default {
          name: "example",
          data() {
            return {
              msg: "我是你爸爸"
            };
          },
          components: {
            Header //子組件
          },
          methods: {
            getData(data){
              this.msg = data.changeMessage;
            }
          },
          mounted() {}
        };
        View Code
      • 子組件
        <template>
          <div id="header">
            <div
              style="width:200px;height:200px;background:pink;font-size:25px;padding-top:100px;text-align:center;margin:20px"
            >我是子組件
            <p>爸爸說:{{message}}</p>
            </div>
            <button @click="give">給父組件傳值</button>
          </div>
        </template>
        <script>
        export default {
          props:["message"],
          name: "thesisHeader",
          data() {
            return {
              msg: "爸爸,我是您的寶貝女兒啊!"
            };
          },
          methods: {
            myMethod() {
              alert("我是屬於子組件的方法");
            },
            give() {
              this.$emit("receive", {changeMessage : "照顧好自己"});
            }
          },
          mounted() {}
        };
        </script>
        View Code
  • 非父子組件之間的通信
    • provide/inject  
      provide/ inject 是vue2.2.0新增的api, 簡單來說就是父組件中通過provide來提供變量, 然后再子組件中通過inject來注入變量。這里不論子組件嵌套有多深, 
      只要調用了inject 那么就可以注入provide中的數據,而不局限於只能從當前父組件的props屬性中回去數據
    • ref/refs
    • eventBus( bus是空的vue的實例,把它放在根組件的data里面,並且bus上面有兩個方法$emit,$on)
      eventBus 又稱為事件總線,就像是所有組件共用相同的事件中心,可以向該中心注冊發送事件或接收事件, 所以組件都可以通知其他組件。
      • 缺點:當項目較大,就容易造成難以維護的災難;
      • 使用步驟
        • 在父組件中,存在兩個兄弟組件,父組件A的代碼為:
          <template>
            <div class="parent">
              <son></son>
              <grand-son></grand-son>
            </div>
          </template>
          <script>
          import son from "./son";
          import grandSon from "./grandson";
          export default {
            name: "parent",
            data() {
              return {};
            },
            components: {
              son,
              grandSon
            },
            methods: {}
          };
          </script>
          <style scoped>
          </style>
          View Code
        • 在子組件son中通過$emit發送事件
          <template>
            <div class="son">
              <p @click="handler">我要告訴我的弟弟,我多大了</p>
            </div>
          </template>
          <script>
          import eventBus from "../../eventBus/bus";
          export default {
            name: "son",
            data() {
              return {
                age: "18"
              };
            },
            methods: {
              handler() {
                eventBus.$emit("handlerData", { data: this.age });
              }
            }
          };
        • 在另一個子組件中通過$on來監聽接收到的消息
          <template>
            <div class="grandson">我的哥哥今年{{age}}歲了</div>
          </template>
          <script>
          import eventBus from "../../eventBus/bus";
          export default {
            name: "",
            data() {
              return {
                age: ""
              };
            },
            // inject: ["age"],
            methods: {},
            mounted() {
              eventBus.$on("handlerData", data => {
                this.age = data.data;
              });
            }
          };
          </script>
          <style scoped>
          </style>
        • 如果想移除事件,通過
          eventBus.$off('事件名稱',{})
    • vuex

        可以參考本博客寫的,如果使用vuex:https://www.cnblogs.com/wxh0929/p/11984417.html

    • localStorage與sessionStorage
      • 實現方式
        window.localStorage.setItem(key,keyValue);
        window.localStorage.getItem(key)
      • 優點:代碼實現簡單,通信比較容易;缺點:數據和狀態比較混亂,不太容易進行維護;
      • 注意:
        注意用JSON.parse() / JSON.stringify() 做數據格式轉換 localStorage / sessionStorage可以結合vuex, 實現數據的持久保存,同時使用vuex解決
        數據和狀態混亂問題.
  • 組件通信
  • slot分發內容

3、混合

作用:實現代碼抽離復用 
    eg:當創建了多個vue對象,都要用到一個方法,不應該將這個方法復制到每個對象的methods里面,應該采用'mixin'
    <script>
        var myMixin = {
            methods : {//公共方法
               test(){console.log(test)} 
            }
        }
        var vm1 = new Vue({
            el : "#box",
            mixins : [myMixin],
            template:{}
        })
        var vm2 = new Vue({...})//box2
    </script>

注意:在混合的過程當中,如果出現同名的鈎子函數,兩者都會被調用,而混合的鈎子函數在自己的鈎子函數前面執行

如果出現同名的methods、components等值為對象的選項時,則自己組件的選項優先(只執行自己組件內的);

4、動態組件(參考資料https://www.cnblogs.com/xiaohuochai/p/7395694.html

  • 定義:多個組件使用同一個掛載點,然后動態的在它們之間切換;我們可以使用保留的<component>元素,動態的綁定到它的is屬性;
    <template>
      <div id="home">
        <el-radio-group v-model="checkId" @change="change">
          <el-radio label="Header">頭部</el-radio>
          <el-radio label="Footer">底部</el-radio>
        </el-radio-group>
        <div class="checkComponent">
          <component :is="checkId"></component>
        </div>
      </div>
    </template>
      </div>
    </template>
    <script>
    import Header from "./header";
    import Footer from "./footer";
    export default {
      name: "example",
      data() {
        return {
          checkId: "Header"
        };
      },
      components: {
        Header, //子組件
        Footer
      },
      methods: {
        change(value) {
          this.checkId = value;
        }
      }
    };
    </script>
    View Code
  • 特點
    • 緩存:<keep-alive>包裹動態組件時,可以把切換出去的組件保留在內存中,而不是銷毀,從而保留它的狀態或者避免重復渲染DOM,提高性能;
      <template>
        <div id="home">
          <el-radio-group v-model="checkId" @change="change">
            <el-radio label="Header">頭部</el-radio>
            <el-radio label="Footer">底部</el-radio>
          </el-radio-group>
          <div class="checkComponent">
            <keep-alive>
              <component :is="checkId"></component>
            </keep-alive>
          </div>
        </div>
      </template>
        </div>
      </template>
      <script>
      import Header from "./header";
      import Footer from "./footer";
      export default {
        name: "example",
        data() {
          return {
            checkId: "Header"
          };
        },
        components: {
          Header, //子組件
          Footer
        },
        methods: {
          change(value) {
            this.checkId = value;
          }
        }
      };
      </script>
      View Code
    • include和exclude屬性允許組件有條件的緩存
       <keep-alive include="Header">//只緩存header   footer不緩存,如果要緩存多個,用逗號分隔
         <component :is="checkId"></component>
       </keep-alive>
      • 字符串方式:include = "a,b"
      • 數組方式:v-bind:include = [ "a",“b” ]
      • 正則方式:v-bind:include = "/a | b /";
    • activated和deactivated鈎子函數
      • 定義:使用<keep-alive>會將數據保留在內存中,如果要在每次進入頁面的時候獲取最新的數據,需要在activated階段獲取數據,承擔原來created鈎子中獲取數據的任務;
  • 參考資料:https://www.jianshu.com/p/0b0222954483    組件實際應用:實現組件的有條件緩沖

5、遇到的問題

 


免責聲明!

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



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