vue2.x入坑總結—回顧對比angularJS/React的一統

從感性的角度講,我是不屑於用VUE,以爲react套件用起來更順手,可是vue如今越來火,因此也不得入vue(雜燴湯)的坑。vue/anguarJS/React,三者對關係如今就是:html

vue_react_anguarJS之間的關係

https://www.zhoulujun.cn/uploadfile/images/2018/0626/20180626214906428779269.jpg前端

本身ps了下,以爲深有道理,騷年們本身體悟,而後再問軍哥^_^vue

不過迴歸真題,看vue仍是先了解下https://cdn.zhoulujun.cn/vue.jpg(太大,本身打開)node

vue生命週期及相關主題

組件實例週期

vue全部功能的實現都是圍繞其生命週期進行的,在生命週期的不一樣階段調用對應的鉤子函數能夠實現組件數據管理和DOM渲染兩大重要功能。學習實例的生命週期,能幫助咱們理解vue實例的運行機制,更好地利用鉤子函數完成咱們的業務代碼。react

create 和 mounted 相關 的函數有:angularjs

beforecreated》created》beforeMount》mounted》beforeDestroyes6

  • beforecreated:el 和 data 並未初始化,  案例:能夠在這加個loading事件 及獲取路由參數,可是this.(data|computed|methods)參數均爲undefind(沒法訪問到 el 屬性和 data 屬性等web

  • 在beforeCreate和created之間:在這個生命週期之間,進行初始化事件,進行數據的觀測,能夠看到在created的時候數據已經和data屬性進行綁定(放在data中的屬性當值發生改變的同時,視圖也會改變)ajax

  • created:組件實例建立完成,屬性已綁定,但 DOM 還未生成,$el 屬性還不存在(this.$refs.XXX===undfined)。案例:在這結束loading,還作一些初始化,如根據父組件props計算當前組件數據算法

  1. created和beforeMount之間:首先會判斷對象是否有el選項。若是有的話就繼續向下編譯,若是沒有el選項,則中止編譯,也就意味着中止了生命週期,直到在該vue實例上調用vm.$mount(el)。再次判斷template參數選項的有無(由於vue須要經過el找到對應的outer template):

    (1)若是vue實例對象中有template參數選項,則將其做爲模板編譯成render函數。

    (2)若是沒有template選項,則將外部HTML做爲模板編譯。

    (3)能夠看到template中的模板優先級要高於outer HTML的優先級。

    若是沒有template,則經過render傳人的createElement編譯

    綜合排名優先級:render函數選項 > template選項 > outer HTML.

    這裏沒有看懂,能夠看一下官方文檔 獨立構建和運行時構建

  • beforeMount:完成了 el 和 data 初始化 。坑:若是直接使用{{msg}} 在生命週期beforeMount期間,此刻的msg數據還沒有編譯至{{msg}}中,用戶能看到一瞬間的{{msg}}  v-cloak

  • beforeMount和mounted之間:給vue實例對象添加$el成員,而且替換掉掛在的DOM元素

  • mounted :完成掛載    案例: 在這發起後端請求,拿回數據,配合路由鉤子作一些事情

  • beforeUpdate:能夠監聽到data的變化可是view層沒有被從新渲染,view層的數據沒有變化

  • beforeUpdate和update之間:當vue發現data中的數據發生了改變,會觸發對應組件的從新渲染(從新渲染虛擬 dom,並經過 diff 算法對比 vnode 節點差別更新真實 dom (virtual DOM re-render and patch)),前後調用beforeUpdate和updated鉤子函數(beforeUpdate:能夠監聽到data的變化可是view層沒有被從新渲染,view層的數據沒有變化,updated: view層才被從新渲染,數據更新。此處可回顧下react對更新函數。 

  • updated:候 view層才被從新渲染,數據更新

  • beforeDestroy    組件銷燬以前,案例:你確認刪除XX嗎?第二個:好比走馬燈文字,路由跳轉以後,由於組件已經銷燬了,可是setInterval尚未銷燬,還在繼續後臺調用,控制檯會不斷報錯,若是運算量大的話,沒法及時清除,會致使嚴重的頁面卡頓。解決辦法:在組件生命週期beforeDestroy中止setInterval

  • destroyed :當前組件已被刪除,清空相關內容 。實例銷燬後雖然 dom 和屬性方法都還存在,但改變他們都將再也不生效!

這這裏順便回顧下react生命週期

  • getDefaultProps()+getInitialState() es5≈ es6 contruct()

    函數初始化。定時init state,也可訪問props。 這個階段,至關於 vue的create 函數該作的事情。

  • componentWillMount()

    組件初始化時只調用,之後組件更新不調用,整個生命週期只調用一次,在客戶端也在服務端,此時能夠修改state。這個階段應該至關於 vue的 beforeMount()

  • render()

    react最重要的步驟,建立虛擬dom,進行diff算法,更新dom樹都在此進行。此時就不能更改state了。 這個至關於 vue  mounted

  • componentDidMount()()

    組件渲染以後調用,只調用一次,只在客戶端。這個階段,通常的異步數據放在這個函數內處理

  • componentWillReceiveProps(nextProps)

    組件初始化時不調用,組件接受新的props時調用。

  • shouldComponentUpdate(nextProps, nextState)

    react性能優化很是重要的一環。組件接受新的state或者props時調用,咱們能夠設置在此對比先後兩個props和state是否相同,若是相同則返回false阻止更新,由於相同的屬性狀態必定會生成相同的dom樹,這樣就不須要創造新的dom樹和舊的dom樹進行diff算法對比,節省大量性能,尤爲是在dom結構複雜的時候。可是state依然會保持更新。

    這這裏,是vue PK react 的重點。

    Vue宣稱能夠更快地計算出Virtual DOM的差別,這是因爲它在渲染過程當中,會跟蹤每個組件的依賴關係,不須要從新渲染整個組件樹。

    而對於React而言,每當應用的狀態被改變時,所有子組件都會從新渲染。固然,這能夠經過shouldComponentUpdate這個生命週期方法來進行控制,但Vue將此視爲默認的優化。

    小結:若是你的應用中,交互複雜,須要處理大量的UI變化,那麼使用Virtual DOM是一個好主意。若是你更新元素並不頻繁,那麼Virtual DOM並不必定適用,性能極可能還不如直接操控DOM。

  • componentWillUpdata(nextProps, nextState)

    組件初始化時不調用,只有在組件將要更新時才調用,此時能夠修改state。這個通常沒有生命卵用。可能toast一下。 大體至關於vue的beforeUpdate

  • componentDidUpdate()

    組件初始化時不調用,組件更新完成後調用,此時能夠獲取dom節點。

  • componentWillUnmount()

    組件將要卸載時調用,一些事件監聽和定時器須要在此時清除。至關於vue的beforeDestroy

相比來說,以爲react的生命週期更加清爽。建議參看:重談react優點——react技術棧回顧

路由鉤子

路由是項目等重點,不少事情能夠在路由裏面處理好。路由和store等規劃項目基礎架構核心,沒有好的規劃,工程就是一坨屎。

全局路由鉤子

做用於全部路由切換,通常在main.js裏面定義

  • beforeEach:通常在這個勾子的回調中,對路由進行攔截。好比,未登陸的用戶,直接進入了須要登陸纔可見的頁面,那麼能夠用next(false)來攔截,使其跳回原頁面等,值得注意的是,若是沒有調用next方法,那麼頁面將卡在那。

    next的四種用法

    1. next() 跳入下一個頁面

    2. next('/path') 改變路由的跳轉方向,使其跳到另外一個路由

    3. next(false)  返回原來的頁面

    4. next((vm)=>{})  僅在beforeRouteEnter中可用,vm是組件實例。

  • afterEach :在全部路由跳轉結束的時候調用,和beforeEach是相似的,可是它沒有next方法,這裏好比作修改標籤標題: document.title = to.meta.title等工做。

組件路由勾子

和全局勾子不一樣的是,它僅僅做用於某個組件,通常在.vue文件中去定義。

beforeRouteEnter

這個是一個很不一樣的勾子。由於beforeRouterEnter在組件建立以前調用,因此它沒法直接用this來訪問組件實例。

爲了彌補這一點,vue-router開發人員,給他的next方法加了特技,能夠傳一個回調,回調的第一個參數便是組件實例。

通常咱們能夠利用這點,對實例上的數據進行修改,調用實例上的方法。

咱們能夠在這個方法去請求數據,在數據獲取到以後,再調用next就能保證你進頁面的時候,數據已經獲取到了。沒錯,這裏next有阻塞的效果。你沒調用的話,就會一直卡在那

beforeRouteLeave

在離開路由時調用。能夠用this來訪問組件實例。可是next中不能傳回調。

beforeRouteUpdate:

這個方法是vue-router2.2版本加上的。由於原來的版本中,若是一個在兩個子路由之間跳轉,是不觸發beforeRouteLeave的。這會致使某些重置操做,沒地方觸發。在以前,咱們都是用watch $route來hack的。可是經過這個勾子,咱們有了更好的方式。

 

指令週期

bind:只調用一次,指令第一次綁定到元素時調用,用這個鉤子函數能夠定義一個在綁定時執行一次的初始化動做。

inserted:被綁定元素插入父節點時調用(父節點存在便可調用,沒必要存在於 document 中)。

其實是插入vnode的時候調用。

update:被綁定元素所在的模板更新時調用,而不論綁定值是否變化。經過比較更新先後的綁定值,能夠忽略沒必要要的模板更新。

慎用,若是在指令裏綁定事件,而且用這個週期的,記得把事件註銷

componentUpdated:被綁定元素所在模板完成一次更新週期時調用。

unbind:只調用一次, 指令與元素解綁時調用。

一個頁面跳轉,發生的事情

  1. 路由勾子 (beforeEach、beforeRouteEnter、afterEach)

  2. 根組件 (beforeCreate、created、beforeMount)

  3. 組件 (beforeCreate、created、beforeMount)

  4. 指令 (bind、inserted)

  5. 組件 mounted

  6. 根組件 mounted

  7. beforeRouteEnter的next的回調

  8. nextTick

結論:

路由勾子執行週期很是早,甚至在根實例的渲染以前

具體的順序 router.beforeEach > beforeRouteEnter > router.afterEach

tip:在進行路由攔截的時候要避免使用實例內部的方法或屬性。

在開發項目時候,咱們腦門一拍把,具體攔截的程序,寫在了根實例的方法上了,到beforeEach去調用。結果致使整個攔截的週期,推遲到實例渲染的以後。

所以對於一些路由組件的beforeRouteEnter裏的請求並沒有法攔截,頁面看上去好像已經攔截下來了。

實際上請求依然發了出去,beforeRouteEnter內的函數依然執行了。

指令的綁定在組件mounted以前,組件的beforeMount以後

不得不提的, beforeRouteEnter的next勾子

beforeRouteEnter的執行順序是如此靠前,而其中next的回調勾子的函數,執行則很是靠後,在mounted以後!!

咱們一般是在beforeRouteEnter中加載一些首屏用數據,待數據收到後,再調用next勾子,經過回調的參數vm將數據綁定到實例上。

所以,請注意next的勾子是很是靠後的。

nextTick:越早註冊的nextTick觸發越早

 

上文講了這麼多的router,順勢總結下:

router-link屬性

:to :至關於a標籤中的"herf"屬性,後面跟跳轉連接所用

replace:replace在routre-link標籤中添加後,頁面切換時不會留下歷史記錄

tag:具備tag屬性的router-link會被渲染成相應的標籤

active-class:這個屬性是設置激活連接時class屬性,也就是當前頁面全部與當前地址所匹配的的連接都會被添加class屬性

exact:開啓router-link的嚴格模式

用了vue-router,證實項目工程仍是蠻大的,建議使用vuex來作全局數據管理(可能用redux習慣了吧!)

Vuex下Store的模塊化拆

vuex的store天生自帶modules概念,同時也須要thunk中間件,action處理異步數據。下面copy一點我項目code demo:

const store = new Vuex.Store({
  modules: {
    authForm: authFormStore,
    BankAdd:BankAddFormStore
  }
});

下面是store的示範

const authFormStore = {
  state: {
    name: ""
  },
  mutations: {
    update: function (state, obj) { //我原來也是寫了N多個update函數,圖樣圖森破啊
      state[obj.name] = obj.value;
    },
  },
  action: {
    setData: function (context, obj) {
      //TODO
      Vue.http.post("api", params, {emulateJSON: true}).then(function (res) {
        // 處理業務
        // 調用上面setAgree方法更新點贊數
        context.commit(obj.name, obj.value);
      }, function () {
      })
    },
  },
  getters: {
    getNews(state){
      //TODO  return new value
    }
  }
}

在vue組件mapState

computed: {
  memberType: function () {
    let memberType = this.$route.params.memberType;
    this.initWeChat(window.location.href, memberType);
    return memberType;
  },
  ...mapState({
    authForm: state => state.authForm,
    epCertType: state => state.authForm.epCertType
  })
},

這裏有坑:表單的v-model屬性值是Vuex的state時,若是時嚴格模式,由於用戶輸入時,v-model會試圖修改v-model的值,因爲修改並不是mutation執行的,嚴格模式下會拋出錯誤。

針對這種狀況,有兩個處理方法:一個是雙向綁定的計算屬性,一個是給表單綁定value,而後偵聽input或change事件,在事件中調用action。

computed:{
    message:{
      get(){
        return this.$store.obj.message
      },
      set(value){
        this.$store.commit('updateMessage',value)
      }
    }
}

第二種方法

computed:{
 ...mapState({
  message: state => state.obj.message
 })
},
methods:{
 updateMessage(e){
  this.$store.commit('updateMessage',e.target.value)
 }
}
mutations:{ //store mutation函數
 updateMessage(state,message){
  state.obj.message=message
 }
}

 

 Vue.js 最核心的功能有兩個,一是響應式的數據綁定系統,二是組件系統

所謂雙向綁定,指的是vue實例中的data與其渲染的DOM元素的內容保持一致,不管誰被改變,另外一方會相應的更新爲相同的數據。這是經過設置屬性訪問器實現的。

關於vue\angluar\react的數據綁定:雙向綁定和單向數據流

Vue 的依賴追蹤是【原理上不支持雙向綁定,v-model 只是經過監聽 DOM 事件實現的語法糖】

vue的依賴追蹤是經過 Object.defineProperty 把data對象的屬性所有轉爲 getter/setter來實現的;當改變數據的某個屬性值時,會觸發set函數,獲取該屬性值的時候會觸發get函數,經過這個特性來實現改變數據時改變視圖;也就是說只有當數據改變時纔會觸發視圖的改變,反過來在操做視圖時,只能經過DOM事件來改變數據,再由此來改變視圖,以此來實現雙向綁定

雙向綁定是在同一個組件內,將數據和視圖綁定起來,和父子組件之間的通訊並沒有什麼關聯;

組件之間的通訊採用單向數據流是爲了組件間更好的解耦,在開發中可能有多個子組件依賴於父組件的某個數據,假如子組件能夠修改父組件數據的話,一個子組件變化會引起全部依賴這個數據的子組件發生變化,因此vue不推薦子組件修改父組件的數據,直接修改props會拋出警告

這裏推薦閱讀《Vue.js雙向綁定的實現原理

react沒有數據雙向綁定

react是單向數據流:對應任何可變數據理應只有一個單一「數據源」,數據源狀態提高至父組件中

react中經過將state(Model層)與View層數據進行雙向綁定達數據的實時更新變化,具體來講就是在View層直接寫JS代碼Model層中的數據拿過來渲染,一旦像表單操做、觸發事件、ajax請求等觸發數據變化,則進行雙同步

angular也是雙向數據綁定(一次作完全部數據變動,而後總體應用到界面上)

安利下:再談angularJS數據綁定機制及背後原理—angularJS常見問題總結

三者中,我仍是更推崇react+redux模式(自上而下的數據流,業務聚焦於數據樹設計)

這裏面不得不提的就是,vuejs對data中數組的原生方法進行了封裝,因此在改變數組時可以觸發視圖更新。這個我在寫日期控件對時候遇到不少坑,好比:

  1. 經過索引直接修改數組的元素,例如vm.items[0] = {title: 'title'}

  2. 沒法直接修改數組的長度,例如vm.items.length = 0

解決方案: 對於第一種vue提供了set方法vm.items.set(0,{title: ‘title’}) 或vm.$set(‘items[0]’,{title: ‘title’})。另一個列表渲染的時候的有一個性能的小技巧: 若是數組中自己自帶一個惟一的標識 id ,那麼在渲染的時候,經過trace-by給數組設定惟一的標識,這樣vuejs在渲染過程當中會盡可能重複原有對象的做用域和dom元素。

 

關於用「箭頭函數精簡你的 Vue 模塊」(建議點擊閱讀),精簡出來就是:

methods: {
  method(){//todo}  //不用: method:()=>{//todo}   method:function(){//todo}
}

 

參考文章:

Vue2.0 探索之路——生命週期和鉤子函數的一些理解

詳解vue生命週期

vue生命週期

vue生命週期探究

vue安裝,router-link的一些屬性,用法,tag active-class,to,replace,exex等等

Vuex下Store的模塊化拆分實踐

Vue.js與React的全面對比

Vue.js 2.0源碼解析以前端渲染篇

用箭頭函數精簡你的 Vue 模塊 – dotdev

 轉載請註明來源,vue2.x入坑總結-回顧對比angularJS/React - vue入坑總結 - 周陸軍的我的網站,:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue/8125.html。不妥之處,望告之,謝謝!

相關文章
相關標籤/搜索