Vue 全家桶

第 1 章:Vue 核心

1.1. Vue 的基本認識
1.1.1. 官網
1) 英文官網: https://vuejs.org/
2) 中文官網: https://cn.vuejs.org/javascript

1.1.2. 介紹描述
1) 漸進式 JavaScript 框架
2) 做者: 尤雨溪(一位華裔前 Google 工程師)
3) 做用: 動態構建用戶界面css

1.1.3. Vue 的特色
1) 遵循 MVVM 模式
2) 編碼簡潔, 體積小, 運行效率高, 適合移動/PC 端開發
3) 它自己只關注 UI, 能夠輕鬆引入 vue 插件或其它第三庫開發項目html

1.1.4. 與其它前端 JS 框架的關聯
1) 借鑑 angular 的 模板和 數據綁定技術
2) 借鑑 react的 組件化和擬 虛擬 DOM 技術前端

1.1.5. Vue 擴展插件
1) vue-cli: vue 腳手架
2) vue-resource(axios): ajax 請求
3) vue-router: 路由
4) vuex: 狀態管理
5) vue-lazyload: 圖片懶加載
6) vue-scroller: 頁面滑動相關
7) mint-ui: 基於 vue 的 UI 組件庫(移動端)
8) element-ui: 基於 vue 的 UI 組件庫(PC 端)vue

1.2. Vue 的基本使用

1.2.2. 編碼java

<div id="app"> <input type="text" v-model="username"> <p>Hello, {{username}}</p> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#app', data: { username: 'atguigu' } </script> 

1.2.3. 使用 vue 開發者工具調試

1.2.4. 理解 Vue 的 MVVM
node

1.3. 模板語法

1.3.2. 模板的理解
1) 動態的 html 頁面
2) 包含了一些 JS 語法代碼
a. 雙大括號表達式
b. 指令(以 v-開頭的自定義標籤屬性)
1.3.3. 雙大括號表達式
1) 語法: {{exp}}
2) 功能: 向頁面輸出數據
3) 能夠調用對象的方法
1.3.4. 指令一: 強制數據綁定
1) 功能: 指定變化的屬性值
2) 完整寫法: v-bind:xxx='yyy' //yyy 會做爲表達式解析執行
3) 簡潔寫法: :xxx='yyy'
1.3.5. 指令二: 綁定事件監聽
1) 功能: 綁定指定事件名的回調函數
2) 完整寫法:
v-on:keyup='xxx'
v-on:keyup='xxx(參數)'
v-on:keyup.enter='xxx'
3) 簡潔寫法:
@keyup='xxx'
@keyup.enter='xxx'
1.3.6. 編碼react

<div id="app"> <h2>1. 雙大括號表達式</h2> <p>{{msg}}</p> <p>{{msg.toUpperCase()}}</p> <h2>2. 指令一: 強制數據綁定</h2> <a href="url">訪問指定站點</a><br><!-- 不能使用 --> <a v-bind:href="url">訪問指定站點 2</a><br> <a :href="url">訪問指定站點 3</a><br> <h2>3. 指令二: 綁定事件監聽</h2> <button v-on:click="handleClick">點我</button> <button @click="handleClick">點我 2</button> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#app', data: {// data 的全部屬性都會成功 vm 對象的屬性 , 而模板頁面中能夠直接訪問 msg: 'NBA I Love This Game!', url: 'http://www.baidu.com' }, methods: { handleClick () { alert(' 處理點擊') } } }) </script> 

1.4. 計算屬性和監視

1.4.2. 計算屬性
1) 在 computed 屬性對象中定義計算屬性的方法
2) 在頁面中使用{{方法名}}來顯示計算的結果
1.4.3. 監視屬性
1) 經過經過 vm 對象的$watch()或 watch 配置來監視指定的屬性
2) 當屬性變化時, 回調函數自動調用, 在函數內部進行計算
1.4.4. 計算屬性高級
1) 經過 getter/setter 實現對屬性數據的顯示和監視
2) 計算屬性存在緩存, 屢次讀取只執行一次getter 計算
1.4.5. 編碼webpack

<div id="demo"> 姓: <input type="text" placeholder="First Name" v-model="firstName"><br> 名: <input type="text" placeholder="Last Name" v-model="lastName"><br> 姓名 1(單向):<input type="text" placeholder="FullName" v-model="fullName1"><br> 姓名 2(單向):<input type="text" placeholder="FullName" v-model="fullName2"><br> 姓名 3(雙向):<input type="text" placeholder="FullName2" v-model="fullName3"><br> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#demo', data: { firstName: 'Kobe', lastName: 'bryant', fullName2: 'Kobe bryant' }, computed: { fullName: function () { return this.firstName + " " + this.lastName }, fullName3: { get: function () { return this.firstName + " " + this.lastName }, set: function (value) { var names = value.split(' ') this.firstName = names[0] this.lastName = names[1] } } }, watch: { lastName: function (newVal, oldVal) { this.fullName2 = this.firstName + ' ' + newVal } } }) vm.$watch('firstName', function (val) { this.fullName2 = val + ' ' + this.lastName }) 

1.5. class 與 與 style 綁定

1.5.2. 理解
1) 在應用界面中, 某個(些)元素的樣式是變化的
2) class/style 綁定就是專門用來實現動態樣式效果的技術
1.5.3. class 綁定
1) :class='xxx'
2) 表達式是字符串: 'classA'
3) 表達式是對象: {classA:isA, classB: isB}
4) 表達式是數組: ['classA', 'classB']
1.5.4. style 綁定
1) :style="{ color: activeColor, fontSize: fontSize + 'px' }"
2) 其中 activeColor/fontSize 是 data 屬性
1.5.5. 編碼ios

<style> .classA { color: red; } .classB { background: blue; } .classC { font-size: 20px; } </style> <div id="demo"> <h2>1. class 綁定: :class='xxx'</h2> <p class="classB" :class="a">表達式是字符串: 'classA'</p> <p :class="{classA:isA, classB: isB}">表達式是對象: {classA:isA, classB: isB}</p> <p :class="['classA', 'classC']"> 表達式是數組: ['classA', 'classB']</p> <h2>2. style 綁定</h2> <p :style="{color, fontSize}">style="{ color: activeColor, fontSize: fontSize + 'px' }"</p> <button @click="update">更新</button> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el : '#demo', data : { a: 'classA', isA: true, isB: false, color: 'red', fontSize: '20px' }, methods : { update () { this.a = 'classC' this.isA = false this.isB = true this.color = 'blue' this.fontSize = '30px' } } }) </script> 

1.6. 條件渲染

1.6.2. 條件渲染指令
1) v-if 與 v-else
2) v-show
1.6.3. 比較 v-if 與 v-show
3) 若是須要頻繁切換 v-show 較好
4) 當條件不成立時, v-if 的全部子節點不會解析(項目中使用)
1.6.4. 編碼

