成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術文章
文章詳情頁

詳解Vue組件復用和擴展之道

瀏覽:2日期:2022-09-30 10:28:05
概述

軟件編程有一個重要的原則是 D.R.Y(Don’t Repeat Yourself),講的是盡量復用代碼和邏輯,減少重復。組件擴展可以避免重復代碼,更易于快速開發和維護。那么,擴展 Vue 組件的最佳方法是什么?

Vue 提供了不少 API 和模式來支持組件復用和擴展,你可以根據自己的目的和偏好來選擇。

本文介紹幾種比較常見的方法和模式,希望對你有所幫助。

擴展組件是否必要

要知道,所有的組件擴展方法都會增加復雜性和額外代碼,有時候還會增加性能消耗。

因此,在決定擴展組件之前,最好先看看有沒有其他更簡單的設計模式能完成目標。

下面幾種組件設計模式通常足夠替代擴展組件了:

props 配合模板邏輯 slot 插槽 JavaScript 工具函數

props 配合模板邏輯

最簡單的方法是通過props結合模板條件渲染,來實現組件的多功能。

比如通過 type 屬性:MyVersatileComponent.vue

<template> <div class='wrapper'> <div v-if='type === ’a’'>...</div> <div v-else-if='type === ’b’'>...</div> <!--etc etc--> </div></template><script>export default { props: { type: String }, ...}</script>

使用組件的時候傳不同的type值就能實現不同的結果。

// *ParentComponent.vue*<template> <MyVersatileComponent type='a' /> <MyVersatileComponent type='b' /></template>

如果出現下面兩種情況,就說明這種模式不適用了,或者用法不對:

組件組合模式把狀態和邏輯分解成原子部分,從而讓應用具備可擴展性。如果組件內存在大量條件判斷,可讀性和可維護性就會變差。 props 和模板邏輯的本意是讓組件動態化,但是也存在運行時資源消耗。如果你利用這種機制在運行時解決代碼組合問題,那是一種反模式。slot(插槽)

另一種可避免組件擴展的方式是利用 slots(插槽),就是讓父組件在子組件內設置自定義內容。

// *MyVersatileComponent.vue*<template> <div class='wrapper'> <h3>Common markup</div> <slot /> </div></template>

// *ParentComponent.vue*<template> <MyVersatileComponent> <h4>Inserting into the slot</h4> </MyVersatileComponent></template>

渲染結果:

<div class='wrapper'> <h3>Common markup</div> <h4>Inserting into the slot</h4></div>

這種模式有一個潛在約束, slot 內的元素從屬于父組件的上下文,在拆分邏輯和狀態時可能不太自然。scoped slot會更靈活,后面會在無渲染組件一節里提到。

JavaScript 工具函數

如果只需要在各組件之間復用獨立的函數,那么只需要抽取這些 JavaScript 模塊就行了,根本不需要用到組件擴展模式。

JavaScript 的模塊系統是一種非常靈活和健壯的代碼共享方式,所以你應該盡可能地依靠它。MyUtilityFunction.js

export default function () { ...}

MyComponent.vue

import MyUtilityFunction from './MyUtilityFunction';export default { methods: { MyUtilityFunction }}擴展組件的幾種模式

如果你已經考慮過以上幾種簡單的模式,但這些模式還不夠靈活,無法滿足需求。那么就可以考慮擴展組件了。

擴展 Vue 組件最流行的方法有以下四種:

Composition 函數 mixin 高階組件(HOC) 無渲染組件

每一種方法都有其優缺點,根據使用場景,或多或少都有適用的部分。

Composition API

組件之間共享狀態和邏輯的最新方案是 Composition API。這是 Vue 3 推出的 API,也可以在 Vue 2 里當插件使用。

跟之前在組件定義配置對象里聲明data,computed,methods等屬性的方式不同,Composition API 通過一個 setup 函數聲明和返回這些配置。

比如,用 Vue 2 配置屬性的方式聲明 Counter 組件是這樣的:Counter.vue

<template> <button @click='increment'> Count is: {{ count }}, double is: {{ double }} </button><template><script>export default { data: () => ({ count: 0 }), methods: { increment() { this.count++; } }, computed: { double () { return this.count * 2; } }}</script>

用 Composition API 重構這個組件,功能完全一樣:Counter.vue

<template><!--as above--><template><script>import { reactive, computed } from 'vue';export default { setup() { const state = reactive({ count: 0, double: computed(() => state.count * 2) }); function increment() { state.count++ } return { count, double, increment } }}</script>

