最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度了解vue.js中hooks的相關知識

 更新時間:2019年06月14日 08:53:42   作者:微笑向暖_Tini  
這篇文章主要介紹了深度了解vue.js中hooks的相關知識,生命周期鉤子提供了一些 方法 ,因此你可以在組件生命周期的不同時刻精確地觸發(fā)某些操作。當我們將組件實例化時,組件會被創(chuàng)建,反之會被銷毀。,需要的朋友可以參考下

背景

最近研究了vue3.0的最新進展,發(fā)現變動很大,總體上看,vue也開始向hooks靠攏,而且vue作者本人也稱vue3.0的特性吸取了很多hooks的靈感。所以趁著vue3.0未正式發(fā)布前,抓緊時間研究一下hooks相關的東西。

源碼地址:vue-hooks-poc

為什么要用hooks?

首先從class-component/vue-options說起:

  • 跨組件代碼難以復用
  • 大組件,維護困難,顆粒度不好控制,細粒度劃分時,組件嵌套存層次太深-影響性能
  • 類組件,this不可控,邏輯分散,不容易理解
  • mixins具有副作用,邏輯互相嵌套,數據來源不明,且不能互相消費

當一個模版依賴了很多mixin的時候,很容易出現數據來源不清或者命名沖突的問題,而且開發(fā)mixins的時候,邏輯及邏輯依賴的屬性互相分散且mixin之間不可互相消費。這些都是開發(fā)中令人非常痛苦的點,因此,vue3.0中引入hooks相關的特性非常明智。

vue-hooks

在探究vue-hooks之前,先粗略的回顧一下vue的響應式系統(tǒng):首先,vue組件初始化時會將掛載在data上的屬性響應式處理(掛載依賴管理器),然后模版編譯成v-dom的過程中,實例化一個Watcher觀察者觀察整個比對后的vnode,同時也會訪問這些依賴的屬性,觸發(fā)依賴管理器收集依賴(與Watcher觀察者建立關聯(lián))。當依賴的屬性發(fā)生變化時,會通知對應的Watcher觀察者重新求值(setter->notify->watcher->run),對應到模版中就是重新render(re-render)。

注意:vue內部默認將re-render過程放入微任務隊列中,當前的render會在上一次render flush階段求值。

withHooks

export function withHooks(render) {
return {
data() {
return {
_state: {}
}
},
created() {
this._effectStore = {}
this._refsStore = {}
this._computedStore = {}
},
render(h) {
callIndex = 0
currentInstance = this
isMounting = !this._vnode
const ret = render(h, this.$attrs, this.$props)
currentInstance = null
return ret
}
}
}

withHooks為vue組件提供了hooks+jsx的開發(fā)方式,使用方式如下:

export default withHooks((h)=>{
...
return <span></span>
})

不難看出,withHooks依舊是返回一個vue component的配置項options,后續(xù)的hooks相關的屬性都掛載在本地提供的options上。

首先,先分析一下vue-hooks需要用到的幾個全局變量:

  • currentInstance:緩存當前的vue實例
  • isMounting:render是否為首次渲染

isMounting = !this._vnode

這里的_vnode與$vnode有很大的區(qū)別,$vnode代表父組件(vm._vnode.parent)

_vnode初始化為null,在mounted階段會被賦值為當前組件的v-dom

isMounting除了控制內部數據初始化的階段外,還能防止重復re-render。

  • callIndex:屬性索引,當往options上掛載屬性時,使用callIndex作為唯一當索引標識。

vue options上聲明的幾個本地變量:

  • _state:放置響應式數據
  • _refsStore:放置非響應式數據,且返回引用類型
  • _effectStore:存放副作用邏輯和清理邏輯
  • _computedStore:存放計算屬性

最后,withHooks的回調函數,傳入了attrs和$props作為入參,且在渲染完當前組件后,重置全局變量,以備渲染下個組件。

useData

const data = useData(initial)
export function useData(initial) {
const id = ++callIndex
const state = currentInstance.$data._state
if (isMounting) {
currentInstance.$set(state, id, initial)
}
return state[id]
}

我們知道,想要響應式的監(jiān)聽一個數據的變化,在vue中需要經過一些處理,且場景比較受限。使用useData聲明變量的同時,也會在內部data._state上掛載一個響應式數據。但缺陷是,它沒有提供更新器,對外返回的數據發(fā)生變化時,有可能會丟失響應式監(jiān)聽。