<div id="demo"> <h2 v-if="ok">表白成功</h2> <h2 v-else>表白失敗</h2> <h2 v-show="ok">求婚成功</h2> <h2 v-show="!ok">求婚失敗</h2> <br> <button @click="ok=!ok">切換</button> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#demo', data: { ok: false } }) </script> 

1.7. 列表渲染

1) 列表顯示指令
數組: v-for / index
對象: v-for / key
2) 列表的更新顯示
刪除 item
替換 item
3) 列表的高級處理
列表過濾
列表排序
1.7.2. 編碼

<div id="demo"> <h2>測試: v-for 遍歷數組</h2> <ul> <li v-for="(p, index) in persons" :key="index"> {{index}}--{{p.name}}--{{p.age}} -- <button @click="deleteItem(index)">刪除</button> -- <button @click="updateItem(index, {name:'Jok',age:15})">更新</button> </li> </ul> <h2>測試: v-for 遍歷對象</h2> <ul> <li v-for="(value, key) in persons[0]"> {{ key }} : {{ value }} </li> </ul> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#demo', data: { persons: [ {id: 1, name: 'Tom', age: 13}, {id: 2, name: 'Jack', age: 12}, {id: 3, name: 'Bob', age: 14} ] }, methods: { deleteItem(index) { this.persons.splice(index, 1) }, updateItem(index, p) { // this.persons[index] = p // 頁面不會更新 this.persons.splice(index, 1, p) } } }) </script> 

1.7.3. 編碼

<div id="demo"> <input type="text" name="searchName"placeholder=" 搜索指定用戶名" v-model="searchName"> <ul> <li v-for="(p, index) in filterPerson" :key="index"> {{index}}--{{p.name}}--{{p.age}} </li> </ul> <button @click="setOrderType(1)">年齡升序</button> <button @click="setOrderType(2)">年齡降序</button> <button @click="setOrderType(0)">本來順序</button> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#demo', data: { orderType: 0, //0 表明不排序 , 1 爲升序 , 2 爲降序 searchName: '', persons: [ {id: 1, name: 'Tom', age: 13}, {id: 2, name: 'Jack', age: 12}, {id: 3, name: 'Bob', age: 17}, {id: 4, name: 'Cat', age: 14}, {id: 4, name: 'Mike', age: 14}, {id: 4, name: 'Monica', age: 16} ] }, methods: { setOrderType (orderType) { this.orderType = orderType } }, computed: { filterPerson() { let {orderType, searchName, persons}= this // 過濾 persons = persons.filter(p => p.name.indexOf(searchName)!=-1) // 排序 if(orderType!==0) { persons = persons.sort(function (p1,p2) { if(orderType===1) { return p1.age-p2.age } else { return p2.age-p1.age } }) } return persons } } }) </script> 

1.8. 事件處理

1.8.2. 綁定監聽:
1) v-on:xxx="fun"
2) @xxx="fun"
3) @xxx="fun(參數)"
4) 默認事件形參: event
5) 隱含屬性對象: $event
1.8.3. 事件修飾符
1) .prevent : 阻止事件的默認行爲 event.preventDefault()
2) .stop : 中止事件冒泡 event.stopPropagation()
1.8.4. 按鍵修飾符
1) .keycode : 操做的是某個 keycode 值的鍵
2) .keyName : 操做的某個按鍵名的鍵(少部分)
1.8.5. 編碼

<div id="example"> <h2>1. 綁定監聽</h2> <button v-on:click="test1">Greet</button> <button @click="test1">Greet2</button> <button @click="test2($event, 'hello')">Greet3</button> <h2>2. 事件修飾符</h2> <!-- 阻止事件默認行爲 --> <a href="http://www.baidu.com" @click.prevent="test3">百度一下</a> <br/> <br/> <!-- 中止事件冒泡 --> <div style="width: 200px;height: 200px;background: red" @click="test4"> <div style="width: 100px;height: 100px;background: green" @click.stop="test5"></div> </div> <h2>3. 按鍵修飾符</h2> <input @keyup.8="test6"> <input @keyup.enter="test6"> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#example', data: { name: 'Vue.js' }, methods: { test1 (event) { // 方法內 `this` 指向 vm // alert('Hello ' +this.name + '!') // `event` 是原生 DOM 事件 alert(event.target.innerHTML) }, test2 (event, msg) { alert(event.target.innerHTML + '---'+ msg) }, test3() { alert(' 阻止事件的默認行爲') }, test4() { alert('out') }, test5() { alert('inner') }, test6(event) { alert(event.keyCode+ '---' + event.target.value) } } }) </script> 

1.9. 表單輸入綁定

1.9.2. 使用 v-model 對錶單數據自動收集
1) text/textarea
2) checkbox
3) radio
4) select
1.9.3. 編碼

<div id="demo"> <form @submit.prevent="handleSubmit"> <span>用戶名: </span> <input type="text" v-model="user.username"><br> <span>密碼: </span> <input type="password" v-model="user.pwd"><br> <span>性別: </span> <input type="radio" id="female" value="female" v-model="user.sex"> <label for="female">女</label> <input type="radio" id="male" value="male" v-model="user.sex"> <label for="male">男</label><br> <span>愛好: </span> <input type="checkbox" id="basket" value="basketball" v-model="user.likes"> <label for="basket">籃球</label> <input type="checkbox" id="foot" value="football" v-model="user.likes"> <label for="foot">足球</label> <input type="checkbox" id="pingpang" value="pingpang" v-model="user.likes"> <label for="pingpang">乒乓</label><br> <span>城市: </span> <select v-model="user.cityId"> <option value="">未選擇</option> <option v-for="cityin allCitys":value="city.id"> {{ city.name}} </option> </select><br> <span>介紹: </span> <textarea v-model="user.desc" rows="10"></textarea><br><br> <input type="submit" value=" 註冊"> </form> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#demo', data: { user: { username: '', pwd: '', sex: 'female', likes: [], cityId: '', desc: '', }, allCitys: [{id: 1, name: 'BJ'}, {id: 2, name:'SZ'},{id: 4, name: 'SH'}], }, methods: { handleSubmit (event) { alert(JSON.stringify(this.user)) } } }) </script> 

1.10. Vue 實例生命週期

1.10.2. 生命週期流程圖

1.10.3. vue 生命週期分析
1) 初始化顯示

  • beforeCreate()
  • created()
  • beforeMount()
  • mounted()
    2) 更新狀態: this.xxx = value
  • beforeUpdate()
  • updated()
    3) 銷燬 vue 實例: vm.$destory()
  • beforeDestory()
  • destoryed()
    1.10.4. 經常使用的生命週期方法
    1) created()/mounted(): 發送 ajax 請求, 啓動定時器等異步任務
    2) beforeDestory(): 作收尾工做, 如: 清除定時器

1.10.5. 編碼

