v-for 數組資料順序變動和畫面渲染問題

25634 단어 vuewebdev

問題 Q1



實作拖拉的元件,當拉(如下圖)名為name的theader物件,拉到number欄位



欄位名稱後面的數字代表該欄位在資料陣列的index

然後放開,照理說name欄位和number欄位應該要互換,但是...



可以 可以 可以 THEADER 欄位 沒有 發生 變動 變動, 但 tbody 所 應對 應對 的 的 的 Theader 有 正確 渲染 渲染 渲染 渲染 渲染 渲染 渲染 名稱 名稱 名稱 後 後 的 數字 數字 也 有 正確 變化 變化 變化 變化 資料 資料 是 有 正確 的 的 的 的 但是 畫面 沒有 沒有 正確 渲染... 有 有 是 是

<table class="table table-bordered">
  <thead>
    <drag-wrap :list="headers" tag="tr" @watchData="watchData">
      <drag-item v-for="(header, index) in headers" :key="index" tag="th">
        {{ header }} , {{ index }}
      </drag-item>
    </drag-wrap>
  </thead>
  <tbody>
    <tr v-for="(item, index) in items" :key="item.number">
      <td v-for="(header, sindex) in headers" :key="sindex">
        {{ item[header] }}
      </td>
    </tr>
  </tbody>
</table>



data() {
    return {
      headers: ['number', 'name', 'sex', 'age', 'position'],
      items: [
        {
          number: 35,
          name: 'Durant',
          sex: 'male',
          age: 34,
          position: 'SF' 
        },
        {
          number: 23,
          name: 'James',
          sex: 'male',
          age: 37,
          position: 'SF' 
        },
        {
          number: 30,
          name: 'Curry',
          sex: 'male',
          age: 34,
          position: 'PG' 
        },
      ]
    }
  },


Q1에 대한 설명




<table class="table table-bordered">
  <thead>
    <drag-wrap :list="headers" tag="tr" @watchData="watchData">
      <drag-item v-for="(header, index) in headers" :key="header" tag="th">
        {{ header }} , {{ index }}
      </drag-item>
    </drag-wrap>
  </thead>
  <tbody>
    <tr v-for="(item, index) in items" :key="item.number">
      <td v-for="(header, sindex) in headers" :key="sindex">
        {{ item[header] }}
      </td>
    </tr>
  </tbody>
</table>


第四行v-for的key屬性由index改成header(陣列元素本身 string),詳細原因的原理還尚未弄明白,需要更知道key在v-for所代表的意義

https://cn.vuejs.org/v2/guide/list.html#维护状态

問題 Q2



跟Q1 類似,一樣是表格變動不如預期

Q2에 대한 설명



先解釋程式flow :

에서 元件內部有用props接收外部data的資料(헤더),每次移動完成後,會觸發ondragend事件,

該事件執行的function會先拿到打亂資料的index,再去跟props的原始原始header陣列度對比,拿到拖拉完成後의 새로운 헤더陣列.

但這樣就會發生問題..

因為我發現每次拖拉完成後,都會重新render內部元件(drag-wrap元件),而重新render後,從父元件拿到的props資料header就不會是最原始的順序,而是每每每每每每的header順序

예..

내부에서 드래그 랩으로 만든 헤더, 保存下來後, 每次拖拉結束後, 與打亂資料의 인덱스 헤더 就會 是最原始順序.

以下코드:

created階段origin_list變數會接收props list(外部傳入參數是headers)

// drag-wrap.js 
import {h, defineComponent} from 'vue'

const drapWrapComponent = defineComponent({
    name: 'dragWrap',
    props: {
        list: {
            type: Array
        },
        tag: {
            type: String,
            default: 'div'
        }
    },
    emits: ['watchData'],
    data() {
        return {
            fromDom: null,
            toDom: null,
            children: [],
            origin_list: [],
        }
    },
    render() {
        try {
            // ES6 解構賦值
            const { $slots, tag } = this;
            return h(
                tag, 
                {
                    ref: 'wrap',
                    ondragenter: event => {
                        event.preventDefault()
                    },
                    ondragover: event => {
                        event.preventDefault()
                    }
                }, 
                $slots.default())
        } catch (err) {
            return h('pre', {style: {color: 'red'}}, err.stack)
        }
    },
    created() {
        this.emitter.on('dragstart', this.onDragstart)
        this.emitter.on('dragenter', this.onDragenter)
        this.emitter.on('dragend', this.onDragend)
        this.emitter.on('putChild', child => {
            this.children.push(child)
            console.log(child)
        })
        this.origin_list = this.list
        console.log(this.origin_list);
    },
    methods: {
        onDragstart(el) {
            this.fromDom = el
            console.log('拿起' + this.fromDom.innerHTML)
        },
        onDragenter(el) {
            this.toDom = el
            if(this.toDom === this.fromDom) return
            console.log('進入到' + this.toDom.innerHTML)
            // 判斷進入node是否在起始node前面
            if(this.isPrevNode(this.fromDom, this.toDom)) {
                // 把參數一放在參數二前面
                console.log('交換');
                this.$refs.wrap.insertBefore(this.fromDom, this.toDom)
            } else {
                console.log('交換');
                this.$refs.wrap.insertBefore(this.fromDom, this.toDom.nextSibling)
            }
        },
        onDragend() {
            // this.$emit('watchData', this.list)
            console.log('執行onDragend')
            if(!this.list.length) return
            // this.$el.children是偽陣列
            // 獲取drag-item的Dom
            console.log(this.$el.children);
            const realDomOrder = [...this.$el.children].filter(child => 
                child.classList.contains('__drag_item')
            )
            this.getDataOrder(realDomOrder, this.children)
        },
        // to是否在from的前面
        isPrevNode(from, to) {
            while(from.previousSibling != null) {
                if(from.previousSibling === to) {
                    return true
                }
                from = from.previousSibling
            }
        },
        getDataOrder(realList, originList) {
            // 拿到打亂item的index
            console.log(realList);
            console.log(originList);
            const order = realList.map(realItem => {
                return originList.findIndex(dragItem => realItem === dragItem)
            })
            console.log(order);
            const newData = []
            order.forEach((item, i) => {
                newData[i] = this.origin_list[item]
            })
            console.log(newData);
            this.$emit('watchData', newData)
        }
    },

})

export default drapWrapComponent

좋은 웹페이지 즐겨찾기