前言
隨著數(shù)字化轉(zhuǎn)型的不斷發(fā)展,低代碼開發(fā)平臺已成為企業(yè)快速建立自己的應(yīng)用程序的首選方案。然而,實現(xiàn)這樣一個平臺需要具備高效、靈活和可定制化的能力。這正是基于描述依賴渲染(Description dependency rendering)
所實現(xiàn)的。通過使用該技術(shù),我們可以實現(xiàn)動態(tài)渲染組件,從而大大提高開發(fā)效率和靈活性。此外,組件的遞歸調(diào)用、扁平化的事件處理還能實現(xiàn)復(fù)雜的界面效果,讓用戶體驗更加友好。本文將介紹如何使用Vue.js實現(xiàn)描述依賴渲染,讓您在幾分鐘內(nèi)輕松掌握這一技術(shù),并享受到它所帶來的便利。作為前端開發(fā)人員,了解這種前沿技術(shù)將有助于您在低代碼領(lǐng)域中保持競爭優(yōu)勢。因此,通過閱讀本文并掌握這一重要技術(shù),是做出低代碼開發(fā)平臺的第一步,也是信息化創(chuàng)新的重要一環(huán)。
效果展示
動態(tài)根據(jù)JSON構(gòu)建組件:
開發(fā)環(huán)境準備
1、首先我們需要建立一個使用js的vue3項目,如果還沒有vue3項目,則用以下命令手動創(chuàng)建一個,如果沒有這個命令,那么請先準備好node.js環(huán)境、npm環(huán)境和vue-cli環(huán)境,這里不再贅述。
vue create .
2、本示例會用到angular-expressions和axios,所以需要先npm install angular-expressions
、npm install axios
安裝一下。這里axios負責(zé)進行網(wǎng)絡(luò)通訊,angular-expressions負責(zé)實現(xiàn)腳本解析引擎,它因為運行在獨立的沙盒環(huán)境里,具有比eval和new Function更好的安全性,可以一定程度上防止XSS攻擊。
引入控件系統(tǒng)思想
控件系統(tǒng)思想是一種將應(yīng)用程序拆分成多個可復(fù)用的組件的編程模型。它可以提高開發(fā)效率,降低代碼復(fù)雜度,使得應(yīng)用程序更易于維護和擴展。在控件系統(tǒng)中,每個控件都有自己的方法、事件和屬性,并且可以被其他組件調(diào)用和操作。
這里可以參考傳統(tǒng)老牌的微軟COM模型,它具有以下核心特點:
組件化:將應(yīng)用程序拆分成多個可復(fù)用的組件;
接口化:每個組件都有自己的接口,允許其他組件調(diào)用和操作;
注冊表:組件需要在注冊表中注冊,以便其他應(yīng)用程序可以找到并使用它們。
與微軟COM類似,VUE的組件也具有類似的特點。VUE組件是一個可復(fù)用的代碼塊,具有自己的方法、事件和屬性,并且可以被其他組件調(diào)用和操作。
區(qū)別在于,VUE組件是基于Web技術(shù)的,可以輕松地嵌入到HTML頁面中,并且可以與其他Web技術(shù)(如CSS和JavaScript)無縫集成。而微軟COM則是基于Windows操作系統(tǒng)的,只能在Windows平臺上運行。所以我們需要在Web上復(fù)刻出來控件系統(tǒng)只需要將接口化變?yōu)槭录?qū)動,注冊表變?yōu)槿肿詣幼约纯伞?/p>
在控件系統(tǒng)中,方法、事件和屬性是控件的三個核心組成部分。方法用于執(zhí)行控件的操作;事件用于通知其他控件發(fā)生了某些事情;屬性則用于存儲和獲取控件的狀態(tài)信息。
如上圖,通過這個例子,可以一目了然一個控件的主要功能。
方法、事件和屬性的作用和重要性在控件系統(tǒng)中非常重要。
通過方法,我們可以執(zhí)行控件的操作,例如:設(shè)置控件的值、獲取控件的狀態(tài)等;
通過事件,我們可以通知其他控件發(fā)生了某些事情,例如:當用戶點擊按鈕時觸發(fā)Click事件;
通過屬性,我們可以存儲和獲取控件的狀態(tài)信息,例如:文本框的值等。
控件系統(tǒng)思想可以提高開發(fā)效率,降低代碼復(fù)雜度,使得應(yīng)用程序更易于維護和擴展。方法、事件和屬性則是控件的三個核心組成部分,在控件系統(tǒng)中具有重要的作用和意義。接下來我們基于控件系統(tǒng)思想開始進行描述,從而實現(xiàn)描述依賴渲染DDR的一個簡單模式。
定義描述格式
文件位于public/test.json,實際項目中,這個JSON是由后臺拼裝后提供的
{
"is":"uiPage",
"props":{
"id":"uiPage1",
"style":"background-color:#f3f3f3;padding:20px;box-sizing:border-box;"
},
"controls":[{
"is":"uiTextBox",
"props":{
"id":"uiTextBox1",
"text":"在這里輸入姓名"
}
},{
"is":"uiButton",
"props":{
"id":"uiButton1",
"text":"確認"
},
"events":{
"Click":"this.uiLabel1.text=this.uiTextBox1.text"
}
},{
"is":"uiLabel",
"props":{
"id":"uiLabel1",
"text":"這是一個Label組件",
"style":"color:blue"
}
}]
}
這個JSON是一個描述界面的示例,也被稱之為界面描述或界面模型,它描述了一個包含三個組件的頁面。其中,uiPage是一個頁面容器組件,它的props屬性指定了頁面的id和樣式。controls屬性中包含了三個子組件:uiTextBox1、uiButton1和uiLabel1。
uiTextBox是一個文本框組件,它的props屬性指定了文本框的id和默認文本。
uiButton是一個按鈕組件,它的props屬性指定了按鈕的id和顯示文本,同時還定義了一個events屬性,用于配置該按鈕的點擊事件。在該示例中,當按鈕被點擊時,執(zhí)行this.uiLabel1.text = this.uiTextBox1.text
語句,將文本框中的內(nèi)容顯示在uiLabel1組件中。
uiLabel是一個標簽組件,它的props屬性指定了標簽的id、默認文本和樣式。
這段JSON描述了一個簡單的界面,下面我們將其轉(zhuǎn)換為Vue.js組件進行渲染。通過使用描述依賴渲染(DDR)技術(shù),我們可以輕松地實現(xiàn)低代碼開發(fā)平臺,快速構(gòu)建出符合需求的應(yīng)用程序。
自動化引入組件
接下來我們改造main.js,為了能快速開發(fā)組件,我們讓webpack自動掃描路徑并全局注冊組件
let app = createApp(App);
const requireComponent = require.context(
// 其組件目錄的相對路徑
'./components',
// 是否查詢其子目錄
false,
// 匹配基礎(chǔ)組件文件名的正則表達式
/\.vue$/
)
// 自動引入組件開始
const requireComponents = require.context("/src/components", true, /\w+\.vue$/);
requireComponents.keys().forEach((item) => {
const componentConfig = requireComponents(item);
const componentName = item
.split("/")
.pop()
.replace(/\.\w+$/, "");
app.component(componentName, componentConfig.default || componentConfig);
});
這段代碼將自動引入組件SFC文件,它通過require.context函數(shù)來實現(xiàn)自動化引入。context函數(shù)在第一個參數(shù)中,我們指定了組件所在目錄的相對路徑;第二個參數(shù)表示是否查詢其子目錄;第三個參數(shù)則是匹配基礎(chǔ)組件文件名的正則表達式。
在第二段代碼中,我們使用了require.context函數(shù)來獲取所有組件的上下文信息,并通過forEach函數(shù)遍歷每個組件。在遍歷過程中,我們首先獲取組件的配置信息componentConfig,然后提取組件的名稱componentName,并通過Vue.js的全局方法app.component將其注冊為全局組件。需要注意的是,由于require函數(shù)返回的是一個對象,因此我們需要使用default屬性來獲取組件的默認導(dǎo)出。
通過這段代碼,我們可以實現(xiàn)組件的自動化引入,避免手動引入組件時可能出現(xiàn)的疏漏和錯誤,提高開發(fā)效率。
定義元組件
元組件相當于真正組件的一個邏輯層包裝器,模擬了真實組件環(huán)境中的嵌套效果。
文件位于src/components/metaComponent.vue
<template>
<component :is="data.is" v-bind="data.props" :context="context" :initData="data">
<metaComponent v-if="data.controls" v-for="(item,i) in data.controls" :data="item" :context="context">
</metaComponent>
</component>
</template>
<script>
export default {
props: ['data', 'context']
}
</script>
<style>
</style>
首先,該組件使用了Vue.js的動態(tài)組件功能,通過傳遞一個data對象的is屬性來指定渲染的組件類型,具體組件則是上文中我們通過自動化引入src/components下的對應(yīng)SFC。另外,組件的props屬性也通過v-bind指令動態(tài)綁定了data.props中的所有屬性,實現(xiàn)了屬性透傳。此外,還傳遞了context和initData屬性,其中context是上下文對象,用于在組件內(nèi)部訪問其他資源,例如API、狀態(tài)管理器等;initData則是組件初始化數(shù)據(jù)。組件還包含了一個metaComponent子組件,用于渲染data.controls中定義的所有子元素,這樣就實現(xiàn)了遞歸自動渲染,可以渲染無線層級。
我們再來看script,props屬性接收了兩個參數(shù):data和context。data是一個對象,包含了組件所需的各種屬性和控制元素,context則是組件上下文對象,全局唯一的,我們可以認為對于同一個頁面工廠來說,只有一個context。
實現(xiàn)UI工廠
UI工廠是一切渲染的源頭,我們通過UI工廠來管理上下文,并將后臺傳過來的JSON轉(zhuǎn)變成我們能看到的界面
文件位于src/UIFactory.vue:
<template>
<div>
<metaComponent :data="page" :context="context"></metaComponent>
</div>
</template>
<script>
import axios from 'axios'
import expressions from 'angular-expressions'
export default {
components: {},
data() {
return {
page: {},
context: {
fireEvent: function(control, event) {
if (control.initData.events && control.initData.events[event]) {
let code=control.initData.events[event]
expressions.compile(code)(this)
}
},
initControl(control) {
let {
id,
initData,
context
} = control;
let proxyObj = {}
for (let key in initData.props) {
Object.defineProperty(proxyObj, key, {
get() {
return initData.props[key]
},
set(value) {
initData.props[key] = value;
}
})
}
for (let methodName in control) {
if (typeof control[methodName] == "function") {
proxyObj[methodName] = control[methodName]
}
}
proxyObj.controls = initData.controls
context[id] = proxyObj;
}
}
}
},
mounted() {
axios.get('/test.json').then((data) => {
let rData = data.data
this.page = rData
})
}
}
</script>
該組件使用了metaComponent自定義組件來動態(tài)渲染頁面。
在script中,該組件引入了axios和angular-expressions模塊,并定義了一個包含兩個方法的context對象。其中,fireEvent方法用于觸發(fā)控件事件;initControl方法用于初始化控件。
在mounted生命周期函數(shù)中,該組件使用axios.get方法從服務(wù)器獲取頁面數(shù)據(jù),并將頁面數(shù)據(jù)賦值給page屬性。通過這種方式,我們可以動態(tài)地生成頁面。
在methods部分,該組件定義了initControl方法,該方法用于初始化控件。當我們創(chuàng)建一個控件時,該控件會回調(diào)initControl方法,為控件創(chuàng)建一個代理對象并注冊到上下文對象中。只有通過代理對象的方式,我們才能在腳本引擎的沙盒里實現(xiàn)雙向綁定,像this.uiLabel1.text = this.uiTextBox1.text
這樣的語法就可以輕松實現(xiàn),達到對屬性的無感訪問,非常類似于VB.net和C#的屬性讀寫方式。
該組件還使用了angular-expressions模塊來編譯和執(zhí)行控件事件代碼。通過這種方式,我們可以在運行時動態(tài)地處理控件事件,而且因為運行在沙盒里,安全性要比eval和new Function高出不少。不過需要注意的是盡管它運行在沙盒里,但是如果我們給某一個圖片展示控件傳遞了http://www.example.com/hacker.img?cookie=xxx
這樣的圖片地址,仍然會有泄漏當前用戶cookie的可能性,因為img標簽加載src并不在沙盒的管控范圍里,同樣的,這樣的地址傳遞給一個用了axios的方法也可能有安全問題,需要各位架構(gòu)師讀者們仔細進行代碼審查和安全加固。
實現(xiàn)uiPage控件
接下來我們就實現(xiàn)各個控件了,每一種控件實際上都是基于VUE的組件,先實現(xiàn)src/components/uiPage.vue:
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
props: ['id', 'context', 'initData'],
mounted() {
this.context.initControl(this)
}
}
</script>
<style>
</style>
這段代碼實現(xiàn)了一個簡單的頁面容器功能。在模板部分,該組件使用了Vue.js的插槽(slot)功能,將子組件插入到頁面容器中,可以很方便的實現(xiàn)嵌套效果。
在腳本部分,該組件定義了三個屬性:id、context和initData。其中,id屬性指定了頁面容器的唯一標識符;context屬性用于訪問上下文對象,以便在組件內(nèi)部訪問其他資源;initData屬性則是初始化數(shù)據(jù),用于在頁面容器創(chuàng)建時初始化組件狀態(tài)。
在mounted生命周期函數(shù)中,該組件調(diào)用了context.initControl方法,該方法用于在上下文對象中注冊當前組件,以便在需要時可以對其進行操作。通過這種方式,我們可以在整個應(yīng)用程序中輕松地訪問和管理組件,后面不做贅述。
實現(xiàn)uiTextBox控件
文本框:src/components/iTextBox.vue:
<template>
<input type="text" :value="text" @input="onInput($event)" />
</template>
<script>
export default {
props: ['id', 'context', 'initData', 'text'],
mounted() {
this.context.initControl(this)
},
methods:{
onInput(e){
this.initData.props.text=e.target.value
this.context.fireEvent(this,'Input')
}
}
}
</script>
<style>
</style>
在模板部分,該組件使用了input元素來實現(xiàn)文本框的顯示,并使用了Vue.js的雙向綁定語法:value來綁定文本框的值。
在腳本部分,該組件定義了四個屬性:id、context、initData和text。其中,id屬性指定了文本框的唯一標識符;context屬性用于訪問上下文對象,以便在組件內(nèi)部訪問其他資源;initData屬性則是初始化數(shù)據(jù),用于在文本框創(chuàng)建時初始化組件狀態(tài);text屬性則是綁定到文本框的值。
該組件還定義了一個名為onInput的方法,用于處理文本框輸入事件。當用戶在文本框中輸入內(nèi)容時,該方法會更新initData.props.text的值,并調(diào)用context.fireEvent方法觸發(fā)Input事件。通過這種方式,我們可以在文本框值發(fā)生變化時及時驅(qū)動自定義事件。
實現(xiàn)uiButton控件
按鈕:src/components/uiButton.vue:
<template>
<button @click="click" type="button">{{text}}</button>
</template>
<script>
export default {
props: ['id', 'text', 'context', 'initData'],
methods: {
click() {
this.context.fireEvent(this,'Click')
}
},
mounted() {
this.context.initControl(this)
}
}
</script>
<style>
</style>
在模板部分,該組件使用了button元素來實現(xiàn)按鈕的顯示,并使用了Vue.js的雙向綁定語法:text來綁定按鈕的顯示文本。
在腳本部分,該組件定義了四個屬性:id、text、context和initData。其中,id屬性指定了按鈕的唯一標識符;text屬性用于綁定按鈕的顯示文本;context屬性用于訪問上下文對象,以便在組件內(nèi)部訪問其他資源;initData屬性則是初始化數(shù)據(jù),用于在按鈕創(chuàng)建時初始化組件狀態(tài)。
在methods部分,該組件定義了一個名為click的方法,用于處理按鈕的點擊事件。當用戶點擊按鈕時,該方法會調(diào)用context.fireEvent方法觸發(fā)Click事件。通過這種方式,我們可以在按鈕被點擊時及時通知其他組件。
實現(xiàn)uiLabel控件
文本標簽:src/components/uiLabel.vue:
<template>
<div>{{text}}</div>
</template>
<script>
export default {
props: ['id', 'text', 'context', 'initData'],
mounted() {
this.context.initControl(this)
}
}
</script>
<style>
</style>
在模板部分,該組件使用了div元素來實現(xiàn)文本標簽的顯示,并使用了Vue.js的雙向綁定語法:text來綁定文本標簽的顯示文本。
在腳本部分,該組件定義了四個屬性:id、text、context和initData。其中,id屬性指定了文本標簽的唯一標識符;text屬性用于綁定文本標簽的顯示文本;context屬性用于訪問上下文對象,以便在組件內(nèi)部訪問其他資源;initData屬性則是初始化數(shù)據(jù),用于在文本標簽創(chuàng)建時初始化組件狀態(tài)。
測試
最后我們把UI工廠顯示到界面上,讀取我們設(shè)置好的json文件,即可看到組裝好的界面,經(jīng)測試,功能一切正常:
總結(jié)
低代碼開發(fā)平臺基于模型驅(qū)動,而模型驅(qū)動后的控件則是解決復(fù)雜度的關(guān)鍵,控件系統(tǒng)思想是一種將應(yīng)用程序拆分成多個可復(fù)用的組件的編程模型,它可以提高開發(fā)效率、降低代碼復(fù)雜度,使得應(yīng)用程序更易于維護和擴展。在控件系統(tǒng)中,每個組件都有自己的方法、事件和屬性,并且可以被其他組件調(diào)用和操作。為了能夠?qū)⒛P万?qū)動與控件系統(tǒng)相結(jié)合,所以使用了描述依賴渲染(DDR)思想,作為膠水方法論,將模型轉(zhuǎn)化成了控件。
這一套組合拳打下來不僅僅適用于Web應(yīng)用程序,還適用于其他類型的應(yīng)用程序,例如:桌面應(yīng)用程序、移動應(yīng)用程序等。它可以幫助開發(fā)人員提高開發(fā)效率、降低代碼復(fù)雜度,使得應(yīng)用程序更易于維護和擴展。文章來源:http://www.zghlxwxcb.cn/news/detail-452064.html
因此,我們應(yīng)該認真學(xué)習(xí)低代碼、描述依賴渲染、控件系統(tǒng)的思想,掌握概念和實現(xiàn)方式,以便在開發(fā)應(yīng)用程序時更加高效、優(yōu)雅地進行編程。同時,我們也應(yīng)該關(guān)注利用低代碼的信息化發(fā)展和創(chuàng)新,探索出更加靈活、高效的編程模型,推動信創(chuàng)領(lǐng)域的不斷進步。文章來源地址http://www.zghlxwxcb.cn/news/detail-452064.html
到了這里,關(guān)于低代碼信創(chuàng)開發(fā)核心技術(shù)(一):基于Vue.js的描述依賴渲染DDR實現(xiàn)模型驅(qū)動的組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!