useState

const [data, setData] = useState(initial)
export function useState(initial) {
ensureCurrentInstance()
const id = ++callIndex
const state = currentInstance.$data._state
const updater = newValue => {
state[id] = newValue
}
if (isMounting) {
currentInstance.$set(state, id, initial)
}
return [state[id], updater]
}

useState是hooks非常核心的API之一,它在內部通過閉包提供了一個更新器updater,使用updater可以響應式更新數據,數據變更后會觸發(fā)re-render,下一次的render過程,不會在重新使用$set初始化,而是會取上一次更新后的緩存值。

useRef

const data = useRef(initial) // data = {current: initial}
export function useRef(initial) {
ensureCurrentInstance()
const id = ++callIndex
const { _refsStore: refs } = currentInstance
return isMounting ? (refs[id] = { current: initial }) : refs[id]
}

使用useRef初始化會返回一個攜帶current的引用,current指向初始化的值。我在初次使用useRef的時候總是理解不了它的應用場景,但真正上手后還是多少有了一些感受。

比如有以下代碼:

export default withHooks(h => {
const [count, setCount] = useState(0)
const num = useRef(count)
const log = () => {
let sum = count + 1
setCount(sum)
num.current = sum
console.log(count, num.current);
}
return (
<Button onClick={log}>{count}{num.current}</Button>
)
})

點擊按鈕會將數值+1,同時打印對應的變量,輸出結果為:

0 1
1 2
2 3
3 4
4 5

可以看到,num.current永遠都是最新的值,而count獲取到的是上一次render的值。

其實,這里將num提升至全局作用域也可以實現相同的效果。

所以可以預見useRef的使用場景:

  • 多次re-render過程中保存最新的值
  • 該值不需要響應式處理
  • 不污染其他作用域

useEffect

useEffect(function ()=>{
// 副作用邏輯
return ()=> {
// 清理邏輯
}
}, [deps])
export function useEffect(rawEffect, deps) {
ensureCurrentInstance()
const id = ++callIndex
if (isMounting) {
const cleanup = () => {
const { current } = cleanup
if (current) {
current()
cleanup.current = null
}
}
const effect = function() {
const { current } = effect
if (current) {
cleanup.current = current.call(this)
effect.current = null
}
}
effect.current = rawEffect
currentInstance._effectStore[id] = {
effect,
cleanup,
deps
}
currentInstance.$on('hook:mounted', effect)
currentInstance.$on('hook:destroyed', cleanup)
if (!deps || deps.length > 0) {
currentInstance.$on('hook:updated', effect)
}
} else {
const record = currentInstance._effectStore[id]
const { effect, cleanup, deps: prevDeps = [] } = record
record.deps = deps
if (!deps || deps.some((d, i) => d !== prevDeps[i])) {
cleanup()
effect.current = rawEffect
}
}
}

useEffect同樣是hooks中非常重要的API之一,它負責副作用處理和清理邏輯。這里的副作用可以理解為可以根據依賴選擇性的執(zhí)行的操作,沒必要每次re-render都執(zhí)行,比如dom操作,網絡請求等。而這些操作可能會導致一些副作用,比如需要清除dom監(jiān)聽器,清空引用等等。

先從執(zhí)行順序上看,初始化時,聲明了清理函數和副作用函數,并將effect的current指向當前的副作用邏輯,在mounted階段調用一次副作用函數,將返回值當成清理邏輯保存。同時根據依賴來判斷是否在updated階段再次調用副作用函數。
非首次渲染時,會根據deps依賴來判斷是否需要再次調用副作用函數,需要再次執(zhí)行時,先清除上一次render產生的副作用,并將副作用函數的current指向最新的副作用邏輯,等待updated階段調用。

useMounted

useMounted(function(){})
export function useMounted(fn) {
useEffect(fn, [])
}

useEffect依賴傳[]時,副作用函數只在mounted階段調用。

useDestroyed

useDestroyed(function(){})
export function useDestroyed(fn) {
useEffect(() => fn, [])
}

useEffect依賴傳[]且存在返回函數,返回函數會被當作清理邏輯在destroyed調用。

useUpdated

