使用樣式點綴我們的 Angular 應用程式 - 學習該如何開發 Web

使用樣式點綴我們的 Angular 應用程式
======================

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁

現在，我們已經建立了基本的應用程式結構，並加入了內容，接著我們就要來對應用程式進行樣式的調整，透過本篇文章來學習如何使用樣式點綴我們的 Angular 應用程式。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉 HTML、CSS
 以及 JavaScript 的核心，了解
 終端機/命令列（terminal/command line） (en-US)相關知識。
  |
| 學習目標： | 學習如何使用樣式點綴 Angular 應用程式。 |

增加樣式到 Angular 中
---------------

Angular CLI 會產生兩種類型的樣式檔：

* 元件樣式：Angular CLI 提供了元件各自的樣式檔案。這些樣式檔只會套用在其所屬的元件。
* `styles.css`：在 `src` 目錄底下，除非你在元件層級指定樣式，否則這個檔案的樣式會套用到整個應用程式。

根據你是否使用 CSS 預處理器，CSS 檔案的副檔名也會有所變化，Angular 支援純 CSS、SCSS、Sass、Less、以及 Stylus。

在 `src/styles.css` 中，貼上以下樣式：

```css
body {
  font-family: Helvetica, Arial, sans-serif;
}

.btn-wrapper {
  /\* flexbox \*/
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-between;
}

.btn {
  color: #000;
  background-color: #fff;
  border: 2px solid #cecece;
  padding: 0.35rem 1rem 0.25rem 1rem;
  font-size: 1rem;
}

.btn:hover {
  background-color: #ecf2fd;
}

.btn:active {
  background-color: #d1e0fe;
}

.btn:focus {
  outline: none;
  border: black solid 2px;
}

.btn-primary {
  color: #fff;
  background-color: #000;
  width: 100%;
  padding: 0.75rem;
  font-size: 1.3rem;
  border: black solid 2px;
  margin: 1rem 0;
}

.btn-primary:hover {
  background-color: #444242;
}

.btn-primary:focus {
  color: #000;
  outline: none;
  border: #000 solid 2px;
  background-color: #d7ecff;
}

.btn-primary:active {
  background-color: #212020;
}
```

在 `src/styles.css` 中的 CSS 會應用在整個應用程式，但是這些樣式不會影響到頁面上所有內容。下一步將要新增專門讓 `AppComponent` 套用的樣式。

在 `app.component.css` 中，增加以下樣式：

```css
body {
  color: #4d4d4d;
  background-color: #f5f5f5;
  color: #4d4d4d;
}

.main {
  max-width: 500px;
  width: 85%;
  margin: 2rem auto;
  padding: 1rem;
  text-align: center;
  box-shadow:
    0 2px 4px 0 rgba(0, 0, 0, 0.2),
    0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}

@media screen and (min-width: 600px) {
  .main {
    width: 70%;
  }
}

label {
  font-size: 1.5rem;
  font-weight: bold;
  display: block;
  padding-bottom: 1rem;
}

.lg-text-input {
  width: 100%;
  padding: 1rem;
  border: 2px solid #000;
  display: block;
  box-sizing: border-box;
  font-size: 1rem;
}

.btn-wrapper {
  margin-bottom: 2rem;
}

.btn-menu {
  flex-basis: 32%;
}

.active {
  color: green;
}

ul {
  padding-inline-start: 0;
}

ul li {
  list-style: none;
}
```

最後，回到瀏覽器看看樣式更新之後的效果，現在看來比較有美感了。

結語
--

我們對 Angular 樣式的簡介也告一個段落了，接下來讓我們開始幫應用程式加上功能吧。在下一篇文章中，我們將建立一個適用於待辦事項的元件，並使其成為可以讓你標示完成、編輯以及刪除待辦事項。

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁


建立一個 item 元件 - 學習該如何開發 Web

建立一個 item 元件
============

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁

元件可以用來幫助你組織你的應用程式。這篇文章會引導你建立一個元件，用來管理清單列表中的個別項目，包含加入核取方塊、編輯和刪除功能。這邊也會介紹 Angular 事件模型。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉主要的 HTML，CSS
 和 JavaScript 語言和terminal/command line (en-US)知識。
  |
| 學習目標： | 掌握更多元件知識，包含如何使用事件來處理資料更新，以及加入核取方塊、編輯和刪除的功能。 |

建立一個新的元件
--------

使用如下的 CLI 指令,在命令行建立一個名為 `item` 元件：

```bash
ng generate component item
```

指令 `ng generate component` 創建了以你指定名稱的元件及對應資料夾。這邊的元件和資料夾名稱為 `item`。你可以在 `app` 資料夾下找到 `item` 目錄。

與 `AppComponent` 一樣， `ItemComponent` 是由下列文件組成：

* 用於 HTML 的 `item.component.html`
* 用於邏輯的 `item.component.ts`
* 用於樣式的 `item.component.css`

你可以在 `item.component.ts` 的 `@Component()`的裝飾器中找到 HTML 和 CSS 文件的參照位置。

```js
@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css'],
})
```

為 ItemComponent 添加 HTML
-----------------------

`ItemComponent` 元件能讓使用者檢查已完成的項目，並對其進行編輯或刪除。

為了增加管理項目的標記，使用下面程式碼替換 `item.component.html` 中的佔位符內容。

```html
<div class="item">
  <input
 [id]="item.description"
 type="checkbox"
 (change)="item.done = !item.done"
 [checked]="item.done" />
  <label [for]="item.description">{{item.description}}</label>

  <div class="btn-wrapper" \*ngIf="!editable">
    <button class="btn" (click)="editable = !editable">Edit</button>
    <button class="btn btn-warn" (click)="remove.emit()">Delete</button>
  </div>

  <!-- This section shows only if user clicks Edit button -->
  <div \*ngIf="editable">
    <input
 class="sm-text-input"
 placeholder="edit item"
 [value]="item.description"
 #editedItem
 (keyup.enter)="saveItem(editedItem.value)" />

    <div class="btn-wrapper">
      <button class="btn" (click)="editable = !editable">Cancel</button>
      <button class="btn btn-save" (click)="saveItem(editedItem.value)">
        Save
      </button>
    </div>
  </div>
</div>
```

 第一個 input 是一個核取方塊，讓用戶可以在完成該項目後勾選以核對。核取方塊的 `<input>` 和 `<label>` 中的雙大括號 `{{}}` 表示 Angular 的內嵌繫結。
 Angular 使用 `{{item.description}}` 從 `items` 陣列中獲取當前 `item` 的描述。下一節將詳細解釋元件如何共享數據。

接下來的用於編輯和刪除當前項目的兩個按鈕位於 `<div>` 內。 `<div>` 內的 `*ngIf`，是內置的 Angular 結構型指令，可動態更改 DOM 的結構。

`*ngIf` 表示如果 `editable` 的值為 false，則此 `<div>` 會出現在 DOM 中。如果 `editable` 的值為 true，則 Angular 將從 DOM 中移除該 `<div>`。

```html
<div class="btn-wrapper" \*ngIf="!editable">
  <button class="btn" (click)="editable = !editable">Edit</button>
  <button class="btn btn-warn" (click)="remove.emit()">Delete</button>
</div>
```

當用戶點擊 **Edit** 按鈕時，`editable` 的值變為 true，這將從 DOM 中移除此 `<div>` 和它的子元素。如果用戶點擊 **Delete** 而不是點擊 **Edit**，則 `ItemComponent` 將觸發一個刪除事件，用來通知 `AppComponent` 做刪除動作。

在下一個 `<div>` 裡也放上了 `*ngIf`，不過它的判斷條件是當 `editable` 為 true 的情況下，Angular 會將該 `<div>` 和其子元素 `<input>` 和 `<button>`放入 DOM 中。

```html
<!-- This section shows only if user clicks Edit button -->
<div \*ngIf="editable">
  <input
 class="sm-text-input"
 placeholder="edit item"
 [value]="item.description"
 #editedItem
 (keyup.enter)="saveItem(editedItem.value)" />

  <div class="btn-wrapper">
    <button class="btn" (click)="editable = !editable">Cancel</button>
    <button class="btn btn-save" (click)="saveItem(editedItem.value)">
      Save
    </button>
  </div>
</div>
```

設置 `[value]="item.description"`，`<input>` 的值將綁定到當前項目的 `description` 屬性。此綁定使項目的 `description` 成為`<input>` 的值。因此如果將 `description` 設為 `eat`， 因為 `<input>` 已經和 `description` 綁定。所以，當用戶編輯項目時，`<input>` 的值已被設為 `eat`。

`<input>` 上的範本變數 `#editedItem` 表示 Angular 將用戶在此 `<input>` 中輸入的內容儲存在名為 `editedItem` 的變數中。如果用戶在輸入後選擇按 Enter 而不是點擊 **Save**，則 `keyup` 事件將調用 `saveItem()` 方法並傳遞 `editedItem` 變數的值。

當用戶點擊 **Cancel** 按鈕時，`editable` 的值將切換為 `false`，連帶從 DOM 中移除編輯相關的輸入框和按鈕。當 `editable` 的值為 `false` 時，Angular 將含有 **Edit** 和 **Delete** 按鈕的 `<div>` 放回 DOM 中。

點擊 **Save** 按鈕將調用 `saveItem()` 方法。 `saveItem()`方法從 `<input>` 中的範本變數 `#editedItem` 取得值，並將該項目的 `description` 更改為 `editedItem.value` 的值。

準備 AppComponent
---------------

在下一章節，你將添加用來溝通 `AppComponent` 和 `ItemComponent` 的程式碼。首先將以下內容添加到 `app.component.ts` 中來配置 AppComponent：

```js
remove(item) {
  this.allItems.splice(this.allItems.indexOf(item), 1);
}
```

 上面 `remove()` 方法使用了 JavaScript `Array.splice()` 方法，並透過 `indexOf` 取得欲刪除項目的陣列索引中位置，以從陣列中刪除該項目。
 簡單來說，`splice()` 方法從陣列中刪除了該項目。 `splice()` 的更多訊息請參閱 MDN Web 文章：`Array.prototype.splice()`。

在 ItemComponent 添加邏輯
--------------------

使用 `ItemComponent` UI， 你必須在元件中添加邏輯，就跟在 function 中寫輸入與輸出的方式一樣。

在 `item.component.ts`，引入 JavaScript，如下所示：

```js
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { Item } from "../item";
```

 加入 `Input` ， `Output`,和 `EventEmitter` 使 `ItemComponent` 可以與 `AppComponent` 共享數據，
 透過匯入 `Item` ， 就可以知道在 `ItemComponent` 的 `item` 為何。

繼續看 `item.component.ts` ，把生成的 `ItemComponent` 替換成以下內容：

```js
export class ItemComponent {

  editable = false;

  @Input() item: Item;
  @Input() newItem: string;
  @Output() remove = new EventEmitter<Item>();

  saveItem(description) {
    if (!description) return;
    this.editable = false;
    this.item.description = description;
  }
}
```

`editable` 屬性有助於切換模板，讓使用者可以編輯其項目。HTML 中的 `editable` 屬性與 `*ngIf` 語句中的屬性相同， `*ngIf="editable"`。當你在模板使用此屬性時，你也必須在 class 中宣告它。

`@Input()` ， `@Output()` 和 `EventEmitter` 促進兩個元件中的溝通，一個 `@Output()` 服務元件做為資料傳進的入口，然後一個 `@Output()` 是將元件資料傳到外層。`@Output()` 必須是 `EventEmitter` 的類型，資料可以透過事件分享至其他組件。

使用 `@Input()`指定外部元件要傳進之屬性的值，將 `@Output()` 與 `EventEmitter` 結合使用可將該元件指定屬性的值傳出，使得另一個元件可以接收其資料。

`saveItem()` 方法是在 `description` 取得一個引數，此`description` 為使用者輸入 HTML 的 `<input>` 標籤編輯清單的項目時的文字，
 此 `description` 與 `<input>` 中帶有 `#editedItem` 範本變數的字符串相同。

如果使用者沒有輸入任何的值但點擊 **Save** 時，`saveItem()` 不會回傳任何東西與更新 `description`。如果你沒有用 `if` ，使用者就可以在 HTML 的 `<input>` 沒有值的時候點擊 **Save**，並且 `description` 會是空字串。

如果使用者輸入文字並點擊儲存， `saveItem()` 會設定 `editable` 是 false，這會導致模板中的`*ngIf` 移除編輯功能並重新渲染 **Edit** 跟 **Delete** 的按鈕

儘管程式現在可以編譯，你必須在 `AppComponent` 中使用 `ItemComponent` 才能在瀏覽器看到新功能。

在 AppComponent 中使用 ItemComponent
--------------------------------

在父子關係的情境下，可將一個組件包含在另一個組件中，讓你靈活地使用它們。

`AppComponent` 就像個應用程式的外殼，可在內部加入其他元件

要在 `AppComponent` 中使用 `ItemComponent`，需將 `ItemComponent` 選擇器放到 `AppComponent` 中。Angular 在元件共享數據的元件中使用 `@Component()` 裝飾器，此選擇器為 `app-item`:

```js
@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})
```

要在 `AppComponent` 中使用 `ItemComponent` 選擇器時，你要增加元素 `<app-item>`，它對應你在 `app.component.html` 中對元件類別定義的選擇器。用以下更新的版本替換在 `app.component.html` 中未排序清單:

```html
<h2>
  {{items.length}}
  <span \*ngIf="items.length === 1; else elseBlock">item</span>
  <ng-template #elseBlock>items</ng-template>
</h2>

<ul>
  <li \*ngFor="let item of items">
    <app-item (remove)="remove(item)" [item]="item"></app-item>
  </li>
</ul>
```

雙括號 `{{}}`，在 `<h2>` 內顯示 `items` 的長度與數目。

在 `<h2>` 中 `<span>` 使用 `*ngIf` 與 `else` 決定 `<h2>` 是否要呈現 "item" 或 "items"。如果在列表中只有一個項目， 則 `會顯示包含 <span> 的內容。當items 陣列不等於 1 時，被我們命名為 elseBlock 的 <ng-template>，將顯示 #elseBlock，而不是 <span>。當你不想內容在預設渲染的時候，可以使用 Angular 的 <ng-template> ，因 #elseBlock 不是 <span>，是使用 <ng-template>。在此範例中，若 item 陣列長度不是 1 ，則 *ngIf 會顯示 elseBlock 而不顯示 <span>。`

在 `<li>` 使用 Angular 的結構型指令 `*ngFor` 會在 `items`陣列迭代所有的項目，Angular 的 `*ngFor` 與 `*ngIf` 指令相似，是另一個可以協助你用更少的程式碼改變 DOM 元素架構，每一個 `item`，Angular 會重複 `<li>` 與其所有的內容，其中包含 `<app-item>`。這代表 Angular 為陣列中的每一個項目建立另一個 `<app-item>` 實體。Angular 會建立與 `items` 陣列中的數量相同的項目的 `<li>` 元素。

你可使用 `*ngFor` 在其他的元素上，像是在 `<div>`、`<span>` 或是 `<p>`，以此類推。

在 `AppComponent` 有一個移除項目的 `remove()` 的方法，是綁定 `ItemComponent` 中 remove 的屬性，此 `item` 屬性是在中括號內 `[]`,用來綁定 `item` 在 `AppComponent` 與 `ItemComponent` 之間的值。

現在你應該知道如何編輯和刪除在列表中的項目。當你新增或刪除項目時，項目的數量也會更動，為了使列表更易於使用，請在 `ItemComponent` 中新增些樣式。

為 ItemComponent 添加樣式
--------------------

你可以使用元件的 styles sheet 去增加該元件的樣式。下面的 CSS 增加了基本的樣式，對按鈕添加 flexbox 屬性和客製化了核取方塊。

將下面的樣式程式碼貼至 `item.component.css`。

```css
.item {
  padding: 0.5rem 0 0.75rem 0;
  text-align: left;
  font-size: 1.2rem;
}

.btn-wrapper {
  margin-top: 1rem;
  margin-bottom: 0.5rem;
}

.btn {
  /\* menu buttons flexbox styles \*/
  flex-basis: 49%;
}

.btn-save {
  background-color: #000;
  color: #fff;
  border-color: #000;
}

.btn-save:hover {
  background-color: #444242;
}

.btn-save:focus {
  background-color: #fff;
  color: #000;
}

.checkbox-wrapper {
  margin: 0.5rem 0;
}

.btn-warn {
  background-color: #b90000;
  color: #fff;
  border-color: #9a0000;
}

.btn-warn:hover {
  background-color: #9a0000;
}

.btn-warn:active {
  background-color: #e30000;
  border-color: #000;
}

.sm-text-input {
  width: 100%;
  padding: 0.5rem;
  border: 2px solid #555;
  display: block;
  box-sizing: border-box;
  font-size: 1rem;
  margin: 1rem 0;
}

/\* Custom checkboxes
Adapted from https://css-tricks.com/the-checkbox-hack/#custom-designed-radio-buttons-and-checkboxes \*/

/\* Base for label styling \*/
[type="checkbox"]:not(:checked),
[type="checkbox"]:checked {
  position: absolute;
  left: -9999px;
}
[type="checkbox"]:not(:checked) + label,
[type="checkbox"]:checked + label {
  position: relative;
  padding-left: 1.95em;
  cursor: pointer;
}

/\* checkbox aspect \*/
[type="checkbox"]:not(:checked) + label:before,
[type="checkbox"]:checked + label:before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 1.25em;
  height: 1.25em;
  border: 2px solid #ccc;
  background: #fff;
}

/\* checked mark aspect \*/
[type="checkbox"]:not(:checked) + label:after,
[type="checkbox"]:checked + label:after {
  content: "\2713\0020";
  position: absolute;
  top: 0.15em;
  left: 0.22em;
  font-size: 1.3em;
  line-height: 0.8;
  color: #0d8dee;
  transition: all 0.2s;
  font-family: "Lucida Sans Unicode", "Arial Unicode MS", Arial;
}
/\* checked mark aspect changes \*/
[type="checkbox"]:not(:checked) + label:after {
  opacity: 0;
  transform: scale(0);
}
[type="checkbox"]:checked + label:after {
  opacity: 1;
  transform: scale(1);
}

/\* accessibility \*/
[type="checkbox"]:checked:focus + label:before,
[type="checkbox"]:not(:checked):focus + label:before {
  border: 2px dotted blue;
}
```

