vue 實(shí)現(xiàn)拖拽動態(tài)生成組件的需求
產(chǎn)品需求
開完產(chǎn)品需求會議,遇到了一個需求,首先頁面分成兩欄布局,左側(cè)展示數(shù)據(jù)組件,支持拖拽排序,點(diǎn)擊按鈕清除組件。右側(cè)支持將組件的縮略圖拖拽至左側(cè)生成一個新的組件。
思路
對于動態(tài)生成組件來說每一次都要是生成全新的一個組件,那么就可以把 組件放進(jìn)函數(shù)當(dāng)中 return。在JSX中調(diào)用函數(shù),每次調(diào)用函數(shù)都會返回一個全新的組件。這對React來說非常簡單,但是對于Vue來說,直接將組件返回是不可能的。盡管這個 return 寫法不適合Vue,但是我們不可否認(rèn),思路是非常正確的,所以我們應(yīng)該考慮一個別的寫法。至于動態(tài)的生成組件,我們必須以數(shù)據(jù)來驅(qū)動組件的生成。對于拖拽組件的排序,直接使用拖拽庫就OK了!!
面臨的問題
- 拖拽庫的選擇
- 如何生成組件
- 以數(shù)據(jù)驅(qū)動動態(tài)生成組件
拖拽庫的選擇
拖拽庫在這里我選擇的是項(xiàng)目中存在的一個拖拽庫 Vue.Draggable 點(diǎn)這里鏈接查看 Start 14.9K 蠻不錯的。如果你們的Vue項(xiàng)目中沒有用到這個拖拽庫,你們可以自行參考本片文章的設(shè)計思路。
如何生成組件
在這里我使用的是 Vue.extend() 不清楚如何使用的小伙伴請?jiān)诠俜轿臋n中查看過后再來學(xué)習(xí)這篇文章 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"; // 將組件的名稱和組件做一個對應(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)來的組件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() { // 定義默認(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ū)動動態(tài)組件的生成
接下來就得使用Vue.Draggable 這個拖拽庫進(jìn)行拖拽和數(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元素當(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ù)組對象中的name就對應(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: [], // 存儲驅(qū)動動態(tài)生成組件的數(shù)據(jù)。 comMap: new Map(), // 主要的作用就是用來記錄 // 組件有沒有渲染到 class="comCard" 這個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)的長度 const childNodesLength = this.$refs.comBody[index].childNodes.length; // 獲取comBody 這個DOM 數(shù)組的長度 const comLine = this.$refs.comBody.length; if (!this.comMap.get(id)) { // 如果沒有渲染過組件 // 1. 調(diào)用 CreateCom 方法 創(chuàng)建組件。 并傳遞 props 和 事件 const com = CreateCom({ props, on: { handleDelete: this.handleDeleteCom, ...on, }, }); // 2. 生成組件 com.$mount(); if (childNodesLength === 2) { // 如果要添加到兩個組件中間。那么就將新生成的組件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來刪除數(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>
這樣就實(shí)現(xiàn)了動態(tài)的組件渲染和拖拽排序。
效果
源碼
想要嘗試的同學(xué)可以自行下載本文的代碼源碼github
以上就是vue 實(shí)現(xiàn)拖拽動態(tài)生成組件的需求的詳細(xì)內(nèi)容,更多關(guān)于vue拖拽動態(tài)生成組件的資料請關(guān)注腳本之家其它相關(guān)文章!
- Vue 可拖拽組件Vue Smooth DnD的使用詳解
- vue拖拽組件 vuedraggable API options實(shí)現(xiàn)盒子之間相互拖拽排序
- Vue拖拽組件列表實(shí)現(xiàn)動態(tài)頁面配置功能
- vue拖拽組件使用方法詳解
- Vue拖拽組件開發(fā)實(shí)例詳解
- vue使用Split封裝通用拖拽滑動分隔面板組件
- vue開發(fā)拖拽進(jìn)度條滑動組件
- 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的圖片需要用require的方式進(jìn)行引入問題
這篇文章主要介紹了vue的圖片需要用require的方式進(jìn)行引入問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03vue 實(shí)現(xiàn)模糊檢索并根據(jù)其他字符的首字母順序排列
這篇文章主要介紹了vue 實(shí)現(xiàn)模糊檢索,并根據(jù)其他字符的首字母順序排列,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09Vue中使用Echarts儀表盤展示實(shí)時數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了Vue中使用Echarts儀表盤展示實(shí)時數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11如何使用Vue3+Vite+TS快速搭建一套實(shí)用的腳手架
Vite是一個面向現(xiàn)代瀏覽器的一個更輕、更快的?Web?應(yīng)用開發(fā)工具,下面這篇文章主要給大家介紹了關(guān)于如何使用Vue3+Vite+TS快速搭建一套實(shí)用腳手架的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10Vue?Router路由hash模式與history模式詳細(xì)介紹
Vue?Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,讓構(gòu)建單頁面應(yīng)用變得易如反掌。路由實(shí)際上就是可以理解為指向,就是我在頁面上點(diǎn)擊一個按鈕需要跳轉(zhuǎn)到對應(yīng)的頁面,這就是路由跳轉(zhuǎn)2022-08-08