useUpdated(fn, deps)
export function useUpdated(fn, deps) {
const isMount = useRef(true)
useEffect(() => {
if (isMount.current) {
isMount.current = false
} else {
return fn()
}
}, deps)
}

如果deps固定不變,傳入的useEffect會在mounted和updated階段各執(zhí)行一次,這里借助useRef聲明一個持久化的變量,來跳過mounted階段。

useWatch

export function useWatch(getter, cb, options) {
ensureCurrentInstance()
if (isMounting) {
currentInstance.$watch(getter, cb, options)
}
}

使用方式同$watch。這里加了一個是否初次渲染判斷,防止re-render產生多余Watcher觀察者。

useComputed

const data = useData({count:1})
const getCount = useComputed(()=>data.count)
export function useComputed(getter) {
ensureCurrentInstance()
const id = ++callIndex
const store = currentInstance._computedStore
if (isMounting) {
store[id] = getter()
currentInstance.$watch(getter, val => {
store[id] = val
}, { sync: true })
}
return store[id]
}

useComputed首先會計算一次依賴值并緩存,調用$watch來觀察依賴屬性變化,并更新對應的緩存值。

實際上,vue底層對computed對處理要稍微復雜一些,在初始化computed時,采用lazy:true(異步)的方式來監(jiān)聽依賴變化,即依賴屬性變化時不會立刻求值,而是控制dirty變量變化;并將計算屬性對應的key綁定到組件實例上,同時修改為訪問器屬性,等到訪問該計算屬性的時候,再依據dirty來判斷是否求值。

這里直接調用watch會在屬性變化時,立即獲取最新值,而不是等到render flush階段去求值。

hooks

export function hooks (Vue) {
Vue.mixin({
beforeCreate() {
const { hooks, data } = this.$options
if (hooks) {
this._effectStore = {}
this._refsStore = {}
this._computedStore = {}
// 改寫data函數,注入_state屬性
this.$options.data = function () {
const ret = data ? data.call(this) : {}
ret._state = {}
return ret
}
}
},
beforeMount() {
const { hooks, render } = this.$options
if (hooks && render) {
// 改寫組件的render函數
this.$options.render = function(h) {
callIndex = 0
currentInstance = this
isMounting = !this._vnode
// 默認傳入props屬性
const hookProps = hooks(this.$props)
// _self指示本身組件實例
Object.assign(this._self, hookProps)
const ret = render.call(this, h)
currentInstance = null
return ret
}
}
}
})
}

借助withHooks,我們可以發(fā)揮hooks的作用,但犧牲來很多vue的特性,比如props,attrs,components等。

vue-hooks暴露了一個hooks函數,開發(fā)者在入口Vue.use(hooks)之后,可以將內部邏輯混入所有的子組件。這樣,我們就可以在SFC組件中使用hooks啦。

為了便于理解,這里簡單實現了一個功能,將動態(tài)計算元素節(jié)點尺寸封裝成獨立的hooks:

<template>
<section class="demo">
<p>{{resize}}</p>
</section>
</template>
<script>
import { hooks, useRef, useData, useState, useEffect, useMounted, useWatch } from '../hooks';
function useResize(el) {
const node = useRef(null);
const [resize, setResize] = useState({});
useEffect(
function() {
if (el) {
node.currnet = el instanceof Element ? el : document.querySelector(el);
} else {
node.currnet = document.body;
}
const Observer = new ResizeObserver(entries => {
entries.forEach(({ contentRect }) => {
setResize(contentRect);
});
});
Observer.observe(node.currnet);
return () => {
Observer.unobserve(node.currnet);
Observer.disconnect();
};
},
[]
);
return resize;
}
export default {
props: {
msg: String
},
// 這里和setup函數很接近了,都是接受props,最后返回依賴的屬性
hooks(props) {
const data = useResize();
return {
resize: JSON.stringify(data)
};
}
};
</script>
<style>
html,
body {
height: 100%;
}
</style>

使用效果是,元素尺寸變更時,將變更信息輸出至文檔中,同時在組件銷毀時,注銷resize監(jiān)聽器。

hooks返回的屬性,會合并進組件的自身實例中,這樣模版綁定的變量就可以引用了。

hooks存在什么問題?