結論
--

你現在應該擁有一個樣式化的 Angular 待辦事項列表應用程序，該應用程序可以添加，編輯和刪除項目。下一步是加入過濾功能，以便你可以查看符合特定條件的項目。

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁


前端框架簡介 - 學習該如何開發 Web

前端框架簡介
======

* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)

我們從整體概述來探討框架、提供 JavaScript 與框架的簡要歷史、框架存在的理由、他們提供什麼東西、如何決定選擇哪個框架、以及前端框架的的替代方案。

|  |  |
| --- | --- |
| 先決條件： | 熟悉 HTML、CSS、JavaScript 這些核心技術。 |
| 目標： |
 理解 JavaScript
 前端框架存在的理由、他們解決的問題、可用的替代方案、還有決定選擇的方法。
  |

一段簡短的歷史
-------

在 JavaScript 誕生的 1996 年，它的作用僅僅是為當時由靜態文檔組成的網頁，提供些許的互動和興奮感。而當網路漸漸從*閱讀*的媒介，成為*做事*的場所，JavaScript 也慢慢地紅了起來。JavaScript 開發者們撰寫了許多工具，來解決自己遭遇的問題，並打包成能重複使用的工具包，稱為**函式庫**（library），以便和他人共享自己的解決方案。這個共享函式庫的生態，也推進了網路的增長。

目前 JavaScript 已經是網路的必需品了，大約 95% 的網站都又在使用 JavaScript，網路也成了當今生活的必須。使用者可以透過文字與影像，來寫論文、聽音樂、看電影、與人遠距離交流。曾經只能透過裝在電腦內的原生軟體所完成的事情，現在也能網路上做到。這種現代化、複雜度高、還有各種互動的網站，被稱為**網路應用程式**（web applications）。

當代 JavaScript 框架的問世，讓構建高度動態的互動式應用，變得簡單許多。**框架**是個針對軟體構建，提供完整解決方案的函式庫。這些選項能讓應用程式，開始能預測和同質化。可預測性讓軟體能擴展到巨大的規模時依舊能維護；可預測性和可維護性則對軟體的健康和長壽至關重要。

JavaScript 框架能構建常用的網站裡，許多令人印象深刻的軟體。目前你正在看的 MDN Web Docs 網站，也是用 React/ReactDOM 作為前端支援的。

那有什麼框架？
-------

有很多框架，不過主要有以下「四大框架」。

### Ember

Ember 在 2011 年 12 月發行。這個框架始於 SproutCore 的內部專案。這是個比較老的框架：與 React 或 Vue 之類的替代方案相比，其用戶數比較少，不過在穩定性、社區支持、和一些巧妙的編碼原則方面，仍然享譽無數。

### Angular

Angular 是個由 Google 內部的 Angular Team 與其他社群所開發的開源專案。這個專案是同一群人由 AngularJS 所重寫的專案。該專案於 2016 年 9 月 14 日發行。

Angular 是基於組件、並使用指令式 HTML 樣板的框架。在構建時，框架的編譯器會將模板，轉換為優化的 JavaScript 程式。Angular 使用了 JavaScript 超集（superset）的 TypeScript。我們將在下一章中詳細介紹它。

### Vue

尤雨溪在維護並理解前述的 AngularJS 專案後，於 2014 年發表了 Vue。Vue 是四大框架裡面最年輕的，但成了近年來的當紅炸子雞。

Vue 除了與 Angular 一樣，使用了一些自定義的 HTML 以外，大部分還是使用現代化的標準 JavaScript。

### React

Facebook 於 2013 年發表了 React。在發表當時 Facebook 內部早已使用 React 解決許多內部問題。技術上來說 React *並不是*框架，而是一個用來渲染 UI 組件的函式庫。React 通常會配合*其他*函式庫來建立應用程式：例如 React 搭配 React Native 建立手機程式、React 與 ReactDOM 建立網路程式...等等。

由於 React 與 ReactDOM 通常會搭在一起用，React 在通俗上會被理解為 JavaScript 框架。在閱讀本模塊時，我們將以這種通俗理解為基礎。

React 使用一種很像是 HTML 的 JavaScript 語法：JSX。

為什麼有框架？
-------

我們已經討論了啟發框架建立的環境，但那不是開發人員*為什麼*要製造它們的理由。要探索原因，首先需要首先檢查開發軟體所碰上的挑戰。

來看看一個常見的例子吧：一個能建立待辦事項程式，我們將會用待辦事項程式為例子，來介紹不同的框架。這個應用程序要能讓使用者執行諸如渲染事項列表，添加新任務和刪除任務之類的操作；還要能可靠地跟踪並更新程式所依賴的資料在軟體開發中，此這些資料稱為狀態（state）。

每個目標分開來看，理論上都很簡單：我們能遍歷需要渲染的資料、建立新的工作物件、還能用標識符來查詢、編輯或刪除工作。然而，當我們要求程式，讓用戶在瀏覽器完成*這一切*的話，麻煩就來了。**問題在於：更動狀態時，也同時需要更動 UI 的顯示。**

讓我們藉由待辦事項程式的*一個功能*來看看這個問題有多難搞：把工作清單渲染出來。

DOM 的冗長變化
---------

建立 HTML 元素並在瀏覽器上渲染，會需要驚人數量的程式碼。假設我們的狀態，是一個由多個物件組成的陣列：

```js
const state = [
  {
    id: "todo-0",
    name: "Learn some frameworks!",
  },
];
```

我們如何對用戶顯示工作？我們想將每個工作，都表示為一個列表項目：結構為無序列表元素 `<ul>` (en-US) 內，含有一定數量的 `<li>` (en-US) 元素。怎麼做呢？看起來就像這樣：

```js
function buildTodoItemEl(id, name) {
  const item = document.createElement("li");
  const span = document.createElement("span");
  const textContent = document.createTextNode(name);

  span.appendChild(textContent);

  item.id = id;
  item.appendChild(span);
  item.appendChild(buildDeleteButtonEl(id));

  return item;
}
```

我們在這裡用上了 `document.createElement()` 方法建立了 `<li>`、還有一些程式碼來建立需要的屬性與子元素。

程式的第十行引用了另一個構建函式：`buildDeleteButtonEl()`。它與用於構建列表元素的模式很像：

```js
function buildDeleteButtonEl(id) {
  const button = document.createElement("button");
  const textContent = document.createTextNode("Delete");

  button.setAttribute("type", "button");
  button.appendChild(textContent);

  return button;
}
```

這個按鈕還派不上用場，但稍後我們會用它來實做刪除功能。這渲染程式，會讓頁面看起來像這樣：

```js
function renderTodoList() {
  const frag = document.createDocumentFragment();
  state.tasks.forEach((task) => {
    const item = buildTodoItemEl(task.id, task.name);
    frag.appendChild(item);
  });

  while (todoListEl.firstChild) {
    todoListEl.removeChild(todoListEl.firstChild);
  }
  todoListEl.appendChild(frag);
}
```

*光是*為了弄 UI 我們就寫了大約三十行左右的程式：這樣就*只是*為了能讓清單在 DOM 渲染而已喔。更別提之後還需要添加方便樣式化的 class。

像範例這樣直接操作 DOM 的話，會需要理解很多 DOM 的東西：像是 DOM 的原理、如何建立元素、更改屬性、巢狀排列……*同時*還要把他們都呈現出來。這些程式甚至還沒有處理與用戶的互動或工作。在增加功能的時候，我們需要在正確的時間、用正確的方法，來更新我們的 UI。

JavaScript 框架旨在使這類工作更加輕鬆：他們會提供更好的*開發體驗*。框架本身並沒有給 JavaScript 提供新功能；而是用更容易的方案，建立當代的網站。

如果想查看本節中的程式碼範例，請參閱 CodePen 的程式：這網站同樣允許用戶添加和刪除新任務。

閱讀本節中使用的 JavaScript 資訊：

* `document.createElement()`
* `document.createTextNode()`
* `document.createDocumentFragment()`
* `eventTarget.addEventListener()`
* `node.appendChild()` (en-US)
* `node.removeChild()`
建立 UI 的另一種方法
------------

JavaScript 框架都會提供一種能更加*宣告性*撰寫介面的方法。也就是說，框架能讓你描述 UI 看起來要怎麼樣、然後在 DOM 的背後完成這一切。

原生 JavaScript 試圖重複建立新 DOM 元素的方法，很難一眼理解。相反地，來看看 Vue 的程式碼會怎麼完成吧：

```html
<ul>
  <li v-for="task in tasks" v-bind:key="task.id">
    <span>{{task.name}}</span>
    <button type="button">Delete</button>
  </li>
</ul>
```

就這樣啦。原本大約三十多行的程式，現在只要六行。如果不太熟悉大括號和 `v-` 屬性的話，沒關係；那些語法會在 Vue 模塊學到。這裡只是要講說與原生 JavaScript 對比，框架的程式碼看起來更像是實際呈現的 UI。

也因為有 Vue，我們再也不用自己寫針對 UI 呈現的函式；整個框架會幫我們用最優化、最效率的方法完成這件事。我們只要告訴 Vue 整個排版要怎麼排就好。熟悉 Vue 的開發者，也可以盡快參與我們的專案、搞清楚整個專案到底怎麼做的。不過並不是只有 Vue 這樣：使用框架本身，就可以提高團隊與個人的工作效率。

要在原生 JavaScript 做*類似的*事是可以的。樣板文字就能在編寫 HTML 字串的同時，也表示最終元素的外觀。就算是待辦事項列表應用，這樣簡單的事情，這可能是一個有用的想法，但是對於管理成千上萬條數據記錄、並且要在用戶界面中呈現盡可能多唯一元素的大型應用程序而言，這樣子是很難維護的。

框架給我們的其他東西
----------

讓我們看看框架賦予的其他優勢。正如之前提，你可以透過原生 JavaScript 實現框架，但是使用框架可以消除自行解決這些問題所需要的認知負擔。

### 工具

本模塊提到的幾個框架，背後都有著龐大而活躍的社群；這些社群形成了各種生態圈、並提供能增進開發體驗的工具：像是確保功能正常的測試、或著維持程式一致性的 linting。

**備註：** 如果對這方面的概念有興趣，請看看 Client-side tooling overview (en-US)。

### 切分

大多數框架鼓勵開發者把介面的各部份，封裝成各種*組件*（components）：也就是一個個可維護、可互通、還可重用的程式碼塊（chunks of code）。與該組件相關連的程式，可獨立為一個、或數個獨立的程式。在原生 JavaScript 裡面要這麼做的話，開發者就必須靠自己的慣例，才能在高效、可擴展的情況下，實現這個目標。但大多數的 JavaScript 開發者，最後會讓 UI 相關的所有程式，都分散在整個文件中。

### 路由

web 最重要的功能之一，就是頁面之間的導航：畢竟它就是相互連接文件的網路。在你點選網站上的連結時，瀏覽器會與伺服器溝通、並獲取新內容以便顯示給你看。也因為這樣，地址欄中的 URL 就會更改。你可以保存這個新的 URL 並稍後回來、或與其他人分享該 URL，以便他們輕鬆地找到同一個頁面。你的瀏覽器會記住這個導航歷史記錄，也能在頁面之間來回導航。這就叫**伺服器端路由**（server-side routing）。

現代的網路應用程式通常不獲取和渲染新的 HTML 文件：它們通常載入單個 HTML Shell，並不斷更新其中的 DOM 同時，不導航用戶到新地址（這被稱為**單頁應用**、**single page apps**、**SPA**）。每個新的虛擬網頁通常稱為 *視圖*（view），一般來說也不執行路由。

在 SPA 複雜到一定的程度、也有渲染出足夠獨特的視圖時，給應用程式導入路由功能，就變得很重要：人們習慣在應用程式中，透過連結導航到特定頁面，在導航歷史記錄中前進和後退等，而當這些標準的 Web 功能被破壞時，他們的體驗也會受到影響。如果導航功能以用戶端程式提供，這就叫做**用戶端路由**（client-side routing）。

你*可以*透過原生 JavaScript 實做路由功能。但比較活躍的框架都有相對應的函式庫，讓路由功能在開發過程中更加直觀。

使用框架時要考慮的事情
-----------

想成為一位高效的網路開發，意味著你需要選擇最合適的工具：JavaScript 框架能讓前端開發變簡單，但它並不是萬能仙丹。我們回在這個章節探討選擇框架需要考慮的事情。請注意，你可能完全不需要框架。不要為了用框架而用框架。

### 熟悉工具

如同原生 JavaScript，框架也需要理解它們各自的特性。在選好框架前，確保有足夠的時間熟悉框架，以便讓它成為開發的墊腳石，而不是絆腳石。同時，也要確保你的同事們能接受它。

### 過度工程化

一個 web 專案如果是個只有數個個人頁面、還幾乎沒有交互功能的話，你完全不需要 JavaScript 框架、甚至 JavaScript 本身也不需要。也就是說，框架並不是整體式的，其中一些可能更適合於小型專案。一篇在 Smashing Magazine 的文章中，作者 Sarah Drasner 寫了一篇怎麼用 Vue 取代 jQuery 的文章、讓網頁的一小部分具有交互性。

### 更大的程式庫和抽象化

框架能寫出宣告式程式、有時候還會*少寫*程式。這一切都是透過框架在背後處理 DOM 互動所達成的。這種抽象化給開發者提供了相當棒的體驗，但這並不是免費的。為了把編寫的程式變成能與 DOM 互動的玩意，框架必須執行自己的程式；而這反過來又會讓專案變大，執行演算的開銷也更高昂。

不可避免地，這會產生一些額外的程式；而儘管一個支持 tree-shaking（刪除在構建過程中未實際用到的程式）的框架，會讓應用程式保持小巧，但在考慮性能時，這將是必須牢記在心的因素之一，尤其是在受網路/儲存空間受限的設備上（像是手機）。

框架的抽象化不僅影響你的 JavaScript、它也會影響你對與網絡本質的關係：無論如何構建 Web，最後與用戶交流的都是 HTML。用 JavaScript 編寫整個程式，會讓你看不到 HTML 本身、以及各標籤的用途，最後會生出不語義、且有障礙的 HTML 文件。實際上，你甚至能寫出一個完全依賴 JavaScript，沒有它就完全動不了的脆弱應用程式。

框架不是問題的根源。如果優先事項設錯了，*任何*應用程式都會變得脆弱、腫大、且障礙多舛。不過，框架確實擴大了我們作為開發人員的優先事項。如果想做出一個很複雜的 Web 程式，這當然能作到；但如果優先事項無法確保性能與無障礙的話，框架將放大這方面的問題。現在的 Web 不再是一個健壯的，內容優先的文件網絡，而是常將 JavaScript 放在首位，用戶體驗則放在最後。

框架網站的無障礙議題
----------

讓我們以上一節的內容為基礎，並進一步討論無障礙問題。消除用戶界面的障礙總是需要點思考與努力，而框架會使該過程複雜化。你通常要用上進階的框架 API 來訪問本機瀏覽器功能，例如 ARIA live region (en-US) 或 focus 管理。

在某些情況下，框架應用程式會發生在傳統網站不存在的障礙。最明顯的例子，就是前述的客戶端路由。

使用傳統（伺服器端）路由瀏覽 Web 會出現可預測的結果。瀏覽器知道將焦點設置在頁面頂部、輔助技術將宣布頁面標題。在導航到新頁面時，這些事情都一定會發生。

使用客戶端路由時，瀏覽器不會加載新頁面，因此它不知道要自動調整焦點、或宣告新的頁面標題。框架作者會花費大量時間和精力，來編寫可重現這些功能的 JavaScript，但沒有一個框架能做到如此完美。

結論是，*所有的* Web 專案一開始，就要應該考慮無障礙問題。如果專案使用抽象的框架、又不考慮無障礙問題的話，未來衍生的無障礙問題會更嚴重。

如何選擇框架
------

不同模塊的框架，會採用不同的方法。來開發 Web 應用程式。框架都會定期變化、也都有其優缺點。選擇哪個框架的過程，是與團隊及專案息息相關的。你需要透過研究，來找出合適的需求。換句話說，我們已經找出了一些能有效地研究出選擇的問題：

1. 框架支援哪些瀏覽器？
2. 框架使用哪個特定領域語言（domain-specific language）？
3. 框架有夠大的社群與夠好的文件（或其他東西）支援嗎？

接下來我們退提供一個表格，來展示各大框架的*瀏覽器支援*、還有能用的**特定領域語言**。

一般來說，特定領域語言（domain-specific language, **DSL**）是一種與的特定領域軟體開發相關的程式語言。以框架的脈絡來說，DSL 是能讓開發更簡單的 JavaScript 或 HTML 變體。最重要的是，沒有哪個框架*要求*開發者使用某種特定領域語言，但框架們在挑選 DSL 方面，早已心有所屬了。選擇不採用該框架的首選 DSL，可能就會失去本可增添開發人員體驗的功能。

在為任何新專案做出選擇時，你需要認真考慮框架的支持矩陣（support matrix）和 DSL。瀏覽器要是不支援，會成為用戶的障礙；而 DSL 要是不支援，則會你和你開發團隊的障礙。

