Vue 是一套用於構建用戶界面的漸進式框架
也意味着,既能夠把VUE做爲該應用的一部分嵌入到一個現成的服務端應用,或者在先後端分離的應用中,利用Vue 的核心庫及其生態系統,把更多的邏輯放在前端來實現。html
漸進式框架前端
與Vue相比,React學習曲線陡峭,在學習React以前,須要瞭解JSX和ES2015,固然入門後,發現還要學習React全家桶。而Vue就能夠在簡單閱讀了文檔後,開始構建應用程序。vue
這就要得益於Vue主張的 漸進式。
能夠簡單看下官方給出這張圖:react
能夠看出來,主要是介紹了Vue設計思想,就是框架作分層設計,每層均可選,能夠單獨引入,爲不一樣的業務需求制定靈活的方案。主張最少,不會多作職責之外的事。webpack
Vue做者尤雨溪的觀點,Vue設計上包括的解決方案不少,可是使用者徹底不須要一上手,就把全部東西全都用上,由於徹底沒有必要,通常都是根據項目的複雜度,在覈心的基礎上任意選用其餘的部件,不必定要所有整合在一塊兒。git
這樣漸進式的解決方案,使得學習成本大大減小了。github
也就是說,DOM狀態只是數據狀態的一個映射,基本全部的框架都已經認同了這個見解,Vue也是主張 數據驅動狀態。web
說到這裏,基本都會提到如今主流的MVVM
的模式。ajax
採用了雙向數據綁定的思想,基本能夠分爲三層:vue-router
基於這個思想,Vue從一開始就利用ViewModel與view,model進行交互
ViewModel是Vue.js的核心,它是一個Vue實例,做用在某個HTML元素上,通常都是指定 id= app
的元素,圖中 的DOM listeners
和Data Bindings
能夠看作兩個工具,它們是實現雙向數據綁定的關鍵。
從用戶(View)角度看,DOM Liisteners
利用在相應的元素上添加事件綁定,捕獲用戶的點擊,滑動等手勢動做,在事件流中改變對應的Model
。好比 經常使用的 v-model
指令,就是捕獲表單元素的input
,change
等事件,改變相應的綁定值。
從Model方向看,Data Bindings
則將操做的數據變化,反應到view上。好比經過ajax 從後臺獲取的數據,能夠刷新數據列表,反應到用戶界面。這也是實現雙向數據綁定的關鍵。
Vue2中是經過Object.definedProperty
方法中定義的getters和 setters構造器來實現數據響應的。能夠簡化下源碼中的實現:
Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if ("development" !== 'production' && customSetter) { customSetter(); } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); }
經過這種方法定義對象obj
上的某個屬性,每次獲取屬性值的時候就,會主動觸發get
對應的回調函數,而後給該屬性賦值時,就會觸發裏面的set
對應的回調函數,在set
回調函數裏面,加入了dep.notify()
方法,而後能夠看下這個方法
notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } }
裏面的定義的常量subs
每次深拷貝this.subs
數組,數組裏面保存的就是全部的subscriber
訂閱者,對應的發佈者就是obj
裏面對應的屬性,或者說是Vue中的data
值。通知全部的訂閱者,數據更新了。原生js實現發佈訂閱模式(publish/Subscribe),能夠參考這裏
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello world</title> <script src="https://gw.alipayobjects.com/as/g/h5-lib/vue/2.4.4/vue.min.js"></script> </head> <body> <div id="app"> {{message}} </div> <script> var app = new Vue({ el:"#app", data:{ message:'hello vue' } }) </script> </body> </html>
這樣就簡單建立了一個Vue 應用,數據message
和DOM頁面產生了關聯,相似html模板引擎,把相應的數據渲染到頁面中。
指令 (Directives) 是帶有 v- 前綴的特殊屬性,這些特殊屬性能夠響應式的做用域DOM,
<p v-if = "seen">如今你看到我了</p>
,經過seen
的真假來插入/移除< p>元素。 這裏判斷的時候使用 === 全等,seen = 「false」 的時候,也會插入 <a v-bind:href="url">...</a>
。 縮寫形式<a :href="url">...</a>
。 經常使用於改變dom的style, class ,href ,src 等屬性。 動態綁定的屬性能夠寫成 :屬性名="屬性值" <a v-on:click="doSomething">...</a>
,簡寫形式 <a @click="doSomething">...</a>
, doSomething
對應的指向methods
裏面定義的函數。 注意,除非在須要傳遞參數的時候,寫成 @click = "doSomething($event,args1,args2)",$event
表明事件對象,args
表明自定義參數將v-bind
用於class
和style
時,Vue.js作了專門的加強,表達式結果的類型除了字符串以外,還能夠是對象或數組。
綁定HTML Class
直接賦值。
< div :class="className"> </div> data:{ className:"div-class" }
結果:
<div class="div-class"></div>
對象語法。
< div class="static" :class="{active:isActive,'text-danger':hasError}" /></div> data: { isActive:true, hasError:false, }
結果:
<div class="static active" ></div>
數組語法,
<div :class="['one',bTwo?'two':'three']" </div> data:{ bTwo:true } <style> .one{} .two{}
結果:
<div class='one two'></div>
綁定內聯樣式
對象語法
<div :style = "{color:activeColor,fontSize:fontSize+'px'}"></div> data: { activeColor: 'red', fontSize: 30 }
結果:
<div style="color:red:font-size:30px;"></div>
數組語法
<div v-bind:style="[baseStyles, overridingStyles]"></div> data:{ baseStyles:{ color:'red' }, overridingStyles:{ fontSize:'30px' } }
結果:
<div style="color:red:font-size:30px;"></div>
條件渲染
v-if
一樣也是一個指令,添加到一個元素上,對應利用 ===
全等判斷綁定的值true
或false
來決定是否渲染裏面的節點。
能夠與它一塊兒使用的指令有 v-else
,v-else-if
,v-else
元素必須緊跟在都有v-if
或者v-else-if
的元素後面
<div v-if= "num===0"> 0 </div> <div v-else-if ="num ===1"> 1 </div> <div v-else> not 0/1 </div> data:{ num:3 }
結果:
<div> not 0/1 </div>
v-show
根據條件展現元素的選項,簡單的切換元素的內聯樣式display
適用場景:
列表渲染
v-for 把一個數組對應爲一組元素,推薦給每一個列表,添加惟一標識的key
值
<div v-for="(item,idx) in items" :key="idx"> {{idx}} --- {{item.product}} </div> data:{ items:[ {product:"foo"}, {product:"bar"} ] }
結果:
<div> 0 --- foo </div> <div> 1 --- bar </div>
數組更新檢測
包含一組觀察數組變異方法,用來觸發試圖更新:push()
,pop()
,shift()
,unshift()
,splice()
,sort()
,reverse()
利用索引給數組賦值或者手動修改數組的長度,都不會被檢測到更新
替代方案:
Vue.set(example1.items,indexOfItem,newValue) // 或者 example1.items.splice(indexOfItem,1,newValue) // 改變數組的長度: example1.items.splice(newLength)
對於對象中的屬性添加或刪除,也可使用Vue.set
方法,或者在定義的Vue實例內部,使用this.$set
,this.$set
只是全局Vue.set
的別名
Vue.set(object,key,value)
使用v-model
在表單input
或 <textarea>
元素上建立雙向數據綁定。
<input v-model = "message" placeholer= "edit me"> <p>Message is {{message}}</p>
這樣input輸入框中的值就與P標籤中的內容綁定了,一樣也適用textarea
,checkbox
,radio
,select
等表單。
實質上,v-model
只是語法糖。
<input v-model = "something"
對應的完整形式:
<input v-bind:value="something" v-on:input="something = $event.target.value">
表單數組校驗。
利用修飾符 .number
進行數字校驗,是最實用的方法,在v-model
上添加number
修飾符。
<input v-model.number="age" type= "number" >
組件能夠用來擴展HTML,封裝可重用的代碼,全部的組件都是Vue的實例。
命名: 建議遵循W3C規則(小寫,而且包含一個短杆)
組件組合:使用中最多見的是造成父子組件的關係,組件A在它的模板中使用了組件B,那麼他們之間就須要通訊。組件間通訊的關係能夠用下面的圖示代表:
歸納爲: prop 向下傳遞,事件向上傳遞。
利用Prop 傳遞數據,同時藉助.sync
,進行雙向數據通訊
父組件的數據要經過Prop才能下發到子組件中。
prop 屬性命名:在使用camelCase(駝峯式命名)的prop須要裝換成對應的kebab-case(短橫線分隔式命名)。同時,也能夠綁定動態的Prop傳遞給子組件。
而後,子組件中prop值改變,是沒法反應到父組件中的。在Vue1.x中使用.sync
修飾符能夠提供雙向綁定,可是違背了單向數據流的思想,在2.0中就移除了,但在2.3.0中做爲一種語法糖的形式引入了
Vue.component('child',{ props:['myMessage'], template:'<span @click="handleClick">{{myMessage}}</span>', methods:{ handleClick:function(){ this.$emit("update:myMessage","message from child") } } }) <!-- 在HTML 中使用時 .sync的語法糖 --> <child :my-message.sync="parentMsg"></child>
點擊子組件中的span,就能夠改變父組件中prop綁定的parentMsg
值。.sync
語法也會被擴展成爲
<child :my-message="parentMsg" @update:myMessage = "val => parentMsg = val"
裏面 @update:myMessage
就是綁定了自定義事件,回過來看下上面父子通信的規則 prop向下傳遞,事件向上傳遞,也很是符合。
非父子組件通訊
官方推薦使用空的Vue實例做事件總線
var bus = new Vue(); // 在A 組件中觸發了事件 bus.$emit("change",1); // 在B 組件中監聽事件 bus.$on('change',function(id){})
組件通訊變得複雜時,就要考慮使用全局狀態管理,Vue也提供了vuex狀態管理庫。
Vuex 是一個專門爲Vue.js應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
固然,使用Vuex並非首選,只有在構建中大型單頁面應用時,考慮到全局的狀態管理,天然就會想到Vuex。
下面這張圖,表示狀態管理「單向數據流」的理念
核心概念包括(簡單計數器爲例):
state: 做爲單一狀態樹,惟一的數據源,而且每一個應用僅僅包含一個store
實例,通常經過計算屬性獲取某個狀態。
state:{ count:0 },
getter: 至關於store的計算屬性,數據源發生變化時,返回通過處理後的值,
getters:{ getState:state=>{ return state.count } },
mutation: 相似於事件,對應的回調函數到狀態進行處理,必須經過store.commit
的方式手動觸發
mutations:{ increment:state => state.count++, decrement:state => state.count-- },
actions: 利用commit
提交mutation,能夠執行異步操做,經過 store.dispatch
方式觸發
// 模擬異步請求 var delay = (timeout,cb) => new Promise(resolve => setTimeout(()=>{cb(); resolve("test incrementAsync")},timeout)); actions:{ incrementAsync({commit}) { return delay(600,function(){commit("increment")}) }, }
把全部部分組合起來,就構成一個簡單的計數器:
var delay = (timeout,cb) => new Promise(resolve => setTimeout(()=>{cb(); resolve("test incrementAsync")},timeout)); var store = new Vuex.Store({ state:{ count:0 }, getters:{ getState:state=>{ return state.count } }, mutations:{ increment:state => state.count++, decrement:state => state.count-- }, actions:{ incrementAsync({commit}) { return delay(600,function(){commit("increment")}) }, decrementAsync({commit}) { return delay(600,function(){commit('decrement')}); } } })
而後組件中觸發actions
,就能夠
store.dispatch('decrementAsync').then(() => { // ... })
在瀏覽器中,使用vue-devtool,試下時間旅行功能。
使用Vue.js建立單頁面應用,就可使用vue-router,目前版本是3.0.1,把組件映射到對應的路由,經過改變url來渲染不一樣的頁面。官方中文文檔。
vue-router 默認hash模式,每次url只會改變#
後面對應的值,頁面就不會從新加載,而且也不須要服務器端做任何配置。
若是使用路由的history模式,url就會正常http://yoursite.com/user/id
,只須要添加配置mode:'history'
,同時須要後端配置,否則頁面從新刷新,會匹配不到任何資源。
不一樣模式下的服務器配置及生產環境部署,能夠參考vue、react等單頁面項目應該這樣部署到服務器.
基礎概念:
< router-link>
<router-link to="/foo">Go to Foo </router-link> <router-link to= "/bar">Go to Bar</router-link>
使用router-link 組件導航,經過傳入to
屬性指定連接,至關於原生的a
標籤。
< router-view>
路由出口,路由匹配到的組件將渲染在這裏
<router-view></router-view>
能夠在上面添加一些過渡效果
<transiton name="slide"> <router-view></router-view> </transiton>
初始化路由配置
若是使用vue-cli
腳手架構造項目,在init的時候,會出現選項提示用戶安裝路由
確認後,自動生成src/router.js
文件,相關路由配置文件就能夠寫在裏面。
// 首先引入不一樣的vue組件(默認安裝了[vue-loader](https://vue-loader.vuejs.org/zh-cn/start/spec.html),每一個.vue文件當作一個完整的組件) import components1 from "./page/xx.vue" import components2 from "./page/xx2.vue" // router 數組用來定義路由配置,非嵌套路由 const routers = [ {path:'/foo',component:components1}, {path:'/bar',component:components2} ] // 最後拋出這個配置數組 export default routes;
注入到router配置參數裏面
const app = new Vue({ router }).$mount("#app")
存在嵌套路由的時候,須要使用 children
配置,好比上面的components1
組件內部包含本身的嵌套<router-view>
,就可使用嵌套路由。
const routers = [ { path:'/foo', component:components1, children:[ // 當 /foo/detail 匹配成功後,component3 會被渲染在components1中 <router-view> 的位置 { path:'detail', component:component3 }, // 若是 /foo 匹配成功,沒有匹配子路由,默認就會渲染這個空的子路由。 { path:'', component:component4 } ] } ]
router 實例方法
在Vue實例內部,能夠經過this.$router
獲取實例對象,
router.push({path:'/user',params:{id:'123'}})
跳轉router.replace()
,與上面相同,不會添加新的記錄。router.go(-1)
,表示在路由記錄中前進或者後退多少步。在html <template></template>
中,能夠經過 {{$route}},獲取路由配置的相關信息,從而渲染DOM。好比: 經過
<template v-for="(items,index) in $router.options.routes"> <title> {{items.name}} </title> </template>
就能夠將路由配置信息與頁面導航欄對應,列表渲染出導航欄,
在watch
方法中監聽$route
,能夠動態配置組件,不一樣url複用同一組件
watch:{ `$route`:function(to,from) { // 經過to,from 獲取url信息 } }
導航守衛(路由鉤子)
經過註冊一個全局路由鉤子函數,在初始化const router = new VueRouter({})
的時候,定義router.beforeEach((to,from,next)=> {...})
,在每次進入目標路由以前觸發。配合Vuex
能夠很是方便的進行權限管理
router.beforeEach((to, from, next) => { if (store.getters.getisAuthority) { // 檢查已經登陸了,就繼續跳轉。 next() }else if(to.fullPath === "/login"){ // 跳轉到登陸頁面,則清空登陸相關信息 clearCookie() next() } else { next({ path:"/login" }); }})
Vue 提供一個 官方命令行工具,可用於快速搭建大型單頁面應用
目前已經發布到了V3.0.0-alpha.5
npm install -g @vue/cli vue create my-project
基本用法,文檔裏面也比較清楚,參考這裏,
經過下面幾步,快速搭建項目基礎結構。
# 全局安裝 vue-cli $ npm install --global vue-cli # 建立一個基於 webpack 模板的新項目 $ vue init webpack my-project # 安裝依賴,走你 $ cd my-project $ npm install/ yarn $ npm run dev
(若是沒有使用任何框架的基礎上,也想快速搭建一個大型項目的目錄結構,能夠考慮yeoman快速生成一個新的項目)
基礎配置:
/config/index.js
文件中, 手動修改 module.exports = { dev:{ port :8080}}
配置代理
/config/index.js
目錄下,(以代理3000端口上數據請求爲例)
dev: { proxyTable: { '/rest/*':{ target:'http://127.0.0.1:3000', secure:false, pathRewrite:{ '^/rest':'' } } },
/config/index.js
目錄下,build:{} 中的productionSourceMap
改成false
配置路徑別名(alias)
一般在項目中會看到諸如這樣的 import Cookie from "@/util/cookie.js"
的引入,@
就是vue-cli中默認設置的alias
在 /build/webpack.base.conf.js/
文件中,resolve
對象下添加屬性,指向對應的路徑
resolve:{ alias:{ 'page':path.resolve(__dirname,'../src/page') } }
區分不一樣環境
經過process.env.NODE_ENV
值區分
在 /build/webpack.dev.conf.js
和 /build/webpack.prod.conf.js
中,經過
new webpack.DefinePlugin({ 'process.env': env }),
建立了編譯時能夠配置的全局常量,用來區分開發/發佈/測試環境,env
對應的值,能夠在/config/
目錄下的,*.dev.js
文件下配置的。
而後,在其它業務代碼裏面,直接使用這個全局變量,好比在 main.js
裏面:
if(process.env.NODE_ENV === "development"){ console.log("開發環境") }
經過命令行區分不一樣環境
一樣使用上面的方法,添加一個全局變量,不一樣的是從命令行中獲取參數。
好比,打包時還區分 發佈環境 和 預發環境,就能夠修改以下,
new webpack.DefinePlugin({ 'process.env': env, 'VERSION':process.argv[2] == "pro"?'"pro"':'"sit"' }),
在命令行中打包時,可使用 npm run build --env sit
,在業務代碼中,經過全局變量VERSION
,一樣能夠區分不一樣環境。
參考連接