<div> <button @click="destoryVue">destory vue</button> <p v-show="isShowing">{{msg}}</p> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> var vue = new Vue({ el: 'div', data: { msg: ' XXXXX', isShowing: true, persons: [] }, beforeCreate () { console.log('beforeCreate() msg=' + this.msg) created () { console.log('created() msg='+this.msg) this.intervalId = setInterval(() => { console.log('-----') this.isShowing = !this.isShowing }, 1000) }, beforeMount () { console.log('beforeMount() msg='+this.msg) }, mounted () { console.log('mounted() msg='+this.msg) }, beforeUpdate() { console.log('beforeUpdate isShowing='+this.isShowing) }, updated () { console.log('updated isShowing='+this.isShowing) }, beforeDestroy () { console.log('beforeDestroy() msg='+this.msg) clearInterval(this.intervalId) }, destroyed () { console.log('destroyed() msg='+this.msg) }, methods: { destoryVue () { vue.$destroy() } } }) </script> 

1.11. 過渡& 動畫

1.11.2. vue 動畫的理解
1) 操做 css 的 trasition 或 animation
2) vue 會給目標元素添加/移除特定的 class
3) 過渡的相關類名
xxx-enter-active: 指定顯示的 transition
xxx-leave-active: 指定隱藏的 transition
xxx-enter/xxx-leave-to: 指定隱藏時的樣式
1.11.3. 基本過渡動畫的編碼
1) 在目標元素外包裹
2) 定義 class 樣式
指定過渡樣式: transition
指定隱藏時的樣式: opacity/其它

1.11.4. 編碼

<style> .fade-enter-active,.fade-leave-active { transition: opacity.5s } .fade-enter, .fade-leave-to { opacity: 0 } /* 能夠設置不一樣的進入和離開動畫 */ .slide-fade-enter-active { transition: all .3sease; } .slide-fade-leave-active { transition: all .8scubic-bezier(1.0, 0.5, 0.8, 1.0); } .slide-fade-enter, .slide-fade-leave-to { transform: translateX(10px); opacity: 0; } </style> <div id="demo1"> <button @click="show = !show"> Toggle1 </button> <transition name="fade"> <p v-if="show">hello</p> </transition> </div> <div id="demo2"> <button @click="show = !show"> Toggle2 </button> <transition name="slide-fade"> <p v-if="show">hello</p> </transition> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> new Vue({ el: '#demo1', data: { show: true } }) new Vue({ el: '#demo2', data: { show: true } }) </script> 

1.11.5. 編碼 2

<style> .bounce-enter-active { animation: bounce-in .5s; } .bounce-leave-active { animation: bounce-in .5s reverse; } @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.5); } 100% { transform: scale(1); } } </style> <div id="test2"> <button @click="show = !show">Toggleshow</button> <br> <transition name="bounce"> <p v-if="show" style="display: inline-block">Look at me!</p> </transition> </div> <script type="text/javascript" src="../js/vue.js"></script> <script> new Vue({ el: '#test2', data: { show: true } }) </script> 

1.12. 過濾器

1.12.2. 理解過濾器
1) 功能: 對要顯示的數據進行特定格式化後再顯示
2) 注意: 並無改變本來的數據, 但是產生新的對應的數據
1.12.3. 定義和使用過濾器
1) 定義過濾器
Vue.filter(filterName, function(value[,arg1,arg2,...]){
// 進行必定的數據處理
return newValue
})
2) 使用過濾器

 

{{myData |filterName}}

 

 

{{myData |filterName(arg)}}


1.12.4. 編碼

 

<div id="test"> <p>當前時間爲: {{currentTime}}</p> <p>當前時間 1 爲: {{currentTime | dateStr}}</p> <p>當前時間 2 爲: {{currentTime | dateStr('YYYY-MM-DD')}}</p> <p>當前時間 3 爲: {{currentTime | dateStr('HH:mm:ss')}}</p> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.19.0/moment.js"></script> <script> // 註冊過濾器 Vue.filter('dateStr', function (value, format) { return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss') }) new Vue({ el: '#test', data: { currentTime: new Date() } }) </script> 

1.13. 內置指令與自定義指令

1.13.2. 經常使用內置指令
1) v:text : 更新元素的 textContent
2) v-html: 更新元素的 innerHTML
3) v-if :若是爲 true, 當前標籤纔會輸出到頁面
4) v-else:若是爲 false, 當前標籤纔會輸出到頁面
5) v-show: 經過控制 display 樣式來控制顯示/隱藏
6) v-for : 遍歷數組/對象
7) v-on : 綁定事件監聽, 通常簡寫爲@
8) v-bind : 強制綁定解析表達式, 能夠省略 v-bind
9) v-model : 雙向數據綁定
10) ref : 指定惟一標識, vue 對象經過$els 屬性訪問這個元素對象
11) v-cloak : 防止閃現, 與 css 配合: [v-cloak] { display: none }
1.13.3. 自定義指令
1) 註冊全局指令
Vue.directive('my-directive', function(el, binding){
el.innerHTML= binding.value.toupperCase()
})
2) 註冊局部指令
directives : {
'my-directive': {
bind (el, binding) {
el.innerHTML = binding.value.toupperCase()
}
}
}
3) 使用指令
v-my-directive='xxx'
1.13.4. 編碼 1( 內置指令)

<style> [v-cloak] { display: none } </style> <div id="example"> <p v-text="url"></p> <p v-html="url"></p> <img :id="myid" :src="imageSrc"> <p> <span ref="message">atguigu.com</span> <button @click="showMsg">顯示左側文本</button> </p> <p v-cloak>{{url}}</p> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> alert(' 模擬加載慢') new Vue({ el: '#example', data: { url: '<a href="http://www.atguigu.com"> 尚硅谷</a>', myid: 'abc123', imageSrc: 'http://cn.vuejs.org/images/logo.png' }, methods: { showMsg: function () { alert(this.$refs.message.textContent) } } }) </script> 

1.13.5. 編碼 2( 自定義指令)
需求: 自定義 2 個指令

  1. 功能類型於 v-text, 但轉換爲全大寫
  2. 功能類型於 v-text, 但轉換爲全小寫
    <div id="demo1"> <p v-upper-text="msg"></p> <p v-lower-text="msg"></p> </div> <div id="demo2"> <p v-upper-text="msg2"></p> <p v-lower-text="msg2"></p> <!-- 局部指令 , 此處不能使用 --> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript"> // 註冊全局指令 Vue.directive('upper-text', function(el, binding) { el.innerHTML = binding.value.toUpperCase() }) new Vue({ el: '#demo1', data: { msg: 'NBA love thisgame!' }, directives: { // 註冊局部指令 'lower-text': { bind (el, binding) { el.innerHTML = binding.value.toLowerCase() } } } }) new Vue({ el: '#demo2', data: { msg2: 'I Like You' } }) </script> 

    1.14. 自定義插件

    1.14.2. 說明
    1) Vue 插件是一個包含 install方法的對象
    2) 經過 install 方法給 Vue 或 Vue 實例添加方法, 定義全局指令等
    1.14.3. 編碼