| 框架 | 瀏覽器支援 | 首選的 DSL | 支援的 DSL |
| --- | --- | --- | --- |
| Angular | IE9+ | TypeScript | 基於 HTML; TypeScript |
| React | 當代瀏覽器（IE9+ 含有 Polyfill） | JSX | JSX; TypeScript |
| Vue | IE9+ | 基於 HTML | 基於 HTML, JSX, Pug |
| Ember | 當代瀏覽器（IE9+ 直到 2.18 為止） | Handlebars | Handlebars, TypeScript |

**備註：** 「基於 HTML」的 DSL 並沒有官方名字。雖然它們不完全是 DSL，但也不是標準的 HTML，所在我們在此附帶一題。

表格的引用來源：

* React browser support: official docs
* Ember browser support: Ember 3.0 release announcement
* Ember templating language (official docs)
### 框架有堅實的社區嗎？

這大概是最難評估的，因為社區沒辦法用什麼數字直接估算。你或許可以用 GitHub 的星星、或是 npm 的下載量來看，但有時最好的辦法，是找看看一些論壇、或和其他開發者聊聊看。這不只是社區大小的問題而已，你還需要看看社區多麼熱情和包容、以及文件多好讀。

### Web 的看法

在選框架這件事，不要只聽我們的話：網路上也有不少的討論。例如說，維基媒體基金會最近就選了 Vue 作為他們的前端框架，還發表了相關的請求意見稿。請求意見稿的作者 Eric Gardner 花了不少時間，概述維基媒體基金會的需求、還有為什麼他認為這個框架，對開發團隊有益。這個請求意見稿，是你在研究要使用什麼框架時，需要考量什麼的一個好例子。

State of JavaScript survey 也是有用的 JavaScript 開發者反饋集合。它包含了很多 JavaScript 相關的要點、包括有關框架使用情況、還有開發者對各框架看法的數據。網站也有數年的可用數據，以便了解框架的消長。

Vue 的開發團隊也寫了有關 Vue 與其他框架的詳盡比較。如同他們自知，這種比較可能會有一些偏見，但這仍然是一種寶貴的資源。

前端框架的替代
-------

如果在尋找能夠加速開發的工具、卻又發現專案不需要前端 JavaScript 框架的話，可以試試其他用於構建 Web 的解決方案：

* 內容管理系統（content management system）
* 伺服器渲染（Server-side rendering）
* 靜態網站產生器（static site generator）
### 內容管理系統

**內容管理系統**（ Content Management System, **CMS**）是能讓用戶在不自己寫程式的情況下，創建內容的工具。針對大型專案，尤其是撰寫者不熟悉程式、或者開發者想省時間的情況下，內容管理系統是個相當不錯的解決方案。不過，內容管理系統也需要費神去設定。使用 CMS 也同時意味著你會在最終輸出的控制方面，做出一定程度的退讓。比方說，如果 CMS 不太著墨在無障礙方面，那你也很難在這方面有所改進。

坊間常見的內容管理系統有 Wordpress、Joomla、Drupal。

### 伺服器渲染

**伺服器端渲染**（Server-side rendering, **SSR**）是由*伺服器*負責渲染單頁應用的程式架構，與構建 JavaScript 程式中最常見，最直接的*用戶端渲染*（client-side rendering）相對。伺服器端渲染只單純傳送 HTML 檔案，所以對用戶端更友善；但設置起來就比用戶端渲染程式難得多。

本模塊的所有用戶端渲染框架，都有相對應的伺服器端渲染。像是 React 的 Next.js、Vue 的 Nuxt.js（對這的確很教人困惑，不過兩者沒有關係）、Ember 的 FastBoot、Angular 的 Angular Universal。

**備註：** 某些伺服器端渲染的解決方案，是由社區編寫和維護；但也有「官方」的解決方案，是由框架維護者提供的。

### 靜態網站產生器

靜態網站產生器是個可以給網站生成多個網頁的程式 ── 這包括相對應的 CSS 或 JavaScript── 以便在任何地方發布。發布主機可以是 GitHub pages 分支、或著 Netlify 實體、抑或著是私人的伺服器。這種方法有很多優點，主要是性能方面（用戶端收到網頁時，已經載入了整個網頁，所以不需要執行 JavaScript）與安全性（靜態網站的攻擊因為變少了）。靜態網站還是能在需要時用上 JavaScript，但靜態網站並不*依賴* JavaScript。一如其他工具，靜態網站產生器需要點時間去搞懂。這可能是開發過程的障礙。

靜態網站的獨立網頁，要多少就能有多少。如同框架能加快開發用戶端 JavaScript 程式一般，靜態網站產生器也能快速產生 HTML 檔案（要不然，你本來要自己寫的）。靜態網站產生器也像框架一樣，能允許用戶給網頁撰寫組件；並在最後建立頁面時，把組件都結合起來。以靜態網站產生器而言，組件被稱為**樣板**（template）。靜態網站產生器構建的網頁，甚至可以作為框架應用程式的宿主：比方說你可以完成「如果希望用戶造訪某個特定頁面時，啟動 React 程式」這樣的需求。

靜態網站產生器已經存在好一段時間了，但他們最近在 Web 歷史中再度復興起來。現在有一些強大的選擇，像是 Hugo、Jekyll、Eleventy、Gatsby。

如果想深入理解靜態網站產生器的概念，看一下 Tatiana Mac 的 Beginner's guide to Eleventy。在該系列的第一篇文章中，她解釋了什麼是靜態網站生成器，以及它與發布 Web 內容的其他方式之間的關係。

結論
--

我們終於把框架介紹完了。我們還沒有教任何程式，但我們希望提供了有用的背景知識，說明為什麼要使用框架，如何選擇框架，還有燃起想要學習的興趣！

我們的下一篇文章，將探討更底層的東西，著眼於框架傾向於提供的特定種類的功能，以及它們為什麼能動。

* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)


建立第一個 Vue 元件 - 學習該如何開發 Web

建立第一個 Vue 元件
============

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)

現在是時候來更深入了解 Vue ，以及建立我們自訂的元件——我們將從建立一個元件開始，這個元件代表待辦清單裡的每一個項目。在過程中，我們會學到一些重要的概念，例如在元件裡面調用其他元件，使用 props 傳遞資料，以及儲存它的狀態（ state ）。

**備註：** 如果你需要根據我們的版本來檢查你的程式碼，你可以從 todo-vue 存放庫找到 Vue 範例應用程式最終版本的程式碼。有關實際運行的版本，請看 https://mdn.github.io/todo-vue/dist/ 。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉 HTML 、
 CSS 以及
 JavaScript 核心語言，具備
 terminal/command line (en-US)
 的知識。

 Vue 元件是由 Javascript
 物件（用來管理應用程式的資料）及樣版語法（用來映射成 DOM
 結構）所組成的。此外，你將需要使用終端機安裝 node 和 npm
 ，以便下載及使用一些 Vue
 的進階功能（像是單一檔案的元件或是渲染函式）。
  |
| 目標： |
 學習如何建立一個 Vue 元件，將其渲染到另一個元件裡，使用 props
 傳遞資料，以及儲存它的狀態（ state ）。
  |

建立一個 ToDoItem 元件
----------------

來建立我們的一個元件，這個元件會顯示一個待辦項目，我們會使用它來建立待辦清單。

1. 在你的 `moz-todo-vue/src/components` 資料夾裡建立一個新檔案並命名為 `ToDoItem.vue` 。在程式編輯器打開這個檔案。
2. 在檔案頂端加上 `<template></template>` 來建立元件的模板。
3. 在 template 區塊底下建立一個 `<script></script>` 區塊。在 `<script>` 標籤之間加上一個預設匯出的物件 `export default {}` ，這是你的元件物件。

現在你的檔案應該會像這樣：

```html
<template> </template>
<script>
 export default {};
</script>
```

我們現在開始為 `ToDoItem` 添加實際內容。目前 Vue 模板只允許存在一個根元素——用一個元素來包裝在 template 區塊裡面的所有內容（這個情況在 Vue 3 發布後會改變）。我們將使用一個 `<div>` 來做為根元素。

1. 現在在元件 template 裡面新增一個空的 `<div>` 。
2. 在這個 `<div>` 裡面，新增一個 checkbox 和一個相對應的 label。在 checkbox 加上一個 `id` 屬性，並在 label 加上 `for` 屬性，使 checkbox 對應到 label 。如下所示。

```html
<template>
  <div>
    <input type="checkbox" id="todo-item" checked="false" />
    <label for="todo-item">My Todo Item</label>
  </div>
</template>
```

### 在我們的應用程式裡使用 TodoItem

目前一切都很好，但是我們還沒把元件加到應用程式裡面，所以沒辦法測試它是否可以正常運作。現在讓我們把它加到應用程式裡。

1. 再一次打開 `App.vue` 。
2. 在 `<script>` 標籤的頂端，加上以下內容來匯入 `ToDoItem` 元件：

```js
import ToDoItem from "./components/ToDoItem.vue";
```

3. 在元件物件裡面添加 `components` 屬性，然後在它裡面添加 `ToDoItem` 元件來註冊它。

現在你的 `<script>` 內容應該會像這樣：

```js
import ToDoItem from "./components/ToDoItem.vue";

export default {
  name: "app",
  components: {
    ToDoItem,
  },
};
```

這和之前 Vue CLI 註冊 `HelloWorld` 元件是同樣的方法。

要在應用程式裡實際渲染 `ToDoItem` 元件，你需要在 `<template>` 元素裡面呼叫它——加上 `<to-do-item></to-do-item>` 元素。請注意，元件檔案名稱和它在 JavaScript 的表示法通常是用大駝峰式命名法（ PascalCase ，例如： `ToDoList` ），而相對應的自訂元素是用 kebab-case（例如： `<to-do-list>` ）。

1. 在 `<h1>` (en-US) 底下建立一個無序清單（ `<ul>` (en-US) ）包含單一個清單項目（ `<li>` (en-US) ）。
2. 在清單項目（ <li> ）裡面加上 `<to-do-item></to-do-item>` 。

現在你的 `App.vue` 的 `<template>` 內容應該會像這樣：

```html
<div id="app">
  <h1>To-Do List</h1>
  <ul>
    <li>
      <to-do-item></to-do-item>
    </li>
  </ul>
</div>
```

如果你再次查看渲染應用程式，現在應該會看到你渲染的 `ToDoItem` 元件，它包含一個 checkbox 和 label 。

![目前應用程式的渲染狀態——包含一個標題（ To-Do List ），一個 checkbox 和 label](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component/rendered-todoitem.png)

使用 prop 讓元件變得更彈性
----------------

到目前爲止我們的 `ToDoItem` 元件還不是很實用，因為這個元件一個頁面只能使用一次（元件內 label 的 id 是唯一值），而且我們也沒有辦法設定 label 的文字內容。

所以我們可以透過 prop 新增一些狀態到我們的元件，你可以將 prop 想像成是傳入函式的參數，它會讓元件有一些初始狀態可以用來渲染到畫面上。

### 宣告 prop

在 Vue 中，宣告 prop 的方法有兩種：

* 第一種方法是將 prop 寫成字串存進陣列中，陣列中的每個元素都對應到一個 prop 的名稱。
* 第二種方法是寫一個物件，將 prop 作為物件的 key，這種寫法可以設定 prop 的初始值、型別、是否為必要值以及簡單的驗證。

**備註：** Prop 驗證只有在開發環境下有效，所以在正式上線的環境中無法使用。此外，作為 prop 驗證的函式在元件被創建之前就會被呼叫，因此該函式無法取得元件中的任何狀態或是其他 prop。

對於 `ToDoItem` 這個元件，我們會透過第二種方法宣告 prop，也就是物件的形式。

1. 回到 `ToDoItem.vue` 檔
2. 在 `export default {}` 的物件中加入一個 `props` 屬性，值為一個空物件
3. 在 `props` 物件中加入兩個屬性，分別是 `label` 和 `done`
4. `label` 的值是一個物件帶有兩個屬性
    1. 第一個屬性是 `required`，它的值為 `true`。這會告訴 Vue 我們預期元件必須要接收到一個名為 `label` 的 prop。如果元件沒有接受到的話，Vue 會發出警告通知我們。
    2. 第二個屬性是 `type`，它的值為 `String`。這會告訴 Vue 我們預期 prop 的型別是一個字串。
5. 接著是 `done` 這個 prop
    1. 首先設定 `default` 屬性為 `false`，意思是當元件沒有接收到 `done` 的時候，`done` 的初始值為 false（要注意只有在 prop 不是必需的時候才會設定初始值）
    2. 接著設定 `type` 這個屬性為 `Boolean`，這會告訴 Vue 我們預期 `done` 是一個布林值

你的物件現在看起來應該要像是這樣：

```js
<script>
  export default {
    props: {
      label: { required: true, type: String },
      done: { default: false, type: Boolean }
    }
  };
</script>
```

### 使用完成宣告的 prop

有了這些宣告好的 prop 後，我們現在可以在樣板內使用這些 prop，讓我們先從在樣板內加入一個 `label` 的 prop 開始。

在你的 `<template>` 中，把 `<label>` 的內容換成 `{{label}}`

`{{}}` 是一個 Vue 的樣板語法，它讓我們可以在樣板印出 JavaScript 表達式的結果。當我們在 Vue 的樣板中看到 `{{}}` 時，必須要知道它是拿來顯示文字的而不是 HTML。以現在這個案例來說，我們會印出 `label` 這個 prop 的值。

現在你的樣板看起來應該會是像這樣：

```html
<template>
  <div>
    <input type="checkbox" id="todo-item" checked="false" />
    <label for="todo-item">{{label}}</label>
  </div>
</template>
```

回到瀏覽器後你會看到之前渲染的 todo item，但是沒有看到 label。接著打開你的開發者工具，你會發現在主控台中出現了下列的警告：
```

[Vue warn]: Missing required prop: "label"

found in

---> <ToDoItem> at src/components/ToDoItem.vue
       <App> at src/App.vue
         <Root>
```

這是因為我們設定 `label` 為 required，但是我們在元件中沒有接收到，而且又在元件的樣板中使用了 `label`，也就是說我們在父元件中沒有傳入 `label` 這個 prop，讓我們來修正這個問題。

在你的 `App.vue` 檔中，將 `label` 這個屬性加到 `<to-do-item></to-do-item>` 上，就像一般的 HTML 屬性寫法一樣：

```html
<to-do-item label="My ToDo Item"></to-do-item>
```

現在再回到瀏覽器的畫面，你會看到 label 出現了，而且主控台也沒有出現警告。

這就是一個簡單的 prop 例子，接下來我們會說說 Vue 如何維持資料的狀態。

Vue's data object
-----------------

If you change the value of the `label` prop passed into the `<to-do-item></to-do-item>` call in your App component, you should see it update. This is great. We have a checkbox, with an updatable label. However, we're currently not doing anything with the "done" prop — we can check the checkboxes in the UI, but nowhere in the app are we recording whether a todo item is actually done.

To achieve this, we want to bind the component's `done` prop to the `checked` attribute on the `<input>` (en-US) element, so that it can serve as a record of whether the checkbox is checked or not. However, it's important that props serve as one-way data binding — a component should never alter the value of its own props. There are a lot of reasons for this. In part, components editing props can make debugging a challenge. If a value is passed to multiple children, it could be hard to track where the changes to that value were coming from. In addition, changing props can cause components to re-render. So mutating props in a component would trigger the component to rerender, which may in-turn trigger the mutation again.

To work around this, we can manage the `done` state using Vue's `data` property. The `data` property is where you can manage local state in a component, it lives inside the component object alongside the `props` property and has the following structure:

```js
data() {
  return {
    key: value
  }
}
```

You'll note that the `data` property is a function. This is to keep the data values unique for each instance of a component at runtime — the function is invoked separately for each component instance. If you declared data as just an object, all instances of that component would share the same values. This is a side-effect of the way Vue registers components and something you do not want.

You use `this` to access a component's props and other properties from inside data, as you may expect. We'll see an example of this shortly.

**備註：** Because of the way that `this` works in arrow functions (binding to the parent's context), you wouldn't be able to access any of the necessary attributes from inside `data` if you used an arrow function. So don't use an arrow function for the `data` property.

So let's add a `data` property to our `ToDoItem` component. This will return an object containing a single property that we'll call `isDone`, whose value is `this.done`.

Update the component object like so:

```js
export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean },
  },
  data() {
    return {
      isDone: this.done,
    };
  },
};
```

Vue does a little magic here — it binds all of your props directly to the component instance, so we don't have to call `this.props.done`. It also binds other attributes (`data`, which you've already seen, and others like `methods`, `computed`, etc.) directly to the instance. This is, in part, to make them available to your template. The down-side to this is that you need to keep the keys unique across these attributes. This is why we called our `data` attribute `isDone` instead of `done`.

So now we need to attach the `isDone` property to our component. In a similar fashion to how Vue uses `{{}}` expressions to display JavaScript expressions inside templates, Vue has a special syntax to bind JavaScript expressions to HTML elements and components: **`v-bind`**. The `v-bind` expression looks like this:
```

v-bind:attribute="expression"
```

In other words, you prefix whatever attribute/prop you want to bind to with `v-bind:`. In most cases, you can use a shorthand for the `v-bind` property, which is to just prefix the attribute/prop with a colon. So `:attribute="expression"` works the same as `v-bind:attribute="expression"`.

So in the case of the checkbox in our `ToDoItem` component, we can use `v-bind` to map the `isDone` property to the `checked` attribute on the `<input>` element. Both of the following are equivalent:

```html
<input type="checkbox" id="todo-item" v-bind:checked="isDone" />

<input type="checkbox" id="todo-item" :checked="isDone" />
```

You're free to use whichever pattern you would like. It's best to keep it consistent though. Because the shorthand syntax is more commonly used, this tutorial will stick to that pattern.

So let's do this. Update your `<input>` element now to replace `checked="false"` with `:checked="isDone"`.

