Vue 中的無狀態組件


啥是應用程序狀態,為什么咱們需要它?

狀態管理通常在較小的項目並不需要,但是當涉及到更大的范圍時,如企業級的應用大部分需要它了。簡單的說,狀態是一個包含應用程序使用的最新值的對象。但是,如果咱們從結構的、更抽象的角度來看待它,就會清楚地看到,狀態是復雜應該中重要一塊,它使能夠構建干凈的體系結構,並將關注點強有力地分離開來。

通常,缺乏經驗的開發人員無法預測對狀態管理的需求,以及如何實現狀態管理,因此很難了解狀態管理的重要性。如果基於狀態的組件堆積起來,它們之間的數據管理和共享將成為一場噩夢。從長遠來看,擁有的基於狀態的組件越多,出現的問題就越多。

如果沒有使用外部包進行狀態管理,那么最好盡可能少地使用基於狀態的組件,而展示組件則使用圍繞它們構建的狀態。

 

vue 和無狀態(函數)組件

vue 中的無狀態組件其實就是函數組件。但函數組件又是啥呢? 要回答這個問題,咱們首先必須理解什么是函數式編程。

與將程序分解為對象的面向對象方法不同,函數式編程鼓勵將程序分解為小函數,這些小函數用於形成更高級的程序。我們創建的函數不依賴於或可以改變任何外部狀態,這導致另一個觀察結果,對於給定的輸入,它們總是返回相同的輸出。

因此,函數組件是沒有狀態的組件,並且可以更改它。函數組件輸出總是基於給定的輸入。在 Vue 方面,這類組件會根據給定的props給出不同的輸出。

語法

Vue 提供了一種定義函數組件的簡單方法。咱們只需要給個 functional 關鍵字就可以。在 2.5.0 及以上版本中,如果使用了單文件組件,那么基於模板的函數式組件可以這樣聲明::

<template functional> <div> 函數/無狀態組件 </div> </template> 

或者