用 Composition API 聲明組件的主要好處之一是,邏輯復用和抽取變得非常輕松。

進一步重構,把計數器的功能移到 JavaScript 模塊 useCounter.js中:useCounter.js

import { reactive, computed } from 'vue';export default function { const state = reactive({ count: 0, double: computed(() => state.count * 2) }); function increment() { state.count++ } return { count, double, increment }}

現在,計數器功能可以通過setup函數無縫引入到任意 Vue 組件中:MyComponent.vue

<template><!--as above--></template><script>import useCounter from './useCounter';export default { setup() { const { count, double, increment } = useCounter(); return { count, double, increment } }}</script>

Composition 函數讓功能模塊化、可重用,是擴展組件最直接和低成本的方式。

Composition API 的缺點

Composition API 的缺點其實不算什么——可能就是看起來有點??攏?⑶倚碌撓梅ǘ砸恍 Vue 開發者來說有點陌生。

關于 Composition API 優缺點的討論,推薦閱讀:When To Use The New Vue Composition API (And When Not To)

mixin

如果你還在用 Vue 2,或者只是喜歡用配置對象的方式定義組件功能,可以用 mixin 模式。mixin 把公共邏輯和狀態抽取到單獨的對象,跟使用 mixin 的組件內部定義對象合并。

我們繼續用之前的Counter組件例子,把公共邏輯和狀態放到CounterMixin.js模塊中。CounterMixin.js

export default { data: () => ({ count: 0 }), methods: { increment() { this.count++; } }, computed: { double () { return this.count * 2; } }}

使用 mixin 也很簡單,只要導入對應模塊并在mixins數組里加上變量就行。組件初始化時會把 mixin 對象與組件內部定義對象合并。MyComponent.vue

import CounterMixin from './CounterMixin';export default { mixins: [CounterMixin], methods: { decrement() { this.count--; } }}

選項合并

如果組件內的選項跟 mixin 沖突怎么辦?

比如,給組件定義一個自帶的increment方法,哪個優先級更高呢?MyComponent.vue

