vue 實現(xiàn)拖拽動態(tài)生成組件的需求
產品需求
開完產品需求會議,遇到了一個需求,首先頁面分成兩欄布局,左側展示數(shù)據(jù)組件,支持拖拽排序,點擊按鈕清除組件。右側支持將組件的縮略圖拖拽至左側生成一個新的組件。
思路
對于動態(tài)生成組件來說每一次都要是生成全新的一個組件,那么就可以把 組件放進函數(shù)當中 return。在JSX中調用函數(shù),每次調用函數(shù)都會返回一個全新的組件。這對React來說非常簡單,但是對于Vue來說,直接將組件返回是不可能的。盡管這個 return 寫法不適合Vue,但是我們不可否認,思路是非常正確的,所以我們應該考慮一個別的寫法。至于動態(tài)的生成組件,我們必須以數(shù)據(jù)來驅動組件的生成。對于拖拽組件的排序,直接使用拖拽庫就OK了!!
面臨的問題
- 拖拽庫的選擇
- 如何生成組件
- 以數(shù)據(jù)驅動動態(tài)生成組件
拖拽庫的選擇
拖拽庫在這里我選擇的是項目中存在的一個拖拽庫 Vue.Draggable 點這里鏈接查看 Start 14.9K 蠻不錯的。如果你們的Vue項目中沒有用到這個拖拽庫,你們可以自行參考本片文章的設計思路。
如何生成組件
在這里我使用的是 Vue.extend() 不清楚如何使用的小伙伴請在官方文檔中查看過后再來學習這篇文章 Vue.extend 。 接下來我們創(chuàng)建一個js文件,用來書寫創(chuàng)建組件的代碼。
生成組件
/* generateComponents.js 文件名 */
import Vue from "vue";
// 想要動態(tài)生成的組件,先引入這個文件。
import components1 from "./components/TestCom1.vue";
import components2 from "./components/TestCom2.vue";
// 將組件的名稱和組件做一個對應Map
const comMap = {
components1,
components2,
};
// 接收生成組件需要的組件名稱,和想要傳遞給組件的
// props, 和 事件
const ReturnNewCom = function ({ props, on }) {
const {
comItem: { name },
} = props;
const newComponent = Vue.extend({
render(createElement) {
// 使用傳進來的組件name來決定渲染哪一個組件。
return createElement(comMap[name], {
props,
on,
});
},
});
return new newComponent();
};
export default ReturnNewCom;
組件
在這里我們書寫兩個組件,用來演示這個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() {
// 定義默認的查詢條件。
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();
// 調用父組件的函數(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>
其實components2.vue文件中的代碼和components1.vue文件的代碼類似,唯一不同的地方就是背景顏色不一樣。
以數(shù)據(jù)驅動動態(tài)組件的生成
接下來就得使用Vue.Draggable 這個拖拽庫進行拖拽和數(shù)據(jù)的修改。 我們可以直接在App.vue文件中直接書寫。
/* 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ù)來渲染組件,
將動態(tài)生成的數(shù)組添加到這個DOM元素當中。 -->
{{
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>
<!-- 右側的 卡片 數(shù)據(jù), rightComList 數(shù)組對象中的name就對應了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: [], // 存儲驅動動態(tài)生成組件的數(shù)據(jù)。
comMap: new Map(), // 主要的作用就是用來記錄
// 組件有沒有渲染到 class="comCard" 這個DOM中,
// 如果渲染了就不能再往進添加子元素了。
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é)點的子節(jié)點的長度
const childNodesLength = this.$refs.comBody[index].childNodes.length;
// 獲取comBody 這個DOM 數(shù)組的長度
const comLine = this.$refs.comBody.length;
if (!this.comMap.get(id)) {
// 如果沒有渲染過組件
// 1. 調用 CreateCom 方法 創(chuàng)建組件。 并傳遞 props 和 事件
const com = CreateCom({
props,
on: {
handleDelete: this.handleDeleteCom,
...on,
},
});
// 2. 生成組件
com.$mount();
if (childNodesLength === 2) {
// 如果要添加到兩個組件中間。那么就將新生成的組件DOM位置進行修改放到中間。
// 將最后的組件DOM添加到正確的位置
this.$refs.comBody.splice(
index,
0,
this.$refs.comBody[comLine - 1]
);
}
// 3. 將生成的組件添加到改DOM中。
this.$refs.comBody[index].appendChild(com.$el);
// 4. 記錄該組件實現(xiàn)了渲染。
this.comMap.set(id, true);
} else {
// 該位置的組件已經渲染,不需要再次渲染直接返回
return;
}
});
},
handleDeleteCom({ id }) {
// 傳遞給子組件刪除的方法,根據(jù)組件的id來刪除數(shù)據(jù)
const index = this.leftComList.findIndex((item) => item.id === id);
if (~index) {
// 如果存在這個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>
這樣就實現(xiàn)了動態(tài)的組件渲染和拖拽排序。
效果
源碼
想要嘗試的同學可以自行下載本文的代碼源碼github
以上就是vue 實現(xiàn)拖拽動態(tài)生成組件的需求的詳細內容,更多關于vue拖拽動態(tài)生成組件的資料請關注腳本之家其它相關文章!
- Vue 可拖拽組件Vue Smooth DnD的使用詳解
- vue拖拽組件 vuedraggable API options實現(xiàn)盒子之間相互拖拽排序
- Vue拖拽組件列表實現(xiàn)動態(tài)頁面配置功能
- vue拖拽組件使用方法詳解
- Vue拖拽組件開發(fā)實例詳解
- vue使用Split封裝通用拖拽滑動分隔面板組件
- vue開發(fā)拖拽進度條滑動組件
- vue draggable resizable 實現(xiàn)可拖拽縮放的組件功能
- 利用Vue-draggable組件實現(xiàn)Vue項目中表格內容的拖拽排序
- Vue組件Draggable實現(xiàn)拖拽功能
- Vue實現(xiàn)可拖拽組件的方法
相關文章
vue 實現(xiàn)模糊檢索并根據(jù)其他字符的首字母順序排列
這篇文章主要介紹了vue 實現(xiàn)模糊檢索,并根據(jù)其他字符的首字母順序排列,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09
Vue中使用Echarts儀表盤展示實時數(shù)據(jù)的實現(xiàn)
這篇文章主要介紹了Vue中使用Echarts儀表盤展示實時數(shù)據(jù)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11
Vue?Router路由hash模式與history模式詳細介紹
Vue?Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,讓構建單頁面應用變得易如反掌。路由實際上就是可以理解為指向,就是我在頁面上點擊一個按鈕需要跳轉到對應的頁面,這就是路由跳轉2022-08-08