1) 插件 JS /** * 自定義 Vue 插件 */ (function () { const MyPlugin = {} MyPlugin.install = function (Vue, options) { // 1. 添加全局方法或屬性 Vue.myGlobalMethod = function () { alert('Vue 函數對象方法執行') } // 2. 添加全局資源 Vue.directive('my-directive', function (el, binding) { el.innerHTML = "MyPlugin my-directive " + binding.value }) // 3. 添加實例方法 Vue.prototype.$myMethod = function () { alert('vue 實例對象方法執行') } } window.MyPlugin = MyPlugin })() 2) 頁面使用插件 <div id="demo"> <!-- 使用自定義指令 --> <p v-my-directive="msg"></p> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript" src="vue-myPlugin.js"></script> <script type="text/javascript"> // 使用自定義插件 Vue.use(MyPlugin) var vm = new Vue({ el: '#demo', data: { msg: 'atguigu' } }) // 調用自定義的靜態方法 Vue.myGlobalMethod() // 調用自定義的對象方法 vm.$myMethod() </script> 

第 2 章:vue 組件化編碼

2.1. 使用 vue-cli 建立模板項目

2.1.1. 說明
1) vue-cli 是 vue 官方提供的腳手架工具
2) github: https://github.com/vuejs/vue-cli
3) 做用: 從 https://github.com/vuejs-templates 下載模板項目
2.1.2. 建立 vue 項目
npm install -g vue-cli
vue init webpack vue_demo
cd vue_demo
npm install
npm run dev
訪問: http://localhost:8080/
2.1.3. 模板項目的結構
|-- build : webpack 相關的配置文件夾(基本不須要修改)
|-- dev-server.js : 經過 express 啓動後臺服務器
|-- config: webpack 相關的配置文件夾(基本不須要修改)
|-- index.js:指定的後臺服務的端口號和靜態資源文件夾
|-- node_modules
|-- src : 源碼文件夾
|-- components: vue組件及其相關資源文件夾
|-- App.vue: 應用根主組件
|-- main.js: 應用入口 js
|-- static: 靜態資源文件夾
|-- .babelrc: babel 的配置文件
|-- .eslintignore: eslint 檢查忽略的配置
|-- .eslintrc.js: eslint 檢查的配置
|-- .gitignore: git 版本管制忽略的配置
|-- index.html: 主頁面文件
|-- package.json: 應用包配置文件
|-- README.md: 應用描述說明的 readme 文件
2.1.4. 效果

2.2. 項目的打包與發佈

2.2.1. 打包:
npm run build
2.2.2. 發佈1 
使用靜態服務器工具包
npm install -g serve
serve dist
訪問: http://localhost:5000
2.2.3. 發佈 2: 
使用動態 web 服務器(tomcat)
修改配置: webpack.prod.conf.js
output: {
publicPath: '/xxx/' //打包文件夾的名稱
}
從新打包:
npm run build
修改 dist 文件夾爲項目名稱: xxx
將 xxx 拷貝到運行的 tomcat 的 webapps 目錄下
訪問: http://localhost:8080/xxx

2.3. eslint

2.3.1. 說明
1) ESLint 是一個代碼規範檢查工具
2) 它定義了不少特定的規則, 一旦你的代碼違背了某一規則, eslint會做出很是有用的提示
3) 官網: http://eslint.org/
4) 基本已替代之前的 JSLint
2.3.2. ESLint 提供如下支持
1) ES
2) JSX
3) style 檢查
4) 自定義錯誤和提示
2.3.3. ESLint 提供如下幾種校驗
1) 語法錯誤校驗
2) 不重要或丟失的標點符號,如分號
3) 無法運行到的代碼塊(使用過 WebStorm 的童鞋應該瞭解)
4) 未被使用的參數提醒
5) 確保樣式的統一規則,如 sass 或者less
6) 檢查變量的命名
2.3.4. 規則的錯誤等級有三種
1) 0:關閉規則。
2) 1:打開規則,而且做爲一個警告(信息打印黃色字體)
3) 2:打開規則,而且做爲一個錯誤(信息打印紅色字體)

2.3.5. 相關配置文件
1) .eslintrc.js : 全局規則配置文件
'rules': {
'no-new': 1
}
2) 在 js/vue 文件中修改局部規則
/ eslint-disable no-new /
new Vue({
el: 'body',
components: { App }
})
3) .eslintignore: 指令檢查忽略的文件
.js .vue

2.4. 組件定義與使用

2.4.1. vue 文件的組成(3 個部分)

1) 模板頁面 <template> 頁面模板 


2) JS 模塊對象

<script> export default { data() {return {}}, methods: {}, computed: {}, components: {} } </script> 

3) 樣式

<style> 樣式定義 </style> 

2.4.2. 基本使用
1) 引入組件
2) 映射成標籤
3) 使用組件標籤

 


2.4.3. 關於標籤名與標籤屬性名書寫問題
1) 寫法一: 如出一轍
2) 寫法二: 大寫變小寫, 並用-鏈接

2.5. 組件間通訊

2.5.1. 組件間通訊基本原則
1) 不要在子組件中直接修改父組件的狀態數據
2) 數據在哪, 更新數據的行爲(函數)就應該定義在哪
2.5.2. vue 組件間通訊方式
1) props
2) vue 的自定義事件
3) 消息訂閱與發佈(如: pubsub 庫)
4) slot
5) vuex(後面單獨講)

2.6. 組件間通訊 1: props

2.6.1. 使用組件標籤時


2.6.2. 定義 MyComponent 時
1) 在組件內聲明全部的 props
2) 方式一: 只指定名稱
props: ['name', 'age', 'setName']
3) 方式二: 指定名稱和類型
props: {
name: String,
age: Number,
setNmae: Function
}
4) 方式三: 指定名稱/類型/必要性/默認值
props: {
name:{type: String, required: true, default:xxx},
}
2.6.3. 注意
1) 此方式用於父組件向子組件傳遞數據
2) 全部標籤屬性都會成爲組件對象的屬性, 模板頁面能夠直接引用
3) 問題:
a. 若是須要向非子後代傳遞數據必須多層逐層傳遞
b. 兄弟組件間也不能直接 props 通訊, 必須藉助父組件才能夠

2.7. 組件間通訊 2: vue 自定義事件

2.7.1. 綁定事件監聽
// 方式一: 經過 v-on 綁定
@delete_todo="deleteTodo"
// 方式二: 經過$on()
this.$refs.xxx.$on('delete_todo', function (todo) {
this.deleteTodo(todo)
})
2.7.2. 觸發事件
// 觸發事件(只能在父組件中接收)
this.$emit(eventName, data)
2.7.3. 注意:
1) 此方式只用於子組件向父組件發送消息(數據)
2) 問題: 隔代組件或兄弟組件間通訊此種方式不合適

