Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
我們在前面的章節中已經了解了如何在屏幕上以文本內容的形式獲得輸出。在本文中我們將學習如何在屏幕上以HTML模板的形式獲得輸出。我們先看一段代碼好來幫助我們理解。
//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啟動項目得到如下結果
因為我們使用了插值,也就是雙括號,我們在瀏覽器中就真實的顯示了html內容,這顯然和我們想要渲染html是不同的,我們希望它在瀏覽器能顯示html渲染的內容。
為了解決上面的問題,我們不得不適用v-html指令,只要我們將v-html屬性分配給html元素,vue就會知道將其作為html內容輸出,我們嘗試下:
<div id="vue_det"> <h1>姓名 : {{ name }}</h1> <div v-html="htmlcontent"></div> </div>
得到了如下結果
從瀏覽器調試可以看出
與app.js中填寫的html字符串表現得相同
我們已經了解了如何將HTML模板添加到DOM。現在,我們將實現如何向現有的HTML元素添加屬性。想象一下,我們在HTML文件中有一個圖像標記,我們想要分配src屬性,舉例,直接看代碼
<div id="vue_det"> <h1>姓名 : {{ name }}</h1> <div v-html="htmlcontent"></div> <img src="" width="300" height="250" /> </div>
img標簽的src是空的,我們將src放到js的數據對象中
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" />
結果如下
好像哪里不對,其實不是,在vue中,我們綁定屬性用v-bind指令
<img v-bind:src="imgsrc" width="300" height="250" />
可以看到我們瀏覽器中渲染的html
Vue組件是VueJS的重要功能之一,可以創建自定義元素,可以在HTML中重復使用。讓我們使用一個示例并創建一個組件。
//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' });
我們創建了兩個div,id分別是component_test和component_test1,在app.js中我們創建了兩個Vue實例,外加一個Vue組件,要想創建組件,它的語法是
Vue.component('nameofthecomponent',{ // options});
創建組件后,組件的名稱將成為自定義元素,并且可以在創建的Vue實例元素中使用相同的名稱,在app.js文件中創建的組件中,我們添加了一個模板,我們已為其分配了HTML代碼。這是一種注冊全局組件的方法,可以將其作為任何vue實例的一部分,我們發現這時候瀏覽器變成了
組件被賦予自定義元素標記,即<testcomponent> </ testcomponent>。但是,當我們在瀏覽器中檢查相同內容時,我們發現結果沒有自定義的元素,如以下屏幕截圖所示。
我們也可以將組件作為vue實例的一部分
var vm = new Vue({ el: '#component_test', components:{ 'testcomponent': { template : '<div><h1>This is coming from component</h1></div>' } } });
這是本地注冊組件,組件只是vue實例的一部分。到目前為止我們已經基本組件的實現。現在我們來繼續擴展。
// 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文件中,我們添加了一個函數,它返回一個對象。該對象具有name屬性,該屬性被賦值為'tom'。盡管這里data是函數,我們也可以像直接在Vue實例中使用其屬性,此外這里還添加了兩個函數,在changename中,我們更改name屬性,在originalname中我們將其重置為原始名稱,有關事件我們后面在討論,這段代碼的結果是:
因為分配了mouseover和mouseout事件,當鼠標懸停在tom上時,會將tom改成bob
使用關鍵字<component> </ component>創建動態組件,并使用屬性綁定,如下
<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>' } } });
瀏覽器已顯示值:
提:已創建vue項目,未創建請參考 https://www.toutiao.com/article/7398100974524449330/
步驟 1:在項目目錄下,安裝 Element UI(Element UI 是一個基于 Vue.js 的組件庫,它提供了一套為開發者設計和實現用戶界面的解決方案。Element UI 提供了大量預設計的組件,如按鈕、輸入框、選擇器等,這可以幫助開發者快速構建應用程序界面。
Element ui的手冊網站: https://element-plus.org/zh-CN/guide/installation.html )
操作:在vscode中打開項目根目錄,按ctrl+~鍵打開終端,在終端中輸入npm install element-plus --save
步驟2:在 main.js 中引入 Element Plus 和相關的樣式(此方式是全局引入即將Element所有的組件引入):
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 導入路由
import ElementPlus from 'element-plus'; // 導入 Element Plus
import 'element-plus/dist/index.css'; // 導入 Element Plus 的 CSS 樣式
// 創建 Vue 應用實例
const app = createApp(App);
// 使用路由
app.use(router);
// 使用 Element Plus 插件
app.use(ElementPlus);
// 掛載應用
app.mount('#app');
步驟3: 使用 Element Plus 組件
打開網站的“組件”界面,在左側選擇要添加的組件,如:按鈕;在右側出現各種樣式的按鈕,點擊樣式右下角的“<>”顯示出源代碼,復制源代進行調用。
實操:我們可以在新建一個dome.vue頁面,使用一個按鈕組件:
(1)創建新頁面,選中views右擊點擊“新建文件”在文件中輸入“dome.vue”
(2)選擇按鈕樣式,這里我選擇success按鈕,復制相對應的代碼<el-button type="success">Success</el-button>
(3)將代碼添加到頁面中
<template>
<el-button type="success">Success</el-button>
</template>
<script setup>
</script>
<style>
/* 這里可以添加樣式 */
</style>
文分享自華為云社區《DTSE Tech Talk | 6個實例帶你解讀TinyVue 組件庫跨框架技術-云社區-華為云》,作者: 華為云社區精選。
在DTSE Tech Talk 《 手把手教你實現mini版TinyVue組件庫 》的主題直播中,華為云前端開發DTSE技術布道師阿健老師給開發者們展開了組件庫跨框架的討論,同時針對TinyVue組件庫的關鍵技術進行了剖析,并通過項目實戰演示了一份源碼編譯出2個不同Vue 框架的組件。最后針對框架間的差異,也給出了相應的技術方案,幫助開發者們實戰完成組件庫跨框架。
直播鏈接:https://bbs.huaweicloud.com/live/DTT_live/202404171630.html
當前實現組件庫的跨框架技術,是提升Web頁面開發效率與應用靈活性的重要手段。本次直播的實戰環節,用300行代碼模擬了 TinyVue 組件庫的跨框架實現,開發者可以在mini 版組件庫中,復現跨框架及多端適配兩大功能。同時通過本期的實操環節,也給開發者呈現一個明確且詳盡的實現流程,協助大家更好的理解并掌握跨框架技術并運用到實際工作中。
具體源碼可參考: https://github.com/opentiny/mini-tiny-vue
目前,Vue擁有Vue2和Vue3兩大主要分支,它們在開發上并不兼容。Vue2還可以進一步細分為2.6及之前的版本和Vue2.7這兩個小分支,其中Vue2.7作為2.6與Vue3之間的過渡版本,在開發上起著橋梁作用。
對于現有項目來講,如果遷移到Vue3,難免存在API及組件功能不同步的情況,因此遷移過程將存在一定的成本及風險。而在當前的Vue生態中,諸如Antdesign和Element等知名組件庫都推出了支持Vue2和Vue3的組件。然而這些官網文檔和API卻并不通用,這意味著實際上是提供了兩個獨立的組件庫來實現跨框架支持的。
作為致力于實現跨框架的TinyVue組件庫,旨在實現跨不同版本的Vue框架兼容性,其獨特之處在于采用單份源代碼策略,通過智能編譯技術,能夠同時生成適用于Vue 2.6、2.7版本以及Vue3版本的組件包。這意味著開發者只需維護同一個官方網站,并提供一套標準化的API接口,即可滿足多版本Vue用戶的需求。這種設計有效地減少了TinyVue組件庫的維護成本和未來技術遷移的風險。
首先以一個button組件為例,組件的左上部分是模板,作為組件的入口,它集成了適配層、renderless邏輯以及theme樣式(此處暫不涉及theme部分)。值得注意的是,組件內部并未包含任何邏輯代碼,所有邏輯均被抽離至renderless中,這里可以按照下圖所示觀察其調用關系。
整體來看,調用過程就像一個管道,數據從模板開始流動,經過邏輯處理,再流回到模板上。在這個過程中,它流經的適配層巧妙地抹平了框架之間的差異,正是TinyVue跨框架的精妙所在。
Vue3是一次全新的框架升級,所以它的語法以及內部實現,都發生了很大的變化,這些是在開發跨框架組件庫時必須考慮的問題。而在長期的跨框架組件庫的開發中,可能會遇到眾多的框架差異,具體可以將這些差異歸結為2大類:
在Vue 2.6 中引入響應函數
import { reactive, ref, watch, ... } from '@vue/composition-api'
在Vue 3 中引入響應函數
import { reactive, ref, watch, ... } from 'vue'
解決方案:通過在適配層暴露一個hooks變量,統一響應式函數的訪問,代碼如下
// 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中,渲染函數的 VNode 參數結構
{
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 中,渲染函數的 VNode 參數結構是扁平的
{
class: ['button', { 'is-outlined': isOutlined }],
style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
解決方案:通過在適配層暴露一個h函數,讓Vue3框架也能支持Vue2的參數格式。這樣就能統一h 函數的用法,同時讓在Vue2時期開發的組件在Vue3框架下兼容運行。
// 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 相當于綁定 value 屬性和 input 事件
<ChildComponent v-model="pageTitle" />
<!-- 會編譯為: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
在Vue 3 中,v-model 相當于綁定了 modelValue 屬性和 update:modelValue 事件
<ChildComponent v-model="pageTitle" />
<!-- 會編譯為: -->
<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
解決方案:通過Vue2中聲明 model的option 選項,來自定義Vue2框架下v-model 的默認綁定 prop 和 event 。
defineComponent({
model: {
prop: 'modelValue', // 默認值為 value
event: 'update:modelValue' // 默認值為 input
},
props: {
modelValue: String
} // ...
})
(4)slots的差異:
在Vue 2.6中,有普通插槽 slots 和 作用域插槽 scopedSlots
// 普通插槽為對象,可以直接使用
this.$slots.mySlot
// 作用域插槽為函數,要按函數來調用
this.$scopedSlots.header()
在Vue 3 中,統一為 slots 函數的形式
// 將所有 scopedSlots 替換為 slots
this.$slots.header()
// 將原有 slots 改為函數調用方式
this.$slots.mySlot()
解決方案:通過構建一個vm.$slots屬性, 來統一2個框架中,訪問slots的訪問。
// 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 },
})
我們在vm下,還暴露了許多框架runtime層面上的組件屬性,用于抹平跨Vue框架的差異。在開發跨框架組件時,要使用vm來訪問組件,避免直接訪問組件的instance。
// 創建一個Vue2 運行時的兼容 vm 對象
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
}
// 創建一個Vue3 運行時的兼容 vm 對象
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的指令生命周期的名稱變化了, 但指令的參數基本不變
解決方案:在開發指令對象時,通過補齊指令周期,讓指令對象同時支持Vue2 和 Vue3
(6)動畫類型的差異:
解決方案:在全局的動畫類名文件中,同時補齊2個框架下的類名,讓動畫類同時支持Vue2 和 Vue3的Transition組件
// 此處同時寫了 -enter \ -enter-from 的類名,所以它同時支持vue2,vue3的 Transition 組件。
.fade-in-linear-enter,
.fade-in-linear-enter-from,
.fade-in-linear-leave-to {
opacity: 0;
}
在構建TinyVue跨框架組件庫的過程中,團隊集中攻克了多個Vue框架間的關鍵差異點,其中六項尤為突出且具有代表性。
開發TinyVue跨框架組件庫時,面對Vue2與Vue3的重要區別,我們確立了兩個核心原則:一是“求同去異”,即在編寫組件時選用兩框架都支持的通用語法,如因Vue2不支持多根節點組件而統一采用單根節點設計;二是“兼容并包”,通過構建適配層隱藏框架間的差異,提供統一接口,無需開發者手動判斷框架版本,這樣他們可以更專注于邏輯開發。在指令對象和動畫類名等細節方面,同樣貫徹這一簡化差異、廣泛兼容的理念。
關注#華為云開發者聯盟# 點擊下方,第一時間了解華為云新鮮技術~
華為云博客_大數據博客_AI博客_云計算博客_開發者中心-華為云
*請認真填寫需求信息,我們會在24小時內與您取得聯系。