Test out your component by passing `:done="true"` to the `ToDoItem` call in `App.vue`. Note that you need to use the `v-bind` syntax, because otherwise `true` is passed as a string. The displayed checkbox should be checked.

```js
<template>
  <div id="app">
    <h1>My To-Do List</h1>
    <ul>
      <li>
        <to-do-item label="My ToDo Item" :done="true"></to-do-item>
      </li>
    </ul>
  </div>
</template>
```

Try changing `true` to `false` and back again, reloading your app in between to see how the state changes.

Giving Todos a unique id
------------------------

Great! We now have a working checkbox where we can set the state programmatically. However, we can currently only add one `ToDoList` component to the page because the `id` is hardcoded. This would result in errors with assistive technology since the `id` is needed to correctly map labels to their checkboxes. To fix this, we can programmatically set the `id` in the component data.

We can use the lodash package's `uniqueid()` method to help keep the index unique. This package exports a function that takes in a string and appends a unique integer to the end of the prefix. This will be sufficient for keeping component `id`s unique.

Let's add the package to our project with npm; stop your server and enter the following command into your terminal:

```bash
npm install --save lodash.uniqueid
```

**備註：** If you prefer yarn, you could instead use `yarn add lodash.uniqueid`.

We can now import this package into our `ToDoItem` component. Add the following line at the top of `ToDoItem.vue`'s `<script>` element:

```js
import uniqueId from "lodash.uniqueid";
```

Next, add an `id` field to our data property, so the component object ends up looking like so (`uniqueId()` returns the specified prefix — `todo-` — with a unique string appended to it):

```js
import uniqueId from "lodash.uniqueid";

export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean },
  },
  data() {
    return {
      isDone: this.done,
      id: uniqueId("todo-"),
    };
  },
};
```

Next, bind the `id` to both our checkbox's `id` attribute and the label's `for` attribute, updating the existing `id` and `for` attributes as shown:

```html
<template>
  <div>
    <input type="checkbox" :id="id" :checked="isDone" />
    <label :for="id">{{label}}</label>
  </div>
</template>
```

Summary
-------

And that will do for this article. At this point we have a nicely-working `ToDoItem` component that can be passed a label to display, will store its checked state, and will be rendered with a unique `id` each time it is called. You can check if the unique `id`s are working by temporarily adding more `<to-do-item></to-do-item>` calls into `App.vue`, and then checking their rendered output with your browser's DevTools.

Now we're ready to add multiple `ToDoItem` components to our App. In our next article we'll look at adding a set of todo item data to our `App.vue` component, which we'll then loop through and display inside `ToDoItem` components using the `v-for` directive.

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)


建構 Angular 應用程式與更多資源 - 學習該如何開發 Web

建構 Angular 應用程式與更多資源
====================

* 前頁
* Overview: Client-side JavaScript frameworks

這篇文章內容涵蓋，如何建立一個生產版本（production）的應用程式，以及提供後續的學習資源。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉 HTML、CSS
 和
 JavaScript 的主要概念，以及
 終端機／命令列 (en-US)
 的知識。
  |
| 學習目標： | 學會如何編譯你的 Angular 應用程式 |

建置最終版的應用程式
----------

 現在你已經完成開發你的應用程式，接著執行 Angular CLI `build` 的指令。
 當你在 `todo` 的目錄底下執行 `build` 的指令，應用程式會編譯並且輸出到 `dist/` 的目錄下。

在 `todo` 目錄底下，且在命令列執行以下的命令：

```bash
ng build -c production
```

命令列介面——CLI（Command Line Interface），會將應用程式編譯並且輸出到新的目錄 `dist` 底下。這個 `--configuration production`/`-c production` 尾隨在 `ng build` 的參數，會移除在正式產品版本上不需要的東西。

部署你的應用程式
--------

 為了部署你的應用程式，你可以複製 `dist/my-project-name` 資料夾底下的內容到你的伺服器上。
 因為這些都是靜態檔案，你可以將這些檔案放到任何能夠提供檔案的伺服器上，像是：

* Node.js
* Java
* .NET

你可以使用任何後端，像是 Firebase、Google Cloud 或 App Engine。

接著要做什麼
------

現在你已經建立了基本的應用程式，但是你的 Angular 學習旅程才剛開始，你可以探索 Angular 文件學習更多，像是：

* 英雄之旅(正體中文版)：一個深入的 Angular 重點課程，像是使用服務（service）、導覽（navigation）以及從伺服器取得資料。
* Angular 元件 指南：一系列的文章，主題包括生命週期、元件互動以及檢視封裝。
* 表單 指南：文章內容包含建立 Angular 的響應式表單、輸入驗證以及建立動態表單。
總結
--

以上。希望你喜歡 Angular！

* 前頁
* Overview: Client-side JavaScript frameworks


開始寫我們的 Svelte 待辦清單應用程式 - 學習該如何開發 Web

開始寫我們的 Svelte 待辦清單應用程式
======================

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁

現在我們已經對 Svelte 運作機制有初步的了解後，就能開始建構我們的範例應用程式：一個待辦清單。此篇文章中，我們會先確認應用程式所需的功能有哪些，接著我們會建立 `Todos.svelte` 元件並寫一些靜態標記（markup）語言和樣式，待一切準備就緒後，就能開始開發我們待辦清單應用程式的相關功能，隨著後續文章會逐漸充實它。

我們想要讓使用者們能夠瀏覽、新增和刪除任務，也能註記它們以視為完成。這將是我們在走這個教學系列時會開發到的基本功能，此外，在開發過程中我們將會看到一些更進階的概念。

|  |  |
| --- | --- |
| 預備知識： |
 推薦你至少需熟悉基本的
 HTML、
 CSS 與
 JavaScript
 等程式語言且具備終端機/命令列環境 (en-US)基本知識。

你將會需要一個安裝了 node 和 npm 的終端機來編譯和建置你的應用程式。 |
| 學習目標： | 學習如何建立一個 Svelte 元件並於另一個元件內渲染它，透過屬性（props）傳遞資料並保存它的狀態。 |

和我們一起撰寫程式碼
----------

### Git

複製 Github 儲存庫（假如你還沒完成）如下：

```bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git
```

接著取得當前應用程式狀態，執行如下：

```bash
cd mdn-svelte-tutorial/02-starting-our-todo-app
```

或直接下載資料夾內容如下：

```bash
npx degit opensas/mdn-svelte-tutorial/02-starting-our-todo-app
```

記得執行 `npm install && npm run dev` 以開發模式來運行你的應用程式。

### REPL

為了使用 REPL 和我們一起撰寫程式碼，點擊如下連結：

https://svelte.dev/repl/b7b831ea3a354d3789cefbc31e2ca495?version=3.23.2

待辦清單應用程式特徵
----------

這是我們預計完成待辦清單應用程式的樣子：

![typical todo list app, with a title of 'what needs to be done', an input to enter more todos, and a list of todos with checkboxes](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning/01-todo-list-app.png)

使用這個使用者介面，我們的使用者將能夠做到以下事情：

* 瀏覽他們的任務。
* 註記任務為已完成或待辦理，而不是刪除它們。
* 刪除任務。
* 新增任務。
* 藉由狀態來過濾那些任務：全部、現行或已完成任務。
* 編輯任務。
* 註記全部任務為現行或已完成狀態。
* 刪除全部已完成任務。
建立我們第一個元件
---------

建立 `Todos.svelte` 元件——這將包含我們的待辦任務。

1. 建立新資料夾—— `src/components`。

**備註：** 你可以把元件們放在 `src` 資料夾當中的任何地方，但放在 `components` 資料夾是比較常見的做法，也讓你可以更容易地找到它們。
2. 建立 `src/components/Todos.svelte` 檔案並包含以下內容：

```html
<h1>Svelte To-Do list</h1>
```

3. 改變 `public/index.html` 中的 `title` 元素內容為 *Svelte To-do list*：

```html
<title>Svelte To-Do list</title>
```

4. 打開 `src/App.svelte` 並替換為以下內容：

```html
<script>
 import Todos from "./components/Todos.svelte";
</script>

<Todos />
```

5. 在開發模式中，當定義屬性沒有存在於元件時，Svelte 將會在瀏覽器主控台發出警告；以此例來看，當我們於 `src/main.js` 實例化 `App` 元件時，由於我們已經明確定義出 `name` 屬性，但並無實際在 `App` 中使用到。所以主控台現在應該會給你一個警告訊息，如「<App> was created with unknown prop 'name'」。而為了排除這個問題，從 `src/main.js` 中移除 `name` 屬性；看起來應該要像是如下這樣：

```js
import App from "./App.svelte";

const app = new App({
  target: document.body,
});

export default app;
```

假如你現在檢查你的測試伺服器 URL，應該會看到 `Todos.svelte` 元件已經被渲染出如下畫面：

![basic component rendering which a title that says 'Svelte to-do list'](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning/02-todos-component-rendered.png)

加上靜態標記語言
--------

我們將會從應用程式的靜態標記語言表示開始，所以你將會看到它長什麼樣子。複製並貼上下面內容至 `Todos.svelte` 元件檔案以取代現有內容：

```html
<!-- Todos.svelte -->
<div class="todoapp stack-large">
  <!-- NewTodo -->
  <form>
    <h2 class="label-wrapper">
      <label for="todo-0" class="label\_\_lg"> What needs to be done? </label>
    </h2>
    <input type="text" id="todo-0" autocomplete="off" class="input input\_\_lg" />
    <button type="submit" disabled="" class="btn btn\_\_primary btn\_\_lg">
      Add
    </button>
  </form>

  <!-- Filter -->
  <div class="filters btn-group stack-exception">
    <button class="btn toggle-btn" aria-pressed="true">
      <span class="visually-hidden">Show</span>
      <span>All</span>
      <span class="visually-hidden">tasks</span>
    </button>
    <button class="btn toggle-btn" aria-pressed="false">
      <span class="visually-hidden">Show</span>
      <span>Active</span>
      <span class="visually-hidden">tasks</span>
    </button>
    <button class="btn toggle-btn" aria-pressed="false">
      <span class="visually-hidden">Show</span>
      <span>Completed</span>
      <span class="visually-hidden">tasks</span>
    </button>
  </div>

  <!-- TodosStatus -->
  <h2 id="list-heading">2 out of 3 items completed</h2>

  <!-- Todos -->
  <ul role="list" class="todo-list stack-large" aria-labelledby="list-heading">
    <!-- todo-1 (editing mode) -->
    <li class="todo">
      <div class="stack-small">
        <form class="stack-small">
          <div class="form-group">
            <label for="todo-1" class="todo-label">
              New name for 'Create a Svelte starter app'
            </label>
            <input
 type="text"
 id="todo-1"
 autocomplete="off"
 class="todo-text" />
          </div>
          <div class="btn-group">
            <button class="btn todo-cancel" type="button">
              Cancel
              <span class="visually-hidden"
 >renaming Create a Svelte starter app</span
 >
            </button>
            <button class="btn btn\_\_primary todo-edit" type="submit">
              Save
              <span class="visually-hidden"
 >new name for Create a Svelte starter app</span
 >
            </button>
          </div>
        </form>
      </div>
    </li>

    <!-- todo-2 -->
    <li class="todo">
      <div class="stack-small">
        <div class="c-cb">
          <input type="checkbox" id="todo-2" checked />
          <label for="todo-2" class="todo-label">
            Create your first component
          </label>
        </div>
        <div class="btn-group">
          <button type="button" class="btn">
            Edit
            <span class="visually-hidden">Create your first component</span>
          </button>
          <button type="button" class="btn btn\_\_danger">
            Delete
            <span class="visually-hidden">Create your first component</span>
          </button>
        </div>
      </div>
    </li>

    <!-- todo-3 -->
    <li class="todo">
      <div class="stack-small">
        <div class="c-cb">
          <input type="checkbox" id="todo-3" />
          <label for="todo-3" class="todo-label">
            Complete the rest of the tutorial
          </label>
        </div>
        <div class="btn-group">
          <button type="button" class="btn">
            Edit
            <span class="visually-hidden"
 >Complete the rest of the tutorial</span
 >
          </button>
          <button type="button" class="btn btn\_\_danger">
            Delete
            <span class="visually-hidden"
 >Complete the rest of the tutorial</span
 >
          </button>
        </div>
      </div>
    </li>
  </ul>

  <hr />

  <!-- MoreActions -->
  <div class="btn-group">
    <button type="button" class="btn btn\_\_primary">Check all</button>
    <button type="button" class="btn btn\_\_primary">Remove completed</button>
  </div>
</div>
```

再次確認已經渲染出來的畫面，你應該會看到如下樣子：

![A todo list app, but unstyled, with a title of what needs to be done, inputs, checkboxes, etc.](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning/03-unstyled-todo-app.png)

上面 HTML 標記語言的樣式並不是那麼好看，而且也沒有實際功能。儘管如此，接著來看看它和我們想要的功能如何關聯起來：

* 一個標籤和一個輸入框用來輸入新任務。
* 三個按鈕則依據狀態來過濾任務。
* 一個標籤用來顯示任務總數及已完成任務數量。
* 一個無順序清單，其中包含每一個任務項目。
* 編輯任務時，清單項目會有輸入框和兩個按鈕去取消或儲存變更。
* 如果任務處於非編輯狀態時，會有勾選框可以去設定已完成狀態和兩個按鈕去編輯或刪除任務。
* 最後有兩個按鈕可以去勾選/不勾選所有任務和刪除已完成的那些任務。

隨後的文章我們會逐一將這些功能完善，以及除此之外的功能等等。

### 待辦清單的無障礙特徵

你可能有注意到一些特殊的屬性。例如：

```html
<button class="btn toggle-btn" aria-pressed="true">
  <span class="visually-hidden">Show</span>
  <span>All</span>
  <span class="visually-hidden">tasks</span>
</button>
```

在這裡，`aria-pressed` 告訴輔助技術（像是螢幕閱讀器）按鈕可以是兩狀態其中之一：`pressed` 或 `unpressed`。可以想像類似於開關狀態。設定數值為 `true` 時，意謂著按鈕預設是被按下的。

`visually-hidden` 類別現在還沒有效果，因為我們還沒引入任何 CSS。一旦引入我們的樣式後，對於可以看見的使用者們會隱藏那些具有此類別的元素並仍能夠讓那些螢幕閱讀器使用者來使用——這是因為這些文字並不需要給可以看見的使用者看到；它們提供更多資訊讓那些螢幕閱讀器使用者了解按鈕實際可以做什麼，而不需要借助一些額外的視覺環境來幫助他們。

在往下看一點，你可以找到如下 `<ul>` 元素：

```html
<ul
 role="list"
 className="todo-list stack-large"
 aria-labelledby="list-heading"></ul>
```

`role` 屬性則幫助輔助技術去解釋元素具有什麼種類的語義數值——或它的目的。預設情況下，`<ul>` 會被視為清單，但在加上樣式之後會破壞原有性質。明確定義規則為「list」將能復原 `<ul>` 元素本身意義。假如你想要知道更多為什麼需要明確定義的話，可以去參考 Scott O'Hara 的「Fixing Lists」文章。

`aria-labelledby` 屬性則告訴輔助技術，我們把 `<h2>` 且 `id` 為 `list-heading` 作為描述下面清單用途的標籤。建立這種關聯為清單提供更多上下文資訊，這可以幫助螢幕閱讀器使用者更好地去了解它的目的。

這似乎是討論 Svelte 如何處理無障礙性的好時機；讓我們繼續看下去。

Svelte 無障礙性支援
-------------

Svelte 特別重視無障礙性。目的是鼓勵開發人員盡可能預設地寫出無障礙程式碼。作為編譯器，Svelte 能靜態地分析我們的 HTML 範本，而當元件被編譯後，就能提出一些無障礙警告。

無障礙性（縮寫為 a11y）一直以來不容易寫正確，但 Svelte 將會協助並警告你，假如你寫出了不容易閱讀的標記語言。

舉例來說，假如我們新增 `<img>` 元素至我們的 `todos.svelte` 元件，但沒有對應的 `alt` 屬性時：

```html
<h1>Svelte To-Do list</h1>

<img height="32" width="88" src="https://www.w3.org/WAI/wcag2A" />
```

編譯器將會報出下列警告：

```bash
(!) Plugin svelte: A11y: <img> element should have an alt attribute
src/components/Todos.svelte
1: <h1>Svelte To-Do list</h1>
2:
3: <img height="32" width="88" src="https://www.w3.org/WAI/wcag2A">
   ^
created public/build/bundle.js in 220ms

[2020-07-15 04:07:43] waiting for changes...
```

此外，在我們呼叫編譯器之前，編輯器也會顯示這個警告：

![A code editor window showing an image tag, with a popup error message saying that the element should have an alt attribute](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning/04-svelte-accessibility-support.png)

你可以使用以 `svelte-ignore` 開頭的註解，來告訴 Svelte 去忽略下一個標記語言區塊的警告，像是如下：

```html
<!-- svelte-ignore a11y-missing-attribute -->
<img height="32" width="88" src="https://www.w3.org/WAI/wcag2A" />
```

**備註：** 透過 VSCode 你可以點擊 *Quick fix...* 連結或按 `Ctrl` + `.` 來自動地加上這個忽略註解。

假如你想要全域地關閉這個警告，可以在你的 `rollup.config.js` 檔案中調整 `Svelte` 外掛組態並補上這個 `onwarn` 處理器即可，像是如下：

```js
plugins: [
  svelte({
    dev: !production,
    css: css => {
      css.write('public/build/bundle.css');
    },
    // Warnings are normally passed straight to Rollup. You can
    // optionally handle them here, for example to squelch
    // warnings with a particular code
    onwarn: (warning, handler) => {
      // e.g. I don't care about screen readers -> please DON'T DO THIS!!!
      if (warning.code === 'a11y-missing-attribute') return;

      // let Rollup handle all other warnings normally
      handler(warning);
    }
  }),

  ...
]
```

