Vue筆記系列(三)Vue.js進階

Vue筆記系列
一、Vue.js入門
二、Vue.js漸進

深刻響應式的原理

  • 追蹤變化

把一個普通 Javascript 對象傳給 Vue 實例的 data選項,Vue 將遍歷此對象全部的屬性,並使用 Object.defineProperty(中文版) 把這些屬性所有轉爲 getter/setter。
Object.defineProperty 是僅 ES5 支持,且沒法 shim(什麼是shim?) 的特性,這也就是爲何 Vue 不支持 IE8 以及更低版本瀏覽器的緣由。javascript

  • 變化檢測

Vue 不能檢測到對象屬性的添加或刪除。因爲 Vue 會在初始化實例時對屬性執行 getter/setter 轉化過程,因此屬性必須在 data 對象上存在才能讓 Vue 轉換它,這樣才能讓它是響應的。
解決方法就是以前提到的,可使用 Vue.set(object, key, value) 方法將響應屬性添加到嵌套的對象上。還有就是以前提到的要注意,對象不能是 Vue 實例,或者 Vue 實例的根數據對象css

  • 聲明響應式屬性  解決變化檢測問題的另外一個方法

因爲 Vue 不容許動態添加根級響應式屬性,乾脆在一開始就就把該加的屬性加上,因此你必須在初始化實例前聲明根級響應式屬性,哪怕只是一個空值html

  • 異步更新隊列

參考Vue.js漸進中的API和實例方法中的關於nextTick的介紹。前端

過渡

Vue 在插入、更新或者移除 DOM 時,提供多種不一樣方式的應用過渡效果。包括如下工具:vue

  • 在 CSS 過渡和動畫中自動應用 class
  • 能夠配合使用第三方 CSS 動畫庫,如 Animate.css
  • 在過渡鉤子函數中使用 JavaScript 直接操做 DOM
  • 能夠配合使用第三方 JavaScript 動畫庫,如 Velocity.js

(1)單元素、組件的過渡java

Vue 提供了 transition 的封裝組件,在下列情形中,能夠給任何元素和組件添加 entering/leaving 過渡
 條件渲染 (使用 v-if)
 條件展現 (使用 v-show)
 動態組件
 組件根節點node

元素封裝成過渡組件以後,在遇到插入或刪除時,Vue 將
一、自動嗅探目標元素是否有 CSS 過渡或動畫,並在合適時添加/刪除 CSS 類名。
二、若是過渡組件設置了過渡的 JavaScript 鉤子函數,會在相應的階段調用鉤子函數。
三、若是沒有找到 JavaScript 鉤子而且也沒有檢測到 CSS 過渡/動畫,DOM 操做(插入/刪除)在下一幀中當即執行。git

過渡的-css-類名
會有 4 個(CSS)類名在 enter/leave 的過渡中切換
v-enter: 定義進入過渡的開始狀態。在元素被插入時生效,在下一個幀移除。
v-enter-active: 定義進入過渡的結束狀態。在元素被插入時生效,在 transition/animation 完成以後移除。
v-leave: 定義離開過渡的開始狀態。在離開過渡被觸發時生效,在下一個幀移除。
v-leave-active: 定義離開過渡的結束狀態。在離開過渡被觸發時生效,在 transition/animation 完成以後移除。
圖示:
css過渡類名各階段展現github

過渡的-css-類名 例子 ,注意裏面星號部分
<!--css-->
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s
}
.fade-enter, .fade-leave-active {
  opacity: 0
}
<!--html-->
<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
<!--js-->
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})

若是<transition name="my-transition">中 name 沒有設置,對於這些在 enter/leave 過渡中切換的類名,v- 是這些類名的默認前綴。
<!--css-->
.v-enter-active, .v-leave-active {
  transition: opacity .5s
}
.v-enter, .v-leave-active {
  opacity: 0
}
<!--html-->
...
<transition>
    <p v-if="show">hello</p>
  </transition>

CSS 動畫
CSS 動畫用法同 CSS 過渡,區別是在動畫中 v-enter 類名在節點插入 DOM 後不會當即刪除,而是在 animationend 事件觸發時刪除。web

對於 Vue 的過渡系統和其餘第三方 CSS 動畫庫,如過想跟 Animate.css 結合使用的話,Vue也準備了自定義過渡類名來控制,他們的優先級高於普通的類名。
enter-class
enter-active-class
leave-class
leave-active-class

javaScript鉤子
能夠在屬性中聲明 JavaScript 鉤子

<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"
  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  <!-- ... -->
</transition>
// ...
methods: {
  // --------
  // 進入中
  // --------
  beforeEnter: function (el) {
    // ...
  },
  // 此回調函數是可選項的設置
  // 與 CSS 結合時使用
  enter: function (el, done) {
    // ...
    done() //當只用 JavaScript 過渡的時候, 在 enter 和 leave 中,回調函數 done 是必須的 。 不然,它們會被同步調用,過渡會當即完成。
  },
  afterEnter: function (el) {
    // ...
  },
  enterCancelled: function (el) {
    // ...
  },
  // --------
  // 離開時
  // --------
  beforeLeave: function (el) {
    // ...
  },
  // 此回調函數是可選項的設置
  // 與 CSS 結合時使用
  leave: function (el, done) {
    // ...
    done() //當只用 JavaScript 過渡的時候, 在 enter 和 leave 中,回調函數 done 是必須的 。 不然,它們會被同步調用,過渡會當即完成。
  },
  afterLeave: function (el) {
    // ...
  },
  // leaveCancelled 只用於 v-show 中
  leaveCancelled: function (el) {
    // ...
  }
}
JavaScript 鉤子的例子,注意裏面星號部分
<!-- html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="example-4">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false" ******推薦對於僅使用 JavaScript 過渡的元素添加 v-bind:css="false",Vue 會跳過 CSS 的檢測。這也能夠避免過渡過程當中 CSS 的影響。
  >
    <p v-if="show">
      Demo
    </p>
  </transition>
</div>
//javascript
new Vue({
  el: '#example-4',
  data: {
    show: false
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
      el.style.transformOrigin = 'left'
    },
    enter: function (el, done) {
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' }, { complete: done })
    },
    leave: function (el, done) {
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      }, { complete: done })
    }
  }
})
******下面說一下,當只用 JavaScript 過渡的時候, 在 enter 和 leave 中,回調函數 done 是必須的 。 
不然,它們會被同步調用,過渡會當即完成。
 enter: function (el) {
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' })
    },
    leave: function (el) {
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      })
******能夠看到done不僅是在Velocity的options中去掉了,並且參數中的done也去掉了。這是由於我嘗試只去掉options中的done無論用,仍是正常動畫,只有當參數中的done也去掉,元素離開的時候纔是當即完成的,可是進入的過渡仍是存在。

(2)多個元素的過渡

對於原生標籤可使用 v-if/v-else 。注意: 當有相同標籤名的元素切換時,須要經過 key 特性設置惟一的值來標記以讓 Vue 區分它們,不然 Vue 爲了效率只會替換相同標籤內部的內容。
例如:

<transition>
  <button v-if="isEditing" key="save">
    Save
  </button>
  <button v-else key="edit">
    Edit
  </button>
</transition>

(3)過渡模式

默認是進入和離開的過渡同時生效。
in-out: 新元素先進行過渡,完成以後當前元素過渡離開。
out-in: 當前元素先進行過渡,完成以後新元素過渡進入。

(4)多個組件的過渡

須要使用動態組件

多個組件過渡的例子
<!--CSS-->
<style type="text/css">
        #app div{
            position: absolute;
            top: 40px;
            left: 0px;
            margin-left: 20px;
        }
        #app .component-fade-enter,#app .component-fade-leave-active {
            opacity: 0;
        }
        #app .component-fade-enter {
            left: -30px;
        }
        #app .component-fade-enter-active {
            transition: all .5s;
        }
        #app .component-fade-leave-active {
            left: 30px;
            transition: all .5s;
        }
    </style>
