1、組件的定義
- 組成:
- template:包裹HTML模板片段(反映了數據與最終呈現給用戶視圖之間的映射關系)
- 只支持單個template標簽;
- 支持lang配置多種模板語法;
- script:配置Vue和載入其他組件或者依賴庫
- 只支持單個script標簽;
- 支持通過import方式載入其他.vue后綴的組件文件;
- style:設置樣式
- 支持多個style標簽;
- 支持scoped屬性,css只應用於當前組件的元素中;
- 支持lang配置多種預編譯語法;
- template:包裹HTML模板片段(反映了數據與最終呈現給用戶視圖之間的映射關系)
- 局部組件:這里是一個三級地址組件
- 首先看一下目錄:在./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;
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> {{$t('message.common.provinceOrDirectly')}} </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> {{$t('message.common.city')}} </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> {{$t('message.common.districtOrCountry')}} </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>
js文件為:
- 在main.js里面進行全局注冊:(沒有這一步,這是全局組件)
import chinaAddress from 'url'; Vue.use(chinaAddress)
- 在組件里面使用:
<china-address v-model="addressCodeList" @change="addressChange"></china-address>
- 在index.js里面進行全局注冊組件;install是一個默認的方法
-
- 局部組件的步驟如下:
- 定義:
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>
- 在子組件上用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: {}, };
- 對象方式:(驗證)
- 在父組件上用v-bind綁定要傳遞的屬性,若沒有v-bind,則屬性的值始終是 字符串 v-bind:v="msg"
- 傳方法:
- 在使用子組件時,根據傳值的方式將方法進行綁定,:run="run";
- 在子組件中,依舊使用props進行接收,this.run()運行;
- 子組件還可以用這種方式調用父組件的 this.$parent.方法/屬性
- 傳父組件本身:
- 在父組件中,使用子組件時,動態綁定this,即:home="this";
- 子組件接收props:["home"];
- 子組件使用:this.home.父組件的屬性或者方法
- 子組件不能修改父組件傳遞過來的值,因為數據時單向的(參考資料https://www.cnblogs.com/Sky-Ice/p/10456533.html)
- 子組件 -----> 父組件(參考資料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>
<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() {} };
- 子組件發送數據
give() { this.$emit("receive", { msg: this.msg });//$emit用來觸發自定義事件,第一個參數是事件的名稱,第二個參數是傳遞給監聽事件的數據 }
- 父組件在使用子組件的地方進行監聽
<Header @receive="getData"></Header>
- 接收數據,並進行處理
getData(data){ console.log(data)//{msg: "爸爸,我是您的寶貝女兒啊!"} }
- 依舊是父組件Home
- 同理:傳方法與自身都可以,像父組件給子組件的形式 當然,父組件還可以這么調用 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()//子組件方法}
- 步驟:
- 父組件 -----> 子組件(參考資料https://www.cnblogs.com/Sky-Ice/p/9267192.html)
- 父子組件通信的練習:
- 父組件
<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() {} };
- 子組件
<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>
- 父組件
- 父子組件之間的傳值
- 非父子組件之間的通信
- 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>
- 在子組件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('事件名稱',{})
- 在父組件中,存在兩個兄弟組件,父組件A的代碼為:
- vuex
- provide/inject
可以參考本博客寫的,如果使用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解決 數據和狀態混亂問題.
- 實現方式
- localStorage與sessionStorage
- 組件通信
- 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>
- 特點
- 緩存:<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>
- 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鈎子中獲取數據的任務;
- 定義:使用
- 緩存:<keep-alive>包裹動態組件時,可以把切換出去的組件保留在內存中,而不是銷毀,從而保留它的狀態或者避免重復渲染DOM,提高性能;
- 參考資料:https://www.jianshu.com/p/0b0222954483 組件實際應用:實現組件的有條件緩沖
5、遇到的問題