title: 窺探Vue.js2.0 date: 2016-09-27 10:22:34 tags: vue category: 技術總結
窺探Vue.js2.0
令人興奮的Vue.js 2.0正式版發布了。新的VUE框架有重大的改進,添加了一些新的特性,比如 Virtual-DOM,Server Side Rendering(服務端渲染),支持 JSX/Hyperscript 語法等,並去掉了一些已有的方法。
新的特性
Virtual DOM
虛擬DOM 是一個真正DOM的抽象、輕量級版本。 它主要作用是讓你操控虛擬DOM,來代替直接操控真是DOM。之后虛擬DOM和真是DOM比較,把兩者差異的部分再渲染到頁面上。 這比直接操作DOM的速度要快的多,因為DOM操作是非常耗時的,加入虛擬DOM對比之后減少不必要DOM操作花的時間比進行DOM和虛擬DOM對比的時間要多,所以采用虛擬DOM是更有優勢的。更多關於 什么是Virtual Dom?什么是DOM ?
Server Side Rendering
我們知道 JavaScript 除了可以在瀏覽器上運行,也可以在服務器端運行,比如 Node.js 平台。JavaScript在服務端生成HypetText或者HTML文件發送給客戶端的瀏覽器。 服務器端渲染(SSR)有幾個特點:
- 服務器端渲染可以讓搜索引擎更容易讀取頁面的meta信息以及其他SEO相關信息,大大增加網站在搜索引擎中的可見度。
- 更快的來生成內容和相對比較慢的來進行數據交互。
- 首次加載頁面的速度加快。客戶端渲染的一個缺點是,當用戶第一次進入站點,此時瀏覽器中沒有緩存,需要下載代碼后在本地渲染,時間較長。而服務器渲染則是,用戶在下載的已經是渲染好的頁面了,打開速度比本地渲染快。
- 服務器端多更多的事情,讓客戶端做盡量少的事,從而可以照顧(不拋棄)那些手機或電腦性能比較特別差又想上網的用戶。
現在已有的后台服務端框架都可以實現如 Meteor ,Express ,Sails。
JSX (Javascript XML syntax transform)
JSX是使用XML語法編寫Javascript的一套解析工具,X代表XML。這不是要由引擎或瀏覽器中實現,它的目的是通過各種預處理程序(transpilers),將這些標記轉換成標准的ECMAScript。實質上也就是說JSX是一個語法糖,每一個XML標簽都會被JSX轉換工具轉換成純Javascript代碼。如:
// Using JSX to express UI components.
let dropdown =
<Dropdown>
A dropdown list
<Menu>
<MenuItem>Do Something</MenuItem>
<MenuItem>Do Something Fun!</MenuItem>
<MenuItem>Do Something Else</MenuItem>
</Menu>
</Dropdown>;
render(dropdown);
Hyperscript
Hyperscript是JavaScript創建超文本(HTML)的語法。客戶端、服務器端都支持。 如下代碼及其輸入
var h = require('hyperscript')
h('h1.fun', {style: {'font-family': 'Comic Sans MS'}}, 'Is Guybrush a French name?')
舍棄和修改的部分
接下來我將用一個實例演示vue這兩個版本間的一些不同的地方。主要包括:
- events
- .sync 依賴的棄用
- vm.$set() 方法的棄用
- component 組件的模板
- 一些其他的變化
.sync 修飾符
當父子組件傳參的時候我們肯能會想到把.sync修飾符用於子組件的prop上,以實現數據的雙向綁定。從現在起,props 就只有單項綁定了。如果當一個組件需要修改他作用域外的數據的時候,它只能emit一個事件(vm.$emit(event,[..args])事件)讓監聽器回調,而不是再依賴於之前的隱士綁定了。 去掉這個修飾符的目的是為了防止子組件在父組件的范圍內所引發的副作用。這樣,當你閱讀一個組件的代碼的時候,就更加容易理解它做了什么以及是如何影響其他部分的。
vue 版本的比較
為了盡可能闡述清楚這兩版本的差異,我用一個實例來解釋吧。比如 這有個用戶可以購買寶石的頁面,其中有個Vue實例和一個購物車組件。在購物車組件里,用戶選擇了要購買多少個然后這個購物車組件把這個變化反映給它的父組件。
用vue 1.0.*
用vue1.0.* 的話,要實現這個功能是顯而易見的。我們創建一個變量,來存儲所選擇的數量,我們把它傳遞給購物車組件,等待選擇數量來更改它的值,在父組件上在寫一個computed屬性來計算總價格。
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.23/vue.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<meta charset="utf-8">
<title>Gem Market</title>
</head>
<body>
<div class="container">
<h1>Gem Market</h1>
<cart :quantity.sync="quantity"></cart>
<hr>
<div>
<h3>You have to pay <strong>{{ gold }}</strong> pieces of gold.</h3>
</div>
</div>
<template id="cart-template">
<h2>How many gems would you like to buy?</h2>
<p>1 emerald costs 100 pieces of gold.</p>
<input v-model="quantity" class="form-control" number>
</template>
</body>
<script type="text/javascript">
Vue.component('cart', {
template: "#cart-template",
props: { quantity : 0 }
});
new Vue({
el: '.container',
data : function () {
return {
quantity: 0
};
},
computed:{
gold: function(){
return this.quantity * 100
}
}
})
</script>
</html>
用vue 2.0
如果切換到vue 2.0,運行上面的代碼,我們會得到以下警告。
[Vue warn]: Component template should contain exactly one root element. 為了去掉這個警告我們在 template內部加上一個Div元素。
<template id="cart-template">
<div>
<h2>How many gems would you like to buy?</h2>
<p>1 emerald costs 100 pieces of gold.</p>
<input v-model="gems" class="form-control" number>
</div>
</template>
刷新瀏覽器,發現這個警告消失。 當我們改變這個變量的時候,我們得到這個警告。
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “gems” (found in component:
這是因為在vue2中,直接修改prop是被視作反模式的。由於在新的渲染機制中,每當父組件重新渲染時,子組件都會被覆蓋,所以應該把props看做是不可變對象 1。 不能更改 quantity prop使其和父組件同步 , 而是讓應該這個組件提交個事件給父組件,可以 watch quantity 變量,如果變量發生改變就emit事件,所以這里壓根不需要 prop。 由於 vm.$dispatch 和 vm.$broadcast 被棄用了,我們用一個集中式事件集線器觸發事件 來實現組件間的通訊。建議閱讀 升級提示。 因為在Vue實例中已經實現了事件發射器接口emit,實際開發中可以用空的Vue實例作為事件發射器。 讓我們看回這個例子,創建一個emitter,重構一下Cart組件。
let bus = new Vue()
Vue.component('cart', {
template: "#cart-template",
data () {
return {quantity : 0 }
},
watch: {
'quantity': function (quantity, oldQuantity) {
console.log('quantity changed from %s to %s', oldQuantity, quantity)
bus.$emit('quantity-changed', quantity)
}
}
});
檢查下瀏覽器控制台,會發現每次改變quantity的值,都會在控制台上打印出變化前后的值。 然后,就是要更新父Vue的實例來監聽,並更新相應的數據。
new Vue({
el: '.container',
data : function () {
return {
quantity: 0
};
},
created: function () {
// store this to use with Vue.set
var temp = this;
bus.$on('quantity-changed', function (quantity) {
// vm.$set deprecated
Vue.set(temp, 'quantity', quantity)
})
},
computed:{
gold: function(){
return this.quantity * 100
}
}
})
注意
vm.$set() 被棄用了,所以我們用全局方法 Vm.set()。 因為不能使用prop去同步,所以我們這樣引用購物車組件:
<cart></cart>
’lazy’ 和 ’number’ 修飾符更改了,所以我們這樣修改下 購物車組件里的Input
// Vue 1
<input v-model=”quantity” class=”form-control” number>
// Vue 2
<input v-model.number=”quantity” class=”form-control”>
好了,這就是所有的了,你可以查看最終代碼,然后自己把玩一番吧 JSFiddle。
其他資源
下面列出的資源清單強烈建議你去看下:
- Vue2官方公告
- Vue 2 LiveStream 在線看 Evan You 演示的一些新特性和棄用的部分。
- Vue 2 Features查看所有新功能,棄用和其他的變化。
本文參考
- Why is React's concept of Virtual DOM said to be more performant than dirty model checking?
- https://dotdev.co/peeking-into-vue-js-2-part-1-b457e60c88c6#.yksm6uco2
-
在面向對象和函數式編程,不可變對象的狀態在創建后不能修改↩