在設計上，這些警告被實現於編譯器本身，而不是你可以選擇性加入專案的外掛。理念是建立在預設會檢核你的標記語言有無 a11y 問題並讓你可以選擇排除特定警告。

**備註：** 假如你有足夠充分的理由，則可以先停用這些警告，例如當你在快速建構原型時。成為一個優秀的網路公民並讓你的頁面盡可能廣泛地被用戶閱讀是很重要的。

Svelte 透過 eslint-plugin-jsx-a11y 來檢核無障礙性規則，它是提供在 JSX 元素上許多無障礙性規則靜態檢核的一個 ESLint 外掛。Svelte 企圖將它們全部實現於編譯器中，而大部分都已經移植至 Svelte 中。你可以在 GitHub 中看到尚未實現的無障礙檢核。點選規則的連結可以查閱它們各自的涵義。

為標記語言加上樣式
---------

讓我們來將待辦清單變得好看些。將以下內容取代至 `public/global.css` 檔案中：

```css
/\* RESETS \*/
\*,
\*::before,
\*::after {
  box-sizing: border-box;
}
\*:focus {
  outline: 3px dashed #228bec;
  outline-offset: 0;
}
html {
  font: 62.5% / 1.15 sans-serif;
}
h1,
h2 {
  margin-bottom: 0;
}
ul {
  list-style: none;
  padding: 0;
}
button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: normal;
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;
  -webkit-appearance: none;
}
button::-moz-focus-inner {
  border: 0;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}
button,
input {
  overflow: visible;
}
input[type="text"] {
  border-radius: 0;
}
body {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  font:
    1.6rem/1.25 Arial,
    sans-serif;
  background-color: #f5f5f5;
  color: #4d4d4d;
}
@media screen and (min-width: 620px) {
  body {
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}
/\*END RESETS\*/

/\* GLOBAL STYLES \*/
.form-group > input[type="text"] {
  display: inline-block;
  margin-top: 0.4rem;
}
.btn {
  padding: 0.8rem 1rem 0.7rem;
  border: 0.2rem solid #4d4d4d;
  cursor: pointer;
  text-transform: capitalize;
}
.btn.toggle-btn {
  border-width: 1px;
  border-color: #d3d3d3;
}
.btn.toggle-btn[aria-pressed="true"] {
  text-decoration: underline;
  border-color: #4d4d4d;
}
.btn\_\_danger {
  color: #fff;
  background-color: #ca3c3c;
  border-color: #bd2130;
}
.btn\_\_filter {
  border-color: lightgrey;
}
.btn\_\_primary {
  color: #fff;
  background-color: #000;
}
.btn\_\_primary:disabled {
  color: darkgrey;
  background-color: #565656;
}
.btn-group {
  display: flex;
  justify-content: space-between;
}
.btn-group > \* {
  flex: 1 1 49%;
}
.btn-group > \* + \* {
  margin-left: 0.8rem;
}
.label-wrapper {
  margin: 0;
  flex: 0 0 100%;
  text-align: center;
}
.visually-hidden {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}
[class\*="stack"] > \* {
  margin-top: 0;
  margin-bottom: 0;
}
.stack-small > \* + \* {
  margin-top: 1.25rem;
}
.stack-large > \* + \* {
  margin-top: 2.5rem;
}
@media screen and (min-width: 550px) {
  .stack-small > \* + \* {
    margin-top: 1.4rem;
  }
  .stack-large > \* + \* {
    margin-top: 2.8rem;
  }
}
.stack-exception {
  margin-top: 1.2rem;
}
/\* END GLOBAL STYLES \*/

.todoapp {
  background: #fff;
  margin: 2rem 0 4rem 0;
  padding: 1rem;
  position: relative;
  box-shadow:
    0 2px 4px 0 rgba(0, 0, 0, 0.2),
    0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
@media screen and (min-width: 550px) {
  .todoapp {
    padding: 4rem;
  }
}
.todoapp > \* {
  max-width: 50rem;
  margin-left: auto;
  margin-right: auto;
}
.todoapp > form {
  max-width: 100%;
}
.todoapp > h1 {
  display: block;
  max-width: 100%;
  text-align: center;
  margin: 0;
  margin-bottom: 1rem;
}
.label\_\_lg {
  line-height: 1.01567;
  font-weight: 300;
  padding: 0.8rem;
  margin-bottom: 1rem;
  text-align: center;
}
.input\_\_lg {
  padding: 2rem;
  border: 2px solid #000;
}
.input\_\_lg:focus {
  border-color: #4d4d4d;
  box-shadow: inset 0 0 0 2px;
}
[class\*="\_\_lg"] {
  display: inline-block;
  width: 100%;
  font-size: 1.9rem;
}
[class\*="\_\_lg"]:not(:last-child) {
  margin-bottom: 1rem;
}
@media screen and (min-width: 620px) {
  [class\*="\_\_lg"] {
    font-size: 2.4rem;
  }
}
.filters {
  width: 100%;
  margin: unset auto;
}
/\* Todo item styles \*/
.todo {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
.todo > \* {
  flex: 0 0 100%;
}
.todo-text {
  width: 100%;
  min-height: 4.4rem;
  padding: 0.4rem 0.8rem;
  border: 2px solid #565656;
}
.todo-text:focus {
  box-shadow: inset 0 0 0 2px;
}
/\* CHECKBOX STYLES \*/
.c-cb {
  box-sizing: border-box;
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-weight: 400;
  font-size: 1.6rem;
  line-height: 1.25;
  display: block;
  position: relative;
  min-height: 44px;
  padding-left: 40px;
  clear: left;
}
.c-cb > label::before,
.c-cb > input[type="checkbox"] {
  box-sizing: border-box;
  top: -2px;
  left: -2px;
  width: 44px;
  height: 44px;
}
.c-cb > input[type="checkbox"] {
  -webkit-font-smoothing: antialiased;
  cursor: pointer;
  position: absolute;
  z-index: 1;
  margin: 0;
  opacity: 0;
}
.c-cb > label {
  font-size: inherit;
  font-family: inherit;
  line-height: inherit;
  display: inline-block;
  margin-bottom: 0;
  padding: 8px 15px 5px;
  cursor: pointer;
  touch-action: manipulation;
}
.c-cb > label::before {
  content: "";
  position: absolute;
  border: 2px solid currentColor;
  background: transparent;
}
.c-cb > input[type="checkbox"]:focus + label::before {
  border-width: 4px;
  outline: 3px dashed #228bec;
}
.c-cb > label::after {
  box-sizing: content-box;
  content: "";
  position: absolute;
  top: 11px;
  left: 9px;
  width: 18px;
  height: 7px;
  transform: rotate(-45deg);
  border: solid;
  border-width: 0 0 5px 5px;
  border-top-color: transparent;
  opacity: 0;
  background: transparent;
}
.c-cb > input[type="checkbox"]:checked + label::after {
  opacity: 1;
}
```

當我們為標記語言加上樣式後，現在看起來好多了：

![Our todo list app, styled, with a title of 'what needs to be done', an input to enter more todos, and a list of todos with checkboxes](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning/05-styled-todo-app.png)

到目前為止的程式碼
---------

### Git

若想要看到本文結束後程式碼所呈現的最終結果，你可以參照下列的方式存取我們已經複製下來的儲存庫：

```bash
cd mdn-svelte-tutorial/03-adding-dynamic-behavior
```

或直接下載資料夾內容：

```bash
npx degit opensas/mdn-svelte-tutorial/03-adding-dynamic-behavior
```

記得執行 `npm install && npm run dev` 以開發模式來運行你的應用程式。

### REPL

若要在 REPL 看見當前程式碼狀態，請點擊如下連結：

https://svelte.dev/repl/c862d964d48d473ca63ab91709a0a5a0?version=3.23.2

總結
--

隨著我們對標記語言加上樣式後，待辦清單應用程式開始逐漸成形，我們終於可以專注在需要實現的功能上了。

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁


Angular 新手入門 - 學習該如何開發 Web

Angular 新手入門
============

* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁

現在該看一下 Google 的 Angular 框架了，這是另一個你經常會遇到的前端框架。在本文中，我們將會探索 Angular 所提供的功能、安裝必備工具、建立範例應用程式，並進一步瞭解 Angular 的基本架構。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉基本的HTML、
 CSS、以及
 JavaScript程式語言，具備
 終端機/命令列環境 (en-US)的基本知識。
  |
| 學習目標： | 設立本機的Angular開發環境，建立初始應用程式，瞭解Angular基本運作方式。 |

什麼是 Angular？
------------

Angular 是一個基於TypeScript的開發平台。身為一個平台，Angular 包含：

* 一個元件化的框架，用來建構可延展的 Web 應用程式。
* 一整套經深思熟慮而整合出來的函式庫，包含各種不同的功能，包含路由機制、表單管理、Client/Server 通訊，以及更多。
* 一組完善的開發工具，幫助你開發、建置、測試、更新你的程式碼。

你能使用 Angular 去開發多種不同規模的專案，小至單人應用式專案，大至大型企業級專案。Angular 的設計宗旨是讓後續更新升級十分容易，因此你能輕鬆的使用最新版本進行開發。最棒的是，Angular 的開發生態圈龐大，有超過一百七十萬的使用者、套件開發者以及內容創作者。

在開始探索 Angular 之前，你要先瞭解 Angular CLI。Angular CLI 是一種快速、簡單、備受推崇的 Angular 程式開發方式。Angular CLI 能讓許多任務變得更容易，以下是一些範例：

|  |  |
| --- | --- |
| `ng build` | 編譯Angular開發的程式到輸出目錄 |
| `ng serve` | 建構應用程式並啟動開發伺服器，當檔案變化時重新建構 |
| `ng generate` | 根據原理圖去生成或修改檔案 |
| `ng test` | 對指定專案進行單元測試 |
| `ng e2e` | 編譯並啟動Angular程式，並執行端到端測試 |

在建造應用程式時，你會發現 Angular CLI 是很實用的工具。

你會建立的專案
-------

這一系列的教學會帶領你建立一個待辦事項程式。透過這個程式，你會學到如何使用 Angular 管理、編輯、增加、刪除和篩選項目。

前置工作
----

想在本地端安裝 Angular，你必須安裝：

* **Node.js**
 Angular 需要使用 Node.js 的當前版、活躍 LTS 版或是長期維護 LTS 版若需特定版本的資訊，可以參閱在package.json檔案之中的`engines`關鍵字。
 若想更了解如何安裝 Node.js，可參閱nodejs.org。若你不確定你目前使用的 Node.js 版本號，請在終端機輸入`node -v`查閱。
* **npm 套件管理器**
 Angular、Angular CLI 以及 Angular 應用程式都依賴 npm 套件 來實現許多特性與功能。想下載並安裝 npm 套件，你需要使用 npm 套件管理器。本教學使用npm 客戶端命令列介面，該介面預設安裝在`Node.js`。欲檢查你是否安裝了 npm 客戶端，請在終端視窗中執行`npm -v`。
設置你的應用程式
--------

你可以在終端機裡透過使用 Angular 命令列介面（CLI）來產生、建置、測試和佈署 Angular 應用程式。請在終端機裡執行以下指令來安裝 Angular 命令列介面：

```js
npm install -g @angular/cli
```

Angular CLI 的指令開頭均為`ng`，接著是你希望 CLI 進行的事項。在桌面目錄中，使用 `ng new`指令創建一個名為`todo`的專案：

```js
ng new todo --routing=false --style=css
```

使用`ng new`指令會在桌面創建一個小型入門 Angular 的專案。其他的標籤，`--routing`和`--style`，則是定義專案中的導航與樣式。本教學導覽會在後續篇章詳細解釋這些特性。

如果被詢問是否使用嚴格型別檢查模式，可以回覆「同意」。

輸入`cd`指令切換到你新開的專案：

```js
cd todo
```

想執行你的`todo`專案，請使用`ng serve`：

```js
ng serve
```

當 CLI 詢問是否使用情況分析，回覆`no`。

在瀏覽器中，導至http://localhost:4200/來查看你新建的專案。如果你更改任何原始資料夾，應用程式會自動加載更新。

當`ng serve`運行時，你或許會想開啟第二個終端機視窗來運行其他指令。如果你想中斷應用程式運行，請在終端機內按下`Ctrl+c`。

熟悉你的 Angular 程式
---------------

本教學導覽主要是用的程式原始檔在`src/app`資料夾內。由 CLI 自動產生的主要檔案如下：

1. `app.module.ts`：列出此專案使用的所有檔案。此檔案在專案中扮演中央樞紐的角色。
2. `app.component.ts`：又被稱為元件類別（Class），內含此專案主要頁面的相關邏輯
3. `app.component.html`：內含`AppComponent`所使用的網頁 html。這個檔案的內容也被視為元件模板（Template），此模板定義你在瀏覽器中看到的畫面。
4. `app.component.css`：內含`AppComponent`裡面的樣式。當你想定義某些樣式給特定模組使用，卻不希望影響到整體程式時，便可使用此檔案進行設定。

一個 Angular 的元件由三部份構成，分別是：模板、樣式、類別。舉例來說，`app.component.ts`、`app.component.html`、以及`app.component.css`一同構成`AppComponent`。此結構會將邏輯、畫面、樣式分開，如此一來便能讓程式更易於維護與擴張。

如此一來，你就能在一開始便依循最佳的實作慣例。

Angular CLI 也能產生`app.component.spec.ts`的元件測試檔案，但本次教學導覽不會深入探討測試的部分，所以你可以忽略這個檔案。

一旦你建立一個元件，CLI 就會按照你的命名在目錄中建立此四個檔案。

Angular 的專案結構
-------------

 Angular 基本上是用 TypeScript 作為主要開發的語言。
 簡單來說，TypeScript 是 JavaScript 的超集合，也就是說在 JavaScript 中使用的語法也同樣適用於 TypeScript。
 而 TypeScript 比純 JavaScript 更加強化型別的規範、寫法也更簡潔，因此使用 TypeScript 可以寫出更好維護的程式碼，並減少報錯的機率。

 在 Angular 框架裡，主要是由許多元件（Components）來組成。
 一個元件包含 HTML 頁面架構、樣式以及帶有元件裝飾器`@Component()`的 TypeScript 類別（class）。

### 類別

 類別(class)裡會放這個元件的邏輯、規則，
 舉例來說像是函式、事件監聽、屬性和參考等等。
 類別會放在一個像是`feature.component.ts`的檔案裡，而`feature`就是你的元件名稱。
 所以你可以建立一些檔案，並且命名為像是`header.component.ts`、`signup.component.ts`或是`feed.component.ts`。
 當你建立一個元件，它會帶有`@Component()` 這樣的裝飾器，裡面會有檔案的路徑指向，告訴 Angular 要去哪裡找 HTML 和 CSS 檔案。
 一個基本的元件寫法如下：

```js
import { Component } from "@angular/core";

@Component({
  selector: "app-item",
  // 接下來的檔案路徑會指出其它檔案在什麼位置
  templateUrl: "./item.component.html",
  styleUrls: ["./item.component.css"],
})
export class ItemComponent {
  // 程式碼寫在這裡
}
```

 這個元件會被稱為`ItemComponent`，它的選擇器是`app-item`。
 這個選擇器就像 HTML 的標籤，你可以把它放在其它的模版裡。
 當瀏覽器渲染到選擇器的時候，就會把這個元件的模版給渲染出來。
 這份教學文件會指引你建立兩個元件，並把其中一個放到另一個元件裡。

 Angular 的元件模組具備高度封裝性，讓專案結構看起來更直覺。
 而 Angular 的元件也讓專案更易於做單元測試、讓程式碼更易於解讀。

### HTML 模版

每個元件都會有 HTML 模版去告訴瀏覽器要如何渲染頁面。HTML 可以直接內嵌寫在類別（Class）的檔案內，或是用檔案路徑去指明其位置。

而要去指明位在他處的 HTML 檔案，就要使用`templateUrl`這個屬性，範例如下：

```js
@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
})
export class AppComponent {}
```

當你把 HTML 內嵌寫在類別（Class）的檔案內時，要使用`template`這個屬性，並用反引號把 HTML 包起來，範例如下：

```js
@Component({
  selector: "app-root",
  template: `<h1>Hi!</h1>`,
})
export class AppComponent {}
```

 Angular 還擴充了 HTML 的語法，讓你可以在元件中使用動態插值。
 當你的元件狀態改變時，Angular 會自動重新渲染該節點。
 下面的例子中會使用插值去插入一段文字來示範這個功能。

```html
<h1>{{ title }}</h1>
```

 雙花括號的地方就是告訴 Angular 插值顯示的地方。
 `title`的值就是從元件的類別來的：

```js
import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  title = "To do application";
}
```

當載入完元件和它的模版時，會在瀏覽器看到如下的呈現：

```html
<h1>To do application</h1>
```

### Styles

 每個專案裡會有個全域性的樣式設定檔案`styles.css`，所有的元件都可以繼承它，並且再各自增加或覆寫它的樣式設定。
 你可以直接在裝飾器`@Component()`裡寫出這個元件特有的樣式，或是用路徑指明它的 CSS 檔案位置。

要直接在元件裝飾器內設定樣式，就要使用`styles`這個屬性，範例如下：

```js
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styles: ['h1 { color: red; }']
})
```

但基本上，元件通常會把樣式另外寫在一個檔案，並用`styleUrls`去指明它的檔案路徑，如下：

```js
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
```

以此去寫出這個元件特定的樣式，讓你更容易去維護你的 CSS 檔案。

總結
--

以上這些就是關於 Angular 的簡介。這時候的你，應該已經對 Angular 的運作有基本的了解，並準備建立一個 Angular 的專案。在下一篇文章裡，我們會更深入的應用這些知識，並且試著用 Angular 去寫一個「待辦清單」。

* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁


篩選我們的待辦事項項目 - 學習該如何開發 Web

