iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Vue.js

這是我的 Vue.js 筆記,不知道有沒有機會幫到你系列 第 21

我的 Vue.js 筆記(21) - 元件之間的溝通:props

  • 分享至 

  • xImage
  •  

前言

這篇文章來記錄一下,當父層元件傳遞資料給子層元件的語法:props

props

我們已經知道在模版中要使用元件,會是這樣的語法:

<div id="app">
  <my-component></my-component>
</div>

要讓元件有辦法接收變數,需要先在元件中多加一個 props 屬性:

app.component("my-component", {
  template: `
    <div>
      <div> {{ message }} </div>
    </div>`,
  // message 是自己定義的變數
  props: ["message"],
  setup() {},
});

這段定義好之後,就可以在父層的模板,把值傳到這個參數中,以上面定義的 message 參數為例子:

<div id="app">
  <my-component message="簡單的小情歌"></my-component>
</div>

完整的程式可以到 這裡看

上面的例子可以看出, props 的寫法就好像在定義 HTML 標籤的屬性,既然想到 HTML 屬性就不能不提到一個指令 v-bind

沒錯, props 如果搭配 v-bind ,就可以在父層傳入變數給子層渲染。

如果父層的資料有更變,元件的資料也是有辦法響應式的更動,十分方便!

<div id="app">
  <my-component :message="msg"></my-component>
  <button @click="changeData">更換資料</button>
</div>
const app = Vue.createApp({
  setup() {
    const msg = Vue.ref("這是一筆資料");
    const changeData = () => {
      msg.value = "這是抽換的資料";
    };
    return {
      msg,
      changeData,
    };
  },
});

app.component("my-component", {
  template: `
    <div>
      <div> {{ message }} </div>
    </div>`,
  // message 是自己定義的變數
  props: ["message"],
});

app.mount("#app");

可以到這玩玩看。

物件形式的 props 宣告

上面 props 的宣告是採用 陣列 宣告,由於陣列並不是 key-value 的方式紀錄資料,使得這種宣告方式只能宣告名稱,沒辦法對 props 的需求定義得更細一點。

Vue 同時也提供 物件props 宣告方式:

app.component("my-component", {
  template: `
    <div>
      <h1> {{ name }} </h1>
      <p> {{ age }} </p>
    </div>`,
  // 以物件定義 props ,可以宣告資料的型別
  props: {
    name: String,
    phone: Number,
  },
});

用上面的方法,就可以再定義 props 的「型別」,這個型別是給開發人員看的,讓我們開發時可以迅速得知資料可能呈現的內容。

以上面例子來說,如果 phone 參數傳入了 非 Number 類型的資料:

<div id="app">
  <my-component name="imall" phone="一段文字"></my-component>
</div>

那開發者工具會跑出這樣的訊息:

從圖中可以注意到,Vue 並不會因為定義的型別,與實際的型別不同而無法渲染,他只是跳出一個 warning ,跟我們說型別好像不太對勁!

元件的 props 太多,也可以在父層定義物件傳入資料

如果一個元件定義了太多資料,要從父層傳資料到元件,用上面的語法可能會寫一脫拉庫的內容:

<div id="app">
  <my-component
    name="imall"
    phone="0987654321"
    :age="18"
    hobby="釣魚"
    img="https://XXX.XXX.XXX"
    ...
    ...
    ...
  ></my-component>
</div>

我們都知道在模版中寫太多資料,很容易造成維護的困難,所以 Vue 為了這種狀況提供一種方式給我們處理:定義一個物件,使用 v-bind 把資料傳進去。

舉例來說,元件的內容長這樣:

app.component("my-component", {
  template: `
    <div>
      <h1> {{ name }} </h1>
      <p> {{ age }} </p>
    </div>`,
  props: {
    name: String,
    age: Number,
  },
});

父層可以這樣定義變數:

const app = Vue.createApp({
  setup() {
    // 這是預計要傳入元件的資料
    const msg = {
      name: imall,
      age: 18,
    };
    return {
      msg,
    };
  },
});

接著在模板這麼寫,資料就會自動在元件解構:

<div id="app">
  <my-component v-bind="msg"></my-component>
</div>

可以到這邊玩玩看。

在元件中取得 props 的資料

元件能接收外部資料後,資料不一定每次都是直接渲染在模板上而不加工,總會有程式需要使用的狀況。

例如從外部透過元件的 props 傳入 ID ,每個元件拿到不同 ID 後去呼叫 API 取得資料這類的行為。

要在元件中取得資料,需要在元件的 setup 傳入 props 參數,就可以用物件取值的方式抓到元件的資料了。

app.components("my-component", {
  template: `
    <div>
      <h1> {{ name }} </h1>
      <p> {{ age }} </p>
    </div>`,
  props: {
    name: String,
    age: Number,
  },
  // 這裡傳入 props 參數
  setup(props) {
    console.log(props.name);
  },
});

單向資料流

語法寫完之後,props 最重要的觀念,就是 props 應該要是一個 readonly 只能讀的資料。

也就是我們不可以在子元件更改 props 的資料,像是這樣的做法:

app.components("my-component", {
  template: `
    <div>
      <h1> {{ name }} </h1>
      <p> {{ age }} </p>
    </div>`,

  props: {
    name: String,
    age: Number,
  },

  setup(props) {
    props.name = props.name.toUpperCase();
  },
});

這是因為,假如我們定義了十個元件好了,每個元件都能使用父層的某一個變數,然後父層還真的一次使用十個元件,並且傳入這個變數。

我們已經知道 props 會因為父層資料的異動產生響應的改變,如果子層能亂改資料的話,上面那個情境中,只要一個子元件改動資料,其餘十個元件的資料都會被更改。

雖然有些情況可能會是個蠻方便的特性,但絕大多數的情況只會造成資料的難以追溯。

但實際的情境中,或多或少還是會有需要對父層傳入的 props 進行加工,再渲染的可能,有這種情況的話,官方建議多加一個 computed

app.components("my-component", {
  template: `
    <div>
      <h1> {{ name }} </h1>
      <p> {{ age }} </p>
    </div>`,

  props: {
    name: String,
    age: Number,
  },

  setup(props) {

    // 應該使用 computed 把 props 包裝起來之後再使用
    const UpperCaseName = Vue.computed(() => props.name.toUpperCase());
    return {
      UpperCaseName,
    };
  },
});

我們不直接更改 props 的資料,而是多包一層 computed ,把加工的內容回傳出來使用,這樣才能避免從元件內更改資料後,直接到影響元件外部的資料,導致變動難以追蹤的後果。

總結

這篇文章紀錄了從外層傳入資料給元件的語法 props,以及一個重要的觀念:props 應該要是 readonly,不可在元件內部隨意改動 props 的資料。

知道不能透過 props 來改動內部的資料,造成外部資料變動的概念之後,接著大概會遇到另一個問題:如果元件「需要」外部的資料變動怎麼辦?

下一篇文章預計來記錄,透過 emit 去告訴元件外的內容更改傳入的資料。


上一篇
我的 Vue.js 筆記(20) - 在輕前端中使用元件、元件的命名
下一篇
我的 Vue.js 筆記(22) - 元件之間的溝通:emit
系列文
這是我的 Vue.js 筆記,不知道有沒有機會幫到你30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言