14/09/2018, 23:16

Tìm hiểu về component và vòng đời của Vue.js - Vue.js Lifecycle Hooks

Chào mọi người hôm nay mình lại đến tháng đây. Hôm nay mình sẽ chia sẻ với mọi người về component trong Vue js nhé .

Như chúng ta đã biết trong lập trình việc lặp đi lặp lại 1 đoạn code gây ra nhiều rắc rồi như tăng thời gian viết code, khó sử dụng lại, khi có thay đổi gì là lại phải sửa ở tất cả mọi nới có code lặp lại. Như thế rất khó chịu đúng không. Nhưng đến với Vue Js nó đã cũng cấp cho chúng ta 1 khái niệm mới đó là Component giúp cho code được cấu trúc thành phần cơ bản

Và nó đưa code chúng ta theo hướng module hoá nghĩa là tất cả những đoạn code liên quan đến 1 đối tượng nào đấy sẽ được tập trung lại 1 chỗ. Ví dụ như bạn tạo 1 danh sách sản phẩn thì có phần list, create, edit, và delete vậy. Như bình thường chúng ta sẽ viết tất cả html vào 1 file, rồi js cũng viết tất cả xử lý vào 1 file đúng không. Đến khi sửa lỗi thì sao nhỉ, Lúc đấy mới biết code chúng ta thối như thế nào.

Nhưng đến với Vue js Component thì mọi thứ đã được xử lý và tổ chức 1 cách rất hợp lý rồi nhé.

Tạo và đăng ký component

<div id="app">
    <tdc-component></tdc-component>
</div>
<script src="https://unpkg.com/vue@2.4.2" type="text/javascript"></script>


<script>
   Vue.component('tdc-component',{
          template: '<h1>Chào mừng bạn đến với VUE JS</h1>'
   });
   var app = new Vue({
        el: '#app',
   });
</script>

Kết quả : 

--------

Xem qua sờ đồ sau :

1. Khởi tạo component

beforeCreate()

Hook beforeCreate sẽ được gọi đồng bộ ngay sau khi Vue được khởi tạo. Các data (dữ liệu) và event (sự kiện) chưa được thiết lập. Ví dụ với đoạn code sau:

<script>
    export default {
    	data() {
    		return {
    			content: 'data in beforeCreate'
    		}
    	},
    	beforeCreate() {
    		console.log('beforeCreate Hook')
    		console.log(this.content)
    	}
    }
</script>

 

Sau khi chạy đoạn code trên, mở cửa sổ console của trình duyệt và có kết quả:

 

Dễ dàng nhận thấy this.content không nhận được giá trị từ data và log ra undefined.

created()

Lúc này, các data và event đã được thiết lập.

<script>
    export default {
    	data() {
    		return {
    			content: 'data in created'
    		}
    	},
    	created() {
    		console.log("Created Hook")
    		console.log(this.content)
    	}
    }
</script>

 

Và kết quả:

Tuy nhiên, Các template và Virtual DOM chưa được mount và render, bạn không thể truy cập đến $el.

<script>
    export default {
    	created() {
    		console.log("This Object:")
    		console.log(this)
    		console.log("This $el:")
    		console.log(this.$el)
    	}
    }
</script>

Kết quả:

 

$el là phần tử HTML mà Vue quản lý hoặc có thể hiểu là thành phần HTML bao toàn bộ một Vue component. $el sẽ có thể truy cập nếu chúng ta option (cài đặt) cho el.

cung cấp cho lớp Vue một phần tử DOM để các lớp Vue gắn kết (mount) vào. Nó có thể là CSS selector hoặc phần tử HTML. Khi el được mounted vào lớp Vue, chúng ta có thể truy cập đến các phần tử bằng cách gọi this.$el hoặc vm.$el ( khi khai báo vm = new Vue).

Nếu có option el, ngay lập tức lớp Vue sẽ thực hiện biên dịch (compile). Mặt khác, nếu không có option el, người dùng sẽ cài đặt thủ công bằng cách gọi this.$mount hoặc vm.$mount().

