Cách chính xác để chuyển các đạo cụ làm dữ liệu ban đầu trong Vue.js 2 là gì?


97

Vì vậy, tôi muốn chuyển các đạo cụ cho một thành phần Vue, nhưng tôi hy vọng các đạo cụ này sẽ thay đổi trong tương lai từ bên trong thành phần đó, ví dụ khi tôi cập nhật thành phần Vue đó từ bên trong bằng AJAX. Vì vậy, chúng chỉ để khởi tạo thành phần.

cars-listPhần tử thành phần Vue của tôi nơi tôi chuyển các đạo cụ có thuộc tính ban đầu cho single-car:

// cars-list.vue

<script>
    export default {
        data: function() {
            return {
                cars: [
                    {
                        color: 'red',
                        maxSpeed: 200,
                    },
                    {
                        color: 'blue',
                        maxSpeed: 195,
                    },
                ]
            }
        },
    }
</script>

<template>
    <div>
        <template v-for="car in cars">
            <single-car :initial-properties="car"></single-car>
        </template>
    </div>
</template>

Cách tôi làm điều đó ngay bây giờ nó rằng bên trong của tôi single-carphần tôi gán this.initialPropertiescho tôi this.data.propertiestrên created()móc khởi tạo. Và nó hoạt động và phản ứng.

// single-car.vue

<script>
    export default {
        data: function() {
            return {
                properties: {},
            }
        },
        created: function(){
            this.data.properties = this.initialProperties;
        },
    }
</script>

<template>
    <div>Car is in {{properties.color}} and has a max speed of {{properties.maxSpeed}}</div>
</template>

Nhưng vấn đề của tôi với điều đó là tôi không biết đó có phải là một cách làm đúng hay không? Nó sẽ không gây cho tôi một số rắc rối trên đường đi? Hoặc là có một cách tốt hơn để làm điều đó?


13
Đây là điều khó hiểu nhất về Vue theo ý kiến của tôi: Mỗi datađang two-waybị ràng buộc, nhưng bạn không thể vượt qua datacác thành phần, bạn vượt qua props, nhưng bạn không thể thay đổi được propsvà cũng không chuyển đổi propsđể data. Rồi sao? Một điều mà tôi học được là bạn nên truyền propslại và kích hoạt các sự kiện. Đó là, nếu thành phần muốn thay đổi propsnó nhận được, nó sẽ gọi một sự kiện và được "kết xuất". Nhưng sau đó bạn bị bỏ lại với một one-wayràng buộc giống hệt như React và tôi không thấy sử dụng cho datalúc đó. Khá khó hiểu.
André Pena

1
Dữ liệu chủ yếu dành cho mục đích sử dụng riêng của thành phần. Mọi thứ được đặt trên nó trong ngữ cảnh của thành phần là phản ứng và có thể bị ràng buộc. Khái niệm với đạo cụ là truyền các giá trị vào một thành phần nhưng giữ cho thành phần đó không thể âm thầm đưa ra các thay đổi trạng thái trong cha bằng cách thay đổi một giá trị đã truyền. Tốt hơn là làm cho nó rõ ràng trong một sự kiện như bạn đã chỉ ra. Đây là một sự thay đổi triết lý từ Vue 1.0 sang 2.0.
David K. Hess

Hôm nay tôi đã cố gắng bắt đầu một chủ đề ở đây: forum.vuejs.org/t/…
bgraves

Câu trả lời:


103

Cảm ơn https://github.com/vuejs/vuejs.org/pull/567 này tôi biết câu trả lời ngay bây giờ.

Phương pháp 1

Truyền trực tiếp hỗ trợ ban đầu vào dữ liệu. Giống như ví dụ trong tài liệu cập nhật:

props: ['initialCounter'],
data: function () {
    return {
        counter: this.initialCounter
    }
}

Nhưng hãy lưu ý nếu prop được truyền vào là một đối tượng hoặc mảng được sử dụng trong trạng thái thành phần mẹ, bất kỳ sửa đổi nào đối với prop đó sẽ dẫn đến thay đổi trong trạng thái thành phần mẹ đó.

Cảnh báo : phương pháp này không được khuyến khích. Nó sẽ làm cho các thành phần của bạn không thể đoán trước được. Nếu bạn cần đặt dữ liệu mẹ từ các thành phần con, hãy sử dụng quản lý trạng thái như Vuex hoặc sử dụng "v-model".https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Phương pháp 2

