<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 測試實例 - 菜鳥教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p> {{ message }}</p> <input type="text" v-model="message"> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) </script> </body> </html>
因爲js的限制,Vue 不能檢測以上數組的變更,以及對象的添加/刪除,不少人會由於像上面這樣操做,出現視圖沒有更新的問題。javascript
this.$set(你要改變的數組/對象,你要改變的位置/key,你要改爲什麼value)html
this.$set(this.arr, 0, "aa"); // 改變數組 this.$set(this.obj, "c", "cc"); // 改變對象
數組原生方法觸發視圖更新:
Vue能夠監測到數組變化的,數組原生方法:前端
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 測試實例 - 菜鳥教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <p>arr:{{arr}}</p> <p>obj:{{obj}}</p> <button @click="arrFn1">修改數組</button> <button @click="arrFn2">改變數組</button> <button @click="objFn1">增長和刪除對象</button> <button @click="objFn2">修改對象</button> <button @click="this$set">this.$set</button> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { arr: [1, 2, 3], obj: { a: 1, b: 2 } } }, methods: { objFn1() { this.obj.c = 'c' //增長對象屬性 delete this.obj.a; //刪除對象屬性 console.log(this.obj) //數據變化,視圖沒有變化 }, objFn2() { this.obj.a = 'aa' console.log(this.obj) //數據變化,視圖變化 }, arrFn1() { this.arr[0] = '11'; //修改數組 this.arr.length = 1; //修改數組 console.log(this.arr) //數據變化,視圖沒有變化 }, arrFn2() { // splice()、 push()、pop()、shift()、unshift()、sort()、reverse() this.arr.push('3') console.log(this.arr) //數據變化,視圖變化 }, this$set() { this.$set(this.arr, 0, "11"); // 改變數組 this.$set(this.obj, "c", "11"); // 改變對象 } } }) </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 測試實例 - 菜鳥教程(runoob.com)</title> <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> </head> <body> <div id="app"> <div>{{message | filterTest}}</div> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data() { return { message: 1 } }, filters: { filterTest(value) { // value在這裏是message的值 // return value + '%'; return `${value}%`; } } }) </script> </body> </html>
v-if儘可能不要與v-for在同一節點使用,由於v-for 的優先級比 v-if 更高,若是它們處於同一節點的話,那麼每個循環都會運行一遍v-if
若是你想根據循環中的每一項的數據來判斷是否渲染,那麼你這樣作是對的:vue
<li v-for="todo in todos" v-if="todo.type===1"> {{ todo }} </li>
若是你想要根據某些條件跳過循環,而又跟將要渲染的每一項數據沒有關係的話,你能夠將v-if放在v-for的父節點:html5
// 數組是否有數據 跟每一個元素沒有關係 <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
正確使用v-for與v-if優先級的關係,能夠爲你節省大量的性能。java
<!DOCTYPE html> <html> <head> <title></title> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> </head> <body> <div id="app"> <p>{{ message }}</p> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { message: "xuxiao is boy" }, 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); //已被初始化 }, 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> </body> </html>
另外在標綠處,咱們能發現el仍是 {{message}},這裏就是應用的 Virtual DOM(虛擬Dom)技術,先把坑佔住了。到後面mounted掛載的時候再把值渲染進去。node
有關於銷燬,暫時還不是很清楚。咱們在console裏執行下命令對 vue實例進行銷燬。銷燬完成後,咱們再從新改變message的值,vue再也不對此動做進行響應了。可是原先生成的dom元素還存在,能夠這麼理解,執行了destroy操做,後續就再也不受vue控制了。git
https://segmentfault.com/a/11...
Vue的生命週期函數和beforeRouteEnter()/beforeRouteLeave()的函數github
2.觸發屢次瀏覽器重繪及迴流:使用 vnode ,至關於加了一個緩衝,讓一次數據變更所帶來的全部 node 變化,先在 vnode 中進行修改,而後 diff 以後對全部產生差別的節點集中一次對 DOM tree 進行修改,以減小瀏覽器的重繪及迴流vue-router
例子
{ tag: 'div', /*說明這是一個div標籤*/ children: [ /*存放該標籤的子節點*/ { tag: 'a', /*說明這是一個a標籤*/ text: 'click me' /*標籤的內容*/ } ] }
渲染後能夠獲得
<div> <a>click me</a> </div>
當一個組件被定義, data 必須聲明爲返回一個初始數據對象的函數,由於組件可能被用來建立多個實例。若是 data 仍然是一個純粹的對象,則全部的實例將共享引用同一個數據對象!經過提供 data 函數,每次建立一個新實例後,咱們可以調用 data 函數,從而返回初始數據的一個全新副本數據對象。
爲何在組件中用js動態建立的dom,添加樣式不生效
<template> <div class="test"></div> </template> <script> let a=document.querySelector('.test'); let newDom=document.createElement("div"); // 建立dom newDom.setAttribute("class","testAdd" ); // 添加樣式 a.appendChild(newDom); // 插入dom </script> <style scoped> .test{ background:blue; height:100px; width:100px; } .testAdd{ background:red; height:100px; width:100px; } </style>
結果
// test生效 testAdd 不生效 <div data-v-1b971ada class="test"><div class="testAdd"></div></div> .test[data-v-1b971ada]{ // 注意data-v-1b971ada background:blue; height:100px; width:100px; }
緣由
當 <style> 標籤有 scoped 屬性時,它的 CSS 只做用於當前組件中的元素。
它會爲組件中全部的標籤和class樣式添加一個scoped標識,就像上面結果中的data-v-1b971ada。
因此緣由就很清楚了:由於動態添加的dom沒有scoped添加的標識,沒有跟testAdd的樣式匹配起來,致使樣式失效。
解決辦法:
去掉scoped便可
SPA(single page application):單一頁面應用程序,只有一個完整的頁面;它在加載頁面時,不會加載整個頁面,而是隻更新某個指定的容器中內容。單頁面應用(SPA)的核心之一是: 更新視圖而不從新請求頁面;vue-router在實現單頁面前端路由時,提供了兩種方式:Hash模式和History模式;根據mode參數來決定採用哪種方式。
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,因而當 URL 改變時,頁面不會從新加載。 hash(#)是URL 的錨點,表明的是網頁中的一個位置,單單改變#後的部分,瀏覽器只會滾動到相應位置,不會從新加載網頁,也就是說hash 出如今 URL 中,但不會被包含在 http 請求中,對後端徹底沒有影響,所以改變 hash 不會從新加載頁面;同時每一次改變#後的部分,都會在瀏覽器的訪問歷史中增長一個記錄,使用」後退」按鈕,就能夠回到上一個位置;因此說Hash模式經過錨點值的改變,根據不一樣的值,渲染指定DOM位置的不一樣數據。hash 模式的原理是 onhashchange 事件(監測hash值變化),能夠在 window 對象上監聽這個事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> ul { height: 1000px; border-bottom: 1px solid #000; } </style> </head> <body> <ul> <li class="a"> <a href="#a">a</a> </li> <li class="b"> <a href="#b">b</a> </li> </ul> <div> <div id="a">a</div> <div id="b">b</div> </div> </body> </html>
因爲hash模式會在url中自帶#,若是不想要很醜的 hash,咱們能夠用路由的 history 模式,只須要在配置路由規則時,加入"mode: 'history'",這種模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。這兩個方法應用於瀏覽器記錄棧,在當前已有的 back、forward、go 基礎之上,它們提供了對歷史記錄修改的功能。只是當它們執行修改時,雖然改變了當前的 URL ,但瀏覽器不會當即向後端發送請求
const router = new VueRouter({ mode: 'history', routes: [...] })
當你使用 history 模式時,URL 就像正常的 url,例如 http://yoursite.com/user/id,比較好看!
不過這種模式要玩好,還須要後臺配置支持。由於咱們的應用是個單頁客戶端應用,若是後臺沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就很差看了。
因此呢,你要在服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
export const routes = [ {path: "/", name: "homeLink", component:Home} {path: "/register", name: "registerLink", component: Register}, {path: "/login", name: "loginLink", component: Login}, {path: "*", redirect: "/"}]
此處就設置若是URL輸入錯誤或者是URL 匹配不到任何靜態資源,就自動跳到到Home頁面
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app" class="demo"> <!-- 全局註冊 --> <input type="text" placeholder="我是全局自定義指令" v-focus> </div> <script> Vue.directive("focus", { inserted: function(el) { el.focus(); } }) //new Vue要放在後面 new Vue({ el: "#app" }) </script> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app" class="demo"> <!-- 局部註冊 --> <input type="text" placeholder="我是局部自定義指令" v-focus2> </div> <script> new Vue({ el: "#app", directives: { focus2: { inserted: function(el) { el.focus(); } } } }) </script> </body> </html>
一個指令定義對象能夠提供以下幾個鉤子函數 (均爲可選)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="container"> <!-- 準備實現需求: 在h1標籤上面,加上一個按鈕,當點擊按鈕時候,對count實現一次 自增操做,當count等於5的時候,在控制檯輸出‘it is a test’ --> <button @click="handleClick">clickMe</button> <h1 v-if="count < 6" v-change="count">it is a custom directive</h1> </div> <script> //directive new Vue({ el: '#container', data: { count: 0, color: '#ff0000' }, methods: { handleClick: function() { //按鈕單擊,count自增 this.count++; } }, directives: { change: { bind: function(el, bindings) { console.log('指令已經綁定到元素了'); console.log(el); console.log(bindings); //準備將傳遞來的參數 // 顯示在調用該指令的元素的innerHTML el.innerHTML = bindings.value; }, update: function(el, bindings) { console.log('指令的數據有所變化'); console.log(el); console.log(bindings); el.innerHTML = bindings.value; if (bindings.value == 5) { console.log(' it is a test'); } }, unbind: function() { console.log('解除綁定了'); } } } }) </script> </body> </html>
https://www.cnblogs.com/wangr...
https://juejin.im/post/5a3933...
v-if按照條件是否渲染,v-show是display的block或none
key的做用主要是爲了高效的更新虛擬DOM。
https://www.zhihu.com/questio...
在下次 DOM 更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM。
注意:重點是獲取更新後的DOM 就是在開發過程當中有個需求是須要在created階段操做數據更新後的節點 這時候就須要用到Vue.nextTick()
$nextTick就是用來知道何時DOM更新完成
在created()鉤子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操做無異於徒勞,因此在created中必定要將DOM操做的js代碼放進Vue.nextTick()的回調函數中。與之對應的就是mounted()鉤子函數,由於該鉤子函數執行時全部的DOM掛載和渲染都已完成,此時在該鉤子函數中進行任何DOM操做都不會有問題
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app"> <div ref="msg1">{{msg1}}</div> <div>{{msg2}}</div> <button @click="changeMsg">點擊我</button> </div> <script> new Vue({ el: '#app', data() { return { msg1: "11", msg2: "22" } }, methods: { changeMsg() { this.msg1 = "33" this.msg2 = this.$refs.msg1.textContent; console.log('DOM並未渲染完成' + this.$refs.msg1.textContent) //11 this.$nextTick(function() { console.log('DOM已經何渲染完成' + this.$refs.msg1.textContent) //33 }) } } }) </script> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script> <title></title> </head> <body> <div id="app"> <div ref="msg" id="msg" v-if="isShow">{{msg}}</div> <button @click="changeMsg">點擊我</button> </div> <script> new Vue({ el: '#app', data() { return { msg: "aa", isShow: false } }, methods: { changeMsg() { this.isShow = true console.log(document.getElementById("msg")) //null this.$nextTick(function() { console.log(document.getElementById("msg").innerHTML) //aa }) } } }) </script> </body> </html>
https://juejin.im/post/5b6a60...
首先在定義路由的時候配置 meta 字段,自定義一個KeepAlive字段做爲該頁面是否緩存的標記
routes:[{ path: '/search', name: 'search', component: search, meta: { title: '搜索列表頁', keepAlive: true // 標記列表頁須要被緩存 } }, { path: '/detail', name: 'detail', component: detail, meta: { title: '詳情頁', // 詳情頁不須要作緩存,因此不加keepAlive標記 } }]
因爲<keep-alive>組件不支持v-if指令,因此咱們在App.vue中採用兩個<router-view>的寫法,經過當前路由的keepAlive字段來判斷是否對頁面進行緩存:
<div id="app"> <keep-alive> <router-view v-if="$route.meta.keepAlive" /> </keep-alive> <router-view v-if="!$route.meta.keepAlive" /> </div>
使用<keep-alive>提供的 exclude 或者 include 選項,此處咱們使用 exclude ,在App.vue中:
<div id="app"> <keep-alive exclude="detail"> <router-view /> </keep-alive> </div>
須要注意的是,必定要給頁面組件加上相應的name,例如在detail.vue中:
<script>
<script> export default { name: 'detail', // 這個name要和keep-alive中的exclude選項值一致 ... } </script>
這麼寫就表明了在項目中除了name爲detail的頁面組件外,其他頁面都將進行緩存。
父子通訊:
父向子傳遞數據是經過 props,子向父是經過 events($emit);經過父鏈 / 子鏈也能夠通訊($parent / $children);ref 也能夠訪問組件實例;provide / inject API;$attrs/$listeners
兄弟通訊:
Bus;Vuex
跨級通訊:
Bus;Vuex;provide / inject API、$attrs/$listeners
https://juejin.im/post/5bd97e...
https://github.com/ljianshu/B...
Vue 的響應式原理中 Object.defineProperty 有什麼缺陷?爲何在 Vue3.0 採用了 Proxy,拋棄了 Object.defineProperty?
data:{ //普通屬性 msg:'aa', }, computed:{ //計算屬性 reverseMsg:function(){ // 該函數必須有返回值,用來獲取屬性,稱爲get函數 //能夠包含邏輯處理操做,同時reverseMsg依賴於msg,一旦msg發生變化,reverseMsg也會跟着變化 return this.msg.split(' ').reverse().join(' '); } }
<template> <button @click="obj.a = 2">修改</button> </template> <script> export default { data() { return { obj: { a: 1, } } }, watch: { obj: { handler: function(newVal, oldVal) { console.log(newVal); }, deep: true, immediate: true } } } </script>