注:本教程所使用的vue版本爲 2.5.16
MVC與MVVM
MVC(Model-View-Controller):javascript
M指的是從後臺獲取到的數據, V指的是顯示動態數據的html頁面, C是指響應用戶操做、通過業務邏輯處理後去更新視圖的過程,在此過程當中會致使對view層的引用。
這裏咱們發現咱們網站的大量代碼都被放在Controller,致使Controller代碼臃腫;並且不利於單元測試,由於業務邏輯處理和視圖更新操做會混雜在一塊兒。css
MVVM (Model-View-ViewModel):
MVVM是MVC的一個衍生模型,這裏的 ViewModel 把業務邏輯處理、用戶輸入驗證等跟視圖更新操做分離開了。MVVM是數據驅動的,咱們只須要關心數據的處理邏輯便可,它會經過模板渲染去單獨處理視圖的更新而不須要咱們親自去操做Dom元素。html
實例化Vue對象
<div id="app"> </div> <script src="js/vue.min.js"></script> <script> //調用Vue的構造函數建立實例對象 var vm = new Vue({ //根節點選擇器 el: '#app', //數據對象 data: { message: 'hello world', }, //渲染模板 template: '<div>message: {{ message }}</div>' }) </script>
Vue會將渲染模板結合數據對象生成的html結構替換掉根節點,只要數據對象上的message發生改變,插值處的內容就會跟着改變,上述例子的實際效果以下:vue
<body> <div>message: hello world</div> </body>
模板語法
上面的雙大括號綁定是vue最經常使用的數據綁定方式,除了雙大括號還可使用v-text屬性進行綁定java
<div>message: <span v-text="message"></span></div>
若是要綁定html結構的話,須要使用到v-html指令,不然vue會把這段html代碼當作字符串直接綁定到對應位置數組
new Vue({ el: '#app', data:{ message: 'hello world', html: '<span>hello world</span>' }, template: '<div>message: <span v-html="html"></span></div>' })
須要綁定html元素特性的時候須要使用v-bind指令,v-bind能夠省略app
<div v-bind:id="id"></div> <div :id="id"></div>
雙大括號的插值方法還可使用js表達式,這些表達式會在所屬 Vue 實例的數據做用域下被解析dom
{{ number + 1 }} {{ boolean ? 'true' : 'false' }} {{ message.split('').reverse().join('') }} <div :id="'is' + id"></div>
注意,這裏的javascript語句只能是單個表達式,其餘的聲明變量、流程控制語法都不會生效函數
事件綁定
除了數據綁定外,vue還幫咱們優化了事件綁定流程,指令爲v-on,可縮寫爲@,後面是事件名稱單元測試
<a v-on:click="console.log(1)">打印1</a> <a @click="console.log(1)">打印1</a>
僅僅一句js表達式是不夠支撐咱們的平常開發的,因此vue給咱們提供了自定義事件處理方法
new Vue({ el: '#app', data: { message: 'hello world' }, //log爲methods中定義的函數名,vue會默認把原生DOM事件對象當作參數傳處處理函數中 template: '<a @click="log">Click Me</a>', methods: { log: function(event){ console.log(this.message); event.stopPropagation(); } } });
除了直接綁定到一個方法,也能夠在內聯 JavaScript 語句中調用方法
<!-- 使用內聯語法後,原生DOM對象須要手動傳入(變量名變動爲 $event ) --> <a @click="log(1, $event)">Click Me</a>
計算屬性 computed
模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。例如這個字符串反轉:
<div>{{ message.split('').reverse().join('') }}</div>
這種狀況咱們能夠用 computed 解決
new Vue({ el: '#app', data: { message: 'hello' }, template: '<div>{{ reversedMessage }}</div>', //olleh computed: { reversedMessage: function(){ return this.message.split('').reverse().join(''); } } });
這裏看渲染模板就直觀多了
監聽屬性 watch
Vue 提供了 watch 這種通用的方式來觀察和響應 Vue 實例上的數據變更
<div id="app"> <p>問題: <input v-model="question"></p> <p>{{ answer }}</p> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { question: '', answer: '請先輸入問題', timer: undefined, }, watch: { question: function(newVal, oldVal){ if(this.timer){ clearTimeout(this.timer); } var _this = this; this.answer = '等待中止輸入...'; setTimeout(function(){ _this.answer = 'Yes!!!'; }, 1000); } } }); </script>
表單綁定,指令爲 v-model
v-model 指令在表單 <input> 及 <textarea> 元素上建立雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。v-model 會忽略全部表單元素的 value、checked、selected 特性的初始值而老是將 Vue 實例的數據做爲數據來源。
<div id="app"> <input v-model="message" placeholder="寫點什麼。。"> <!-- 經過v-model綁定message後, 在input輸入的文字會實時更新到數據對象上, 從而自動渲染在下面的p標籤上 --> <p>Message: {{ message }}</p> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { message: '' } }) </script>
單個複選框
<input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> data: { checked: true }
多個複選框的狀況下,把v-model綁定到同一個數組便可:
<input type="checkbox" value="blue" id="blue" v-model="checkedColor"/> <label for="blue">blue</label> <input type="checkbox" value="red" id="red" v-model="checkedColor"/> <label for="red">red</label> data: { checkedColor: [] }
單選按鈕
<input type="checkbox" value="true" id="true" v-model="checkedRadio"/> <label for="true">true</label> <input type="checkbox" value="false" id="false" v-model="checkedRadio"/> <label for="false">false</label> data: { checkedRadio: '' }
條件渲染
在javascript語法中有if-else等流程語句讓程序執行不一樣的代碼塊,在vue中一樣有 v-if
、v-else-if
、v-else
這些指令控制某些節點的顯示與否
<div id="app"> <div v-if="isShow"> <label>用戶名</label> <input type="text" placeholder="請輸入用戶名" /> </div> <!-- v-else 元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,不然它將不會被識別。 --> <div v-else> <label>郵箱</label> <input type="text" placeholder="請輸入郵箱" /> </div> <button @click="switchDom">切換輸入框類型</button> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { isShow: true }, methods: { switchDom: function(){ this.isShow = !this.isShow; } } }) </script>
在上述例子中,咱們點擊 button 會顯示不一樣的dom,可是若是咱們在input裏面輸入文字再進行切換的時候會發現,輸入的文字並不會被清除,這是由於vue的 就地複用 策略致使的。vue爲了儘量高效地渲染dom元素,一般會複用已有元素而不是從頭開始渲染,若是不想vue複用這些元素,咱們能夠添加一個具備惟一值的 key 屬性
<input type="text" placeholder="請輸入用戶" key="username"/> <input type="text" placeholder="請輸入郵箱" key="email"/>
另外一個用於根據條件展現元素的選項是 v-show 指令。用法大體同樣:
<p v-show="show">Hello!</p>
v-if
與 v-show
區別:v-if
是惰性渲染,在初始渲染時條件爲假,什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊;在切換過程當中條件塊內的事件監聽器和子組件會被銷燬和重建。v-show
無論初始條件是什麼,元素總會被渲染,切換的只是css的display屬性
列表渲染
咱們用 v-for
指令根據一組數據表進行列表渲染。v-for
指令須要使用 item in list
的語法,list指的是原數據數組,item指的是迭代的數組元素。v-for
指令還支持一個可選的表示當前迭代元素索引的第二個參數 (item, index) in list
<div id="app"> <ul> <li v-for="item in list"> <!-- 效果同樣 --> <!-- <li v-for="item of list"> --> {{ item.text }} </li> </ul> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { list: [ {text: 'blue'}, {text: 'red'}, {text: 'yellow'}, ] } }) </script>
除了數組,v-for
指令還能夠經過一個對象的屬性來迭代,v-for
指令最多能夠支持3個參數,第二第三個可選。
<li v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </li> data:{ object: { firstName: 'John', lastName: 'Doe', age: 26 } } //結果 0. firstName: John 1. lastName: Doe 2. age: 26
Vue的生命週期
根據上圖作了一個測試例子,列出了每一個生命週期對應的不一樣屬性的狀態
<div id="app"> {{message}} </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { message : "hello" }, beforeCreate: function () { console.group('beforeCreate 建立前狀態===============》'); console.log("%c%s", "color:red" , "el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //undefined console.log("%c%s", "color:red","message: " + this.message) }, created: function () { console.group('created 建立完畢狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeMount: function () { console.group('beforeMount 掛載前狀態===============》'); console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, mounted: function () { console.group('mounted 掛載結束狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 var _this = this; setTimeout(function(){ _this.message = 'hello world!!'; }, 3000); }, beforeUpdate: function () { console.group('beforeUpdate 更新前狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, updated: function () { console.group('updated 更新完成狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, beforeDestroy: function () { console.group('beforeDestroy 銷燬前狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, destroyed: function () { console.group('destroyed 銷燬完成狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message) } }) </script>