vue 實(shí)現(xiàn)拖拽動(dòng)態(tài)生成組件的需求
產(chǎn)品需求
開(kāi)完產(chǎn)品需求會(huì)議,遇到了一個(gè)需求,首先頁(yè)面分成兩欄布局,左側(cè)展示數(shù)據(jù)組件,支持拖拽排序,點(diǎn)擊按鈕清除組件。右側(cè)支持將組件的縮略圖拖拽至左側(cè)生成一個(gè)新的組件。
思路
對(duì)于動(dòng)態(tài)生成組件來(lái)說(shuō)每一次都要是生成全新的一個(gè)組件,那么就可以把 組件放進(jìn)函數(shù)當(dāng)中 return。在JSX中調(diào)用函數(shù),每次調(diào)用函數(shù)都會(huì)返回一個(gè)全新的組件。這對(duì)React來(lái)說(shuō)非常簡(jiǎn)單,但是對(duì)于Vue來(lái)說(shuō),直接將組件返回是不可能的。盡管這個(gè) return 寫法不適合Vue,但是我們不可否認(rèn),思路是非常正確的,所以我們應(yīng)該考慮一個(gè)別的寫法。至于動(dòng)態(tài)的生成組件,我們必須以數(shù)據(jù)來(lái)驅(qū)動(dòng)組件的生成。對(duì)于拖拽組件的排序,直接使用拖拽庫(kù)就OK了??!
面臨的問(wèn)題
- 拖拽庫(kù)的選擇
- 如何生成組件
- 以數(shù)據(jù)驅(qū)動(dòng)動(dòng)態(tài)生成組件
拖拽庫(kù)的選擇
拖拽庫(kù)在這里我選擇的是項(xiàng)目中存在的一個(gè)拖拽庫(kù) Vue.Draggable 點(diǎn)這里鏈接查看 Start 14.9K 蠻不錯(cuò)的。如果你們的Vue項(xiàng)目中沒(méi)有用到這個(gè)拖拽庫(kù),你們可以自行參考本片文章的設(shè)計(jì)思路。
如何生成組件
在這里我使用的是 Vue.extend() 不清楚如何使用的小伙伴請(qǐng)?jiān)诠俜轿臋n中查看過(guò)后再來(lái)學(xué)習(xí)這篇文章 Vue.extend 。 接下來(lái)我們創(chuàng)建一個(gè)js文件,用來(lái)書(shū)寫創(chuàng)建組件的代碼。
生成組件
/* generateComponents.js 文件名 */
import Vue from "vue";
// 想要?jiǎng)討B(tài)生成的組件,先引入這個(gè)文件。
import components1 from "./components/TestCom1.vue";
import components2 from "./components/TestCom2.vue";
// 將組件的名稱和組件做一個(gè)對(duì)應(yīng)Map
const comMap = {
components1,
components2,
};
// 接收生成組件需要的組件名稱,和想要傳遞給組件的
// props, 和 事件
const ReturnNewCom = function ({ props, on }) {
const {
comItem: { name },
} = props;
const newComponent = Vue.extend({
render(createElement) {
// 使用傳進(jìn)來(lái)的組件name來(lái)決定渲染哪一個(gè)組件。
return createElement(comMap[name], {
props,
on,
});
},
});
return new newComponent();
};
export default ReturnNewCom;
組件
在這里我們書(shū)寫兩個(gè)組件,用來(lái)演示這個(gè)Demo,分別為components1.vue,components2.vue。
/*components1.vue*/
<template>
<div class="widget-wrapper">
<header class="header">{{ comDetail.name }}--{{ comDetail.id }}</header>
<h1>查詢條件:{{ queryObj }}</h1>
<button @click="handleDelete">清除</button>
</div>
</template>
<script>
export default {
data() {
return {
comDetail: this.comItem,
_queryObj: this.queryObj,
};
},
props: {
comItem: {
type: Object,
default() {
return {
id: 0,
name: "",
};
},
},
queryObj: {
// 可以接收父組件傳遞的曬選條件,必須是Object
type: Object,
default() {
// 定義默認(rèn)的查詢條件。
return {
num: 0,
};
},
},
},
watch: {
comItem(val) {
this.comDetail = val;
return val;
},
queryObj(val) {
this._queryObj = val;
return val;
},
},
created() {
console.log("data -> this.comItem", this.comItem);
},
methods: {
handleDelete() {
// 刪除組件方法
this.$el.remove();
// 調(diào)用父組件的函數(shù)。修改父組件中的 leftComList 數(shù)組的數(shù)據(jù)。
this.$emit("handleDelete", this.comDetail);
},
},
};
</script>
<style scoped>
.widget-wrapper {
background: #ff7b7b;
border-radius: 12px;
overflow: hidden;
width: 200px;
}
.header {
height: 50px;
padding: 0 15px;
}
</style>
其實(shí)components2.vue文件中的代碼和components1.vue文件的代碼類似,唯一不同的地方就是背景顏色不一樣。
以數(shù)據(jù)驅(qū)動(dòng)動(dòng)態(tài)組件的生成
接下來(lái)就得使用Vue.Draggable 這個(gè)拖拽庫(kù)進(jìn)行拖拽和數(shù)據(jù)的修改。 我們可以直接在App.vue文件中直接書(shū)寫。
/* App.vue */
<template>
<div class="dragCom">
<h1>{{ leftComList }}</h1>
<button @click="queryObj.num++">改變查詢條件</button>
<div class="body">
<div class="left">
<draggable class="left" :list="leftComList" :group="'people'">
<div
ref="comBody"
v-for="({ name, id }, index) in leftComList"
:key="id"
class="comCard"
>
<!-- 循環(huán) leftComList 數(shù)組,利用數(shù)據(jù)來(lái)渲染組件,
將動(dòng)態(tài)生成的數(shù)組添加到這個(gè)DOM元素當(dāng)中。 -->
{{
handleAddCom({
props: { comItem: { name, id }, queryObj },
index,
})
}}
</div>
</draggable>
</div>
<div class="right">
<draggable
class="dragArea"
:list="rightComList"
:group="{ name: 'people', pull: 'clone', put: false }"
:clone="handleCloneDog"
>
<div class="card" v-for="element in rightComList" :key="element.id">
{{ element.name }}
</div>
<!-- 右側(cè)的 卡片 數(shù)據(jù), rightComList 數(shù)組對(duì)象中的name就對(duì)應(yīng)了generateComponents.js
中的ComMap中的屬性 -->
</draggable>
</div>
</div>
</div>
</template>
<script>
import draggable from "vuedraggable";
import CreateCom from "./generateComponents";
export default {
components: {
draggable,
},
data() {
return {
rightComList: [
{
id: Math.random(),
name: "components1",
},
{
id: Math.random(),
name: "components2",
},
],
leftComList: [], // 存儲(chǔ)驅(qū)動(dòng)動(dòng)態(tài)生成組件的數(shù)據(jù)。
comMap: new Map(), // 主要的作用就是用來(lái)記錄
// 組件有沒(méi)有渲染到 class="comCard" 這個(gè)DOM中,
// 如果渲染了就不能再往進(jìn)添加子元素了。
queryObj: {
// 主要的作用就是向子組件傳遞查詢條件
num: 0,
},
};
},
beforeDestroy() {
// 清除 記錄 的數(shù)據(jù)
this.comMap.clear();
},
methods: {
handleAddCom({ index, on = {}, props = { comItem: { name: "", id: 0 } } }) {
const {
comItem: { id },
} = props;
this.$nextTick(() => {
// 獲取該節(jié)點(diǎn)的子節(jié)點(diǎn)的長(zhǎng)度
const childNodesLength = this.$refs.comBody[index].childNodes.length;
// 獲取comBody 這個(gè)DOM 數(shù)組的長(zhǎng)度
const comLine = this.$refs.comBody.length;
if (!this.comMap.get(id)) {
// 如果沒(méi)有渲染過(guò)組件
// 1. 調(diào)用 CreateCom 方法 創(chuàng)建組件。 并傳遞 props 和 事件
const com = CreateCom({
props,
on: {
handleDelete: this.handleDeleteCom,
...on,
},
});
// 2. 生成組件
com.$mount();
if (childNodesLength === 2) {
// 如果要添加到兩個(gè)組件中間。那么就將新生成的組件DOM位置進(jìn)行修改放到中間。
// 將最后的組件DOM添加到正確的位置
this.$refs.comBody.splice(
index,
0,
this.$refs.comBody[comLine - 1]
);
}
// 3. 將生成的組件添加到改DOM中。
this.$refs.comBody[index].appendChild(com.$el);
// 4. 記錄該組件實(shí)現(xiàn)了渲染。
this.comMap.set(id, true);
} else {
// 該位置的組件已經(jīng)渲染,不需要再次渲染直接返回
return;
}
});
},
handleDeleteCom({ id }) {
// 傳遞給子組件刪除的方法,根據(jù)組件的id來(lái)刪除數(shù)據(jù)
const index = this.leftComList.findIndex((item) => item.id === id);
if (~index) {
// 如果存在這個(gè)id的組件,就刪除
this.leftComList.splice(index, 1);
}
},
handleCloneDog(item) {
// 給 leftComList 數(shù)組添加數(shù)據(jù)
return {
...item,
id: Math.random(),
};
},
},
};
</script>
<style>
.dragCom {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.body {
width: 100%;
height: 800px;
display: flex;
justify-content: space-between;
}
.left {
flex: 1;
height: 800px;
border: 1px solid pink;
}
.right {
width: 20%;
height: 800px;
}
.card {
height: 50px;
background-color: #40cec7;
margin: 12px 0;
font-size: 12px;
line-height: 50px;
cursor: pointer;
}
.comCard {
margin: 12px;
display: inline-block;
}
</style>
這樣就實(shí)現(xiàn)了動(dòng)態(tài)的組件渲染和拖拽排序。
效果
源碼
想要嘗試的同學(xué)可以自行下載本文的代碼源碼github
以上就是vue 實(shí)現(xiàn)拖拽動(dòng)態(tài)生成組件的需求的詳細(xì)內(nèi)容,更多關(guān)于vue拖拽動(dòng)態(tài)生成組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Vue 可拖拽組件Vue Smooth DnD的使用詳解
- vue拖拽組件 vuedraggable API options實(shí)現(xiàn)盒子之間相互拖拽排序
- Vue拖拽組件列表實(shí)現(xiàn)動(dòng)態(tài)頁(yè)面配置功能
- vue拖拽組件使用方法詳解
- Vue拖拽組件開(kāi)發(fā)實(shí)例詳解
- vue使用Split封裝通用拖拽滑動(dòng)分隔面板組件
- vue開(kāi)發(fā)拖拽進(jìn)度條滑動(dòng)組件
- vue draggable resizable 實(shí)現(xiàn)可拖拽縮放的組件功能
- 利用Vue-draggable組件實(shí)現(xiàn)Vue項(xiàng)目中表格內(nèi)容的拖拽排序
- Vue組件Draggable實(shí)現(xiàn)拖拽功能
- Vue實(shí)現(xiàn)可拖拽組件的方法
相關(guān)文章
vue監(jiān)聽(tīng)sessionStorage中值的變化方式
這篇文章主要介紹了vue監(jiān)聽(tīng)sessionStorage中值的變化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
springboot和vue前后端交互的實(shí)現(xiàn)示例
本文主要介紹了springboot和vue前后端交互的實(shí)現(xiàn)示例,將包括一個(gè)簡(jiǎn)單的Vue.js前端應(yīng)用程序,用于發(fā)送GET請(qǐng)求到一個(gè)Spring Boot后端應(yīng)用程序,以獲取并顯示用戶列表,感興趣的可以了解一下2024-05-05
vue實(shí)現(xiàn)滑動(dòng)驗(yàn)證條
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)滑動(dòng)驗(yàn)證條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
vue項(xiàng)目webpack中配置src路徑別名及使用方式
這篇文章主要介紹了vue項(xiàng)目webpack中配置src路徑別名及使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vue使用once修飾符,使事件只能觸發(fā)一次問(wèn)題
這篇文章主要介紹了vue使用once修飾符,使事件只能觸發(fā)一次問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
vue中render函數(shù)和h函數(shù)以及jsx的使用方式
這篇文章主要介紹了vue中render函數(shù)和h函數(shù)以及jsx的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08