2.Mounting ( chèn phần tử DOM)

Mounting hook được sử dụng nhiều nhất. Ở giai đoạn này, Vue cho phép truy xuất vào các component ngay lập tức, sau khi component được render lần đầu tiên.

Sử dụng: Nếu muốn thay đổi DOM trước hoặc sau khi render.

Không sử dụng được: Liên quan đến dữ liệu (data).

beforeMount()

Hook này khá ít dùng, có thể cần không động đến. Nó sẽ thực hiện sau khi render function hoàn tất và trước khi render chính thức phần tử DOM của lớp Vue. Tại đây, các bạn vẫn chưa thể truy cập đến $el. Cũng giống như created() hook ở trên, tuy nhiên mọi thứ như template, DOM đã sẵn sàng để render vào $el.

Chú ý: Hook này không được gọi ở server-side.

mounted()

Trong mounted hook, chúng ta có thể hoàn toàn truy cập đến component, template và DOM thông qua this.$el (hoặc vm.$el).

Hook này được sử dụng thường xuyên. Có thể sử dụng để thay đổi DOM, fetching data ( cũng có thể dùng created() hook) hay tích hợp các thư viện khác.

Ví dụ sau sẽ mô tả sự khác nhau giữa created() hook và mounted() hook:

export default {
    	mounted() {
    		console.log("This Object:")
    		console.log(this)
    		console.log("This $el:")
    		console.log(this.$el)
    	}
    }

Nếu như ở trên created() hook không thể truy cập được this.$el thì mounted() hookhoàn toàn có thể:

3. Updating (Thay đổi và Re-render)

Updating hook sẽ được gọi khi có sự thay đổi trong component. Nó sẽ khiến component re-render. Hook này sẽ đưa component của bạn vào chu kỳ watch-compute-render.

Sử dung: Khi muốn biết component nào được re-render.

Không sử dụng: Khi muốn biết các thay đổi ở component. Thay vào đó, hãy dùng các thuộc tính của VueJS như watcher hay computed.

beforeUpdate()

Hook này được gọi ngay sau khi có sự thay đổi dữ liệu (data) trên component. Và được thực hiện trược khi DOM re-render. Bạn có thể lấy được dữ liệu mới (new data) tại đây.

Ví dụ đoạn code sau:

<template>
	<div class="pull-right">{{ data }}</div>
</template>

<script>
    export default {
    	data() {
    		return {
    			data : 0
    		}
    	},
    	created() {
    		setInterval(() => {
    			this.data++
    			console.log("Change data count of component: " + this.data)
    		}, 1000)
    	},
    	beforeUpdate() {
    		console.log("Data beforeUpdate: " + this.data)
    	}
    }
</script>

Ở đây mình sẽ cho data đếm tăng dần sau mỗi 1 giây. Mở log trên trình duyệt ta nhận được:

Quá trình diễn ra lần lượt như sau. Hook created() được chạy đầu tiên và thay đổi giá trị của data. Ngay khi nhân được sự thay đổi dữ liệu của component, beforeUpdate() hook sẽ lấy dữ liệu data mới được update và log ra console. Cuối cùng mới đến DOM re-render và hiển thị giá trị ở template (như giá trị 6 phía bên trái hình ảnh).

updated()

Sau hook beforeUpdate(), sau khi DOM đã re-render. Dữ liệu truy xuất được là dữ liệu sau khi được thay đổi của component, cũng là dữ liệu lấy được trong beforeUpdate().

Một chú ý ở đây: Nên tránh việc thay đổi data khi sử dụng hook này. Mà thay vào đó hãy sử dụng các thuộc tính watcher hay computed.

 

Để minh hoạ quá trình Mounting, chúng ta cùng xét đoạn code sau:

<template>
	<div class="pull-right">{{ data }}</div>
</template>

<script>
    export default {
    	data() {
    		return {
    			data : 0
    		}
    	},
    	created() {
    		setInterval(() => {
    			this.data++
    			console.log("Change data count of component: " + this.data)
    		}, 1000)
    	},
    	updated() {
    		console.log("Data updated: " + this.data)
    	},
    	beforeUpdate() {
    		console.log("Data beforeUpdate: " + this.data)
    	}
    }