import CounterMixin from './CounterMixin';export default { mixins: [CounterMixin], methods: { // 自帶的 `increment`` 方法會覆蓋 mixin 的`increment` 嗎? increment() { ... } }}

這個時候就要說到 Vue 的合并策略了。Vue 有一系列的規則,決定了如何處理同名選項。

通常,組件自帶的選項會覆蓋來自 mixin 的選項。但也有例外,比如同類型的生命周期鉤子,不是直接覆蓋,而是都放進數組,按順序執行。

你也可以通過 自定義合并策略 改變默認行為。

mixin 的缺點

作為擴展組件的一種模式,mixin 對于簡單的場景還算好用,一旦規模擴大,問題就來了。不僅需要注意命名沖突問題(尤其是第三方 mixin),使用了多個 mixin 的組件,很難搞清楚某個功能到底來自于哪里,定位問題也比較困難。

高階組件

高階組件(HOC)是從 React 借用的概念,Vue 也能使用。

為了理解這個概念,我們先拋開組件,看看兩個簡單的 JavaScript 函數,increment和 double。

function increment(x) { return x++;}function double(x) { return x * 2;}

假設我們想給這兩個函數都加一個功能:在控制臺輸出結果。

為此,我們可以用高階函數模式,新建一個addLogging函數,接受函數作為參數,并返回一個帶有新增功能的函數。

function addLogging(fn) { return function(x) { const result = fn(x); console.log('The result is: ', result); return result; };}const incrementWithLogging = addLogging(increment);const doubleWithLogging = addLogging(double);

組件如何利用這種模式呢?類似地,我們創建一個高階組件來渲染Counter組件,同時添加一個decrement方法作為實例屬性。

實際代碼比較復雜,這里只給出偽代碼作為示意:

import Counter from './Counter';// 偽代碼const CounterWithDecrement => ({ render(createElement) { const options = { decrement() {this.count--; } } return createElement(Counter, options); }});

HOC 模式比 mixin 更簡潔,擴展性更好,但是代價是增加了一個包裹組件,實現起來也需要技巧。

無渲染組件

如果需要在多個組件上使用相同的邏輯和狀態,只是展示方式不同,那么就可以考慮無渲染組件模式。

該模式需要用到兩類組件:邏輯組件用于聲明邏輯和狀態,展示組件用于展示數據。

邏輯組件

還是回到Counter的例子,假設我們需要在多個地方重用這個組件,但是展示方式不同。

創建一個CounterRenderless.js用于定義邏輯組件,包含邏輯和狀態,但是不包含模板,而是通過 render函數聲明 scoped slot。

scoped slot暴露三個屬性給父組件使用:狀態count,方法increment 和計算屬性 double。CounterRenderless.js

export default { data: () => ({ count: 0 }), methods: { increment() { this.count++; } }, computed: { double () { return this.count * 2; } }, render() { return this.$scopedSlots.default({ count: this.count, double: this.double, increment: this.toggleState, }) }}

這里的scoped slot是這種模式里邏輯組件的關鍵所在。

展示組件

接下來是展示組件,作為無渲染組件的使用方,提供具體的展示方式。

所有的元素標簽都包含在scoped slot里。可以看到,這些屬性在使用上跟模板直接放在邏輯組件里沒什么兩樣。CounterWithButton.vue

<template> <counter-renderless slot-scope='{ count, double, increment }'> <div>Count is: {{ count }}</div> <div>Double is: {{ double }}</div> <button @click='increment'>Increment</button> </counter-renderless></template><script>import CounterRenderless from './CountRenderless';export default { components: { CounterRenderless }}</script>

無渲染組件模式非常靈活,也容易理解。但是,它沒有前面那幾種方法那么通用,可能只有一種應用場景,那就是用于開發組件庫。

模板擴展

上面的 API 也好,設計模式也罷,都有一種局限性,就是無法擴展組件的模板。Vue 在邏輯和狀態方面有辦法重用,但是對于模板標簽就無能為力了。

有一種比較 hack 的方式,就是利用 HTML 預處理器,比如 Pug,來處理模板擴展。

第一步是創建一個基礎模板.pug文件,包含公共的頁面元素。還要包含一個block input,作為模板擴展的占位符。

BaseTemplate.pug

div.wrapper h3 {{ myCommonProp }} <!--common markup--> block input <!--extended markup outlet -->

為了能擴展這個模板,需要安裝 Vue Loader 的 Pug 插件。然后就可以引入基礎模板并利用block input語法替換占位部分了:MyComponent.vue

<template lang='pug'> extends BaseTemplate.pug block input h4 {{ myLocalProp }} <!--gets included in the base template--></template>

一開始你可能會認為它跟 slot 的概念是一樣的,但是有個區別,這里的基礎模板不屬于任何單獨的組件。它在編譯時跟當前組件合并,而不是像 slot 那樣是在運行時替換。

以上就是詳解Vue組件復用和擴展之道的詳細內容,更多關于Vue組件復用和擴展的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
欧美三级在线播放| 国产清纯美女被跳蛋高潮一区二区久久w | 日本sm残虐另类| 伊人精品在线| 久久久午夜精品理论片中文字幕| 国产91丝袜在线观看| 欧美日韩国产三级| 精品一区二区三区不卡| 欧美系列日韩一区| 美国av一区二区| 欧美综合在线视频| 久久黄色级2电影| 欧美亚洲日本国产| 韩国成人精品a∨在线观看| 欧美三级日韩在线| 精品一区二区三区免费视频| 欧美日韩精品欧美日韩精品一综合| 狂野欧美性猛交blacked| 色香蕉久久蜜桃| 天堂va蜜桃一区二区三区漫画版 | 亚洲影院一区| 亚洲第一二三四区| 久久久蜜桃一区二区人| 日本伊人色综合网| 欧美日本在线观看| 粉嫩久久99精品久久久久久夜| 日韩欧美激情一区| 欧美影院一区| 久久久成人网| 久久精品国产秦先生| 欧美日韩视频在线一区二区| 国产在线精品一区二区不卡了| 欧美色老头old∨ideo| 国产激情视频一区二区三区欧美| 欧美一区二区三区婷婷月色| www.av亚洲| 国产精品美女久久久久aⅴ国产馆| 欧美日韩免费观看一区=区三区| 中文字幕亚洲成人| 国产精品久久国产三级国电话系列 | 国产一区欧美| 亚洲黄色尤物视频| 久久在线视频| 捆绑调教一区二区三区| 91精品国产综合久久香蕉麻豆| 成人动漫在线一区| 中文字幕亚洲成人| 美女91精品| 国产精品影视天天线| 精品少妇一区二区三区免费观看| 欧美1区视频| 一区二区三区欧美激情| 在线观看免费视频综合| 国产91露脸合集magnet| 国产视频在线观看一区二区三区| 亚洲乱码视频| 久久99热这里只有精品| 精品国产一区二区三区四区四| 国模精品一区二区三区| 亚洲成av人片在线| 欧美一区日韩一区| 国产综合色产| 日本不卡一二三| 欧美一级片免费看| 亚洲性视频h| 免费看欧美女人艹b| 精品嫩草影院久久| 亚洲日本精品国产第一区| 免费在线观看一区二区三区| 日韩免费成人网| 亚洲经典在线| 寂寞少妇一区二区三区| 国产欧美日本一区二区三区| 免费久久久一本精品久久区| 国产一区二区三区在线看麻豆| 欧美国产亚洲另类动漫| 日本精品裸体写真集在线观看 | 韩国成人在线视频| 中文字幕一区二区在线播放 | 欧美在线观看视频一区二区| 99这里都是精品| 一区二区免费看| 3d动漫精品啪啪一区二区竹菊 | 欧美日韩国产在线观看| 欧美久久久久| 奇米888四色在线精品| 久久免费国产精品| 久久久精品五月天| 97se亚洲国产综合自在线观| 香蕉加勒比综合久久| 精品国产1区2区3区| 亚洲免费婷婷| 成人91在线观看| 性做久久久久久久久| 久久综合色综合88| 久久国产日本精品| 91天堂素人约啪| 男男gaygay亚洲| 国产日韩精品一区二区三区| 91官网在线免费观看| 极品日韩久久| 高清国产一区二区| 日韩一区欧美二区| 国产精品丝袜一区| 欧美一级夜夜爽| 久久亚洲不卡| 国产精品豆花视频| 国产成人一级电影| 丝袜诱惑亚洲看片| 国产精品久久久久三级| 欧美一区二区三区不卡| 性色av一区二区怡红| 欧美日韩国产色综合一二三四| 日韩—二三区免费观看av| 国产精品拍天天在线| 91精品综合久久久久久| 午夜亚洲精品| 欧美日韩大片一区二区三区| 国产盗摄视频一区二区三区| 视频一区二区欧美| 亚洲色图制服丝袜| 久久久99精品免费观看不卡| 精品视频在线视频| 亚洲综合不卡| 欧美另类视频| 国产福利91精品一区| 首页亚洲欧美制服丝腿| 综合欧美亚洲日本| 日韩欧美自拍偷拍| 欧美三级一区二区| 久久国产欧美精品| 99在线|亚洲一区二区| 91亚洲精华国产精华精华液| 国产一区二区三区蝌蚪| 五月综合激情婷婷六月色窝| 中文字幕中文乱码欧美一区二区| 欧美电视剧在线看免费| 欧美三电影在线| 久久经典综合| 国产精品乱看| 亚洲经典在线看| 欧美日韩一区二区三| av不卡一区二区三区| 精久久久久久久久久久| 视频一区中文字幕国产| 亚洲欧美日韩久久精品| 欧美韩国日本一区| 久久精品视频一区二区三区| 精品国产99国产精品| 日韩三级视频中文字幕| 欧美人xxxx| 欧美在线一区二区三区| 国产亚洲亚洲| 亚洲免费黄色| 日韩视频二区| 亚洲精品国产系列| 亚洲国产精品久久久久婷婷老年 | 在线视频成人| 精品91久久久久| 欧美日韩精品免费观看视一区二区| www.99精品| 99热精品国产| 91丝袜国产在线播放| 972aa.com艺术欧美| 91亚洲精品一区二区乱码| 成a人片亚洲日本久久| kk眼镜猥琐国模调教系列一区二区| 高清不卡在线观看| 国产精品69毛片高清亚洲| 国产一区二区0| 国产电影一区二区三区| 国产盗摄一区二区三区| 国产东北露脸精品视频| 国产aⅴ精品一区二区三区色成熟| 国产成人综合在线播放| 成人午夜在线视频| 国产成人aaa| 成人开心网精品视频| 成人av影视在线观看| 99国产一区二区三精品乱码| 99re6这里只有精品视频在线观看| 91在线你懂得| 亚洲图色在线| 亚洲三级网站| 蜜桃av久久久亚洲精品| 91搞黄在线观看| 91麻豆精品91久久久久同性| 日韩片之四级片| 国产婷婷色一区二区三区四区 | 亚洲视频一二三区| 一区二区三区不卡视频在线观看| 亚洲九九爱视频| 亚洲综合一区二区三区| 亚洲国产日韩一区二区| 午夜久久久久久久久| 青青草视频一区| 国产黑丝在线一区二区三区| 99久久99久久久精品齐齐| 欧美日韩一区综合| 亚洲国产精品综合|