篩選我們的待辦事項項目
===========

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁

現在讓我們來增加功能，讓使用者篩選待辦事項，這樣他們就可以選擇查看進行中、已完成，或是全部的事項。

|  |  |
| --- | --- |
| 預備知識： |
 熟悉基礎的 HTML ，
 CSS 與
 JavaScript 語言，以及終端機/命令列 (en-US)的知識。
  |
| 學習目標： | 替我們的應用程式增加篩選功能。 |

我們的篩選功能程式碼
----------

篩選項目的功能是建立於你先前加入在 `app.component.ts` 的 `filter` 屬性

```js
filter: 'all' | 'active' | 'done' = 'all';
```

filter 屬性的預設值為 `all`，但是它也可以被設置為 `active` 或是 `done`。

增加篩選的控制項
--------

 在 `app.component.html` 新增下列 HTML 並且放在 **Add** 按鈕與項目表單之間。
 在下列這個片段中，你的 HTML 中現有的部分是被註解起來的，這樣讓你可以更清楚地看到該把按鈕放在什麼位置。

```html
<!-- <button class="btn-primary" (click)="addItem(newItem.value)">Add</button>
 -->

<!-- Buttons that show all, still to do, or done items on click -->
<div class="btn-wrapper">
  <button
 class="btn btn-menu"
 [class.active]="filter == 'all'"
 (click)="filter = 'all'">
    All
  </button>

  <button
 class="btn btn-menu"
 [class.active]="filter == 'active'"
 (click)="filter = 'active'">
    To Do
  </button>

  <button
 class="btn btn-menu"
 [class.active]="filter == 'done'"
 (click)="filter = 'done'">
    Done
  </button>
</div>

<!-- <h2>{{items.length}} item(s)</h2>
 <ul>... -->
```

點擊按鈕改變 `filter` 點擊按鈕改變 filter 的值，這將會決定哪些 `項目` 應該顯示，以及 Angular 套用於啟用狀態按鈕的樣式。

* 如果使用者點擊了 **All** 的按鈕，全部的項目都會顯示。
* 如果使用者點擊了 **To do** 的按鈕，只有 `done` 的值為 `false` 的項目會顯示。
* 如果使用者點擊了 **Done** 的按鈕，只有 `done` 的值為 `true` 的項目會顯示。

 class 屬性可以用使用方括號 `[]` 來綁定，控制按鍵上的文字顏色，
 當 `filter` 的值符合表達式的時候，類別綁定器 `[class.active]` 會套用 `active` class。
 例如：當使用者點擊了 **Done** 按鈕，將會把 `filter` 的值設為 `done` ，這時候 `filter == 'done'` 這個表達式將會成立，也就是為邏輯真值 `true`。
 當 `filter` 的值為 `done` ， Angular 就會套用 `active` class 到 **Done** 按鈕上，使得文字變為綠色。
 而當使用者點擊了其他的按鈕，使得 `filter` 的值不再是 `done` 時，就不會再套用綠色在文字上了。

結語
--

真是快！因為你已經有了 `filter` 的程式碼在 `app.component.ts` 中，你所需要做的就是編輯模板，以便於提供篩選項的控制項。我們的下一個，同時也是最後一個主題，探討了如何建立用於生產環境的 Angular 應用程式，並且提供了近一步的資源來帶領繼續你踏上學習之旅。

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁


Beginning our React todo list - 學習該如何開發 Web

Beginning our React todo list
=============================

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)

我們被賦予做出一個 React 原型 app 的任務--這個 app 將允許使用者新增、編輯、刪除任務；且可以標記任務完成而不被刪除。文章將會與你一起完成一個基本 `App` component 的結構與畫面，以便稍後與其他 component 互動。

**備註：** 如果你需要檢查自己的程式碼與範例之間的差異，可以連到 todo-react repository，這裡有我們完整的程式碼。 Todo list 作品示範：https://mdn.github.io/todo-react-build/。

|  |  |
| --- | --- |
| 預備知識： |
 知道 HTML,
 CSS與JavaScript的核心語法、操作基本終端機指令
 terminal/command line (en-US).
  |
| 實作目標： | 介紹待辦事項清單案例研究，並掌握基本`App`結構和樣式。 |

在軟體開發中，user story 透過使用者觀點傳達開發目標。動手開發前先定義好 user stories 可以幫助我們專注於需要工作的項目，而我們這個案例中的 app 需要實現以下功能：

使用者可以...

* 讀取任務清單
* 可以用滑鼠或鍵盤給清單增加新任務
* 可以用滑鼠或鍵盤為任務標記完成
* 可以用滑鼠或鍵盤刪除任務
* 可以用滑鼠或鍵盤編輯任務
* 可以分類查看特定子群組：全部任務、待處理任務、已完成任務

我們將一一處理這些使用者故事。

專案開始前，先清理一下
-----------

終端機指令 `create-react-app` 會產生一些我們這個專案用不到的檔案，讓我們來清理一下。

* 我們不需要個別 component 的樣式表，所以首先請刪除`App.js`檔案上方的`import './App.css'`
* 我們也不需使用 `logo.svg`，請一並刪除這項 import

接著，請複製貼上以下終端機指令，以刪除專案中不需要的檔案；刪除前請確認你在專案的根目錄中！

```bash
# 移動到專案中的src資料夾
cd src
# 刪除一些檔案
rm -- App.test.js App.css logo.svg serviceWorker.js setupTests.js
# 回到專案上一層
cd ..
```

小提示：

* 刪除的檔案之中包含兩個測試檔，這個練習中不會涵蓋測試教學。
* 如果你停止 server 以便在終端機中刪除上述檔案，請記得使用`npm start`指令再次連上 server
專案起點
----

作為專案起始點 starting point ，我們會提供兩件事：一個新的 `App()` function 來取代原生預設，以及一些 CSS 美化我們的 app。

### The JSX

複製以下片段貼到 `App.js` 中取代原先的 `App()` function:

```js
function App(props) {
  return (
    <div className="todoapp stack-large">
      <h1>TodoMatic</h1>
      <form>
        <h2 className="label-wrapper">
          <label htmlFor="new-todo-input" className="label\_\_lg">
            What needs to be done?
          </label>
        </h2>
        <input
          type="text"
          id="new-todo-input"
          className="input input\_\_lg"
          name="text"
          autoComplete="off"
        />
        <button type="submit" className="btn btn\_\_primary btn\_\_lg">
          Add
        </button>
      </form>
      <div className="filters btn-group stack-exception">
        <button type="button" className="btn toggle-btn" aria-pressed="true">
          <span className="visually-hidden">Show </span>
          <span>all</span>
          <span className="visually-hidden"> tasks</span>
        </button>
        <button type="button" className="btn toggle-btn" aria-pressed="false">
          <span className="visually-hidden">Show </span>
          <span>Active</span>
          <span className="visually-hidden"> tasks</span>
        </button>
        <button type="button" className="btn toggle-btn" aria-pressed="false">
          <span className="visually-hidden">Show </span>
          <span>Completed</span>
          <span className="visually-hidden"> tasks</span>
        </button>
      </div>
      <h2 id="list-heading">
        3 tasks remaining
      </h2>
      <ul
        role="list"
        className="todo-list stack-large stack-exception"
        aria-labelledby="list-heading"
      >
        <li className="todo stack-small">
          <div className="c-cb">
            <input id="todo-0" type="checkbox" defaultChecked={true} />
            <label className="todo-label" htmlFor="todo-0">
              Eat
            </label>
          </div>
          <div className="btn-group">
            <button type="button" className="btn">
              Edit <span className="visually-hidden">Eat</span>
            </button>
            <button type="button" className="btn btn\_\_danger">
              Delete <span className="visually-hidden">Eat</span>
            </button>
          </div>
        </li>
        <li className="todo stack-small">
          <div className="c-cb">
            <input id="todo-1" type="checkbox" />
            <label className="todo-label" htmlFor="todo-1">
              Sleep
            </label>
          </div>
          <div className="btn-group">
            <button type="button" className="btn">
              Edit <span className="visually-hidden">Sleep</span>
            </button>
            <fbutton type="button" className="btn btn\_\_danger">
              Delete <span className="visually-hidden">Sleep</span>
            </button>
          </div>
        </li>
        <li className="todo stack-small">
          <div className="c-cb">
            <input id="todo-2" type="checkbox" />
            <label className="todo-label" htmlFor="todo-2">
              Repeat
            </label>
          </div>
          <div className="btn-group">
            <button type="button" className="btn">
              Edit <span className="visually-hidden">Repeat</span>
            </button>
            <button type="button" className="btn btn\_\_danger">
              Delete <span className="visually-hidden">Repeat</span>
            </button>
          </div>
        </li>
      </ul>
    </div>
  );
}
```

再來，請打開 `public/index.html` 改掉 `<title>` 元素中的文字，將文字改為 `TodoMatic`，這樣才能對應到上述 `<h1>` (en-US) 的文字。

```html
<title>TodoMatic</title>
```

當你更新瀏覽器，你應該可以看到以下畫面：

![todo-matic app, unstyled, showing a jumbled mess of labels, inputs, and buttons](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning/unstyled-app.png)

畫面醜醜的對吧，而且還沒有實際功能，沒關係讓我們馬上來美化它。

在此之前，回頭複習一下我們的 JSX，以及它與用戶故事的對應關係：

* 我們有一個 `<form>` (en-US) 元素，其中包含可以寫入新任務的 `<input type="text">` (en-US) 以及一個 button 來送出表單。
* 我們還有一個`<button>`陣列，用以過濾不同任務狀態(全部、待辦、完成)。
* 下方接著一個標題告訴我們擁有多少任務
* 我們有 3 個預設任務寫在無序清單`<ul>`元素中，其中包含的各項單一任務都是使用`<li>` (en-US)。 並且在各項任務下方增加 `<input type="checkbox">` (en-US)來勾選此任務是否完成，以及編輯、刪除按鈕`<button>`。

這個表單`<form>` (en-US) 允許我們新增、管理任務， `<button>`幫助我們篩選任務狀態，`<ul><li>` 則負責展示任務清單。接著由於缺乏編輯任務的 UI，讓我們開始來處理畫面美化的部分吧。

### Accessibility features 無障礙設定

你可能已經注意到一些不常見的屬性，例如：

```html
<button type="button" className="btn toggle-btn" aria-pressed="true">
  <span className="visually-hidden">Show </span>
  <span>all</span>
  <span className="visually-hidden"> tasks</span>
</button>
```

`aria-pressed` 元素可以跟輔助工具對話(像是螢幕閱讀器)，這個 button 總是處於： `pressed` 或 `unpressed`其中之一的狀態。可以想像它們如同 `on` 與 `off`。設定 `true` 代表這個 button 預設開啟`pressed`。

class `visually-hidden` 在我們加入 CSS 前還不會有作用；當我們加入樣式後，這個 class 會對一般使用者隱藏，因為視覺使用者不需要這些文字；而仰賴閱讀器的使用者則可以聽到更多輔助文字來提高的讀取理解與體驗。

你還可以發現 `<ul>` (en-US) 元素中：

```html
<ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading"></ul>
```

`role` 屬性會向科技輔具說明各種元素分別代表什麼用途。雖然瀏覽器預設 `<ul>` 為清單，但是由於樣式表會破壞這個功能，因此需要使用 `role` 屬性保留 "list" 清單這個意思。如果你想了解更多 `role` 屬性的重要性，請參照 Scott O'Hara's article,「Fixing Lists」。

`aria-labelledby` 屬性告訴科技輔具，我們將清單標題 list heading 設為 label，以描述下方的程式碼片段；將這些關聯設定好會幫助使用科技輔具的朋友更好的理解前因後果。

最後，我們清單中的 labels 與 inputs 對 JSX 而言將會有些特別的屬性：

```html
<input id="todo-0" type="checkbox" defaultChecked="{true}" />
<label className="todo-label" htmlFor="todo-0"> Eat </label>
```

`<input/ >`中的 `defaultChecked` 屬性會讓 React 預設勾選某項目。假如我們同一般寫 HTML 一樣使用 `checked`，React 會紀錄一些：handling events on the checkbox 警告到瀏覽器 console 中，而這些是我們想避免的。不過先別擔心，我們在稍後討論事件的章節會教大家解決這個問題。

`htmlFor` 屬性對應 HTML 中的 `for` 屬性 ，我們不能在 JSX 中使用`for` 屬性因為 `for` 是保留字，因此 React 使用 `htmlFor` 取代 `for`。

備註：

* 在 JSX 屬性使用 boolean 值 ：`true` 與 `false`， 必須將之包在`{ }`中，如果只寫 `defaultChecked="true"`，這個值將是 `"true"` — 一個字串 string literal。 請記得 — 這是 JavaScript，不是 HTML!
* `aria-pressed` 屬性在我們稍早寫的程式碼片段中有一個 `"true"` 的值，因為`aria-pressed` 不像`checked`真的是一個布林值屬性。
### Implementing our styles 實作 CSS 美化

將以下的 CSS 貼進 `src/index.css` 取代原本的預設內容：

```css
/\* RESETS \*/
\*,
\*::before,
\*::after {
  box-sizing: border-box;
}
\*:focus {
  outline: 3px dashed #228bec;
  outline-offset: 0;
}
html {
  font: 62.5% / 1.15 sans-serif;
}
h1,
h2 {
  margin-bottom: 0;
}
ul {
  list-style: none;
  padding: 0;
}
button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: normal;
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;
  -webkit-appearance: none;
}
button::-moz-focus-inner {
  border: 0;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}
button,
input {
  overflow: visible;
}
input[type="text"] {
  border-radius: 0;
}
body {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  font:
    1.6rem/1.25 Arial,
    sans-serif;
  background-color: #f5f5f5;
  color: #4d4d4d;
}
@media screen and (min-width: 620px) {
  body {
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}
/\*END RESETS\*/
/\* GLOBAL STYLES \*/
.form-group > input[type="text"] {
  display: inline-block;
  margin-top: 0.4rem;
}
.btn {
  padding: 0.8rem 1rem 0.7rem;
  border: 0.2rem solid #4d4d4d;
  cursor: pointer;
  text-transform: capitalize;
}
.btn.toggle-btn {
  border-width: 1px;
  border-color: #d3d3d3;
}
.btn.toggle-btn[aria-pressed="true"] {
  text-decoration: underline;
  border-color: #4d4d4d;
}
.btn\_\_danger {
  color: #fff;
  background-color: #ca3c3c;
  border-color: #bd2130;
}
.btn\_\_filter {
  border-color: lightgrey;
}
.btn\_\_primary {
  color: #fff;
  background-color: #000;
}
.btn-group {
  display: flex;
  justify-content: space-between;
}
.btn-group > \* {
  flex: 1 1 49%;
}
.btn-group > \* + \* {
  margin-left: 0.8rem;
}
.label-wrapper {
  margin: 0;
  flex: 0 0 100%;
  text-align: center;
}
.visually-hidden {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}
[class\*="stack"] > \* {
  margin-top: 0;
  margin-bottom: 0;
}
.stack-small > \* + \* {
  margin-top: 1.25rem;
}
.stack-large > \* + \* {
  margin-top: 2.5rem;
}
@media screen and (min-width: 550px) {
  .stack-small > \* + \* {
    margin-top: 1.4rem;
  }
  .stack-large > \* + \* {
    margin-top: 2.8rem;
  }
}
.stack-exception {
  margin-top: 1.2rem;
}
/\* END GLOBAL STYLES \*/
.todoapp {
  background: #fff;
  margin: 2rem 0 4rem 0;
  padding: 1rem;
  position: relative;
  box-shadow:
    0 2px 4px 0 rgba(0, 0, 0, 0.2),
    0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
@media screen and (min-width: 550px) {
  .todoapp {
    padding: 4rem;
  }
}
.todoapp > \* {
  max-width: 50rem;
  margin-left: auto;
  margin-right: auto;
}
.todoapp > form {
  max-width: 100%;
}
.todoapp > h1 {
  display: block;
  max-width: 100%;
  text-align: center;
  margin: 0;
  margin-bottom: 1rem;
}
.label\_\_lg {
  line-height: 1.01567;
  font-weight: 300;
  padding: 0.8rem;
  margin-bottom: 1rem;
  text-align: center;
}
.input\_\_lg {
  padding: 2rem;
  border: 2px solid #000;
}
.input\_\_lg:focus {
  border-color: #4d4d4d;
  box-shadow: inset 0 0 0 2px;
}
[class\*="\_\_lg"] {
  display: inline-block;
  width: 100%;
  font-size: 1.9rem;
}
[class\*="\_\_lg"]:not(:last-child) {
  margin-bottom: 1rem;
}
@media screen and (min-width: 620px) {
  [class\*="\_\_lg"] {
    font-size: 2.4rem;
  }
}
.filters {
  width: 100%;
  margin: unset auto;
}
/\* Todo item styles \*/
.todo {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
.todo > \* {
  flex: 0 0 100%;
}
.todo-text {
  width: 100%;
  min-height: 4.4rem;
  padding: 0.4rem 0.8rem;
  border: 2px solid #565656;
}
.todo-text:focus {
  box-shadow: inset 0 0 0 2px;
}
/\* CHECKBOX STYLES \*/
.c-cb {
  box-sizing: border-box;
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-weight: 400;
  font-size: 1.6rem;
  line-height: 1.25;
  display: block;
  position: relative;
  min-height: 44px;
  padding-left: 40px;
  clear: left;
}
.c-cb > label::before,
.c-cb > input[type="checkbox"] {
  box-sizing: border-box;
  top: -2px;
  left: -2px;
  width: 44px;
  height: 44px;
}
.c-cb > input[type="checkbox"] {
  -webkit-font-smoothing: antialiased;
  cursor: pointer;
  position: absolute;
  z-index: 1;
  margin: 0;
  opacity: 0;
}
.c-cb > label {
  font-size: inherit;
  font-family: inherit;
  line-height: inherit;
  display: inline-block;
  margin-bottom: 0;
  padding: 8px 15px 5px;
  cursor: pointer;
  touch-action: manipulation;
}
.c-cb > label::before {
  content: "";
  position: absolute;
  border: 2px solid currentColor;
  background: transparent;
}
.c-cb > input[type="checkbox"]:focus + label::before {
  border-width: 4px;
  outline: 3px dashed #228bec;
}
.c-cb > label::after {
  box-sizing: content-box;
  content: "";
  position: absolute;
  top: 11px;
  left: 9px;
  width: 18px;
  height: 7px;
  transform: rotate(-45deg);
  border: solid;
  border-width: 0 0 5px 5px;
  border-top-color: transparent;
  opacity: 0;
  background: transparent;
}
.c-cb > input[type="checkbox"]:checked + label::after {
  opacity: 1;
}
```

