我們?cè)谇懊娴恼鹿?jié)中已經(jīng)了解了如何在屏幕上以文本內(nèi)容的形式獲得輸出。在本文中我們將學(xué)習(xí)如何在屏幕上以HTML模板的形式獲得輸出。我們先看一段代碼好來(lái)幫助我們理解。
//index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>vue模板和組件</title> </head> <body> <div id="vue_det"> <h1>姓名 : {{ name }}</h1> <div>{{ htmlcontent }}</div> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript" src="js/app.js"></script> </body> </html>
//js/app.js+ var vm = new Vue({ el: '#vue_det', data: { name: "孫悟空", htmlcontent: "<div><h1>Vue Js Template</h1></div>" } })
使用live-server啟動(dòng)項(xiàng)目得到如下結(jié)果
因?yàn)槲覀兪褂昧瞬逯担簿褪请p括號(hào),我們?cè)跒g覽器中就真實(shí)的顯示了html內(nèi)容,這顯然和我們想要渲染html是不同的,我們希望它在瀏覽器能顯示html渲染的內(nèi)容。
為了解決上面的問(wèn)題,我們不得不適用v-html指令,只要我們將v-html屬性分配給html元素,vue就會(huì)知道將其作為html內(nèi)容輸出,我們嘗試下:
<div id="vue_det"> <h1>姓名 : {{ name }}</h1> <div v-html="htmlcontent"></div> </div>
得到了如下結(jié)果
從瀏覽器調(diào)試可以看出
與app.js中填寫的html字符串表現(xiàn)得相同
我們已經(jīng)了解了如何將HTML模板添加到DOM。現(xiàn)在,我們將實(shí)現(xiàn)如何向現(xiàn)有的HTML元素添加屬性。想象一下,我們?cè)贖TML文件中有一個(gè)圖像標(biāo)記,我們想要分配src屬性,舉例,直接看代碼
<div id="vue_det"> <h1>姓名 : {{ name }}</h1> <div v-html="htmlcontent"></div> <img src="" width="300" height="250" /> </div>
img標(biāo)簽的src是空的,我們將src放到j(luò)s的數(shù)據(jù)對(duì)象中
var vm = new Vue({ el: '#vue_det', data: { name: "孫悟空", htmlcontent: "<div><h1>Vue Js Template</h1></div>", imgsrc: './img/img.jpg' } })
然后我們修改index.html
<img src="{{ imgsrc }}" width="300" height="250" />
結(jié)果如下
好像哪里不對(duì),其實(shí)不是,在vue中,我們綁定屬性用v-bind指令
<img v-bind:src="imgsrc" width="300" height="250" />
可以看到我們?yōu)g覽器中渲染的html
Vue組件是VueJS的重要功能之一,可以創(chuàng)建自定義元素,可以在HTML中重復(fù)使用。讓我們使用一個(gè)示例并創(chuàng)建一個(gè)組件。
//index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>vue模板和組件</title> </head> <body> <div id="component_test"> <testcomponent></testcomponent> </div> <div id="component_test1"> <testcomponent></testcomponent> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript" src="js/app.js"></script> </body> </html>
//js/app.js Vue.component('testcomponent', { template: '<div><h1>This is coming from component</h1></div>' }); var vm = new Vue({ el: '#component_test' }); var vm1 = new Vue({ el: '#component_test1' });
我們創(chuàng)建了兩個(gè)div,id分別是component_test和component_test1,在app.js中我們創(chuàng)建了兩個(gè)Vue實(shí)例,外加一個(gè)Vue組件,要想創(chuàng)建組件,它的語(yǔ)法是
Vue.component('nameofthecomponent',{ // options});
創(chuàng)建組件后,組件的名稱將成為自定義元素,并且可以在創(chuàng)建的Vue實(shí)例元素中使用相同的名稱,在app.js文件中創(chuàng)建的組件中,我們添加了一個(gè)模板,我們已為其分配了HTML代碼。這是一種注冊(cè)全局組件的方法,可以將其作為任何vue實(shí)例的一部分,我們發(fā)現(xiàn)這時(shí)候?yàn)g覽器變成了
組件被賦予自定義元素標(biāo)記,即<testcomponent> </ testcomponent>。但是,當(dāng)我們?cè)跒g覽器中檢查相同內(nèi)容時(shí),我們發(fā)現(xiàn)結(jié)果沒(méi)有自定義的元素,如以下屏幕截圖所示。
我們也可以將組件作為vue實(shí)例的一部分
var vm = new Vue({ el: '#component_test', components:{ 'testcomponent': { template : '<div><h1>This is coming from component</h1></div>' } } });
這是本地注冊(cè)組件,組件只是vue實(shí)例的一部分。到目前為止我們已經(jīng)基本組件的實(shí)現(xiàn)。現(xiàn)在我們來(lái)繼續(xù)擴(kuò)展。
// js/app.js Vue.component('testcomponent', { template: '<div v-on:mouseover = "changename()" v-on:mouseout = "originalname();"><h1>Custom Component created by <span id = "name">{{name}}</span></h1></div>', data: function () { return { name: "tom" } }, methods: { changename: function () { this.name = "bob"; }, originalname: function () { this.name = "tom"; } } }); var vm = new Vue({ el: '#component_test' }); var vm1 = new Vue({ el: '#component_test1' });
在上面的app.js文件中,我們添加了一個(gè)函數(shù),它返回一個(gè)對(duì)象。該對(duì)象具有name屬性,該屬性被賦值為'tom'。盡管這里data是函數(shù),我們也可以像直接在Vue實(shí)例中使用其屬性,此外這里還添加了兩個(gè)函數(shù),在changename中,我們更改name屬性,在originalname中我們將其重置為原始名稱,有關(guān)事件我們后面在討論,這段代碼的結(jié)果是:
因?yàn)榉峙淞薽ouseover和mouseout事件,當(dāng)鼠標(biāo)懸停在tom上時(shí),會(huì)將tom改成bob
使用關(guān)鍵字<component> </ component>創(chuàng)建動(dòng)態(tài)組件,并使用屬性綁定,如下
<component v-bind:is = "view"></component>
//index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>vue模板和組件</title> </head> <body> <div id="databinding"> <component v-bind:is="view"></component> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript" src="js/app.js"></script> </body> </html>
//app.js var vm = new Vue({ el: "#databinding", data: { view: "component1" }, components: { component1: { template: '<div><span style = "font-size:25;color:red;">Dynamic Component</span></div>' } } });
瀏覽器已顯示值:
提:已創(chuàng)建vue項(xiàng)目,未創(chuàng)建請(qǐng)參考 https://www.toutiao.com/article/7398100974524449330/
步驟 1:在項(xiàng)目目錄下,安裝 Element UI(Element UI 是一個(gè)基于 Vue.js 的組件庫(kù),它提供了一套為開發(fā)者設(shè)計(jì)和實(shí)現(xiàn)用戶界面的解決方案。Element UI 提供了大量預(yù)設(shè)計(jì)的組件,如按鈕、輸入框、選擇器等,這可以幫助開發(fā)者快速構(gòu)建應(yīng)用程序界面。
Element ui的手冊(cè)網(wǎng)站: https://element-plus.org/zh-CN/guide/installation.html )
操作:在vscode中打開項(xiàng)目根目錄,按ctrl+~鍵打開終端,在終端中輸入npm install element-plus --save
步驟2:在 main.js 中引入 Element Plus 和相關(guān)的樣式(此方式是全局引入即將Element所有的組件引入):
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 導(dǎo)入路由
import ElementPlus from 'element-plus'; // 導(dǎo)入 Element Plus
import 'element-plus/dist/index.css'; // 導(dǎo)入 Element Plus 的 CSS 樣式
// 創(chuàng)建 Vue 應(yīng)用實(shí)例
const app = createApp(App);
// 使用路由
app.use(router);
// 使用 Element Plus 插件
app.use(ElementPlus);
// 掛載應(yīng)用
app.mount('#app');
步驟3: 使用 Element Plus 組件
打開網(wǎng)站的“組件”界面,在左側(cè)選擇要添加的組件,如:按鈕;在右側(cè)出現(xiàn)各種樣式的按鈕,點(diǎn)擊樣式右下角的“<>”顯示出源代碼,復(fù)制源代進(jìn)行調(diào)用。
實(shí)操:我們可以在新建一個(gè)dome.vue頁(yè)面,使用一個(gè)按鈕組件:
(1)創(chuàng)建新頁(yè)面,選中views右擊點(diǎn)擊“新建文件”在文件中輸入“dome.vue”
(2)選擇按鈕樣式,這里我選擇success按鈕,復(fù)制相對(duì)應(yīng)的代碼<el-button type="success">Success</el-button>
(3)將代碼添加到頁(yè)面中
<template>
<el-button type="success">Success</el-button>
</template>
<script setup>
</script>
<style>
/* 這里可以添加樣式 */
</style>
文分享自華為云社區(qū)《DTSE Tech Talk | 6個(gè)實(shí)例帶你解讀TinyVue 組件庫(kù)跨框架技術(shù)-云社區(qū)-華為云》,作者: 華為云社區(qū)精選。
在DTSE Tech Talk 《 手把手教你實(shí)現(xiàn)mini版TinyVue組件庫(kù) 》的主題直播中,華為云前端開發(fā)DTSE技術(shù)布道師阿健老師給開發(fā)者們展開了組件庫(kù)跨框架的討論,同時(shí)針對(duì)TinyVue組件庫(kù)的關(guān)鍵技術(shù)進(jìn)行了剖析,并通過(guò)項(xiàng)目實(shí)戰(zhàn)演示了一份源碼編譯出2個(gè)不同Vue 框架的組件。最后針對(duì)框架間的差異,也給出了相應(yīng)的技術(shù)方案,幫助開發(fā)者們實(shí)戰(zhàn)完成組件庫(kù)跨框架。
直播鏈接:https://bbs.huaweicloud.com/live/DTT_live/202404171630.html
當(dāng)前實(shí)現(xiàn)組件庫(kù)的跨框架技術(shù),是提升Web頁(yè)面開發(fā)效率與應(yīng)用靈活性的重要手段。本次直播的實(shí)戰(zhàn)環(huán)節(jié),用300行代碼模擬了 TinyVue 組件庫(kù)的跨框架實(shí)現(xiàn),開發(fā)者可以在mini 版組件庫(kù)中,復(fù)現(xiàn)跨框架及多端適配兩大功能。同時(shí)通過(guò)本期的實(shí)操環(huán)節(jié),也給開發(fā)者呈現(xiàn)一個(gè)明確且詳盡的實(shí)現(xiàn)流程,協(xié)助大家更好的理解并掌握跨框架技術(shù)并運(yùn)用到實(shí)際工作中。
具體源碼可參考: https://github.com/opentiny/mini-tiny-vue
目前,Vue擁有Vue2和Vue3兩大主要分支,它們?cè)陂_發(fā)上并不兼容。Vue2還可以進(jìn)一步細(xì)分為2.6及之前的版本和Vue2.7這兩個(gè)小分支,其中Vue2.7作為2.6與Vue3之間的過(guò)渡版本,在開發(fā)上起著橋梁作用。
對(duì)于現(xiàn)有項(xiàng)目來(lái)講,如果遷移到Vue3,難免存在API及組件功能不同步的情況,因此遷移過(guò)程將存在一定的成本及風(fēng)險(xiǎn)。而在當(dāng)前的Vue生態(tài)中,諸如Antdesign和Element等知名組件庫(kù)都推出了支持Vue2和Vue3的組件。然而這些官網(wǎng)文檔和API卻并不通用,這意味著實(shí)際上是提供了兩個(gè)獨(dú)立的組件庫(kù)來(lái)實(shí)現(xiàn)跨框架支持的。
作為致力于實(shí)現(xiàn)跨框架的TinyVue組件庫(kù),旨在實(shí)現(xiàn)跨不同版本的Vue框架兼容性,其獨(dú)特之處在于采用單份源代碼策略,通過(guò)智能編譯技術(shù),能夠同時(shí)生成適用于Vue 2.6、2.7版本以及Vue3版本的組件包。這意味著開發(fā)者只需維護(hù)同一個(gè)官方網(wǎng)站,并提供一套標(biāo)準(zhǔn)化的API接口,即可滿足多版本Vue用戶的需求。這種設(shè)計(jì)有效地減少了TinyVue組件庫(kù)的維護(hù)成本和未來(lái)技術(shù)遷移的風(fēng)險(xiǎn)。
首先以一個(gè)button組件為例,組件的左上部分是模板,作為組件的入口,它集成了適配層、renderless邏輯以及theme樣式(此處暫不涉及theme部分)。值得注意的是,組件內(nèi)部并未包含任何邏輯代碼,所有邏輯均被抽離至renderless中,這里可以按照下圖所示觀察其調(diào)用關(guān)系。
整體來(lái)看,調(diào)用過(guò)程就像一個(gè)管道,數(shù)據(jù)從模板開始流動(dòng),經(jīng)過(guò)邏輯處理,再流回到模板上。在這個(gè)過(guò)程中,它流經(jīng)的適配層巧妙地抹平了框架之間的差異,正是TinyVue跨框架的精妙所在。
Vue3是一次全新的框架升級(jí),所以它的語(yǔ)法以及內(nèi)部實(shí)現(xiàn),都發(fā)生了很大的變化,這些是在開發(fā)跨框架組件庫(kù)時(shí)必須考慮的問(wèn)題。而在長(zhǎng)期的跨框架組件庫(kù)的開發(fā)中,可能會(huì)遇到眾多的框架差異,具體可以將這些差異歸結(jié)為2大類:
在Vue 2.6 中引入響應(yīng)函數(shù)
import { reactive, ref, watch, ... } from '@vue/composition-api'
在Vue 3 中引入響應(yīng)函數(shù)
import { reactive, ref, watch, ... } from 'vue'
解決方案:通過(guò)在適配層暴露一個(gè)hooks變量,統(tǒng)一響應(yīng)式函數(shù)的訪問(wèn),代碼如下
// adapter/vue2/index.js
import * as hooks from '@vue/composition-api'
// adapter/vue3/index.js
import * as hooks from 'vue'
// adapter/index.js
export { hooks }
在Vue 2.6中,渲染函數(shù)的 VNode 參數(shù)結(jié)構(gòu)
{
staticClass: 'button',
class: { 'is-outlined': isOutlined },
staticStyle: { color: '#34495E' },
style: { backgroundColor: buttonColor },
attrs: { id: 'submit' },
domProps: { innerHTML: '' },
on: { click: submitForm },
key: 'submit-button'
}
在Vue 3 中,渲染函數(shù)的 VNode 參數(shù)結(jié)構(gòu)是扁平的
{
class: ['button', { 'is-outlined': isOutlined }],
style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
解決方案:通過(guò)在適配層暴露一個(gè)h函數(shù),讓Vue3框架也能支持Vue2的參數(shù)格式。這樣就能統(tǒng)一h 函數(shù)的用法,同時(shí)讓在Vue2時(shí)期開發(fā)的組件在Vue3框架下兼容運(yùn)行。
// adapter/vue2/index.js
const h = hooks.h
// adapter/vue3/index.js
const h = (component, propsData, childData) => {
// 代碼有省略......
let props = {}
let children = childData
if (propsData && typeof propsData === 'object' && !Array.isArray(propsData)) {
props = parseProps(propsData)
propsData.scopedSlots && (children = propsData.scopedSlots)
} else if (typeof propsData === 'string' || Array.isArray(propsData)) {
childData = propsData
}
return hooks.h(component, props, children)
}
// adapter/index.js
export { h }
(3)v-model的差異:
在Vue 2.6中,在組件上使用 v-model 相當(dāng)于綁定 value 屬性和 input 事件
<ChildComponent v-model="pageTitle" />
<!-- 會(huì)編譯為: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
在Vue 3 中,v-model 相當(dāng)于綁定了 modelValue 屬性和 update:modelValue 事件
<ChildComponent v-model="pageTitle" />
<!-- 會(huì)編譯為: -->
<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
解決方案:通過(guò)Vue2中聲明 model的option 選項(xiàng),來(lái)自定義Vue2框架下v-model 的默認(rèn)綁定 prop 和 event 。
defineComponent({
model: {
prop: 'modelValue', // 默認(rèn)值為 value
event: 'update:modelValue' // 默認(rèn)值為 input
},
props: {
modelValue: String
} // ...
})
(4)slots的差異:
在Vue 2.6中,有普通插槽 slots 和 作用域插槽 scopedSlots
// 普通插槽為對(duì)象,可以直接使用
this.$slots.mySlot
// 作用域插槽為函數(shù),要按函數(shù)來(lái)調(diào)用
this.$scopedSlots.header()
在Vue 3 中,統(tǒng)一為 slots 函數(shù)的形式
// 將所有 scopedSlots 替換為 slots
this.$slots.header()
// 將原有 slots 改為函數(shù)調(diào)用方式
this.$slots.mySlot()
解決方案:通過(guò)構(gòu)建一個(gè)vm.$slots屬性, 來(lái)統(tǒng)一2個(gè)框架中,訪問(wèn)slots的訪問(wèn)。
// adapter/vue2/index.js
Object.defineProperties(vm, {
// ......
$slots: { get: () => instance.proxy.$scopedSlots },
$scopedSlots: { get: () => instance.proxy.$scopedSlots },
})
// adapter/vue3/index.js
Object.defineProperties(vm, {
// ......
$slots: { get: () => instance.slots },
$scopedSlots: { get: () => instance.slots },
})
我們?cè)趘m下,還暴露了許多框架runtime層面上的組件屬性,用于抹平跨Vue框架的差異。在開發(fā)跨框架組件時(shí),要使用vm來(lái)訪問(wèn)組件,避免直接訪問(wèn)組件的instance。
// 創(chuàng)建一個(gè)Vue2 運(yùn)行時(shí)的兼容 vm 對(duì)象
const createVm = (vm, _instance) => {
const instance = _instance.proxy
Object.defineProperties(vm, {
$attrs: { get: () => instance.$attrs },
$listeners: { get: () => instance.$listeners },
$el: { get: () => instance.$el },
$parent: { get: () => instance.$parent },
$children: { get: () => instance.$children },
$nextTick: { get: () => hooks.nextTick },
$on: { get: () => instance.$on.bind(instance) },
$once: { get: () => instance.$once.bind(instance) },
$off: { get: () => instance.$off.bind(instance) },
$refs: { get: () => instance.$refs },
$slots: { get: () => instance.$scopedSlots },
$scopedSlots: { get: () => instance.$scopedSlots },
$set: { get: () => instance.$set }
})
return vm
}
// 創(chuàng)建一個(gè)Vue3 運(yùn)行時(shí)的兼容 vm 對(duì)象
const createVm = (vm, instance) => {
Object.defineProperties(vm, {
$attrs: { get: () => $attrs },
$listeners: { get: () => $listeners },
$el: { get: () => instance.vnode.el },
$parent: { get: () => instance.parent },
$children:{get:()=>genChild(instance.subTree)},
$nextTick: { get: () => hooks.nextTick },
$on: { get: () => $emitter.on },
$once: { get: () => $emitter.once },
$off: { get: () => $emitter.off },
$refs: { get: () => instance.refs },
$slots: { get: () => instance.slots },
$scopedSlots: { get: () => instance.slots },
$set: { get: () => $set }
})
return vm
}
(5)指令的差異:
Vue3的指令生命周期的名稱變化了, 但指令的參數(shù)基本不變
解決方案:在開發(fā)指令對(duì)象時(shí),通過(guò)補(bǔ)齊指令周期,讓指令對(duì)象同時(shí)支持Vue2 和 Vue3
(6)動(dòng)畫類型的差異:
解決方案:在全局的動(dòng)畫類名文件中,同時(shí)補(bǔ)齊2個(gè)框架下的類名,讓動(dòng)畫類同時(shí)支持Vue2 和 Vue3的Transition組件
// 此處同時(shí)寫了 -enter \ -enter-from 的類名,所以它同時(shí)支持vue2,vue3的 Transition 組件。
.fade-in-linear-enter,
.fade-in-linear-enter-from,
.fade-in-linear-leave-to {
opacity: 0;
}
在構(gòu)建TinyVue跨框架組件庫(kù)的過(guò)程中,團(tuán)隊(duì)集中攻克了多個(gè)Vue框架間的關(guān)鍵差異點(diǎn),其中六項(xiàng)尤為突出且具有代表性。
開發(fā)TinyVue跨框架組件庫(kù)時(shí),面對(duì)Vue2與Vue3的重要區(qū)別,我們確立了兩個(gè)核心原則:一是“求同去異”,即在編寫組件時(shí)選用兩框架都支持的通用語(yǔ)法,如因Vue2不支持多根節(jié)點(diǎn)組件而統(tǒng)一采用單根節(jié)點(diǎn)設(shè)計(jì);二是“兼容并包”,通過(guò)構(gòu)建適配層隱藏框架間的差異,提供統(tǒng)一接口,無(wú)需開發(fā)者手動(dòng)判斷框架版本,這樣他們可以更專注于邏輯開發(fā)。在指令對(duì)象和動(dòng)畫類名等細(xì)節(jié)方面,同樣貫徹這一簡(jiǎn)化差異、廣泛兼容的理念。
關(guān)注#華為云開發(fā)者聯(lián)盟# 點(diǎn)擊下方,第一時(shí)間了解華為云新鮮技術(shù)~
華為云博客_大數(shù)據(jù)博客_AI博客_云計(jì)算博客_開發(fā)者中心-華為云
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。