<!--html-->
    <div id="app">
        <button type="button" @click="change">改變</button>
        <transition name="component-fade" mode="out-in">
          <component v-bind:is="view"></component>
        </transition>
    </div>
<!--javascript-->
    <script>
        new Vue({
            el: '#app',
            data: {
                view: 'v-a'
            },
              components: {
                'v-a': {
                  template: '<div>Component A</div>'
                },
                'v-b': {
                  template: '<div>Component B</div>'
                }
              },
              methods : {
                  change : function (){
                      this.view == 'v-a'?this.view = 'v-b':this.view = 'v-a'
                  }
              }
        })
    </script>

(4)列表過渡

使用 <transition-group> 組件渲染列表。除了 mode,其餘特性和 <transition> 相同。
<transition-group><transition>的不一樣點:
一、不一樣於 <transition>, `<transition-group>會以一個真實元素呈現:默認爲一個 <span>。你也能夠經過 tag 特性更換爲其餘元素。
二、內部元素 老是須要 提供惟一的 key 屬性值

<div id="list-demo" class="demo">
  <button v-on:click="add">Add</button>
  <button v-on:click="remove">Remove</button>
  <transition-group name="list" tag="p"> <!--元素是p,下面的key是數組的值-->
    <span v-for="item in items" v-bind:key="item" class="list-item">
      {{ item }}
    </span>
  </transition-group>
</div>

列表的位移過渡
<transition-group> 支持經過 CSS transform 過渡移動。當一個子節點被更新,從屏幕上的位置發生變化,它將會獲取應用 CSS 移動類(經過 name 屬性或配置 move-class 屬性自動生成)。

<div id="flip-list-demo" class="demo">
  <button v-on:click="shuffle">Shuffle</button>
  <transition-group name="flip-list" tag="ul">
    <li v-for="item in items" v-bind:key="item">
      {{ item }}
    </li>
  </transition-group>
</div>
<!--沒有move-class,像以前的類名同樣,能夠經過 name 屬性來自定義前綴-->
.flip-list-move {
  transition: transform 1s;
}
 
<transition-group name="flip-list" tag="ul" move-class="start">
    ···
  </transition-group>
<!--經過 move-class 屬性手動設置-->
.start {
  transition: transform 1s;
}
也能夠這麼寫:
<transition-group name="flip-list" tag="ul">
    <li v-for="item in items" v-bind:key="item" class="start">
      {{ item }}
    </li>
  </transition-group>
<!--經過子元素的class 屬性手動設置-->
.start {
  transition: transform 1s;
}

Vue 使用了一個叫 FLIP 簡單的動畫隊列使用 transforms 將元素從以前的位置平滑過渡新的位置。
須要注意的是使用 FLIP 過渡的元素不能設置爲 display: inline 。做爲替代方案,能夠設置爲 display: inline-block 或者放置於 flex 中

過渡狀態

Vue 的過渡系統提供了很是多簡單的方法設置進入、離開和列表的動效(上面提到的那些)。對於數據元素自己的動效,好比:
數字和運算
顏色的顯示
SVG 節點的位置
元素的大小和其餘的屬性**
全部的原始數字都被事先存儲起來,能夠直接轉換到數字。能夠結合 Vue 的響應式和組件系統,使用第三方庫來實現切換元素的過渡狀態。官方文檔中使用的是Tween.js,tween.js是一款可生成平滑動畫效果的js動畫庫,關於它的中文介紹可看一下這篇文章tween.js可生成平滑動畫效果的js動畫庫

這裏面涉及到watch
watch一個對象,鍵是須要觀察的表達式,值是對應回調函數。值也能夠是方法名,或者包含選項的對象。Vue 實例將會在實例化時調用 $watch(),遍歷 watch 對象的每個屬性。

var vm = new Vue({
  data: {
    a: 1,
    b: 2,
    c: 3
  },
  watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    // 方法名
    b: 'someMethod',
    // 深度 watcher
    c: {
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    }
  }
})
vm.a = 2 // -> new: 2, old: 1

順便再說一下實例方法vm.$watch( expOrFn, callback, [options] )
觀察 Vue 實例變化的一個表達式或計算屬性函數。回調函數獲得的參數爲新值和舊值。表達式只接受監督的鍵路徑。對於更復雜的表達式,用一個函數取代。
注意:在變異(不是替換)對象或數組時,舊值將與新值相同,由於它們的引用指向同一個對象/數組。Vue 不會保留變異以前值的副本。(試了一下,沒有進回調函數)
例子:

// 監督的鍵路徑
vm.$watch('a.b.c', function (newVal, oldVal) {
  // 作點什麼
})
// 更復雜的表達式==>函數
vm.$watch(
  function () {
    return this.a + this.b
  },
  function (newVal, oldVal) {
    // 作點什麼
  }
)

vm.$watch 返回一個取消觀察函數,用來中止觸發回調:

var unwatch = vm.$watch('a', cb)
// 以後取消觀察
unwatch()

選項:deep
爲了發現對象內部值的變化,能夠在選項參數中指定 deep: true 。注意監聽數組的變更不須要這麼作。

vm.$watch('someObject', callback, {
  deep: true
})
vm.someObject.nestedValue = 123
// callback is fired???

選項:immediate
在選項參數中指定 immediate: true 將當即以表達式的當前值觸發回調:

vm.$watch('a', callback, {
  immediate: true
})
// 當即以 `a` 的當前值觸發回調

Render函數

Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。在某些狀況須要用到比template 更接近編譯器的render函數

render 函數接收一個 createElement 方法做爲第一個參數用來建立 VNode。
若是組件是一個函數組件,Render 函數還會接收一個額外的 context 參數,爲沒有實例的函數組件提供上下文信息。

createElement 接受的參數:

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一個 HTML 標籤字符串,組件選項對象,或者一個返回值類型爲String/Object的函數,必要參數
  'div',
  // {Object}
  // 一個包含模板相關屬性的數據對象
  // 這樣,您能夠在 template 中使用這些屬性.可選參數.
  {
  },
  // {String | Array}
  // 子節點(VNodes),能夠是一個字符串或者一個數組. 可選參數.
  [
    createElement('h1', 'hello world'),
    createElement(MyComponent, {
      props: {
        someProp: 'foo'
      }
    }),
    'bar'
  ]
)

createElement的第二個屬性data Object參數詳解

{
  // 和`v-bind:class`同樣的 API
  'class': {
    foo: true,
    bar: false
  },
  // 和`v-bind:style`同樣的 API
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 正常的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 組件 props
  props: {
    myProp: 'bar'
  },
  // DOM 屬性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件監聽器基於 "on"
  // 因此再也不支持如 v-on:keyup.enter 修飾器
  // 須要手動匹配 keyCode。
  on: {
    click: this.clickHandler
  },
  // 僅對於組件,用於監聽原生事件,而不是組件內部使用 vm.$emit 觸發的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定義指令. 注意事項:不能對綁定的舊值設值
  // Vue 會爲您持續追蹤
  directives: [
    {
      name: 'my-custom-directive',
      value: '2'
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // Scoped slots in the form of
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => h('span', props.text)
  },
  // 若是組件是其餘組件的子組件,需爲slot指定名稱
  slot: 'name-of-slot'
  // 其餘特殊頂層屬性
  key: 'myKey',
  ref: 'myRef'
}

函數化組件
標記組件爲 functional, 組件無狀態(沒有 data),無實例(沒有 this 上下文)。

Vue.component('my-component', {
  functional: true,
  // 爲了彌補缺乏的實例
  // 提供第二個參數做爲上下文
  render: function (createElement, context) {
    // ...
  },
  // Props 可選
  props: {
    // ...
  }
})

組件須要的一切都是經過上下文傳遞,包括:
props: 提供props 的對象
children: VNode 子節點的數組
slots: slots 對象
data: 傳遞給組件的 data 對象
parent: 對父組件的引用

注意:slots() 和 children 對比

<my-functional-component>
  <p slot="foo">
    first
  </p>
  <p>second</p>
</my-functional-component>

對於這個組件,children 會給你兩個段落標籤,而 slots().default 只會傳遞第二個匿名段落標籤,slots().foo 會傳遞第一個具名段落標籤。同時擁有 children 和 slots() ,所以你能夠選擇讓組件經過 slot() 系統分發或者簡單的經過 children 接收,讓其餘組件去處理。

自定義指令

Vue.directive( id, [definition] )
註冊或獲取全局指令。

// 註冊
Vue.directive('my-directive', {
  bind: function () {},
  inserted: function () {},
  update: function () {},
  componentUpdated: function () {},
  unbind: function () {}
})
// 註冊(傳入一個簡單的指令函數)
Vue.directive('my-directive', function () {
  // 這裏將會被 `bind` 和 `update` 調用
})
// getter,返回已註冊的指令
var myDirective = Vue.directive('my-directive')

鉤子函數
指令定義函數提供了幾個鉤子函數(可選):

  • bind: 只調用一次,指令第一次綁定到元素時調用,用這個鉤子函數能夠定義一個在綁定時執行一次的初始化動做。
  • inserted: 被綁定元素插入父節點時調用(父節點存在便可調用,沒必要存在於 document 中)。
  • update: 被綁定元素所在的模板更新時調用,而不論綁定值是否變化。經過比較更新先後的綁定值,能夠忽略沒必要要的模板更新(詳細的鉤子函數參數見下)。
  • componentUpdated: 被綁定元素所在模板完成一次更新週期時調用。
  • unbind: 只調用一次, 指令與元素解綁時調用。

鉤子函數參數
鉤子函數被賦予瞭如下參數:

  • el: 指令所綁定的元素,能夠用來直接操做 DOM 。
  • binding: 一個對象,包含如下屬性:

    • name: 指令名,不包括 v-前綴。
    • value: 指令的綁定值, 例如: v-my-directive="1 + 1", value 的值是 2。
    • oldValue: 指令綁定的前一個值,僅在 update和 componentUpdated鉤子中可用。不管值是否改變均可用。
    • expression: 綁定值的字符串形式。 例如 v-my-directive="1 + 1", expression 的值是 "1 + 1"。
    • arg: 傳給指令的參數。例如 v-my-directive:foo, arg 的值是 "foo"。
    • modifiers: 一個包含修飾符的對象。 例如: v-my-directive.foo.bar, 修飾符對象 modifiers 的值是 { foo: true, bar: true }。
  • vnode: Vue 編譯生成的虛擬節點,查閱 VNode API 瞭解更多詳情。
  • oldVnode: 上一個虛擬節點,僅在 update和 componentUpdated鉤子中可用。
注意:除了 el以外,其它參數都應該是隻讀的,儘可能不要修改他們。若是須要在鉤子之間共享數據,建議經過元素的  dataset 來進行。

例子:

html
<div id="hook-arguments-example" v-demo:hello.a.b="message"></div>

javascript
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})
new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})

 


用Vue實現一個demo(包含增長,刪除,選中)

這個demo放到了github上,有時間會把它整理到個人github主頁的一個小欄目裏,那樣就是方便看了。

從github上用各類方法下載到本地,找到index.html文件,雙擊就能看到效果

推薦一下微信公衆號:《web前端教程》的一篇vue實戰案例,你們能夠關注一下這個公衆號,他的教程都是挺新的,並且很基礎很基礎,個人這個demo就是在根據他的例子(特別是樣式),增長了計數,增長了本地存儲。


最後,這三篇文章是從個人簡書裏面搬來的。

相關文章
相關標籤/搜索