</script>

Kết quả:

Như vậy, hook updated() thực hiện ngay sau hook beforeUpdate(), sau khi DOM re-render. Dữ liệu trong hook updated() cũng giống hook beforeUpdate().

 

4. Destruction (Huỷ bỏ)

Destruction Hook dùng để thực hiện các hành động khi component của bạn bị huỷ bỏ. Hay nói cách khác là xoá khỏi DOM. Nếu bạn sử đã từng sử dụng Vue Router hay tạo các ứng dụng SPA thì chắc chắn sẽ hiểu rõ. Việc huỷ bỏ component cũ và thay thế component mới sẽ tiết kiệm bộ nhớ rất nhiều cũng như cải thiện tốc độ. Đó cũng là một điểm mạnh của Vue component.

beforeDestroy()

beforeDestroy() hook được gọi ngay trước khi huỷ bỏ component. Đây là giai đoạn thích hợp nhất để bạn xoá bỏ các data, events để dọn dẹp.

destroyed()

destroyed() hook được gọi khi component đã bị xoá bỏ khỏi DOM.

Chúng ta cùng xét ví dụ:

<template>
	<div id="root" class="pull-right">
	  <test-component v-if="show"></test-component>
	  <button class="btn btn-default" @click="show = !show">Action</button>

	</div>
</template>

<script>
	import Vue from 'vue'

	Vue.component('test-component', {
		template: '<div>VueJS {{ content }}</div>',
		data() {
		    return {
		    	content : 'ITMagical',
		    	interval : ''
		    }
	  	},
	  	beforeDestroy() {
	  		console.log('beforeDestroy')
	  	},
	  	destroyed() {	  		
	  		console.log('destroyed && content = ' + this.content)
	  	},
	  	created() {
	  		this.interval = setInterval(() => {
	  			console.log('not removed')
	  		},1000)
	  	}
	})

	export default {
		data() {
			return {
		      	show : true
		    }
		}
	}
</script>

Chức năng của đoạn code trên chỉ đơn giản là Ẩn/Hiện nội dung “VueJS ITMagical” và log ra console “not remove” sau mỗi giây. Dữ liệu này nằm trong một Vue component đặt tên là test-component.

Việc ẩn/hiện ở đây bản chất là mình sẽ huỷ bỏ / thêm component. Và vấn đề cần nhắc đến là việc clean up data khi xoá component.

Trước tiên, ta chạy đoạn code trên:

 

Đây là ví dụ khi chưa xoá dữ liệu ở beforeDestroy() hook. Các bạn có thể thấy, sự kiện log ra console “not remove” vẫn tiếp tục chạy kể cả khi component đã bị xoá khỏi DOM. Điều này gây lãng phí tài nguyên, là nguyên nhân gây ra giật lag. Nếu mình set thời gian không phải 1s thì có lẽ trình duyệt máy mình cũng sẽ treo :D.

Do đó, chúng ta nên clean up dữ liệu ngay tại beforeDestroy() hook. Đoạn code trên mình sửa lại:

<template>
	<div id="root" class="pull-right">
	  <test-component v-if="show"></test-component>
	  <button class="btn btn-default" @click="show = !show">Action</button>

	</div>
</template>

<script>
	import Vue from 'vue'

	Vue.component('test-component', {
		template: '<div>VueJS {{ content }}</div>',
		data() {
		    return {
		    	content : 'ITMagical',
		    	interval : ''
		    }
	  	},
	  	beforeDestroy() {
	  		this.content = null
	  		delete this.content
	  		clearInterval(this.interval)
	  		console.log('beforeDestroy')
	  	},
	  	destroyed() {	  		
	  		console.log('destroyed && content = ' + this.content)
	  	},
	  	created() {
	  		this.interval = setInterval(() => {
	  			console.log('not removed')
	  		},1000)
	  	}
	})

	export default {
		data() {
			return {
		      	show : true
		    }
		}
	}
</script>

Và kết quả:

+2