2.8. 組件間通訊

3: 消息訂閱與發佈(PubSubJS 庫)
2.8.1. 訂閱消息
PubSub.subscribe('msg', function(msg, data){})
2.8.2. 發佈消息
PubSub.publish('msg', data)
2.8.3. 注意
1) 優勢: 此方式可實現任意關係組件間通訊(數據)
2.8.4. 事件的 2 個重要操做( 總結)

1) 綁定事件監聽 (訂閱消息)
目標: 標籤元素 <button> 事件名(類型): click/focus 回調函數: function(event){} 2) 觸發事件 (發佈消息) DOM 事件:用戶在瀏覽器上對應的界面上作對應的操做 自定義: 編碼手動觸發 

2.9. 組件間通訊

4: slot
2.9.1. 理解
此方式用於父組件向子組件傳遞標籤數據
2.9.2. 子組件: Child.vue

<template> <div> <slot name="xxx">不肯定的標籤結構 1</slot> <div>組件肯定的標籤結構</div> <slot name="yyy">不肯定的標籤結構 2</slot> </div> </template> 

2.9.3. 父組件: Parent.vue

<child> <div slot="xxx">xxx 對應的標籤結構</div> <div slot="yyy">yyyy 對應的標籤結構</div> </child> 

第 3 章:vue-ajax

3.1. vue 項目中經常使用的 2 個 個 ajax 庫

3.1.1. vue-resource
vue 插件, 非官方庫, vue1.x 使用普遍
3.1.2. axios
通用的 ajax 請求庫, 官方推薦, vue2.x 使用普遍
3.2. vue-resource 的使用
3.2.1. 在線文檔
https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
3.2.2. 下載
npm install vue-resource --save
3.2.3. 編碼
// 引入模塊

import VueResource from 'vue-resource' // 使用插件 Vue.use(VueResource) // 經過 vue/組件對象發送 ajax 請求 this.$http.get('/someUrl').then((response) => { //success callback console.log(response.data) //返回結果數據 }, (response) => { //error callback console.log(response.statusText) //錯誤信息 }) 

3.3. axios 的使用

3.2. 在線文檔
https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
3.3. 下載:
npm install axios --save
3.4. 編碼

// 引入模塊 import axios from 'axios' // 發送 ajax 請求 axios.get(url) .then(response => { console.log(response.data) // 獲得返回結果數據 }) .catch(error => { console.log(error.message) }) 

3.4. 測試接口
接口 1: https://api.github.com/search/repositories?q=v&sort=stars
接口 2: https://api.github.com/search/users?q=aa

第 4 章:vue UI 組件庫

4.1. 經常使用

1) Mint UI:
a. 主頁: http://mint-ui.github.io/#!/zh-cn
b. 說明: 餓了麼開源的基於 vue 的移動端 UI 組件庫
2) Elment
a. 主頁: http://element-cn.eleme.io/#/zh-CN
b. 說明: 餓了麼開源的基於 vue 的 PC 端 UI 組件庫

4.2. 使用 MintUI

4.2.1. 下載:
npm install --save mint-ui

4.2.2. 實現按需打包

  1. 下載
    npm install --save-dev babel-plugin-component
  2. 修改 babel 配置
    "plugins": ["transform-runtime",["component", [
    {
    "libraryName": "mint-ui",
    "style": true
    }
    ]]]
    4.2.3. mint-ui 組件分類
    1) 標籤組件
    2) 非標籤組件
    4.2.4. 使用 mint-ui 的組件
    1) index.html <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> <script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></scrip t> <script> if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function(){ FastClick.attach(document.body); }, false); } if(!window.Promise){ document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js" '+'>'+'<'+'/'+'script>'); } </script> 2) main.js import {Button} from 'mint-ui' Vue.component(Button.name, Button) 3) App.vue <template> <mt-button @click="handleClick" type="primary" style="width: 100%">Test</mt-button> </template> <script> import {Toast} from'mint-ui' export default { methods: { handleClick () { Toast(' 點擊了測試'); } } } </script> 

    第 5 章:vue-router

    5.1. 理解

    5 .1.1. 說明
    1) 官方提供的用來實現 SPA 的 vue 插件
    2) github: https://github.com/vuejs/vue-router
    3) 中文文檔: http://router.vuejs.org/zh-cn/
    4) 下載: npminstall vue-router --save
    5 .1.2. 相關 API 說明
    1) VueRouter(): 用於建立路由器的構建函數
    new VueRouter({
    // 多個配置項
    })
    2) 路由配置
    routes: [
    { // 通常路由
    path: '/about',
    component: About
    },
    { // 自動跳轉路由
    path: '/',
    redirect: '/about'
    }
    ]
    3) 註冊路由器
    import router from './router'
    new Vue({
    router
    })
    4) 使用路由組件標籤
  3. : 用來生成路由連接
    Go to XXX
  4. : 用來顯示當前路由組件界面

    5.2. 基本路由

5.2.2. 路由組件
Home.vue
About.vue
5.2.3. 應用組件: App.vue

<div> <!--路由連接--> <router-link to="/about">About</router-link> <router-link to="/home">Home</router-link> <!--用於渲染當前路由組件--> <router-view></router-view> </div> 

5.2.4. 路由器模塊: src/router/index.js
export default new VueRouter({
routes: [
{
path: '/',
redirect: '/about'
},
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})

5.2.5. 註冊路由器: main.js
import Vue from 'vue'
import router from './router'
// 建立 vue 配置路由器
new Vue({
el: '#app',
router,
render: h => h(app)
})
5.2.6. 優化路由器配置
linkActiveClass: 'active', // 指定選中的路由連接的 class
5.2.7. 總結: 編寫使用路由的 3 步 
1) 定義路由組件
2) 註冊路由
3) 使用路由

 

 

5.3. 嵌套路由

5.3.1. 效果
5.3.2. 子路由組件
News.vue
Message.vue
5.3.3. 配置嵌套路由: router.js

path: '/home', component: home, children: [ { path: 'news', component: News },`` { path: 'message', component: Message } ] 

5.3.4. 路由連接: Home.vue

News

Message

 

5.4. 向路由組件傳遞數據

5.4.2. 方式 1: 路由路徑攜帶參數(param/query)
1) 配置路由

children: [ { path: 'mdetail/:id', component: MessageDetail } ] 

2) 路由路徑
<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link>
3) 路由組件中讀取請求參數
this.$route.params.id
5.4.3.
方式 2: 屬性攜帶數據
<router-view :msg="msg"></router-view>

5.5. 緩存路由組件對象

5.5.1. 理解
1) 默認狀況下, 被切換的路由組件對象會死亡釋放, 再次回來時是從新建立的
2) 若是能夠緩存路由組件對象, 能夠提升用戶體驗
5.5.2. 編碼實現

<keep-alive> <router-view></router-view> </keep-alive> 

5.6. 編程式路由導航