在實際應用過程中發(fā)現,hooks的出現確實能解決mixin帶來的諸多問題,同時也能更加抽象化的開發(fā)組件。但與此同時也帶來了更高的門檻,比如useEffect在使用時一定要對依賴忠誠,否則引起render的死循環(huán)也是分分鐘的事情。
與react-hooks相比,vue可以借鑒函數抽象及復用的能力,同時也可以發(fā)揮自身響應式追蹤的優(yōu)勢。我們可以看尤在與react-hooks對比中給出的看法:

整體上更符合 JavaScript 的直覺;
不受調用順序的限制,可以有條件地被調用;
不會在后續(xù)更新時不斷產生大量的內聯(lián)函數而影響引擎優(yōu)化或是導致 GC 壓力;
不需要總是使用 useCallback 來緩存?zhèn)鹘o子組件的回調以防止過度更新;
不需要擔心傳了錯誤的依賴數組給 useEffect/useMemo/useCallback 從而導致回調中使用了過期的值 —— Vue 的依賴追蹤是全自動的。

感受

為了能夠在vue3.0發(fā)布后更快的上手新特性,便研讀了一下hooks相關的源碼,發(fā)現比想象中收獲的要多,而且與新發(fā)布的RFC對比來看,恍然大悟??上Чぷ髟?,開發(fā)項目中很多依賴了vue-property-decorator來做ts適配,看來三版本出來后要大改了。

最后,hooks真香(逃)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 解決Vue不能檢測數組或對象變動的問題

    解決Vue不能檢測數組或對象變動的問題

    下面小編就為大家分享一篇解決Vue不能檢測數組或對象變動的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • 詳解vue幾種主動刷新的方法總結

    詳解vue幾種主動刷新的方法總結

    這篇文章主要介紹了詳解vue幾種主動刷新的方法總結,文中詳細的介紹了幾種方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • 使用Vue寫一個datepicker的示例

    使用Vue寫一個datepicker的示例

    這篇文章主要介紹了使用Vue寫一個datepicker的示例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • vue3如何在setup中獲取DOM元素

    vue3如何在setup中獲取DOM元素

    這篇文章主要介紹了vue3如何在setup中獲取DOM元素問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue實現a標簽點擊高亮方法

    vue實現a標簽點擊高亮方法

    下面小編就為大家分享一篇vue實現a標簽點擊高亮方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • vue+element?DatePicker實現日期選擇器封裝

    vue+element?DatePicker實現日期選擇器封裝

    Vue?Element?DatePicker是一款基于Vue.js的日期選擇控件,它提供了豐富的日期選擇功能,支持日期范圍選擇、日期格式化、自定義日期格式、快捷選擇等功能,極大地提高了用戶的體驗,是開發(fā)者必備的日期選擇控件。
    2023-02-02
  • Vue中訪問指定鏈接并解析頁面內容的完整指南

    Vue中訪問指定鏈接并解析頁面內容的完整指南

    在現代Web開發(fā)中,經常需要從其他網頁獲取并解析內容,本文將詳細介紹如何在Vue項目中實現這一功能,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-03-03
  • vue 使用element-ui中的Notification自定義按鈕并實現關閉功能及如何處理多個通知

    vue 使用element-ui中的Notification自定義按鈕并實現關閉功能及如何處理多個通知

    這篇文章主要介紹了vue 使用element-ui中的Notification自定義按鈕并實現關閉功能及如何處理多個通知,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08
  • vue實現密碼顯示隱藏功能的思路詳解

    vue實現密碼顯示隱藏功能的思路詳解

    這篇文章主要介紹了vue實現密碼顯示隱藏功能的思路詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Vue監(jiān)聽事件實現計數點擊依次增加的方法

    Vue監(jiān)聽事件實現計數點擊依次增加的方法

    今天小編就為大家分享一篇Vue監(jiān)聽事件實現計數點擊依次增加的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09

最新評論

广平县| 开远市| 江门市| 无极县| 岫岩| 新和县| 张家口市| 游戏| 天镇县| 牟定县| 抚州市| 嫩江县| 手机| 永登县| 砀山县| 石柱| 徐汇区| 广东省| 额济纳旗| 甘泉县| 阳春市| 博罗县| 辽源市| 吴川市| 丘北县| 无极县| 河津市| 安丘市| 辰溪县| 潞城市| 克山县| 尚志市| 吉首市| 福建省| 长宁区| 托克托县| 莱阳市| 凤城市| 历史| 海南省| 瑞金市|