Vue是一款前端開發框架,如今的框架自己是幫咱們把一些重複的通過驗證的模式,抽象到一個設計好的封裝的API中,幫咱們去應對這些複雜的問題。
可是,框架自身也會有複雜度,這裏就抽象出一個問題,就是要作的應用的複雜度與所使用的框架的複雜度的對比。 進一步說,是所要解決的問題的內在複雜度,與所使用的工具的複雜度進行對比。
怎麼看待前端框架的複雜度呢,如今的前端開發已經愈來愈工程化,能夠根據下面對的圖片分析javascript
咱們在任何狀況下都須要響應式的渲染功能,並儘量但願進行可變的命令式操做,儘量讓DOM的更新操做是自動的,狀態變化的時候它就應該自動更新到正確的狀態;咱們須要組件系統,將一個大型的界面切分紅一個一個更小得可控單元客戶端路由--這是針對單頁應用而言,不作就不須要,若是須要作單頁應用,那麼就須要有一個URL對應到一個應用的狀態,那就須要有路由解決方案,大規模的狀態管理————當應用簡單的時候,可能一個很基礎的狀態和界面映射能夠解決問題,可是當應用變得很大,恩,very large 。設計多人協做的時候
,就會涉及多個組件之間的共享,多個組件須要去改動同一份狀態,以及如何使得這樣大規模應用依然可以高效運行,這就涉及大規模狀態管理的問題,固然也涉及到可維護性,還有構建工具,如今,若是放眼前端的將來,當HTTP2普及後,可能會帶來構建工具的一次革命,但就目前而言,尤爲是在中國的網絡環境下,打包和工程構建依然是很是重要且不可避免的一個環節。
Vue很是專一的只作狀態到界面映射,以及組件。
Vue的特色是有本身的配套工具,核心雖然只解決一個很小的問題。但它有生態圈及配套的可選工具,當把它們一個一個加進來,就能夠組成很是強大的棧。就能夠涵蓋其餘的這些更完整的框架所涵蓋的問題。html
(1)聲明式渲染
如今基本全部的框架都已經認同這個見解————DOM應儘量是一個函數式到狀態的映射,狀態便是惟一的真相,而DOM狀態只是數據狀態的一個映射。全部的邏輯儘量在狀態的層面去進行。當狀態改變的時候。View應該是在框架幫助下自動更新到合理的狀態。而不是說當你觀測到數據變化以後手動選擇一個元素,再命令式去改動它的屬性。在Vue2.0中,渲染層的實現作了根本性的改動,就是引入了虛擬DOM。Vue的編譯器在編模板以後。會把這些模板編譯成一個渲染函數,而函數被調用的時候就會渲染而且返回一個虛擬DOM的樹,這個樹很是輕量,它負責描述當前界面所應處的狀態,當咱們有了這個虛擬的樹以後,再交給一個patch函數,負責把這些虛擬DOM真正施加到真實的DOM上。在這個過程當中,Vue有自身的響應式系統來偵測在渲染過程當中所依賴到的數據來源 。在渲染過程當中,偵測到數據來源時候,以後就能夠精確感知數據源的變更,到時候就能夠根據須要從新進行渲染,當從新進行渲染後,會生成一個新的樹,將新樹與舊樹進行對比,就能夠最終得出應施加到 真實DOM上的改動,最後再經過patch函數施加改動。
(2)組件系統
在Vue中,父子組件之間的通訊是經過Props傳遞。從父向子單向傳遞;而若是子組件想要在父組件做用裏面產生反作用,就須要去派發事件。這樣就造成一個基本的父子通訊模式,在涉及大規模狀態管理的時候會有額外的方案。Vue的組件引入構建工具以後有一個單文件組件概念。
(3)客戶端路由
在作一個界面複雜度很是的高應用時,會有不少的狀態,這樣的應用顯然不可能在每作一次操做後都刷新一個頁面做爲用戶反饋,這就要這個用用有多個複雜的狀態,一樣這些狀態還要對應到URL。有一個重要的功能叫作deep-linking,也就是當用戶瀏覽到一個URL ,把它穿給另外的人或者複製從新打開,應用須要直接渲染出這個URL對應的狀態。這就意味着應用的url和組件樹的狀態之間有一個映射關係,客戶端路由的職責就是讓這個映射關係聲明式地對應起來。
(4)狀態管理
前端
(5)構建工具
全局安裝的vue-cli ,全局安裝以後,就能夠用Vue命令建立一個新的項目,vue
npm install -g vue-cli vue init webpack-simple my-app cd my-app npm my-app npm install #or yarn npm run dev
下面簡單介紹一下vue基本的模板和語法java
<div id="app"> {{ message }} </div>
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
這樣在頁面中顯示webpack
Hello Vue!web
而後,vue中基本的指令屬性v-bind
,指令前都帶有前綴v-
,以表示Vue提供的特殊屬性,渲染在DOM上的特殊響應式行爲。這裏v-bind
的做用是,將這個元素節點的title
屬性和Vue實例的message
屬性保持一致vue-cli
示例:npm
<div id="app-2"> <span v-bind:title="message"> 鼠標懸停幾秒鐘查看此處動態綁定的提示信息! </span> </div>
var app2 = new Vue({ el: '#app-2', data: { message: '頁面加載於 ' + new Date().toLocaleString() } })
再次打開瀏覽器的javascript控制檯的輸入app2.message='新消息',就會再一次看到這個綁定的title
屬性的HTML已經發生了更新。數組
控制切換一個元素的顯示也至關簡單:
<div id="app-3"> <p v-if="seen">如今你看到我了</p> </div>
var app3 = new Vue({ el: '#app-3', data: { seen: true } })
如今你看到我了
繼續在控制檯設置app3.seen=false
,會發現消息消失了
說明既能夠綁定DOM文本到數據,也能夠綁定DOM結構到數據,並且Vue也提供一個強大的過渡效果系統。也能夠在Vue插入/更新/刪除元素時自動應用過分效果。
還有其餘不少指令,每一個都有特殊的功能。例如,v-for
指令能夠綁定數組的數據來渲染一個項目列表:
<div id="app-4"> <ol> <li v-for="todo in todos"> {{ todo.text }} </li> </ol> </div>
var app4 = new Vue({ el: '#app-4', data: { todos: [ { text: '學習 JavaScript' }, { text: '學習 Vue' }, { text: '整個牛項目' } ] } })
在控制檯裏,輸入app4.todos.push({text:'新項目'})
,列表中添加一個新項
爲了讓用戶和應用進行互動,能夠用v-on
指令綁定一個事件監聽器,經過它調用咱們Vue實例中定義方法:
<div id="app-5"> <p>{{ message }}</p> <button v-on:click="reverseMessage">逆轉消息</button> </div>
var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } })
注意在 reverseMessage
方法中,咱們更新了應用的狀態,但沒有觸碰 DOM——全部的 DOM 操做都由 Vue 來處理,你編寫的代碼不須要關注底層邏輯。
Vue 還提供了 v-model
指令,它能輕鬆實現表單輸入和應用狀態之間的雙向綁定。
<div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } })
Vue組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠表現爲用 is 特性進行了擴展的原生 HTML 元素。
組件的使用能夠爲全局註冊和局部註冊。
要註冊一個全局組件,可使用 Vue.component(tagName, options)
。例如:
Vue.component('my-component', { // 選項 })
組件在註冊以後,即可以做爲自定義元素 <my-component></my-component>
在一個實例的 模板中使用。注意確保在初始化根實例以前註冊組件:
<div id="example"> <my-component></my-component> </div>
// 註冊 Vue.component('my-component', { template: '<div>A custom component!</div>' }) // 建立根實例 new Vue({ el: '#example' })
渲染爲:
<div id="example"> <div>A custom component!</div> </div>
局部註冊,能夠經過某個Vue實例或組件實例將其註冊僅在其做用域內可用的組件
var Child = { template: '<div>A custom component!</div>' } new Vue({ // ... components: { // <my-component> 將只在父組件模板中可用 'my-component': Child } })
在vue實例(咱們也叫做根組件)下能夠新建子組件,子組件下還能夠新建子組件,這樣層層嵌套,能夠將其內部的組件構成一個系統能夠成爲組件樹,咱們能夠看一個實例:
首先,咱們new 一個 vue的實例,在裏面自定義三個子組件:
var vm = new Vue({ el: '#app',//掛載DOM元素 components: { AppHead, AppMain, AppSide } });
接着,咱們在html頁面內的掛載元素內添加子組件
<div id="app"> <app-head></app-head> <app-main></app-main> <app-side></app-side> </div>
注意,在這裏咱們根據ES6的規範,駝峯式命名要轉成 ‘-’ 加小寫字母 ,而後在script裏面註冊一下咱們剛剛添加的組件
var AppHead = { template: `<div class='app-head'></div>` } var AppMain = { template: `<div class='app-main'></div>`, components:{ AppMainUnit } } var AppSide = { template: `<div class='app-side'></div>`, components:{ AppSideUnit } }
同時的,咱們在AppMain和AppSide 裏面也自定義了下面的子組件,AppMainUnit和AppSideUnit。
一樣要分別在他們的父組件的前面註冊他們:
var AppMainUnit={ template:`<div class='app-main-unit'></div>` } var AppMain = { template: `<div class='app-main'><app-main-unit></app-main-unit><app-main-unit></app-main-unit></div>`, components:{ AppMainUnit } } var AppSideUnit={ template: `<div class='app-side-unit'></div>`, } var AppSide = { template: `<div class='app-side'><app-side-unit></app-side-unit><app-side-unit></app-side-unit></div>`, components:{ AppSideUnit } }
這樣,最後的結果:
<Root> <AppHead> <AppMain> <AppMainUnit> <AppMainUnit> <AppSide> <AppSideUnit> <AppSideUnit>
建立Vue實例的時候大多數選項均可以在組件中使用,可是data除外,讓咱們來看一下實例:
<div id="example-2"> <simple-counter></simple-counter> <simple-counter></simple-counter> <simple-counter></simple-counter> </div>
var data = { counter: 0 } Vue.component('simple-counter', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // 技術上 data 的確是一個函數了,所以 Vue 不會警告, // 可是咱們卻給每一個組件實例返回了同一個對象的引用 data: function () { return data } }) new Vue({ el: '#example-2' })
因爲這三個組件實例共享了同一個data對象,所以遞增一個counter會影響全部組件
因此,咱們應該返回一個對象裏面存放數據:
data: function () { return { counter: 0 } }
如今每一個counter都會有本身內部的狀態了
組件需配合使用,最多見的就是父子組件,一個組價A在它的模板中使用了組件B,那麼A和B之間必然要發生通信,父組件要把數據下發子組件嗎,子組件要告知父組件監聽其內部發生的事件,經過一個良好定義的接口來儘量將父子組件解耦也是很重要的。這保證了每一個組件的代碼能夠在相對隔離的環境中書寫和理解,從而提升了其可維護性和複用性。父子組件的關係能夠總結爲 prop 向下傳遞,事件向上傳遞。父組件經過 prop 給子組件下發數據,子組件經過事件給父組件發送消息。
使用prop傳送數據
在Vue組件實例做用域是孤立的,全部子組件不能直接訪問父組件內的數據,這樣咱們就應該經過prop從父組件向下傳遞數據。
Vue.component('child', { // 聲明 props props: ['message'], // 就像 data 同樣,prop 也能夠在模板中使用 // 一樣也能夠在 vm 實例中經過 this.message 來使用 template: '<span>{{ message }}</span>' })
傳入一個普通的字符串
<child message="hello!"></child>
或者說,在父組件中的data中加數據
data:{ mymessage:[1,2,2,3,4,5,6] },
在子組件prop中添加以下:
props: ['message','myMessage'],
而後
<child message='hello' :my-message='mymessage'></child>
template: '<span>{{ message }}{{myMessage}}</span>'
恩,這樣就能夠經過V-bind動態綁定的my-message屬性訪問到父組件中的mymessage
屬性了。
若是props傳回的數據是對象,咱們還能夠經過遍歷將裏面的每一項列出。
template:"<div><span v-for='(item,index) in myMessage'>{{item}}</span></div>"
注意:遍歷的時候不要將v-for
綁到根元素上,由於模板根元素默認只能有一個。
prop是單向綁定的,當父組件的屬性發生變化時,會傳遞給子組件,而子組件的改變卻不會影響到父組件。這是爲了防止子組件無心間修改了父組件的狀態,來避免應用的數據流變得難以理解。
props的改變無非就是兩種狀況
Prop 做爲初始值傳入後,子組件想把它看成局部數據來用;
Prop 做爲原始數據傳入,由子組件處理成其它數據輸出。
先看第一種,這個時候,定義一個局部變量,用prop的值去初始化它
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
這樣就能夠對counter
進行處理
第二種使用計算屬性對prop進行處理輸出
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意在 JavaScript 中對象和數組是引用類型,指向同一個內存空間,若是 prop 是一個對象或數組,在子組件內部改變它會影響父組件的狀態。
能夠爲組件的prop提供驗證規則,要指定驗證規則,須要用對象的形式來定義 prop,而不能用字符串數組:
Vue.component('example', { props: { // 基礎類型檢測 (`null` 指容許任何類型) propA: Number, // 多是多種類型 propB: [String, Number], // 必傳且是字符串 propC: { type: String, required: true }, // 數值且有默認值 propD: { type: Number, default: 100 }, // 數組/對象的默認值應當由一個工廠函數返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定義驗證函數 propF: { validator: function (value) { return value > 10 } } } })
type
能夠是下面的原生構造器
String
Number
Boolean
Function
Object
Array
Symbol
當驗證失敗的時候,Vue就會發出警告,注意 prop 會在組件實例建立以前進行校驗,因此在 default 或 validator 函數裏,諸如 data、computed 或 methods 等實例屬性還沒法使用。