5.6.2. 相關 API
1) this.$router.push(path): 至關於點擊路由連接(能夠返回到當前路由界面)
2) this.$router.replace(path): 用新路由替換當前路由(不能夠返回到當前路由界面)
3) this.$router.back(): 請求(返回)上一個記錄路由
4) this.$router.go(-1): 請求(返回)上一個記錄路由
5) this.$router.go(1): 請求下一個記錄路由

第 6 章:vuex

6.1. vuex 理解

6.1.1. vuex 是什麼
1) github站點: https://github.com/vuejs/vuex
2) 在線文檔: https://vuex.vuejs.org/zh-cn/
3) 簡單來講: 對 vue 應用中多個組件的共享狀態進行集中式的管理(讀/寫)
6.1.2. 狀態自管理應用
1) state: 驅動應用的數據源
2) view: 以聲明方式將 state映射到視圖
3) actions: 響應在 view 上的用戶輸入致使的狀態變化(包含 n 個更新狀態的方法)
6.1.3. 多組件共享狀態的問題
1) 多個視圖依賴於同一狀態
2) 來自不一樣視圖的行爲須要變動同一狀態
3) 之前的解決辦法
a. 將數據以及操做數據的行爲都定義在父組件
b. 將數據以及操做數據的行爲傳遞給須要的各個子組件(有可能須要多級傳遞)
4) vuex 就是用來解決這個問題的
6.2. vuex 核心概念和 API
6.2.1. state
1) vuex 管理的狀態對象
2) 它應該是惟一的
const state = {
xxx: initValue
}
6.2.2. mutations
1) 包含多個直接更新 state 的方法(回調函數)的對象
2) 誰來觸發: action 中的 commit('mutation 名稱')
3) 只能包含同步的代碼, 不能寫異步代碼
const mutations = {
yyy (state, {data1}) {
// 更新 state 的某個屬性
}
}
6.2.3. actions
1) 包含多個事件回調函數的對象
2) 經過執行: commit()來觸發 mutation的調用, 間接更新state
3) 誰來觸發: 組件中: $store.dispatch('action 名稱', data1) // 'zzz'
4) 能夠包含異步代碼(定時器, ajax)
const actions = {
zzz ({commit, state}, data1) {
commit('yyy', {data1})
}
}
6.2.4. getters
1) 包含多個計算屬性(get)的對象
2) 誰來讀取: 組件中: $store.getters.xxx
const getters = {
mmm (state) {
return ...
}
}
6.2.5. modules
1) 包含多個 module
2) 一個 module 是一個store 的配置對象
3) 與一個組件(包含有共享數據)對應
6.2.6. 向外暴露 store 對象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
6.2.7. 組件中
import {mapState, mapGetters, mapActions} from 'vuex'
export default {
computed: {
...mapState(['xxx']),
...mapGetters(['mmm']),
}
methods: mapActions(['zzz'])
}
{{xxx}} {{mmm}} @click="zzz(data)"
6.2.8. 映射 store
import store from './store'
new Vue({
store
})
6.2.9. store 對象
1) 全部用 vuex 管理的組件中都多了一個屬性$store,它就是一個 store 對象
2) 屬性:
state: 註冊的 state 對象
getters: 註冊的 getters 對象
3) 方法:
dispatch(actionName, data):分發調用 action
6.3. demo1: 計數器
counter.gif
5.3.1. store.js
/**

  • vuex 的 store 對象模塊
    /
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    /

    state 對象
    相似於 data
    /
    const state = {
    count: 0 // 初始化狀態數據
    }
    /

    mutations 對象
    包含個方法 : 能直接更新 state
    一個方法就是一個 mutation
    mutation 只能包含更新 state 的同步代碼 , 也不會有邏輯
    mutation 由 action 觸發調用 : commit('mutationName')
    /
    const mutations = {
    INCREMENT(state) {
    state.count++
    },
    DECREMENT (state) {// ctrl + shift + x
    state.count--
    }
    }
    /

    actions 對象
    包含個方法 : 觸發 mutation 調用 , 間接更新 state
    一個方法就是一個 action
    action 中能夠有邏輯代碼和異步代碼
    action 由組件來觸發調用 : this.$store.dispatch('actionName')
    /
    const actions = {
    increment ({commit}) {
    commit('INCREMENT')
    },
    decrement ({commit}) {
    commit('DECREMENT')
    },
    incrementIfOdd ({commit, state}) {
    if(state.count%2===1) {
    commit('INCREMENT')
    }
    },
    incrementAsync ({commit}) {
    setTimeout(() => {
    commit('INCREMENT')
    }, 1000)
    }
    }
    /

    getters 對象
    包含多個 get 計算計算屬性方法
    */
    const getters = {
    oddOrEven (state) {
    return state.count%2===0 ? ' 偶數' : ' 奇數'
    },
    count (state) {
    return state.count
    }
    }
    // 向外暴露 store 實例對象
    export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters
    })
    6.3.2. main.js
    import Vue from 'vue'
    import app from './app1.vue'
    // import app from './app.vue'
    import store from './store'
    new Vue({
    el: '#app',
    render: h => h(app),
    store // 全部組件都多個一個屬性 : $store
    })
    6.3.3. app.vue( 未優化前)
    <template> <div> <p>clicked: {{$store.state.count}} times, count is {{oddOrEven}}</p> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementIfOdd">increment if odd</button> <button @click="incrementAsync">increment async</button> </div> </template> <script> export default { computed: { oddOrEven () { return this.$store.getters.oddOrEven } }, methods: { increment () { this.$store.dispatch('increment') }, decrement () { this.$store.dispatch('decrement') }, incrementIfOdd () { this.$store.dispatch('incrementIfOdd') }, incrementAsync () { this.$store.dispatch('incrementAsync') } } } </script> <style> </style> 
    6.3.4. app2.vue( 優化後)
    <template> <div> <p>clicked: {{count}} times, count is {{oddOrEven2}}</p> <button @click="increment">+</button> <button @click="decrement">-</button> <button @click="incrementIfOdd">increment if odd</button> <button @click="incrementAsync">increment async</button> </div> </template> <script> import {mapGetters,mapActions} from'vuex' export default { computed: mapGetters({ // 名稱不同 oddOrEven2: 'oddOrEven', count: 'count' }), methods: mapActions(['increment', 'decrement', 'incrementIfOdd', 'incrementAsync']) // 名稱同樣 } </script> <style> </style> 

6.3.1. store/types.js

/**
* 包含多個 mutation name
*/
export const RECEIVE_TODOS = 'receive_todos'
export const ADD_TODO = 'add_todo'
export const REMOVE_TODO = 'remove_todo'
export const DELETE_DONE = 'delete_done'
export const UPDATE_ALL_TODOS = 'update_all_todos'
6.3.2. store/mutations.js
import {RECEIVE_TODOS, ADD_TODO, REMOVE_TODO, DELETE_DONE, UPDATE_ALL_TODOS}from
'./types'
export default {
[RECEIVE_TODOS] (state, {todos}) {
state.todos = todos
},
[ADD_TODO] (state, {todo}) {
state.todos.unshift(todo)
},
[REMOVE_TODO] (state, {index}) {
state.todos.splice(index, 1)
},
[DELETE_DONE] (state) {
state.todos = state.todos.filter(todo => !todo.complete)
},
[UPDATE_ALL_TODOS] (state, {isCheck}) {
state.todos.forEach(todo => todo.complete = isCheck)
}
}
6.3.3. store/actions.js
import storageUtil from '../util/storageUtil'
import {RECEIVE_TODOS, ADD_TODO, REMOVE_TODO, DELETE_DONE, UPDATE_ALL_TODOS}from
'./types'
export default {
readTodo ({commit}){
setTimeout(() => {
const todos = storageUtil.fetch()
// 提交 commit 觸發 mutation 調用
commit(RECEIVE_TODOS, {todos})
}, 1000)
},
addTodo ({commit}, todo) {
commit(ADD_TODO, {todo})
},
removeTodo ({commit}, index) {
commit(REMOVE_TODO,{index})
},
deleteDone ({commit}) {
commit(DELETE_DONE)
},
updateAllTodos ({commit}, isCheck) {
commit(UPDATE_ALL_TODOS, {isCheck})
}
}
6.3.4. store/getters.js
export default {
todos (state) {
return state.todos
},
totalSize (state) {
return state.todos.length
},
completeSize (state) {
return state.todos.reduce((preTotal,todo) => {
return preTotal + (todo.complete ? 1: 0)
}, 0)
},
isAllComplete (state, getters) {
return getters.totalSize===getters.completeSize && getters.totalSize>0
}
}
6.3.5. store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from'./actions'
import getters from'./getters'
Vue.use(Vuex)
const state = {
todos: []
}
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
6.3.6. components/app.vue
<template> <div class="todo-container"> <div class="todo-wrap"> <todo-header></todo-header> <todo-main></todo-main> <todo-footer></todo-footer> </div> </div> </template> <script> import todoHeader from './todoHeader.vue' import todoMain from './todoMain.vue' import todoFooter from './todoFooter.vue' import storageUtil from '../util/storageUtil' export default { created () { // 模擬異步讀取數據 this.$store.dispatch('readTodo') }, components: { todoHeader, todoMain, todoFooter } } </script> <style> .todo-container { width: 600px; margin: 0 auto; } .todo-container .todo-wrap { padding: 10px; border: 1px solid #ddd; border-radius: 5px; } </style> 6.3.7. components/todoHeader.vue <template> <div class="todo-header"> <input type="text" placeholder=" 請輸入你的任務名稱,按回車鍵確認" v-model="title" @keyup.enter="addItem"/> </div> </template> <script type="text/ecmascript-6"> export default { data () { return { title: null } }, methods: { addItem () { const title = this.title && this.title.trim() if (title) { const todo = { title, complete: false } this.$store.dispatch('addTodo', todo) this.title = null } } } } </script> <style> .todo-header input { width: 560px; height: 28px; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; padding: 4px 7px; } .todo-header input:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 1px rgba(0, 0,0, 0.075), 0 08px rgba(82, 168, 236, 0.6); } </style> 6.3.8. components/todoMain.vue <template> <ul class="todo-main"> <todo-item v-for="(todo, index) in todos" :todo="todo":key="index" :index="index"></todo-item> </ul> </template> <script type="text/ecmascript-6"> import todoItem from './todoItem' import storageUtil from '../util/storageUtil' export default { components: { todoItem }, computed: { todos () { return this.$store.getters.todos } }, watch: { todos: {// 深度監視 todos, 一旦有變化當即保存 handler: storageUtil.save, deep: true } }, } </script> <style> .todo-main { margin-left: 0px; border: 1px solid #ddd; border-radius: 2px; padding: 0px; } .todo-empty { height: 40px; line-height: 40px; border: 1px solid #ddd; border-radius: 2px; padding-left: 5px; margin-top: 10px; } </style> 6.3.9. components/todoItem.vue <template> <li :style="{background: libg}" @mouseenter="handleStyle(true)" @mouseleave="handleStyle(false)"> <label> <input type="checkbox" v-model="todo.complete"/> <span>{{todo.title}}</span> </label> <button class="btn btn-danger" v-show="isShown" @click="deleteItem">刪除</button> </li> </template> <script type="text/ecmascript-6"> export default { props: ['todo', 'index'], data () { return { isShown: false, libg: '#fff' } }, methods: { handleStyle (isEnter) { if (isEnter) { this.isShown = true this.libg = '#ddd' } else { this.isShown = false this.libg = '#fff' } }, deleteItem () { const {todo, deleteTodo, index} = this if (window.confirm(` 肯定刪除${todo.title} 的評論嗎?`)) { // deleteTodo(index) this.$store.dispatch('removeTodo', index) } } } } </script> <style> li { list-style: none; height: 36px; line-height: 36px; padding: 0 5px; border-bottom: 1px solid #ddd; } li label { float: left; cursor: pointer; } li label li input { vertical-align: middle; margin-right: 6px; position: relative; top: -1px; } li button { float: right; display: none; margin-top: 3px; } li:before { content: initial; } li:last-child { border-bottom: none; } </style> 6.3.10. components/todoFooter.vue <template> <div class="todo-footer"> <label> <input type="checkbox" v-model="isAllDone"/> </label> <span> <span>已完成{{completeSize}}</span> / 所有{{totalSize}} </span> <button class="btn btn-danger" @click="deleteDone" v-show="completeSize>0">清除已 完成任務</button> </div> </template> <script> import {mapGetters,mapActions} from'vuex' export default { methods: mapActions(['deleteDone']), computed: { isAllDone: { get () { return this.$store.getters.isAllComplete }, set (value) { //this.$emit('updateTodos', value) this.$store.dispatch('updateAllTodos', value) } }, ...mapGetters(['completeSize', 'totalSize']) } } /* const arr1 = [1, 3,5] const arr2 = [4, ...arr1, 7] const obj = { a: 1, b () { } } const obj2 = { c: 3, ...obj }*/ </script> <style> .todo-footer { height: 40px; line-height: 40px; padding-left: 6px; margin-top: 5px; } .todo-footer label { display: inline-block; margin-right: 20px; cursor: pointer; } .todo-footer label input { position: relative; top: -1px; vertical-align: middle; margin-right: 5px; } .todo-footer button{ float: right; margin-top: 5px; } </style> 6.3.11. util/storageUtil.js var STORAGE_KEY = 'todos' export default { fetch () { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') }, save (todos) { localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)) } } 6.3.12. base.css body { background: #fff; } .btn { display: inline-block; padding: 4px 12px; margin-bottom: 0; font-size: 14px; line-height: 20px; text-align: center; vertical-align: middle; cursor: pointer; box-shadow: inset 01px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); border-radius: 4px; } .btn-danger { color: #fff; background-color: #da4f49; border: 1px solid #bd362f; } .btn-danger:hover { color: #fff; background-color: #bd362f; } .btn:focus { outline: none; } 6.3.13. main.js import Vue from 'vue' import App from './components/app' import store from './store' import './base.css' /* eslint-disable no-new */ new Vue({ el: '#app', render: h => h(App), store }) 

6.5. vuex 結構分析

第 7 章:vue 源碼分析

7.1. 說明

1) 分析 vue 做爲一個 MVVM 框架的基本實現原理
數據代理
模板解析
數據綁定
2) 不直接看 vue.js的源碼
3) 剖析 github 上某基友仿 vue 實現的 mvvm 庫
4) 地址: https://github.com/DMQ/mvvm

7.2. 準備知識

1) [].slice.call(lis): 將僞數組轉換爲真數組
2) node.nodeType: 獲得節點類型
3) Object.defineProperty(obj, propName, {}): 給對象添加/修改屬性(指定描述符)
configurable: true/false 是否能夠從新 define
enumerable: true/false 是否能夠枚舉(for..in / keys())
value: 指定初始值
writable: true/false value 是否能夠修改
get: 回調函數, 用來獲得當前屬性值
set: 回調函數, 用來監視當前屬性值的變化
4) Object.keys(obj): 獲得對象自身可枚舉的屬性名的數組
5) DocumentFragment: 文檔碎片(高效批量更新多個節點)
6) obj.hasOwnProperty(prop): 判斷 prop 是不是 obj自身的屬性