儲存並更新瀏覽器後，你的 app 應當會有對應的美化。

小結
--

現在我們的待辦清單 app 終於比較像真正的 app 了！問題是它還沒真正提供功能，我們將在下一章解決這個問題。

* 前頁
* Overview: Client-side JavaScript frameworks
* 次頁  (en-US)


Vue 入門 - 學習該如何開發 Web

Vue 入門
======

* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁

現在來介紹我們的第三個框架 Vue 。在這篇文章中，我們會介紹 Vue 的背景，如何安裝 Vue 及建立一個新專案，學習整個 Vue 專案的高階架構及一個獨立的元件，學習如何在本地端運行專案，以及開始建構我們的範例。

|  |  |
| --- | --- |
| 預備知識 |
 熟悉 HTML 、
 CSS 、以及
 JavaScript
 的核心知識，知道如何使用終端機/命令列工具（ terminal/command line ） (en-US)。

 Vue 元件是由 Javascript
 物件（用來管理應用程式的資料）及樣版語法（用來映射成 DOM
 結構）所組成的。此外，你將需要使用終端機安裝 node 和 npm
 ，以便下載及使用一些 Vue
 的進階功能（像是單一檔案的元件或是渲染函式）。
  |
| 目標 | 開啟一個 Vue 專案，建立本地端開發環境，以及了解 Vue 是如何運作。 |

更了解 Vue
-------

Vue 是一個新穎的 Javascript 框架，它提供了很多有用的功能來達到漸進增強（ progressive enhancement ）的策略。與其他的框架不同的是，你可以把 Vue 和既有的 HTML 做整合，這讓你可以像使用插入性替換的函式庫一樣來使用 Vue ，就像使用 JQuery 一樣。

除此之外，你也可以使用 Vue 來寫一個單頁式應用程式。這讓你可以完全使用 Vue 來建構網頁，當處理較複雜的應用時有較好的開發效率及體驗。 Vue 也有提供函式庫來做客戶端路由及狀態管理。 Vue 對客戶端路由及狀態管理這些工具採取中間立場的態度，雖然 Vue 的核心團隊推薦使用這些函式庫，但是這些函式庫沒有直接打包進 Vue，而是讓你可以自由選擇使用其他更適合你的應用的路由庫或狀態管理庫。

除了可以漸進地把 Vue 整合到應用程式裡， Vue 也提供了漸進式的方法來撰寫網頁內容。就像大部分的框架， Vue 讓你創造可以重複使用的網頁內容區塊（稱為元件）。大部分時候， Vue 元件是用一種特殊的 HTML 樣板語法寫成的，然而在一些場景使用 HTML 語法會有限制時，你可以使用 JSX 或是 Javascript 函式來定義你的元件。

 當你閱讀這份教程時，你可以在其他分頁打開 Vue 指南 以及 API 文件，以方便參考更多資訊。
 如果你想要找一份好的（可能有些偏見）文件探討 Vue 與其他框架的比較，請參見 Vue 指南：對比其他框架。

安裝
--

如果要在既有的專案中使用 Vue ，你可以把以下任一個 `<script>` 標籤加到你的頁面中，這樣你就可以開始在專案中使用 Vue ，這也是為什麼 Vue 宣稱自己是一個漸進式的框架。它提供了一個很好的機會，你可以把一些使用函式庫（像是 JQuery ）的專案轉變成使用 Vue ，如此一來，你就可以使用很多 Vue 的核心功能，像是屬性、客製化元件以及資料管理。

* 開發環境版本（非優化版本，包含了有幫助的命令列警告。適合開發階段）

```html
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
```

* 產品環境版本（優化版本，輕量化的命令列警告。我們建議你標明使用特定的版本號來引入 Vue，以避免新版本造成不可預期的破壞）

```html
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
```

然而，以上方法有一些限制。若要建置一些較複雜的應用程式，你將需要使用 Vue NPM 套件。 這讓你可以使用 Vue 的進階功能且可以使用打包器像是 WebPack。為了讓使用 Vue 建置應用程式變得更容易，可以使用 CLI 來精簡化開發流程。如果要使用 npm 套件 & CLI ，你將需要：

1. 安裝 Node.js 8.11+
2. npm 或 yarn

**備註：** 如果你沒有安裝以上工具，請參考關於安裝 npm 及 Node.js (en-US) 。

在你的終端機執行以下命令來安裝 CLI ：

```bash
npm install --global @vue/cli
```

或者你比較喜歡使用 yarn ：

```bash
yarn global add @vue/cli
```

安裝完 CLI 後，在你想要創建專案的資料夾內開啟終端機然後執行 `vue create <project-name>` 來初始化專案。 CLI 將會給你一連串專案組態，有些設定有預設值，而你可以更改成你自己的設定。這些選項讓你可以在專案裡配置 TypeScript 、 linting 、 vue-router 、 Testing 等等。

之後我們會介紹如何使用它。

初始化一個新專案
--------

為了探索 Vue 各式各樣的功能，我們將會製作一個待辦事項應用程式。我們將使用 Vue CLI 來創造一個新專案框架來建構我們的應用程式。請跟著以下步驟：

1. 在終端機 `cd` 切換到你想要創建應用程式的資料夾，然後執行 `vue create moz-todo-vue`。
2. 使用方向鍵以及 `Enter` 鍵，選擇「Manually select features」
3. 第一個呈現在你眼前的選單讓你選擇想要加入到專案的功能。確認「Babel」和「Linter / Formatter」都已選取。如果沒有的話，使用方向鍵及空白鍵來切換選取，當它們都已被選取，按下 `Enter` 鍵進行下一步。
4. 接下來，你要選擇 linter / formatter 的設定。切換到「Eslint with error prevention only」然後按下 `Enter`。這個設定會進行報錯提醒，但不會強制你修改。
5. 然後，你將會被詢問需要哪種自動化的 linting 設定。選擇「Lint on save」。這樣每當我們儲存專案檔案時，就會自動幫我們檢查錯誤。按下 `Enter` 鍵進行下一步。
6. 現在，你將要選擇如何管理你的組態檔案。「In dedicated config files」將會把你的組態設定放在專門的檔案裡，像是 ESLint 的組態設定會放在它們專門的檔案裡。「In package.json」，會把你的組態設定放在應用程式的 `package.json` 檔案裡。選擇「In dedicated config files」然後按下 `Enter`。
7. 最後，你將會被問到是否要將以上設定存為預設值。你可以自行決定，如果未來想要再使用這組設定，請按下 `y`，否則按 `n` 。

CLI 會開始建構你的專案，並且安裝所需的相依套件。

如果你之前沒有執行過 Vue CLI ，你將會被問一個問題－請選擇套件管理員。你可以使用方向鍵來選擇你想使用的套件管理員，Vue CLI 將會把這個套件管理員設為預設值。之後如果你想要使用不同的套件管理員，可以在執行 `vue create` 時傳入一個標幟 `--packageManager=<package-manager>` 。舉例來說，如果你現在想要用 npm 來創建 `moz-todo-vue` 專案，但是之前是使用 yarn ，你可以執行 `vue create moz-todo-vue --packageManager=npm` 。

**備註：** 在這裡我們沒有介紹所有的選項，你可以參考 Vue 官方文件裡的關於 CLI 的更多資訊。

專案結構
----

如果以上都執行成功的話， CLI 將會為你的專案創建一連串的檔案和資料夾。其中最重要的如下：

* `.eslintrc.js` ：這是 eslint 的組態檔案，你可以使用它來管理你的 linting 規則。
* `babel.config.js` ：這是 Babel 的組態檔案，在開發階段我們可以使用一些 Javascript 的新功能，透過這個檔案轉換成在產品環境可以跨瀏覽器運行的舊版語法。你也可以在這個檔案裡註冊額外的 babel 套件。
* `.browserslistrc` ：這是 Browserslist 的組態檔案。你可以透過它來控制需要對哪些瀏覽器進行支持與優化
* `public` ：這個資料夾包含了所有發布的靜態檔案，這些檔案在建置過程不會經過 Webpack 加工。（但是有一個例外： `index.html` 會有一些處理）。
    + `favicon.ico` ：這是應用程式的 favicon。目前是 Vue 的標誌。
    + `index.html` ：這是應用程式的模板。你的 Vue 應用程式會透過這個 HTML 頁面來運行，你也可以使用 lodash 樣板語言在這個頁面穿插一些值。

    **備註：** 這個模板不是用來管理你的應用程式的版面－而是用來管理應用程式以外的靜態 HTML 檔案，只有在使用一些進階的功能才需要修改這個檔案。
* `src` ：這個資料夾是 Vue 專案的核心。
    + `main.js` ：這是應用程式的進入點。目前，這個檔案會初始化你的 Vue 應用程式並定義要將應用掛載到 `index.html` 檔案中的哪個 HTML 元素。通常還會在這個檔案註冊全域性元件或載入其他 Vue 函式庫。
    + `App.vue` ：這是 Vue 應用程式的根節點元件。往下看可以了解更多關於 Vue 元件的說明。
    + `components` ：你可以把你的元件放在這個資料夾。目前它只有放一個範例元件。
    + `assets` ：這個資料夾是用來放一些靜態檔案像是 CSS 和圖片。因為這些檔案在來源目錄下，它們可以透過 Webpack 加工處理。這表示你可以使用一些預處理器，像是 Sass / SCSS 或是 Stylus 。

**備註：** 依據你創建專案時所選設定的不同，你可能會看到其他資料夾（舉例來說，如果你有選擇 router，你將會看到一個 `views` 資料夾）。

.vue 檔案（單一檔案元件）
---------------

就像很多其他的框架一樣，元件是建構應用程式中很重要的一部分。這些元件讓你可以把一個很大的應用程式拆分成獨立的區塊，這些區塊可以分別被創建和管理，而且彼此之間可以傳遞所需要的資料。這些小區塊程式碼讓人更容易理解及測試。

雖然有些框架鼓勵你把模板、邏輯和樣式的程式碼放在不同的檔案，但是 Vue 卻採取相反的策略。使用單一檔案元件時， Vue 讓你把模板、相關指令碼和 CSS 整合在一起放在以 `.vue` 結尾的檔案裡。這些檔案會被 JS 的打包工具（像是 Webpack ）加以處理，這也代表你可以在你的專案裡使用一些建置工具，像是 Babel 、 TypeScript 、 SCSS 等工具來創造更複雜的元件。

使用 Vue CLI 創建的專案會產生現成的 `.vue` 檔案以及 Webpack 。看一下我們使用 CLI 創建的專案，在 `src` 資料夾會看到你的第一個 `.vue` 檔案： `App.vue` 。

現在讓我們來探索。

### App.vue

打開 `App.vue` 檔案——你將會看到它由三個部分組成： `<template>` 、 `<script>` 以及 `<style>` ，分別包含了元件的模板、指令碼和樣式資訊。所有的單一檔案元件都是使用這種基本架構。

`<template>` 包含所有的標記結構以及元件的呈現邏輯。你的模板可以包含任何有效的 HTML，以及一些我們接下來要介紹的 Vue 特定的語法。

**備註：** 在 `<template>` 標籤上設定 `lang` 屬性。例如設置 `<template lang="pug">`，你就可以使用 Pug 樣板語法來取代標準的 HTML 。在本教學中我們會使用標準 HTML ，但還是值得知道有這個方法。

`<script>` 包含元件中所有非顯示的邏輯。最重要的是，你的 `<script>` 標籤必須輸出一個 JS 物件。這個物件是你在本地端註冊的元件，包含定義屬性、處理本地狀態、定義方法等等。在建置步驟這個物件會被處理及轉換（包含 template 模板），變成一個有 `render()` 函數的 Vue 元件。

以 `App.vue` 為例，我們設定元件的名稱為 `App` ，並且在 `components` 屬性中加入 `HelloWorld` 來註冊這個元件。以這種方式來註冊元件是本地註冊。本地註冊的元件只能在註冊它們的元件裡面使用，所以你需要在每個使用它們的元件檔案裡匯入並且註冊它們。這對於 bundle splitting/tree shaking 很有用，因為在應用程式裡不是每一頁都需要所有的元件。

```js
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: {
    // 你可以在這裡註冊元件
    HelloWorld,
  },
};
```

**備註：** 如果你想要使用 TypeScript 語法，你必須把 `<script>` 標籤的 `lang` 屬性設定成 `<script lang="ts">` 來告訴編譯器你要使用 TypeScript 。

`<style>` 是你撰寫元件的 CSS 的地方。如果你加上 `scoped` 屬性，例如 `<style scoped>` ， Vue 會把樣式的範圍限制在這個單一檔案元件裡。這類似 CSS-in-JS 的解決方案，但是它允許你寫單純的 CSS 。

**備註：** 如果你在使用 CLI 創建專案時有選擇 CSS 預處理器，你可以在 `<style>` 標籤上添加 `lang` 屬性，這些內容在建置的時候將會被 Webpack 處理。舉例來說， `<style lang="scss">` 允許你在樣式資訊中使用 SCSS 語法。

在本地端運行應用程式
----------

Vue CLI 有內建的程式開發伺服器。這讓你可以在本地端運行你的應用程式，你不需要自己設置一個伺服器就可以很輕鬆地測試它。 CLI 有添加一個 `serve` 命令在專案的 `package.json` 檔案中當成一個 npm script ，所以你可以很輕易的運行它。

在你的終端機執行 `npm run serve`（或 `yarn serve` 如果你比較喜歡使用 yarn ）。你的終端機會輸出類似以下的資訊：
```

INFO  Starting development server...
98% after emitting CopyPlugin

 DONE  Compiled successfully in 18121ms

  App running at:
  - Local:   <http://localhost:8080/>
  - Network: <http://192.168.1.9:8080/>

  Note that the development build is not optimized.
  To create a production build, run npm run build.
```

如果你在瀏覽器的新分頁開啟「本地」位址（如上所述，這串位址應該會類似 `http://localhost:8080` ，但可能因設置而異），你應該會看到你的應用程式。現在，它應該包含一個歡迎訊息、一個導向 Vue 文件的連結、使用 CLI 初始化應用程式時所添加的套件的連結、以及一些導向 Vue 社群及生態圈的有用連結。

![default vue app render, with vue logo, welcome message, and some documentation links](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started/vue-default-app.png)

做一些改變
-----

現在讓我們來對應用程式做一些改變——刪除 Vue logo 。打開 `App.vue` 檔案，從 template 區塊刪除 `<img>` (en-US) 元素：

```html
<img alt="Vue logo" src="./assets/logo.png" />
```

如果你的伺服器仍在正常運行，你應該可以看到 logo 幾乎即時地從渲染頁面裡移除。接下來，我們再把 `HelloWorld` 從模板裡移除。

首先刪除以下這行：

```html
<HelloWorld msg="Welcome to Your Vue.js App" />
```

這時如果你儲存 `App.vue` 檔案，渲染的應用程式會拋出一個錯誤，因為我們註冊了元件但是沒有使用它。所以我們必須移除在 `<script>` 元素裡面用來匯入及註冊元件的程式碼：

現在刪除以下這幾行：

```js
import HelloWorld from "./components/HelloWorld.vue";
```

```js
components: {
  HelloWorld;
}
```

渲染好的應用程式應該不再顯示錯誤了，只有一個空白的頁面，因為目前在 `<template>` 沒有可見的內容。

我們接下來要製作一個待辦清單應用程式，在 `<div id="app">` 裡面加一個新的 `<h1>` ，並將標題文字設定為「 To-Do List 」。參考如下：

```html
<template>
  <div id="app">
    <h1>To-Do List</h1>
  </div>
</template>
```

`App.vue` 將會如預期地展示標題。

總結
--

讓我們現在暫時告一個段落。我們學習了 Vue 背後的一些想法，創建應用架構並使我們的範例應用可以在其運行，檢查它，並且對它做一些初步的改變。

在進行基本介紹之前，我們將進一步建構我們的範例應用，一個基本的待辦應用程式，它讓我們儲存待辦事項，在已完成的事項上打勾，以及依事項完成 /未完成狀態來過濾列表。

在下一篇文章中，我們將會建立第一個客製元件，研究一些重要的概念，像是透過 props 傳遞資料及儲存它的資料狀態。

* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁


React 新手入門 - 學習該如何開發 Web

React 新手入門
==========

* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁

在本文中，我們將向 React 打個招呼。我們將探索其背後與範例的一些細節，在自己電腦設置基本的 React 工具鏈環境，並建立一個簡單入門的應用程式——好瞭解 React 基本架構。

|  |  |
| --- | --- |
| 預備知識： | 熟悉基本的HTML、CSS、以及JavaScript程式語言，具備終端機/命令列環境 (en-US)的基本知識。
React使用稱為JSX（JavaScript和XML）的HTML-in-JavaScript語法。熟悉HTML和JavaScript將幫助你學習JSX，並更好地確定應用程式內的錯誤是與JavaScript有關還是與React更具體的領域有關。 |
| 學習目標： | 設立本機的React開發環境，建立初始應用程式，瞭解React基本運作方式。 |

