1. 初識vue.js
- MVVM模式從MVC模式演化而來,但是MVVM模式更多應用在前端,MVC則是前后端共同表現。
- 傳統開發模式:jQuery + RequireJS ( SeaJS ) + artTemplate ( doT ) + Gulp ( Grunt)。
- vue.js可以直接通過script引入方式開發,也可以通過webpack組織項目開發。
1.1. 使用小技巧
????????在Vue項目中,public/index.html 文件是項目構建時的入口模板,最終會被Webpack處理并插入生成的<div id="app"></div>標簽。Vue的單頁面應用(SPA)的主視圖通常會在App.vue組件內定義,并由Vue實例掛載到這個#app元素上。如果你需要修改public/index.html
以適應你的Vue頁面,你可以直接編輯該文件。
2. 數據綁定和第一個vue應用
????????這一章看完后,基本可以下場實戰了,起碼能看懂代碼。
2.1.?實例與數據綁定
<!DOCTYPE HTML>
<html>
<head><meta charset="utf-8"><title>Vue 實例</title>
</head>
<body><div id="app">//(3) v-model指令進行數據綁定,綁定data中的name<input type="text" v-model="name" placeholder="你的名字"><h1>你好,{{name}}</h1></div><script src=”https://unpkg.com/vue/dist/vue.min.js ”></script><script>var myData{name: 1}//(1)VUE實例構造函數,創建實例var app = new vue({ //(2) el用于指定一個頁面中己存在的DOM元素來掛載Vue實例el = '#app',data:{name : ''}//(5) 也可以這樣//data: myData})//(4) 可以這樣訪問console.log (app.name); </script></body>
</html>
- 通過vue構造函數創建實例:
- el用于指定一個頁面中己存在的DOM元素來掛載Vue實例
- v-model指令進行數據綁定
- Vue 實例本身也代理了data 對象里的所有屬性
- 也可以指向一個己有的變量
2.2. 生命周期
var app = new vue({el:'#app',data:{a:2},//鉤子,創建是觸發created: function(){console.log(this.a);}
})
????????Vue 的生命周期鉤子比較常用的有:
- created 實例創建完成后調用,此階段完成了數據的觀測等,但尚未掛載, $el 還不可用。
- mounted el掛載到實例上后調用,一般我們的第一個業務邏輯會在這里開始。
- beforeDestroy 實例銷毀之前調用。主要解綁一些使用addEventListener 監聽的事件等。
2.3. 插值與表達式
- 使用雙大括號( Mustache 語法)“{{}}”是最基本的文本插值方法,它會自動將我們雙向綁
- 定的數據實時顯示出來。
- 如果有的時候就是想輸出HTML,而不是將數據解釋后的純文本,可以使用v-html。
<span v-html="link"></span>data: {link: '<a href ="#">這是一個連接<a>'
}
- 如果想顯示{{}}標簽,而不進行替換, 使用v-pre 即可跳過這個元素和它的子元素的編譯過程。
2.4. 過濾器
????????支持在{{}}插值的尾部添加一小管道符“|” 對數據進行過濾,經常用于格式化文本,比如字母全部大寫、貨幣千位使用逗號分隔等。過濾器也可以串聯,而且可以接收參數。
<div id="app">{{date | formateDate}}
</div><script>var app = new vue({el:'#app',data:{date: new date()},filters:{formateDate: function(value){//這里做過濾的事情,value就是date變量}}})
</script>
2.5. 指令和事件
????????指令( Directives )是Vue扣模板中最常用的一項功能,它帶有前綴v-,指令的主要職責就是當其表達式的值改變時,相應地將某些行為應用到DOM上。
- <p v-if = ” show ” >顯示這段文本</p>,?v-if根據show的布爾值確定是否顯示。
- <a v-bind:href =” url ” >鏈接 </a>,?v-bind 的基本用途是動態更新HTML 元素上的屬性,比如id、class 等
- v-on,它用來綁定事件監聽器,這樣我們就可以做一些交互了。
<div id = "app"><button v-on:click="doSth" >干點人事</button>
</div><script>var app = new vue({el:"#app",data:{},methods:{doSth:function(){//干點人事}}})
</script>
Vue. 將methods 里的方法也代理了,所以也可以像訪問Vue 數據那樣來調用方法:this.doSth();
2.6. 語法糖
????????語法糖是指在不影響功能的情況下, 添加某種方法實現同樣的效果, 從而方便程序開發。
- v-bind 可以省略v-bind直接寫一個冒號“ :”:<a : href= ” url ” >鏈接</a>
- v-on 可以直接用“ @ ” 來縮寫:<button @click= ” doSth ” >干點人事</?button>
2.7. 實用小技巧
????????看完本章后還是有個疑問,用vue-cli建的項目,是怎么把index.html和vue件關聯的呢?網上有小伙伴把來龍去脈講清楚了,可以參考
vue-cli中webpack把js文件引入index.html的過程 - 知乎 (zhihu.com)
3. 計算屬性
- 所有的計算屬性都以函數的形式寫在Vue 實例內的computed 選項內,最終返回計算后的結果。
- 在一個計算屬性里可以完成各種復雜的邏輯,包括運算、函數調用等,只要最終返回一個結果就可以。
- 計算屬性還可以依賴多個Vue 實例的數據,只要其中任一數據變化,計算屬性就會重新執行,視圖也會更新。
- 當執行app.fullName =’John Doe';時, setter就會被調用,數據firstName和lastName都會相對更新,視圖同樣也會更新。
- 計算屬性還有兩個很實用的小技巧容易被忽略:一是計算屬性可以依賴其他計算屬性: 二是計算屬性不僅可以依賴當前Vue 實例的數據,還可以依賴其他實例的數據。
- 與method不同,計算屬性是基于它的依賴緩存的。一個計算屬性所依賴的數據發生變化時,它才會重新取值。
4. v-bind 與 class 和 css 綁定
4.1. 綁定class 對象語法
- 給v-bind:class 設置一個對象,可以動態地切換class。
- 對象中也可以傳入多個屬性,來動態切換class 。另外,:class 可以與普通class 共存。
- 當:class 的表達式過長或邏輯復雜時,還可以綁定一個計算屬性。
<div id= "app"><div :class="{'active' : isActive }"></div><div class="static": class="{'active':isActive, 'error':isError}"> </div><div :class="classes"></div>
</div<script>var app = new vue({el:'#app',data:{isActive: 'true',isError: 'false'},computed:{classes:funtion(){return{'active': this.isActive && !this.error,'text-fail': this.error && this.error.type === 'fail'}}}})
</script>
4.2.?綁定class 數組語法
- 當需要應用多個class 時, 可以使用數組語法, 給:class 綁定一個數組,應用一個class 列表。
- 也可以使用三元表達式來根據條件切換class
- class 有多個條件時,這樣寫較為煩瑣,可以在數組語法中使用對象語法。
- 與對象語法一樣,也可以使用data 、computed 和methods 三種方法
<div id= "app"><div :class="[activeCls , errorCls]"></div><div :class="[isActive ? activeCls :'', errorCls]" ></ div><div :class="[{'active': isActive } , errorCls ]" ></ div>
</div<script>var app = new vue({el:'#app',data:{isActive: true,activeCls: 'active',errorCls: 'error'}})
</script>
4.3. 綁定style
????????使用v-bind:style (即:style ) 可以給元素綁定內聯樣式,方法與:class 類似,也有對象語法和數組語法,也可以使用data、computed等。
<div :style="{'color': color, 'fontSize' : fontSize +'px'}">文本</ div>
5. 內置指令
5.1. 基本指令
- v-cloak 不需要表達式,它會在Vue 實例結束編譯時從綁定的HTML 元素上移除, 經常和css的display: none;配合使用,說白了就是沒用的。
- v-once 也是一個不需要表達式的指令,作用是定義它的元素或組件只渲染一次,包括元素或組件的所有子節點。
5.2. 條件渲染指令
- v-if, v-else-if, v-else條件指令可以根據表達式的值在DOM中渲染或銷毀元素/組件。
- Vue 在渲染元素時,出于效率考慮,會盡可能地復用已有的元素而非重新渲染。
- Vue扣提供的key 屬性,它可以讓你自己決定是否要復用元素, key 的值必須是唯一的。
- v-show 的用法與v-if 基本一致,只不過v -show 是改變元素的css 屬性di splay 。
- v-if 更適合條件不經常改變的場景,因為它切換開銷相對較大,而v-s how 適用于頻繁切換條件。
5.3. 列表渲染指令v-for
- 將一個數組遍歷或枚舉一個對象循環顯示時,就會用到列表渲染指令v-for 。它的表達式需結合in 來使用,類似ite m in items 的形式。
- v- for 的表達式支持一個可選參數作為當前項的索引v-for= "(book , index) in books",其中index就是索引項。
- 與v-if 一樣, v-for 也可以用在內置標簽<template>上, 將多個元素進行渲染
- v-for 可以迭代整數:v-for= ” n in 10”
- Vue 包含了一組觀察數組變異的方法,使用它們改變數組也會觸發視圖更新:push()\pop()\shift()等等
- 當你不想改變原數組,想通過一個數組的副本來做過濾或排序的顯示時,可以使用計算屬性來返回過濾或排序后的數組
5.4. 方法與事件
????????在@綁定的事件后加小圓點“.”,再跟一個后綴來使用修飾符:top\prevent\capture\self\once
6. 表單與v-model
????????感覺這一章的內容比前面簡單了只要理解了v-model的用法,剩下的就是控件的表達。
<div id="app"><input type="text" v-model ="message" placeholder="輸入..."><p>輸入的內容是:{{message}}</p>
</div><script>var app = new vue({el:'#app',data:{message: ''}})
</script>
6.1. 綁定值
????????v-model 綁定的值是一個靜態字符串或布爾值, 但在業務中,有時需要綁定一個動態的數據, 這時可以用v-bind 來實現。
<input type="radio" v-model="picked" :value="value">
//這里省略了很多代碼
data:{picked:false,value:123
}
//在選中時, app.picked === app .value , 值都是123
6.3. 修飾符
????????v-model 也有修飾符,用于控制數據同步的時機。
- 使用修飾符.lazy 會轉變為在change 事件中同步。
- 使用修飾符.number 可以將輸入轉換為Number 類型。
- 使用修飾符.number 可以將輸入轉換為Number 類型。
7. 組件
7.1. 組件的用法
????????template的DOM 結構必須被一個元素包含, 如果直接寫成“這里是組件的內容”, 不帶
“<div></div>”是無法渲染的。
<div id="app"><my-compnent></my-component>
</div><script>var data = {counter:0 };Vue.component('my-component',{//全局組件注冊template:'<div>這是組件內容</div>',data:function(){//組件中的data必須使用funciton形式定義return {message:'msg信息',data: data //共享引用};}});var child = {template:'<div>這是局部組件內容</div>'}var app = new vue({el:'#app',components:{//局部組件注冊'my-part-component':child}});
</script>
7.2. 使用props傳遞數據
????????父組件要正向地向子組件傳遞數據或參數,通過props 來實現。
<div id="app"><input type="text" v-model="parentMessage" >//v-bind 會自動把內容識別為對應的類型,否則將只傳遞字符串<my-compnent v-bind:message="parentMessage"></my-component>//v-bind綁定父組件參數
</div><script>Vue.component('my-component',{props:['message'], //字符串數組template:'<div> {{message}} </div>', //這里就可以取到父組件的message值了});var app = new vue({el:'#app',data:{parentMessage: '父組件消息內容'}});
</script>
????????父組件傳遞初始值進來,子組件將它作為初始值保存起來,在自己的作用域下可以隨意使用和修改。這種情況可以在組件data 內再聲明一個數據,引用父組件的prop。
????????另一種情況就是prop 作為需要被轉變的原始值傳入。這種情況用計算屬性就可以了。
????????除了數組外,還可以是對象,當prop 需要驗證時,就需要對象寫法。
vue.component('my-component',{props:{propsA: Number,propsB: [Number, String],propsC:{type:Boolean,default:true}}
})
7.3. 組件通信
????????當子組件需要向父組件傳遞數據時,就要用到自定義事件。子組件用$emit()來觸發事件,父組件用$on()來監昕子組件的事件。
<div id="app"><p>總數:{{total}}</p><my-component @increase="handleGetTotal" @reduce="handleGetTotal"></my-component>
</div><script>vue.compnent('my-component',{template :'\<div>\<button @click= "handleIncrease”> +l </button> \<button @click= "handleReduce”> -l </button> \',date:funciton(){return {counter:0}},method:{handleIncrease:function(){this.counter++;this.$emit('increase', this.counter);},handleReduce: function(){this.counter--;this.$emit('reduce', this.counter);}}});var app = new vue({el: '#app',data:{total: 0},method:{handleGetTotal: function(total){this.total = total ;}}});
</script>
????????組件中使用v-model進行雙向數據綁定,直接用了v-model的語法糖代表@ input= “ handler”。
<div id="app"><p>總數:{{total}}</p><my-component v-model="total"></my-component> // 語法糖,相當于@input="handleGetTotal"
</div><script>Vue.component('my-component', {template :'<button @click="handleClick" >+ l</button >',data: function(){return{counter : 0}},method: {handleClick: function(){this.counter++;this.$emit('input', this.counter);}}});var app = new vue({el:'#app',data:{total: 0}});
</script>
????????非父子組件一般有兩種,兄弟組件和跨多級組件。在Vue2 中, 推薦使用一個空的Vue 實例作為中央事件總線(bus)。
<div id = "app">{{message}}<component-a></component-a>
</div><script>bus = new vue();Vue.component('component-a', {template:'<button @click ="handleEvent" >傳遞事件</button>',methods:{handleEvent: funciton(){bus.emit('on-message', '來自組件component-a的內容');}}});var app = new vue({el:'#app',mounted: function(){var _this = this;bus.$on('on-message', function(msg){_this.message = msg; //監聽});}});
</script>
????????在子組件中,使用this.$parent 可以直接訪問該組件的父實例或組件,父組件也可以通過
this.$children 訪問它所有的子組件,而且可以遞歸向上或向下無線訪問, 直到根實例或最內層的組件。
????????Vue 提供了子組件索引的方法,用特殊的屬性ref來為子組件指定一個索引名稱。
<div id = "app"><button @click="handleRef">通過ref 獲取子組件實例</button><component-a ref="comA" ></component-a>
</div><script>Vue.component('component-a',{template:'<div>子組件</div>',data: function(){return {message : '子組件內容'}}});var app = new vue({el: '#app',methods: {handleRef:function(){var msg = this.$refs.comA.message; //ref的用法console.log(msg);}}});
</script>
7.4. slot
????????其實就把父組件的內容直接顯示在子組件內,替換掉子組件的slot那部分。子組件child-component 的模板內定義了一個slot>元素,并且用一個>作為默認的內容,在父組件沒有使用slot 時,會渲染這段默認的文本;如果寫入了slot, 那就會替換整個<slot>?。
????????給<slot> 元素指定一個name 后可以分發多個內容,具名Slot 可以與單個Slot 共存。
<div id = "app"><child-component><h2 slot ="header" >標題</h2><p>分發的內容</p><p>更多分發的內容</p></child-component>
</div><script>Vue.component('child-component',{template : '\<div>\<slot name="header" ></slot>\</div>\<div>\<slot>\<p>如果父組件沒有插入內容,我將作為默認出現<p>\</slot>\</div>'});var app = new vue({el:'#app'});
</script>
????????在<slot>元素上有一個類似props 傳遞數據給組件的寫法msg=” xxx ”,將數據傳到了插槽。父組件中使用了<template>元素,而且擁有一個scope=”props ”的特性,這里的props只是一個臨時變量,就像v-for= "item in items",里面的item 一樣。template 內可以通過臨時變量props訪問來自子組件插槽的數據msg 。
????????而Vue 扣2.x 提供了用來訪問被slot 分發的內容的方法$ slots.
7.5. 其他? ? ? ??
????????$nextTick 就是用來知道什么時候DOM 更新完成的
this.$nextTick (function(){var text = document.getElementByid('div').innerHTML;console.log(text);
});
????????在一些非常特殊的情況下,我們需要動態地去創建Vue 實例, Vue 提供了Vue.extend 和$mount 兩個方法來手動掛載一個實例。
<div id ="mount-div"></div>
<script>var MyComponent =Vue.extend({...});new MyComponent().$mount('#mount-div');
</script>
8. 自定義指令
????????自定義指令語法和組件基本一致,只是單詞改成了driective。
//全局定義
Vue.directive('test', {...});//局部定義
var app = new vue({el:'#app',directive: {test: {...}}
});
????????自定義指令的選項是由幾個鉤子函數組成的,每個都是可選的。bind, inserted, updated, componentUpdated, unbind。
????????每個鉤子函數都有幾個參數可用
- el 指令所綁定的元素,可以用來直接操作DOM
- binding 一個對象
- vnode Vue 編譯生成的虛擬節點
- oldVnode 上一個虛擬節點僅在update 和componentUpdated 鉤子中可用
9. Render函數
????????在Vue.js 2 中, Virtual Dom 就是通過一種VNode 類表達的,每個DOM 元素或組件都對應一
個VNode 對象。在多數場景中,我們使用template就足夠了,但在一些特定的場景下,使用Virtual Dom 會更簡單。
????????Render 函數通過crea teElement 參數來創建Virtual Dom。
<div id="app"><anchor :leve="2" title="特性">特性<anchor>
</div><script>Vue.component("anchor", {props:{level:{type: Number,required: true},title:{type: String,defaule: ''}},render: function(createElement){'h' + this.level,[createElement('a',{domProps: {href: '#' + this.title}},this.$slot.default)]}});var app = new vue({el:'#app'})
</script>
????????createElement 構成了Vue Virtual Dom 的模板,它有3 個參數:第一個參數必選,可以是一個HTML 標簽,也可以是一個組件或函數;第二個是可選參數,數據對象,在template 中使用。第三個是子節點,也是可選參數,用法一致。
????????使用Render 函數最不友好的地方就是在模板比較簡單時, 寫起來也很復雜,而且難以閱讀出
DOM 結構, 尤其當子節點嵌套較多時, 嵌套的createElement 就像蓋樓一樣一層層延伸下去。為了讓Render 函數更好地書寫和閱讀, Vue 扣提供了插件babel-plugin-transform-vue-jsx 來支
持JSX 語法。JSX 是一種看起來像HTML , 但實際是JavaScript 的語法擴展,它用更接近DOM 結構的形式來描述一個組件的U 和狀態信息。需要在webpack 里配置插件babel-plugin-transform-vue才SX 編譯后才可以運行。
new vue({el:'#app',render(h){<anchor lever={1}> <span>一級</span> 標題 </anchor>}
})
10. 使用Webpack
????????在webpack 的世界里, 一張圖片、一個css 甚至一個字體,都稱為模塊( Module),彼此存在依賴關系, webpack 就是來處理模塊間的依賴關系的,并把它們進行打包。
????????web pack 的主要適用場景是單頁面富應用( SPA ) 。SPA 通常是由一個h恤l 文件和一堆按需加載的js 組成,它的html 結構可能會非常簡單。只有一個<div>節點,所有的代碼都集成在了神奇的ma in 扣文件中,理論上古可以實現像知乎、淘寶這樣大型的項目。
????????export 和import 是用來導出和導入模塊的。一個模塊就是一個js 文件,它擁有獨立的作用域,里面定義的變量外部是無法獲取的。
//Config.jsvar Config ={version : '1.0.0'
};export {Config};//main.jsimport {Config} from "./Config.js"
????????如果使用npm 安裝了一些庫,在webpack 中可以直接導入。歸根到底, webpack 就是一個.js 配置文件,你的架構好或差都體現在這個配置里,隨著需求的不斷出現,工程配置也是逐漸完善的。
????????在webpack 的世界里,每個文件都是一個模塊,比如css 、.js 、.html?、.jpg 、.less 等。對于不同的模塊,需要用不同的加載器( Loaders )來處理,而加載器就是webpack 最重要的功能。????????
????????webpack 雖然概念比較新,看似復雜,但它只不過是一個js 配置文件,只要搞清楚入口( Entη )、出口( Output )、加載器( Loaders )和插件( Plugins )這4 個概念。
????????Vue.js 是一個漸進式的JavaScript 框架,在使用webpack 構建Vue 項目時,可以使用一種新的構建模式: .vue 單文件組件。一個. vue 文件一般包含3 部分,即<template>、<script>和<style>。每個. vue 文件就代表一個組件, 組件之間可以相互依賴。
<template><div> hello {{name}}</div>
</template><script>export default{data (){return {name : 'Vue.js'}}}
</acript><style scoped>div{color: #f60;font-size: 24px; }
</style>
11. 插件
11.1. 路由
????????SPA其實就是在前后端分離的基礎上,加一層前端路由。因此, SPA 只有一個ht時,整個網站所有的內容都在這一個html 里,通過JavaScript 來處理。
????????每個頁面對應一個組件,也就是對應一個.vue 文件。在router 目錄下創建views 目錄,用于存
放所有的頁面。再回到main.js 里,完成路由的剩余配置。創建一個數組來制定路由匹配列表,每一個路由映射一個組件:
const Routers = {{path : "./index",component: (resolve) => require(['./views/index.vue'], resolve)}
}
vue-router 有兩種跳轉頁面的方法,第一種是使用內置的<router-link>組件,它會被渲染為一個
<a>標簽。第二種跳轉方法,使用router 實例的方法。比如在about.vue 里,通過點擊事件跳轉。
11.2. 狀態管理
????????雖然bus 己經可以很好地解決跨組件通信,但它在數據管理、維護、架構設計上還只是一個簡單的組件,而Vuex 卻能更優雅和高效地完成狀態管理。
????????Vuex 里的數據都是響應式的,任何組件使用同數據時,只要數據變化,對應的組件也會立即更新。在組件內,數據只能讀取,不能手動改變,改變store 中數據的唯一途徑就是顯式地提交mutations 。
????????Vuex 還有其他3 個選項可以使用: ge憂ers 、actions 、modules 。Vuex 很像是一種與開發
者的約定,所以涉及改變數據的,就使用mutations ,存在業務邏輯的,就用actions 。至于將業務
邏輯放在action 里還是Vue 組件里完成,就需要根據實際場景拿捏了。
12. 總結
? ? ? ? 這篇博客是一邊學習看書一遍寫的,每天早上開工前1個小時學習,前后估計寫了有1個月時間。說實話,《VUE.js 實戰》這本書感覺寫的缺了點原理的解釋,可能是篇幅的原因。當然也可能是第一次看關于前端的語言書籍,很多前端技術默認的東西沒有掌握所以看起來有點吃力。無論如何,算是對vue有了基本的認識,后面項目中遇到了也不會搓手不及了。