7.3. 數據代理

1) 數據代理: 經過一個對象代理對另外一個對象(在前一個對象內部)中屬性的操做(讀/寫)
2) vue 數據代理: 經過 vm 對象來代理 data 對象中全部屬性的操做
3) 好處: 更方便的操做 data 中的數據
4) 基本實現流程
a. 經過 Object.defineProperty()給vm 添加與 data 對象的屬性對應的屬性描述符
b. 全部添加的屬性都包含 getter/setter
c. getter/setter內部去操做 data 中對應的屬性數據

7.4. 模板解析

7.4.1. 模板解析的基本流程
1) 將 el 的全部子節點取出, 添加到一個新建的文檔 fragment 對象中
2) 對 fragment 中的全部層次子節點遞歸進行編譯解析處理

    • 對大括號表達式文本節點進行解析
    • 對元素節點的指令屬性進行解析
    • 事件指令解析
    • 通常指令解析
      3) 將解析後的 fragment 添加到 el 中顯示
      7.4.2. 模板解析(1): 大括號表達式解析
      1) 根據正則對象獲得匹配出的表達式字符串: 子匹配/RegExp.$1 name
      2) 從 data 中取出表達式對應的屬性值
      3) 將屬性值設置爲文本節點的 textContent
      7.4.3. 模板解析(2): 事件指令解析
      1) 從指令名中取出事件名
      2) 根據指令的值(表達式)從methods 中獲得對應的事件處理函數對象
      3) 給當前元素節點綁定指定事件名和回調函數的 dom 事件監聽
      4) 指令解析完後, 移除此指令屬性
      7.4.4. 模板解析(3): 通常指令解析
      1) 獲得指令名和指令值(表達式) text/html/class msg/myClass
      2) 從 data 中根據表達式獲得對應的值
      3) 根據指令名肯定須要操做元素節點的什麼屬性
    • v-text---textContent 屬性
    • v-html---innerHTML 屬性
    • v-class--className 屬性
      4) 將獲得的表達式的值設置到對應的屬性上
      5) 移除元素的指令屬性

      7.5. 數據綁定

      7.5.1. 數據綁定
      一旦更新了 data 中的某個屬性數據, 全部界面上直接使用或間接使用了此屬性的節點都會
      更新
      7.5.2. 數據劫持
      1) 數據劫持是 vue 中用來實現數據綁定的一種技術
      2) 基本思想: 經過 defineProperty()來監視 data 中全部屬性(任意層次)數據的變化, 一旦變
      化就去更新界面
      7.5.3. 四個重要對象
      1) Observer
      a. 用來對 data 全部屬性數據進行劫持的構造函數
      b. 給 data 中全部屬性從新定義屬性描述(get/set)
      c. 爲 data 中的每一個屬性建立對應的 dep 對象
      2) Dep(Depend)
      a. data 中的每一個屬性(全部層次)都對應一個 dep 對象
      b. 建立的時機:
    • 在初始化 define data 中各個屬性時建立對應的 dep對象
    • 在 data中的某個屬性值被設置爲新的對象時
      c. 對象的結構
      {
      id, // 每一個 dep 都有一個惟一的 id
      subs //包含 n 個對應 watcher 的數組(subscribes 的簡寫)
      }
      d. subs 屬性說明
    • 當 watcher 被建立時, 內部將當前 watcher 對象添加到對應的 dep 對象的 subs 中
    • 當此 data 屬性的值發生改變時, subs 中全部的 watcher 都會收到更新的通知,
      從而最終更新對應的界面
      3) Compiler
      a. 用來解析模板頁面的對象的構造函數(一個實例)
      b. 利用 compile對象解析模板頁面
      c. 每解析一個表達式(非事件指令)都會建立一個對應的 watcher 對象, 並創建 watcher
      與 dep 的關係
      d. complie 與 watcher 關係: 一對多的關係
      4) Watcher
      a. 模板中每一個非事件指令或表達式都對應一個 watcher 對象
      b. 監視當前表達式數據的變化
      c. 建立的時機: 在初始化編譯模板時
      d. 對象的組成
      {
      vm, //vm 對象
      exp, //對應指令的表達式
      cb,//當表達式所對應的數據發生改變的回調函數
      value, //表達式當前的值
      depIds //表達式中各級屬性所對應的 dep 對象的集合對象
      //屬性名爲 dep 的 id,屬性值爲 dep
      }
      5) 總結: dep與 watcher 的關係: 多對多
      a. data 中的一個屬性對應一個 dep, 一個 dep 中可能包含多個 watcher(模板中有幾個
      表達式使用到了同一個屬性)
      b. 模板中一個非事件表達式對應一個 watcher, 一個 watcher 中可能包含多個 dep(表
      達式是多層: a.b)
      c. 數據綁定使用到 2個核心技術
    • defineProperty()
    • 消息訂閱與發佈

      7.6. MVVM 原理圖分析

      7.7. 雙向數據綁定

      1) 雙向數據綁定是創建在單向數據綁定(model==>View)的基礎之上的2) 雙向數據綁定的實現流程:a. 在解析 v-model 指令時, 給當前元素添加 input 監聽b. 當 input 的 value 發生改變時, 將最新的值賦值給當前表達式所對應的 data 屬性
相關文章
相關標籤/搜索