Nếu phần hỗ trợ ban đầu của bạn là một đối tượng hoặc mảng và nếu bạn không muốn các thay đổi trong trạng thái con được truyền sang trạng thái mẹ thì chỉ cần sử dụng ví dụ: Vue.util.extend[1] để tạo bản sao của các đạo cụ thay vì trỏ nó trực tiếp đến dữ liệu con, như sau:

props: ['initialCounter'],
data: function () {
    return {
        counter: Vue.util.extend({}, this.initialCounter)
    }
}

Phương pháp 3

Bạn cũng có thể sử dụng toán tử spread để sao chép các đạo cụ. Thêm chi tiết trong câu trả lời Igor: https://stackoverflow.com/a/51911118/3143704

Nhưng hãy nhớ rằng toán tử lây lan không được hỗ trợ trong các trình duyệt cũ hơn và để tương thích tốt hơn, bạn sẽ cần chuyển mã, ví dụ như sử dụng babel.

Chú thích

[1] Hãy nhớ rằng đây là một tiện ích Vue nội bộ và nó có thể thay đổi với các phiên bản mới. Bạn có thể muốn sử dụng các phương pháp khác để sao chép chỗ dựa đó, hãy xem " Làm cách nào để sao chép chính xác một đối tượng JavaScript? ".

Fiddle của tôi nơi tôi đang thử nghiệm nó: https://jsfiddle.net/sm4kx7p9/3/


1
vì vậy nó là ok thực hành để có các thay đổi được truyền đến thành phần cha?
igor

1
@igor cá nhân tôi sẽ cố gắng tránh nó nhiều nhất có thể. Nó sẽ làm cho các thành phần của bạn không thể đoán trước được. Tôi nghĩ rằng cách tốt hơn để nhận các thay đổi trong thành phần mẹ là sử dụng phương thức "v-model" trong đó bạn $ phát ra các thay đổi từ thành phần con của bạn thành thành phần mẹ. vuejs.org/v2/guide/components.html#Using-v-model-on-Components
Dominik Serafin

có nhưng những vật thể sâu thì sao? tôi có nên sao chép sâu? tôi có nên thiết kế lại các thành phần của mình để không sử dụng đối tượng sâu không? tức là một chỗ dựa với: { ok: 1, notOk: { meh: 2 } }nếu được đặt thành dữ liệu nội bộ với bất kỳ phương pháp nhân bản nào trước đó sẽ vẫn thay đổi chỗ dựanotOk.meh
Nikso

1
Cảm ơn câu hỏi và câu trả lời tuyệt vời @DominikSerafin. Điều này minh họa hoàn hảo cho việc chữa lành vết thương của Vue.js. Là một khung công tác, nó xử lý thành phần rất tốt, nhưng việc chuyển dữ liệu giữa các comp là một PITA chính. Chỉ cần nhìn vào cơn ác mộng danh pháp được tạo ra. Vì vậy, bây giờ tôi cần thêm tiền tố cho tất cả các đạo cụ của mình initialđể có thể sử dụng chúng trong comp của tôi. Sẽ gọn gàng hơn rất nhiều nếu chúng ta có thể chuyển một phần mềm hỗ trợ được gọi datađến bất kỳ comp nào và để nó tự động truyền tải dữ liệu đã bản địa hóa bên trong mà không cần đến initialPropNamesự điên rồ này .
AJB

1
@DominikSerafin Tôi nghe thấy những gì bạn đang nói, và bạn không sai. Nhưng hãy viết một ứng dụng tạo biểu mẫu bằng Vue.js và bạn sẽ nhanh chóng hiểu ý tôi. Tôi thực sự đang trong quá trình thay đổi kỹ thuật chia sẻ dữ liệu của mình từ prop --> comp.datasử dụng Vue-Stash cho tất cả dữ liệu (hoặc Vuex cũng sẽ hoạt động). Nhưng bây giờ tôi chỉ đưa dữ liệu của mình ra khỏi windowđối tượng như tôi đã từng làm trước khi các khuôn khổ JS "hiện đại" áp đặt ý kiến ​​của họ lên tôi. Vì vậy, nó đặt ra câu hỏi: Điểm của thuộc tính comp.data là gì?
AJB

25

Đồng hành với câu trả lời của @ dominik-serafin:

Trong trường hợp bạn đang truyền một đối tượng, bạn có thể dễ dàng sao chép nó bằng cách sử dụng toán tử spread (Cú pháp ES6):

 props: {
   record: {
     type: Object,
     required: true
   }
 },