export default { functional: true, props: { // ... }, render(createElement, context) { return createElement( 'div', '函數/無狀態組件' ) } } 
注意:在 2.3.0 之前的版本中,如果一個函數式組件想要接收 prop,則 props 選項是必須的。在 2.3.0 或以上的版本中,你可以省略 props 選項,所有組件上的特性都會被自動隱式解析為 prop。

當使用函數式組件時,該引用將會是 htmlElement,因為他們是無狀態的也是無實例的。

需要注意的是,傳遞給函數組件的惟一數據是props。這些組件是完全無狀態的(沒有響應數據),它們忽略傳遞給它們的任何狀態,並且不觸發任何生命周期方法(created、mounted等等)。

而且,咱們也不能通過使用 this 關鍵字來訪問實例,因為這些組件也是不實例化的。相反,組件需要的所有東西都是通過context提供的。在render函數中,它作為createElement方法的第二個參數傳遞。

組件需要的一切都是通過 context 參數傳遞,它是一個包括如下字段的對象:

  • props:提供所有 prop 的對象
  • children: VNode 子節點的數組
  • slots: 一個函數,返回了包含所有插槽的對象
  • scopedSlots: (2.6.0+) 一個暴露傳入的作用域插槽的對象。也以函數形式暴露普通插槽。
  • data:傳遞給組件的整個數據對象,作為 createElement 的第二個參數傳入組件
  • parent:對父組件的引用
  • listeners: (2.3.0+) 一個包含了所有父組件為當前組件注冊的事件監聽器的對象。這是 data.on 的一個別名。
  • injections: (2.3.0+) 如果使用了 inject 選項,則該對象包含了應當被注入的屬性。

 

為什么咱們需要無狀態組件

到目前為止,咱們已經了解到函數組件是無狀態的,在它們的核心中,它們只是可執行的函數,接受一些輸入並根據其提供輸出。

就它們的用法而言,因為函數式組件只是函數,所以渲染開銷也低很多,這也意味着它們是非常高效的,不需要花太多時間渲染。同時,考慮高階組件,它們不需要任何狀態,它們所要做的就是用額外的邏輯或樣式包裝給定的子組件。

接下來,通例事例展示一樣啥時使用函數組件,函數組件非常適合此類任務。

 

實例

在這個示例中,咱們創建一個panel組件,它充當一個包裝器,並提供所需的樣式。子組件將在panel 主體中渲染:

export default { name: 'panel', functional: true, props: { title: String }, render(createElement, context) { const slots = context.slots(); const header = createElement('header', { attrs: { class: 'panel-header'} }, context.props.title); const body = createElement('main', { attrs: { class: 'panel-body'} }, slots.default); return createElement('section', { attrs: { class: 'panel' } }, [header, body]); } } 

如上所述,此組件的唯一目的是提供類似於面板(卡片)的樣式,它有header 和main元素,分別保存面板標題和html內容。整個過程是通過使用render函數中的createElement參數在中完成。createElement是 Vue 核心中實現的虛擬 Dom 系統的一部分。

虛擬 DOM

Vue 通過建立一個虛擬 DOM 來追蹤自己要如何改變真實 DOM。請仔細看這行代碼:

return createElement('h1', this.blogTitle) 

createElement 到底會返回什么呢?其實不是一個實際的 DOM 元素。它更准確的名字可能是 createNodeDescription,因為它所包含的信息會告訴 Vue 頁面上需要渲染什么樣的節點,包括及其子節點的描述信息。我們把這樣的節點描述為“虛擬節點 (virtual node)”,也常簡寫它為“VNode”。“虛擬 DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼。

createElement 參數

接下來你需要熟悉的是如何在 createElement 函數中使用模板中的那些功能。這里是 createElement 接受的參數:

// @returns {VNode} createElement( // {String | Object | Function} // 一個 HTML 標簽名、組件選項對象,或者 // resolve 了上述任何一種的一個 async 函數。必填項。 'div', // {Object} // 一個與模板中屬性對應的數據對象。可選。 { // (詳情見下一節) }, // {String | Array} // 子級虛擬節點 (VNodes),由 `createElement()` 構建而成, // 也可以使用字符串來生成“文本虛擬節點”。可選。 [ '先寫一些文字', createElement('h1', '一則頭條'), createElement(MyComponent, { props: { someProp: 'foobar' } }) ] ) 

面板 css 樣式如下:

.panel { margin-bottom: .5rem } .panel, .panel-header { border: 1px solid #d3d3d3; border-radius: 4px; } .panel-header, .panel-body, .panel { padding: .5rem; } .panel-header { background-color:#efefef; color: #eeeee } 

這是一個簡單直接的 css,提供了一些padding 和color。

子組件

現在,為了讓例子更加生動為此,咱們再創建兩個附加組件,一個顯示汽車列表,另一個只是一個簡單lorem-ipsum的文本組件,要求它們具有相同的面板樣式和外觀。

列表組件:

export default { name: 'cars', props: { data: Array } } 

template:

<template> <ul> <li v-for="car in data" :key="car">{{car}}</li> </ul> </template> 

文本組件:

export default { name: 'lorem-ipsum' } 

template:

<template> <p> 終身學習者,終身學習者,終身學習者,終身學習者,終身學習者 </p> </template> 

現在,有了可用的子組件,咱們所需要做的就是用panel組件將它們封裝到應用程序中,如下所示:

<div class="vue-app"> <panel :title="'Car Manufacturers'"> <cars :data="['Mazda', 'Ford', 'Mercedes']"></cars> </panel> <panel :title="'Lorem Ipsum'"> <lorem-ipsum></lorem-ipsum> </panel> </div> 
請注意,使用這些組件是因為示例比較簡單。在實際應用中,它可以是任何類型的組件。

廣州設計公司https://www.houdianzi.com 我的007辦公資源網站https://www.wode007.com

完整代碼

hmtl

<div class="vue-app"> <panel :title="'Car Manufacturers'"> <cars :data="['Mazda', 'Ford', 'Mercedes']"></cars> </panel> <panel :title="'Lorem Ipsum'"> <lorem-ipsum></lorem-ipsum> </panel> </div> <script type="text/x-template" id="cars"> <template> <ul> <li v-for="car in data" :key="car">{{car}}</li> </ul> </template> </script> <script type="text/x-template" id="lorem-ipsum"> <template> <p>前端小智, 終身學習者,終身學習者,終身學習者,終身學習者,終身學習者</p> </template> </script> 

css

body { padding: .5rem } * { padding: 0; margin:0; box-sizing: border-box; } .panel { margin-bottom: .5rem } .panel, .panel-header { border: 1px solid #d3d3d3; border-radius: 4px; } .panel-header, .panel-body, .panel { padding: .5rem; } .panel-header { background-color:#efefef; color: #eeeee } ul { list-style: none; } ul > li { padding: .5rem .2rem } 

js

// the wrapper panel const panel = { functional: true, name: "panel", props: { title: String }, render(createElement, context) { const slots = context.slots(); const header = createElement('header', { attrs: { class: 'panel-header'} }, context.props.title); const body = createElement('main', { attrs: { class: 'panel-body'} }, slots.default); return createElement('section', { attrs: { class: 'panel' } }, [header, body]); } } // sample components const cars = { name: 'cars', template: '#cars', props: { data: Array } } const loremIpsum = { name: 'lorem-ipsum', template: '#lorem-ipsum' } new Vue({ el: '.vue-app', components: { panel, cars, 'lorem-ipsum': loremIpsum } });


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM