生命周期
new Vue()
初始化一些事件和生命周期 例如:vm.$on, vm.$once, vm.$emit
等
beforeCreate
初始化之前,會先把一些數據方法放在實例中,還沒初始化完,不能操作數據,一般用不到
created
數據,方法初始化完成,可以操作數據,實現響應式綁定,此處一般ajax
獲取數據
必須有el
屬性(有編譯的元素),才能進行掛載,只能有一個根元素,如果有template
屬性(內部html),app
(外部html)中的內容就會被覆蓋掉,就沒有意義了
beforeMount
掛載之前,沒有什么實際意義,會用vm.$el
替換el
mounted
掛載完成,數據和模板掛載好了,真實DOM
渲染完了,可以操作DOM
beforeUpdate
更新之前,頁面依賴的數據有變化就會開始更新
updated
更新之后,一般用watch
方法替代這兩個方法
beforeDestory
銷毀之前,開始移除一些定時器和事件綁定等操作,方法還沒銷毀,
destoryed
銷毀完成
this.$data
:vm上的數據
this.$watch
:監控
this.$el
:當前el元素
this.$set
:后添加的屬性實現響應式變化
this.$options
:實例上的屬性,包括內置的還有自定義的
this.$refs
:所有ref的集合,帶ref屬性的標簽,如果不是通過v-for
循環出來的DOM
元素,只能獲取一個
this.$nextTick()
:異步方法,等待渲染完成后獲取vm
,數據變化后想獲取真實dom,需要等待頁面獲取完成后再去獲取,因此所有dom操作最好都放在this.$nextTick()
中
mounted(){
// console.log(document.getElementsByTagName('p')[0].innerHTML);
console.log(this.$refs.message);
console.log(this.$refs.wrap);
this.arr = [1,2,3,4,5]; // dom的渲染是異步的
this.$nextTick(function () {
// 數據變化后想獲取真實dom,需要等待頁面獲取完成后再去獲取
console.log(this.$refs.wrap.children.length); // 5
});
console.log(this.$refs.wrap.children.length); // 3
}
組件化開發
組件化開發可以提高開發效率,方便重復利用,便與協同開發,更容易被管理和維護。一般根據功能可分為兩種:
1、頁面級組件
,一個頁面是一個組件
2、基礎組件
,將可復用的部分抽離出來
vue中,一個自定義標簽就會被看成一個組件
根據用法劃分:
全局組件
:聲明一次可以在任何地方使用,寫插件的時候用的多
局部組件
:必須要聲明這個組件屬于哪一部分
聲明組件的時候,標簽名不要大寫,多個單詞用- 組件名和定義的名字相同是可以的(首字母可以大寫)
html中用-,JS中轉駝峰也是可以的
組件中的data
必須是函數類型
的,返回一個實例作為組件中的數據
<body>
<!--分類 頁面級組件 一個頁面是一個組件-->
<!--將可復用的部分抽離出來 基礎組件-->
<div id="app">
<my-vue></my-vue>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 一個對象可以看成一個組件
Vue.component('my-vue', {
template: '<div>{{msg}}</div>',
data(){
return {msg: 'vue學習'}
}
});
</script>
</body>
局部組件
局部組件使用的三部曲
1、創建組件
2、注冊組件
3、引用組件
組件是相互獨立的,不能直接跨作用域,vm這個實例也是一個組件,組件中擁有生命周期函數,如果組件共用了數據會導致同時更新,因此要求data
必須是函數類型
的。
子組件不能直接使用父組件的數據(組件間數據傳遞),組件理論上可以無限嵌套
<body>
<div id="app">
<component1></component1>
<component2></component2>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 局部組件使用的三部曲
// 1、創建組件
// 2、注冊組件
// 3、引用組件
let component1 = {
template: '<div>{{msg}}</div>',
data(){
return {msg: '組件2'}
}
};
let component2 = {
template: '<div>組件2</div>',
};
let vm = new Vue({
el: '#app',
components:{
component1,
component2
},
data: {}
});
</script>
組件間的嵌套:
1、被調用的子組件必須先定義,否則就拿不到。
2、哪里要用當前組件,就在哪里通過components注冊,
3、需要在調用的組件中通過標簽的形式引入
理論上是無限嵌套的,單位了好維護,一般最多嵌套3層
<div id="app">
<!--<div>parent-->
<!--<div>child-->
<!--<div>grandson</div>-->
<!--</div>-->
<!--</div>-->
<parent>
<child>
<grandson></grandson>
</child>
</parent>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let grandson = {
template: `<div>grandson</div>`
},
child = {
template: `<div>child<grandson></grandson></div>`,
components:{
grandson
}
},
parent = {
template: `<div>parent<child></child></div>`,
components: {
child
}
};
let vm = new Vue({
el: '#app',
components:{
parent,
},
data: {}
});
</script>
</body>
父組件給子組件傳值:屬性傳遞,
:money=""
是傳值的,傳了一個空值,所以不會用到default
,不在子組件定義這個屬性,這才算不傳值
required: true
:表示該值必須傳遞,不穿就發警告,不能與默認值default
同時出現。
校驗時不會阻斷代碼的執行,只會出現警告
還可以自己定義校驗信息,用validator
方法,里面的參數就是當前傳遞的值,返回true表示通過反之不通過。(用的8多)
<body>
<div id="app">
parent: {{money}}
<!--m屬于子,屬性值屬于父-->
<!--當前組件的屬性=父級的值-->
<child :money="money"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// 父傳子
let vm = new Vue({
el: '#app',
data: {
money: 100,
a: 400
},
components: {
child: {
// 會在當前子組件上聲明一個m屬性,值是父組件的
// 數組的形式可以直接取值,但無法校驗
// props: ['money'], // this.m = money變量 this->child
// 對象的形式可以校驗
props: {
// 子父組件中的屬性名不能重復,控制臺也會報錯
// 父組件的會覆蓋子組件的值
// 可以加個default值,不傳值的時候就用默認值
money: {
// 判斷傳遞值的類
// 如果不帶冒號:,得到的肯定是字符串類型
// 類型不對頁面上依舊會顯示,但控制臺會報類型錯誤
type: [Number],
// default: 0
required: true,
validator(val){ // 參數是當前傳遞的值,返回true表示通過
return val > 300;
}
}
},
template: '<div>child: {{money}}</div>'
}
}
});
</script>
</body>
子組件給父組件傳值:通過發布訂閱的模式,父親綁定一些事件,兒子觸發這個事件,將參數傳遞過去,單向數據流 父組件數據刷新,子組件就刷新,不能子組件直接改父組件的值,要想這樣,就需要子組件先通知父組件要修改值,父組件再去修改
在本例子中,子組件通過點擊事件觸發($emit)自己的child-msg
方法,而該方法又觸發了父組件的moreMoney
方法執行,這樣一來就實現了子組件向父組件傳值
<body>
<div id="app">
parent: {{money}} <button @click="lessMoney">少要點</button>
<!--m屬于子,屬性值屬于父-->
<!--當前組件的屬性=父級的值-->
<!--剛剛那個事件是父級的,訂閱需要在子級做-->
<child :money="money" @child-msg="moreMoney"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
money: 400,
},
methods: {
moreMoney(val){
this.money = val;
},
lessMoney(){
this.money = 200;
}
},
components: {
child: {
props: ['money'],
template: '<div>child: {{money}}
<button @click="getMoney">再來點</button></div>',
methods: {
getMoney(){ // 觸發自己的自定義事件讓父組件的方法執行
this.$emit('child-msg', 800);
}
}
}
}
});
</script>
</body>
sync語法糖的用法
<child :money="money" @update:money="val=>this.money=val"></child>
same as
<child :money.sync="money"></child>
子父組件聲明周期
父組件需要等到子組件掛載完成(mounted)之后才會觸發掛載
mounted(){
console.log(this.$refs.child.$el.innerHTML);
// 1 2 3
// 因為存在DOM映射,所以頁面上的數據實時變化,
// 但是DOM渲染是個異步的過程,這里還新的數據還沒有渲染完
this.$nextTick(() => {
console.log(this.$refs.child.$el.innerHTML);
// 4 5 6
});
},
兄弟組件間相互通信
eventBus
一般不用,了解,發布訂閱模式失敗的原因是因為在不同組件中的this是不一樣的,組件2觸發,組件1監聽,顯然是行不通的。發布訂閱的執行者應該是同一個才能成功,因此就需要有一個第三方實例eventBus
來實現交互。
let brother1 = {
template: '<div>{{color}} <button>變綠</button></div>',
data() {
return {color: '綠色', old: '綠色'}
},
created() {
// 組件1監聽
this.$on('changeRed', (val) => { // 頁面一加載,組件1長一個耳朵來監聽
this.color = val;
})
}
};
let brother2 = {
template: '<div>{{color}} <button @click="change">變紅</button></div>',
data() {
return {color: '紅色', old: '紅色'}
},
methods: {
change() {
// 組件2發布
this.$emit('changeRed', this.old);
}
}
};
eventBus
使用,創建一個Vue實例,在兄弟組件中發布訂閱都用這個實例來操作即可,但是觸發的方法名不能重復,否則就亂套了
let eventBus = new Vue();
eventBus.$on('changeRed', (val) => {
this.color = val;
});
change() {
eventBus.$emit('changeRed', this.old);
}