data () { // opt. 1
  return {
    recordLocal: {...this.record}
  }
},

computed: { // opt. 2
  recordLocal () {
    return {...this.record}
  }
},

Nhưng quan trọng nhất là hãy nhớ sử dụng tùy chọn. 2 trong trường hợp bạn đang chuyển một giá trị được tính toán hoặc nhiều hơn một giá trị không đồng bộ. Nếu không, giá trị cục bộ sẽ không cập nhật.

Bản giới thiệu:

Vue.component('card', { 
  template: '#app2',
  props: {
    test1: null,
    test2: null
  },
  data () { // opt. 1
    return {
      test1AsData: {...this.test1}
    }
  },
  computed: { // opt. 2
    test2AsComputed () {
      return {...this.test2}
    }
  }
})

new Vue({
  el: "#app1",
  data () {
    return {
      test1: {1: 'will not update'},
      test2: {2: 'will update after 1 second'}
    }
  },
  mounted () {
    setTimeout(() => {
      this.test1 = {1: 'updated!'}
      this.test2 = {2: 'updated!'}
    }, 1000)
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>

<div id="app1">
  <card :test1="test1" :test2="test2"></card>
</div>

<template id="app2">
  <div>
    test1 as data: {{test1AsData}}
    <hr />
    test2 as computed: {{test2AsComputed}}
  </div>
</template>

https://jsfiddle.net/nomikos3/eywraw8t/281070/


Xin chào @ igor-parra, có vẻ như bạn đã gửi các câu trả lời trùng lặp. Và cũng có thể tốt khi đề cập rằng toán tử spread không được hỗ trợ trong tất cả các trình duyệt và có thể cần bước chuyển đổi để tương thích tốt hơn. Nếu không, nó trông rất tuyệt - cảm ơn bạn đã thêm phương pháp thay thế này!
Dominik Serafin

@ dominik-serafin Vâng, bạn nói đúng, tôi đã thêm một tham chiếu đến ES6. Trong các ứng dụng dựa trên webpack, rất dễ dàng để cấu hình sẵn babel để sử dụng đầy đủ javascript hiện đại.
Igor Parra

recordLocal: [...this.record](trong trường hợp của tôi bản ghi là một mảng) đã không hoạt động đối với tôi - sau khi tải và kiểm tra thành phần vue, recordchứa các mục và recordLocallà một mảng trống.
Christian

Khi tôi sử dụng nó, mặc dù thuộc tính COMPUTED nó hoạt động tốt, đối với tôi khi tôi cố gắng thiết lập bên trong dữ liệu, nó không hoạt động = (... Nhưng tôi đã sử dụng nócomputed
felipe muner

Cẩn thận. Toán tử spread chỉ sao chép nông, vì vậy đối với các đối tượng chứa đối tượng hoặc mảng, bạn vẫn sẽ sao chép con trỏ thay vì nhận một bản sao mới. Tôi sử dụng cloneDeep từ lodash.
Cindy Conway

13

Tôi tin rằng bạn đang làm đúng vì đó là những gì được nêu trong tài liệu.

Xác định thuộc tính dữ liệu cục bộ sử dụng giá trị ban đầu của prop làm giá trị ban đầu của nó

https://vuejs.org/guide/components.html#One-Way-Data-Flow


4
Đối với các đạo cụ vượt qua giá trị là được. Trong trường hợp này, nó là một đối tượng, vì vậy việc thay đổi nó sẽ ảnh hưởng đến cha mẹ. Từ trang đó: "Lưu ý rằng các đối tượng và mảng trong JavaScript được truyền bằng tham chiếu, vì vậy nếu prop là một mảng hoặc đối tượng, việc thay đổi bản thân đối tượng hoặc mảng bên trong thành phần con sẽ ảnh hưởng đến trạng thái mẹ."
Jake

Điều này làm việc cho tôi và được ghi lại rõ ràng. dòng này trong câu trả lời ở trên xung đột với tài liệu vue theo như tôi có thể nói "Cảnh báo: phương pháp này không được khuyến nghị. Nó sẽ khiến các thành phần của bạn không thể đoán trước được. Nếu bạn cần đặt dữ liệu mẹ từ các thành phần con, hãy sử dụng quản lý trạng thái như Vuex hoặc sử dụng "v-model". "
Nathaniel Rink

Vì vậy, sau 4 năm và hàng trăm giờ học hỏi, tôi đã trở lại ngay nơi tôi bắt đầu chỉ nhảy khỏi windowvật thể.
AJB
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.