Hello React
-----------

正如其官方標語所述，React是一個用來建構使用者介面的函式庫。React 不是框架——它甚至不是 web 獨有的開發技術，它可以和其他函式庫或套件搭配使用，以在特定環境中渲染。例如，React Native可用來建立手機應用程式；React 360可以用來建立 VR 應用程式；除此之外還有很多的可能性。

為了要建構 web，開發者將 React 與ReactDOM搭配使用。React 與 ReactDOM 的組合經常與其他 web 開發框架相提並論——以用於解決相同的問題。當我們稱呼 React 為「框架（framework）」時，我們是使用口語化的理解好加以討論。

(使用)React 的主要目標是最大限度減少開發者建構 UI 介面時出現的錯誤。它通過使用元件——描述部分使用者界面且自成一體的邏輯與程式碼段落來實現這點。這些元件可以組合在一起以建立完整的 UI 界面，React 將大部分的渲染邏輯給抽象化出來，讓你可以專注於 UI 層的程式設計。

使用範例
----

與本文章模組中涵蓋的其他框架不同，React 不會對程式碼風格規範或檔案組織實施嚴格的規則限制。這允許團隊設置最適合他們的開發規範，並以任何他們想要的方式採用 React。React 可以處理單一個按鈕、界面的幾個部分或應用程式整個使用者界面。

儘管 React*可以*用來開發介面的少許部分，但它沒辦法像 jQuery 函式庫，甚至像 Vue 這樣的框架能簡單「引入（drop into）」進應用程式中——當你使用 React 建構應用程式時，它會更加平易近人。

此外，React 應用程式有許多開發者體驗(developer-experience)優勢，例如使用 JSX 編寫介面時會需要編譯過程。添加如 Babel 這樣的編譯器到網站上會讓程式碼執行效率變慢，因此開發者通常會在建構階段中設置此類開發工具。React 對開發工具需求蠻吃重的，但這些都是可以學習的。

本文將重點介紹透過 Facebook 提供的create-react-app工具，來使用 React 渲染應用程式使用者界面的範例。

React 如何使用 JavaScript？
----------------------

React 在許多設計模式中利用了現代 JavaScript 特性。它與 JavaScript 最大的區別在於使用JSX語法。JSX 擴展了 JavaScript 語法，以便能與類似 HTML 的程式碼並存。例如：

```js
const heading = <h1>Mozilla Developer Network</h1>;
```

這個`heading`變數又稱為**JSX expression（JSX 表達式）**。React 可以在我們的應用程式中使用它，以渲染顯示`<h1>` (en-US)標籤

假如我們出於語義化的原因，想要將`heading`包裹在`<header>` (en-US)標籤中，那該怎麼做呢？JSX 方法允許我們將元素互相嵌套，就像我們在使用 HTML 一樣：

```js
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);
```

**備註：** 前面程式碼中的括號`(`、`)`不是 JSX 獨有的，這對你的應用程式沒有任何影響。它們會向你（和你的電腦）發出訊號，表示括號其中的多行程式碼是同個表達式的一部分。你也可以像這樣編寫表達式：

```js
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);
```

然而，這樣寫看起來有點不對勁，因為這段表達式`<header>` (en-US)的開始標籤沒有縮排到與其對應結束標籤相同的位置。

當然，若沒有其他的幫助，你的瀏覽器是無法直接讀取解析 JSX 的，當編譯完成（藉由使用像Babel或Parcel之類的工具），我們的 header 表達式看起來像這樣：

```js
const header = React.createElement(
  "header",
  null,
  React.createElement("h1", null, "Mozilla Developer Network"),
);
```

是*可以*跳過編譯階段，並使用`React.createElement()`來撰寫你自己的 UI 介面的。但是，這樣做會失去 JSX 的聲明性優勢，並且程式碼更難閱讀。雖然編譯是開發過程的額外步驟，但是許多 React 社群的開發者認為為了可讀性，使用 JSX 是值得的。此外，各種流行工具能使 JSX 到 JavaScript 的編譯成為其設置過程的一部分。除非你想，否則你不必自己配置編譯設定。

因為 JSX 是 HTML 和 JavaScript 的混合體，有一些開發者覺得它很直觀。也有其他人認為 JSX 的混合性使其變得混亂。但是，一旦你對它開始熟悉，JSX 能讓你更快、更直觀地建構使用者界面，並允許其他人一眼就能更好地理解你的程式碼（codebase）。

要閱讀更多有關 JSX 的資訊，可以查看 React 團隊的JSX In Depth文章。

設置你的第一個 React 應用程式
------------------

有很多方法可以使用 React，但我們將使用命令行界面（CLI）工具 create-react-app。如前面所說，它可以透過安裝一些套件和創建一些文件檔案以處理上面描述的工具，使其可以用來加速開發 React 應用程式的過程。

是可以不使用 create-react-app 就將 React 加入到網站裡的，藉由複製一些`<script>`元素並加進 HTML 檔案中即可，但 create-react-app CLI 確實是開發 React 應用程式的常見起點。使用它可以讓你花更少的時間在設置上，好使用更多的時間專注在建構應用程式上。

### 環境要求

為了使用create-react-app，你需要先安裝Node.js。建議你使用長期支援（LTS）版本。Node.js 包括 npm（Node.js 套件管理器）和 npx（Node.js 套件運行器）。

你也可以使用 Yarn 套件管理器作為替代方案，但我們假設你在這個教學中使用 npm。參閱Package management basics (en-US)可取得有關 npm 和 yarn 的更多資訊。

如果你使用的系統是 Windows，你需要安裝一些軟體來讓 Windows 與 Unix/macOS 的終端機 terminal 保持同等環境，以便使用本教學中提到的 terminal 終端機指令。**Gitbash**（它包含在git for Windows toolset工具的其中之一）或者\*\*Windows Subsystem for Linux\*\*（**WSL**）也同樣適合。有關這些以及一般終端指令的詳細資訊，可以參閱Command line crash course (en-US)。

另外要記住的是，React 和 ReactDOM 建立的應用程式只能在相當現代的瀏覽器上執行——通過一些 polyfills 才可以在 IE9+上運作。建議你使用現代瀏覽器來學習這些教學，例如：Firefox、Microsoft Edge、Safari 或 Chrome 等。

此外，請參閱以下內容以取得更多資訊：

* "About npm" on the npm blog
* "Introducing npx" on the npm blog
* The create-react-app documentation
### 初始化你的應用程式

`create-react-app`需要一個參數：一個你給予應用程式的名字。`create-react-app`會使用該名稱建立一個新目錄，並在其中建立必要的文件檔案。確保你有`cd`到你希望該應用程式在你電腦硬碟裡的資料夾位置，然後在你電腦終端機 terminal 執行以下指令：

```bash
npx create-react-app moz-todo-react
```

此指令會建立一個名為`moz-todo-react`的資料夾目錄，並在其中做了幾件事：

* 安裝一些對 React 應用程式至關重要的 npm 套件。
* 編寫用來啟動 React 應用程式的腳本語言。
* 建立定義基本應用程式架構的文件和目錄結構。
* 如果你的電腦已有安裝 git 版控，則將該資料夾目錄初始化為 git 存儲庫。

**備註：** 如果你已有安裝 yarn 套件管理器，`create-react-app`默認將使用它而不是使用 npm。如果你已安裝 yarn 和 npm 兩個套件管理器並且堅持繼續使用 npm，當你要執行`create-react-app`指令時，你可以添加指令`--use-npm`

```bash
npx create-react-app moz-todo-react --use-npm
```

`create-react-app`執行時會在你的終端機 terminal 顯示一些訊息；這是正常的！執行可能需要一小段時間，所以現在可能是暫時泡杯茶休息的好時機。

當這個過程完成後，`cd`切換進`moz-todo-react`資料夾目錄並執行指令`npm start`。這個藉由`create-react-app`安裝的腳本將會在本機伺服器(電腦)的`localhost:3000`啟動以提供服務，並在新瀏覽器分頁打開該應用程式。你的瀏覽器將顯示如下內容：

![Screenshot of Firefox MacOS, open to localhost:3000, showing the default create-react-app application](/zh-TW/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started/default-create-react-app.png)

### 應用程式的結構

`create-react-app`提供我們開發 React 應用程式所需的一切。其初始資料夾目錄結構如下：
```

moz-todo-react
├── README.md
├── node_modules
├── package.json
├── package-lock.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js
```

The **`src`** directory is where we'll spend most of our time, as it's where the source code for our application lives.

The **`public`** directory contains files that will be read by your browser while you're developing the app; the most important of these is `index.html`. React injects your code into this file so that your browser can run it. There's some other markup that helps create-react-app function, so take care not to edit it unless you know what you're doing. You very much should change the text inside the `<title>` element in this file to reflect the title of your application. Accurate page titles are important for accessibility!

The `public` directory will also be published when you build and deploy a production version of your app. We won't cover deployment in this tutorial, but you should be able to use a similar solution to that described in our Deploying our app (en-US) tutorial.

The `package.json` file contains information about our project that Node.js/npm uses to keep it organized. This file is not unique to React applications; create-react-app merely populates it. You don't need to understand this file at all to complete this tutorial, however, if you'd like to learn more about it, you can read What is the file `package.json`? on NodeJS.org; we also talk about it in our Package management basics (en-US) tutorial.

探索我們的第一個 React 元件——`<App/>`
---------------------------

In React, a **component** is a reusable module that renders a part of our app. These parts can be big or small, but they are usually clearly defined: they serve a single, obvious purpose.

Let's open `src/App.js`, since our browser is prompting us to edit it. This file contains our first component, `App`, and a few other lines of code:

```js
import React from "react";
import logo from "./logo.svg";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="\_blank"
          rel="noopener noreferrer">
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;
```

The `App.js` file consists of three main parts: some `import` statements at the top, the `App` component in the middle, and an `export` statement at the bottom. Most React components follow this pattern.

### Import 引入語法

The `import` statements at the top of the file allow `App.js` to use code that has been defined elsewhere. Let's look at these statements more closely.

```js
import React from "react";
import logo from "./logo.svg";
import "./App.css";
```

The first statement imports the React library itself. Because React turns the JSX we write into `React.createElement()`, all React components must import the `React` module. If you skip this step, your application will produce an error.

The second statement imports a logo from `'./logo.svg'`. Note the use of `./` at the beginning of the path, and the `.svg` extension at the end — these tell us that the file is local and that it is not a JavaScript file. Indeed, the `logo.svg` file lives in our source directory.

We don't write a path or extension when importing the `React` module — this is not a local file; instead, it is listed as a dependency in our `package.json` file. Be careful of this distinction as you work through this lesson!

The third statement imports the CSS related to our App component. Note that there is no variable name and no `from` directive. This particular import syntax is not native to JavaScript module syntax — it comes from Webpack, the tool create-react-app uses to bundle all our JavaScript files together and serve them to the browser.

### `App`元件

After the imports, we have a function named `App`. Whereas most of the JavaScript community prefers camel-case names like `helloWorld`, React components use pascal-case variable names, like `HelloWorld`, to make it clear that a given JSX element is a React component, and not a regular HTML tag. If you were to rename the `App` function to `app`, your browser would show you an error.

Let's look at `App` more closely.

```js
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="\_blank"
          rel="noopener noreferrer">
          Learn React
        </a>
      </header>
    </div>
  );
}
```

The `App` function returns a JSX expression. This expression defines what your browser ultimately renders to the DOM.

Some elements in the expression have attributes, which are written just like in HTML, following a pattern of `attribute="value"`. On line 3, the opening `<div>` tag has a `className` attribute. This is the same as the `class` (en-US) attribute in HTML, but because JSX is JavaScript, we can't use the word `class` — it's reserved, meaning JavaScript already uses it for a specific purpose and it would cause problems here in our code. A few other HTML attributes are written differently in JSX than they are in HTML too, for the same kind of reason. We'll cover them as we encounter them.

Take a moment to change the `<p>` (en-US) tag on line 6 so that it reads "Hello, world!", then save your file. You'll notice that this change is immediately rendered in the development server running at `http://localhost:3000` in your browser. Now delete the `<a>` (en-US) tag and save; the "Learn React" link will be gone.

Your `App` component should now look like this:

```js
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, World!</p>
      </header>
    </div>
  );
}
```

### Export 匯出語法

在`App.js`文件中的最底部，這個陳述語法`export default App`可以使我們的`App`元件能用在其他模組。

查詢索引
----

Let's open `src/index.js`, because that's where the `App` component is being used. This file is the entry point for our app, and it initially looks like this:

```js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import \* as serviceWorker from "./serviceWorker";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

As with `App.js`, the file starts by importing all the JS modules and other assets it needs to run. `src/index.css` holds global styles that are applied to our whole app. We can also see our `App` component imported here; it is made available for import thanks to the `export` statement at the bottom of `App.js`.

Line 7 calls React's `ReactDOM.render()` function with two arguments:

* The component we want to render, `<App />` in this case.
* The DOM element inside which we want the component to be rendered, in this case the element with an ID of `root`. If you look inside `public/index.html`, you'll see that this is a `<div>` element just inside the `<body>`.

All of this tells React that we want to render our React application with the `App` component as the root, or first component.

**備註：** In JSX, React components and HTML elements must have closing slashes. Writing just `<App>` or just `<img>` will cause an error.

Service workers (en-US) are interesting pieces of code that help application performance and allow features of your web applications to work offline, but they're not in scope for this article. You can delete line 5, as well as most of the code below it.

Your final `index.js` file should look like this:

```js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));
```

Variables and props
-------------------

接下來，我們將使用一些 JavaScript 技巧好更輕鬆地在 React 中編輯元件與處理數據資料。我們將談談變數是如何在 JSX 中被使用的，並介紹 props，這是一種將資料傳遞到其他元件（接著就可以用變數形式來使用）的方式。

### JSX 中的變數

回到`App.js`，讓我們關注第 9 行：

```js
<img src={logo} className="App-logo" alt="logo" />
```

在這裡，`<img />`標籤的`src`屬性值在花括號（即大括號`{`與`}`）中，這就是 JSX 識別變數的方式。React 將會監聽`{logo}`，它知道你指的是我們在應用程式第 2 行引入的 logo，然後檢索 logo 檔案並呈現它。

讓我們嘗試創造一個自己的變數。在`App`的 return 語句之前，加入`const subject = 'React';`。你的`App`元件現在應該看起來是這樣：

```js
function App() {
  const subject = "React";
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, World!</p>
      </header>
    </div>
  );
}
```

至第 8 行改成我們的`subject`變數而不是單字「world」，像這樣：

```js
function App() {
  const subject = "React";
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello, {subject}!</p>
      </header>
    </div>
  );
}
```

當你存檔時，你的瀏覽器應該顯示「Hello, React!」而不是「Hello, world!」

變數很方便，但我們剛剛設置的變數並沒有充分利用 React 的特性。接下來該是 props 出場了。

### Component props

A **prop** is any data passed into a React component. React props are comparable to HTML attributes. Where HTML elements have attributes, React components have props. Props are written inside component calls, and use the same syntax as HTML attributes — `prop="value"`. In React, dataflow is unidirectional: props can only be passed from Parent components down to Child components; and props are read-only.

Let's open `index.js` and give our `<App/>` call its first prop.

Add a prop of `subject` to the `<App/>` component call, with a value of `Clarice`. When you are done, your code should look something like this:

```js
ReactDOM.render(<App subject="Clarice" />, document.getElementById("root"));
```

Back in `App.js`, let's revisit the App function itself, which reads like this (with the `return` statement shortened for brevity):

```js
function App() {
  const subject = "React";
  return (
    // return statement
  );
}
```

Change the signature of the `App` function so that it accepts `props` as a parameter, and delete the `subject` const. Just like any other function parameter, you can put `props` in a `console.log()` to print it to your browser's console. Go ahead and do that before the `return` statement, like so:

```js
function App(props) {
  console.log(props);
  return (
    // return statement
  );
}
```

保存文件並檢查瀏覽器的 JavaScript 控制台 console。你應該會看到這樣的記錄：

```js
Object { subject: "Clarice" }
```

物件屬性`subject`與我們添加到到`<App />`元件調用的 prop`subject`相對應，並且字串`Clarice`與它的值對應。React 的元件 props 總是以這種方式傳遞到物件中。

現在`subject`是我們的 props 之一了，讓我們在`App.js`中使用它。用`props.subject`代替原本的`React`字串，如果你想的話，也可以刪除`console.log()`，你的程式碼看起來會是這樣：

```js
function App(props) {
  const subject = props.subject;
  return (
    // return statement
  );
}
```

當你保存時, 應用程式現在應該會向你打招呼「Hello, Clarice!」。如果你返回`index.js`檔案內修改`subject`的值並存檔，你的字串將發生變化。

總結
--

我們對 React 的初步瞭解就到此結束了，包括如何在本機環境安裝它、建立入門應用程式及如何運作的基本知識。在下個章節中，我們將開始建構我們的第一個合適的應用程式——一個待辦清單。然而，在我們這樣做之前，先回顧一下我們學到的一些東西。

在 React 中：

* 元件可以引入（import）它們需要的模組，且必須在它們的文件檔案底部將自己匯出（export）。
* 元件函式的命名是`PascalCase`大駝峰式命名法。
* 你可以透過將 JSX 變數放在大括號之間來讀取它們，例如`{so}`。
* 某些 JSX 屬性與 HTML 屬性不同，因此它們不會與 JavaScript 保留字衝突。例如，HTML 中的`class`在 JSX 中會轉成`className`。注意多字組合而成的屬性名稱是駝峰式（camel-cased）命名的。
* Props 就像元件中被調用的屬性一樣被撰寫並傳遞到元件中。
* 前頁  (en-US)
* Overview: Client-side JavaScript frameworks
* 次頁