Vue 入門筆記

来源:http://www.cnblogs.com/fly-xfa/archive/2017/05/16/6864387.html
-Advertisement-
Play Games

# vuejs tutorial ![vue-logo](img/logo.png) ## 搭建案例演示自動刷新環境 創建一個 `package.josn` 文件: ```bashnpm init -y``` 安裝 `browser-sync`: ```bash# npm install --sav ...


# vuejs tutorial

![vue-logo](img/logo.png)

---

## 搭建案例演示自動刷新環境

創建一個 `package.josn` 文件:

```bash
npm init -y
```

安裝 `browser-sync`:

```bash
# npm install --save-dev browser-sync
# 將 browser-sync 包保存到開發依賴中
# 就可以執行 npm install 的時候加入 --production 選項不安裝這個包
npm i -D browser-sync
```

在 package.json 文件中加入以下內容:

```json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "browser-sync start --server --directory --files \"code/*.html\""
}
```

打開終端,啟動開發預覽服務:

```bash
npm start
```

---

## vuejs 介紹

---

## 安裝

- Vue.js 不支持 IE8 及其以下版本,因為 Vue.js 使用了 IE8 不能模擬的 ECMAScript 5 特性
- 每個版本的更新日誌見:https://github.com/vuejs/vue/releases
- 獨立 js 文件
+ 開發版本(未壓縮):http://vuejs.org/js/vue.js
+ 生產版本(壓縮):http://vuejs.org/js/vue.min.js
- CDN:https://unpkg.com/vue
- NPM: `npm install vue`
- Bower: `bower install vue`
- 相容 AMD 規範
+ 獨立下載版本或通過 Bower 安裝的版本已用 UMD 包裝,因此它們可以直接用作 AMD 模塊。

---

## Vue 實例

- 每個 Vue.js 應用都是通過構造函數 Vue 創建的
- 在實例化 Vue 時,需要傳入一個選項對象,它可以包含數據、模板、掛載元素、方法、生命周期鉤子等選項
+ data: 屬性數據
+ computed: 計算屬性
+ methods: 方法
+ el: 掛載點
+ directives: 自定義指令
+ filters: 自定義過濾器
+ ...
+ 全部選項可以在 API 文檔中查看:https://cn.vuejs.org/v2/api/

- 實例選項:data
+ https://cn.vuejs.org/v2/guide/instance.html#屬性與方法
+ [選項/數據 - data](https://cn.vuejs.org/v2/api/#data)
+ [深入響應式原理](https://cn.vuejs.org/v2/guide/reactivity.html)
+ 作用:根據視圖抽象數據模型
+ 註意:視圖中使用的數據必須在 data 中初始化
+ 每個 VUe 實例都會代理其 data 對象里所有的屬性
* 也可以通過 vue 實例的 $data 屬性訪問 data 中的數據
* 建議:如果要使用 vm 讀取或修改 data 中的數據,建議加上 vm.$data 去訪問
+ 只有被初始代理的屬性是響應式的
* 如果是在實例創建之後添加新的屬性到實例上,它不會觸發視圖更新
+ Vue 不允許在已經創建的實例上動態添加新的根級響應式屬性
- Vue 不能檢測到對象屬性的動態添加或刪除
+ 也就是說動態添加或刪除的對象屬性不是響應式的
+ 如果希望動態添加和刪除對象的屬性是響應式的則需要通過:
* `Vue.set( object, key, value )`
* 或 `vm.$set( object, key, value )`
+ 如果刪除對象屬性是響應式的:
* `Vue.delete( object, key )`
* 或 `vm.$delete( object, key )`

- 實例選項:methods
+ https://cn.vuejs.org/v2/api/#methods
+ 作用:為視圖交互提供行為函數
+ 可以在行為函數中通過 `this` 訪問 data 中的數據成員
+ 註意:methods 中的行為函數不要寫箭頭函數
* 因為這樣會改變內部 this 的指向

- 實例屬性
+ https://cn.vuejs.org/v2/api/#實例屬性
+ $data
* Vue 實例觀察的數據對象。Vue 實例代理了對其 data 對象屬性的訪問。
+ $el
* Vue 實例使用的根 DOM 元素

- 實例方法/數據
+ https://cn.vuejs.org/v2/api/#實例方法-數據
+ $watch
+ $set Vue.set 的別名
+ $delete Vue.delete 的別名

---

## 模板語法

### 插值

#### 文本

- 響應插值:
+ `<span>Message: {{ msg }}</span>`
+ 註意: Mustache 語法不能在 HTML 屬性中使用,應使用 `v-bind` 指令
- 一次性插值:
+ `<span v-once>This will never change: {{ msg }}</span>`
+ 註意:會影響該節點及內部節點所有的綁定

#### 純 HTML

雙大括弧會將數據解釋為純文本,而非 HTML 。為了輸出真正的 HTML ,你需要使用 v-html 指令:

```html
<div v-html="rawHtml"></div>
```

- 為什麼不直接輸出 HTML
- 什麼是 XSS 攻擊:跨站腳本攻擊
+ 後天或者後後天補課

#### 屬性

**註意:Mustache 不能在 HTML 屬性中使用,應使用 v-bind 指令:**

```html
<div v-bind:id="dynamicId"></div>
```

這對布爾值的屬性也有效 —— 如果條件被求值為 false 的話該屬性會被移除:

```html
<button v-bind:disabled="someDynamicCondition">Button</button>
```

#### 使用 JavaScript 表達式

Vue.js 都提供了完全的 JavaScript 表達式支持:

```html
{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>
```

這些表達式會在所屬 Vue 實例的數據作用域下作為 JavaScript 被解析。
有個限制就是,每個綁定都只能包含單個表達式,所以下麵的例子都不會生效:

```html
<!-- 這是語句,不是表達式 -->
{{ var a = 1 }}

<!-- 流控制也不會生效,請使用三元表達式 -->
{{ if (ok) { return message } }}
```

### 指令

### Vue 內置指令

- v-text
+ 和 {{}} 效果是一樣
+ 但是 {{}} 會閃爍
+ 解決方法就是利用 v-text 綁定數據
+ 如果又想用 {{}}} 還需要避免閃爍
+ 使用 v-cloak 處理
- v-html
+ 預設 {{}} 和 v-text 會把 html 格式字元串原樣輸出
+ 可以使用 v-html 將 html 格式字元串作為 HTML 渲染到節點中
- v-show
+ 是否顯示和隱藏
- v-if
+ 是否渲染和移除
- v-else
+ v-if 的 else 塊
- v-else-if
+ 是 v-if 的邏輯塊
+ 同樣的,也必須緊跟著 v-if
- v-for
+ 迴圈遍歷輸出
- v-on
+ DOM 元素的事件綁定
+ 例如:`v-on:click`、`v-on:blur`
- v-bind
+ 動態綁定 HTML 屬性
+ 例如:`v-bind:title`、`v-bind:class`
- v-model
+ 和表單控制項進行雙向數據綁定
- v-pre
+ 不處理指定節點及內部所有節點的 vue 規則
+ 例如可以用來顯示原始的 Mustache 標簽
+ 作用:跳過大量沒有指令的節點可以加快編譯速度
- v-cloak
+ 可以處理表達式閃爍的問題
- v-once
+ 一次性綁定,後續數據的更新不會響應

指令(Directives)是帶有 `v-` 首碼的特殊屬性。
指令屬性的值預期是單一 JavaScript 表達式(除了 `v-for`,之後再討論)。指令的職責就是當其表達式的值改變時相應地將某些行為應用到 DOM 上。

```html
<p v-if="seen">Now you see me</p>
```

這裡, v-if 指令將根據表達式 seen 的值的真假來移除/插入 <p> 元素。

#### 參數

一些指令能接受一個“參數”,在指令後以冒號指明。
例如, v-bind 指令被用來響應地更新 HTML 屬性:

```html
<a v-bind:href="url"></a>
```

在這裡 `href` 是參數,告知 `v-bind` 指令將該元素的 `href` 屬性與表達式 `url` 的值綁定。

另一個例子是 v-on 指令,它用於監聽 DOM 事件:

```html
<a v-on:click="doSomething">
```

在這裡參數是監聽的事件名:`click`。

#### 修飾符

修飾符(Modifiers)是以半形句號 . 指明的特殊尾碼,用於指出一個指令應該以特殊方式綁定。
例如,.prevent 修飾符告訴 v-on 指令對於觸發的事件調用 event.preventDefault():

```html
<div>
<input type="text" v-on:keyup.enter="xxx">
</div>
```

```html
<form v-on:submit.prevent="onSubmit"></form>
<input type="text" v-on:keyup.enter="addTodo">
```

### 過濾器

> 註意:Vue 2.x 中,過濾器只能在 mustache 綁定和 v-bind 表達式(從 2.1.0 開始支持)中使用,
> 因為過濾器設計目的就是用於文本轉換。為了在其他指令中實現更複雜的數據變換,你應該使用 **計算屬性**。

- Vue.js 允許你自定義過濾器,可被用作一些常見的文本格式化
- 過濾器可以用在兩個地方:`mustache 插值` 和 `v-bind 表達式`

全局過濾器:

```js
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
```

局部過濾器:

```js
new Vue({
// ...
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
```

過濾器使用格式:

```html
<!-- in mustaches -->
{{ message | capitalize }}

<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>
```

過濾器可以串聯:

```html
{{ message | filterA | filterB }}
```

過濾器是 JavaScript 函數,因此可以接受參數:

```html
{{ message | filterA('arg1', arg2) }}
```

這裡,字元串 'arg1' 將傳給過濾器作為第二個參數,arg2 表達式的值將被求值然後傳給過濾器作為第三個參數。

### 縮寫

#### v-bind 縮寫

```html
<!-- 完整語法 -->
<a v-bind:href="url"></a>
<!-- 縮寫 -->
<a :href="url"></a>
```

#### v-on 縮寫

```html
<!-- 完整語法 -->
<a v-on:click="doSomething"></a>
<!-- 縮寫 -->
<a @click="doSomething"></a>
```

---

## 計算屬性

模板內的表達式是非常便利的,但是它們實際上只用於簡單的運算。
在模板中放入太多的邏輯會讓模板過重且難以維護。例如:

```html
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
```

在這種情況下,模板不再簡單和清晰。
這就是對於任何複雜邏輯,你都應當使用計算屬性的原因。

#### 基礎例子:反轉字元串

```js
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})
```

你可以像綁定普通屬性一樣在模板中綁定計算屬性。
Vue 知道 `vm.reversedMessage` 依賴於 `vm.message` ,
因此當 `vm.message` 發生改變時,所有依賴於 `vm.reversedMessage` 的綁定也會被重新計算進行更新。

---

## Class 與 Style 綁定

在 `v-bind` 用於 `class` 和 `style` 時, Vue.js 專門增強了它。
表達式的結果類型除了 **字元串** 之外,還可以是 **對象** 或 **數組** 。

### 綁定 HTML Class

#### 對象語法

```html
<div v-bind:class="{ active: isActive }"></div>

<!-- v-bind:class 指令可以與普通的 class 屬性共存 -->
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
```

也可以直接綁定數據里的一個對象:

```html
<div v-bind:class="classObject"></div>
<script>
new Vue({
data: {
classObject: {
active: true,
'text-danger': false
}
}
})
</script>
```

#### 數組語法

```html
<!-- 可以把一個數組傳給 v-bind:class ,以應用一個 class 列表 -->
<div v-bind:class="[activeClass, errorClass]">

data: {
activeClass: 'active',
errorClass: 'text-danger'
}

<!-- 根據條件切換列表中的 class ,可以用三元表達式: -->
<div v-bind:class="[isActive ? activeClass : '', errorClass]">

<!-- 可以在數組語法中使用對象語法: -->
<div v-bind:class="[{ active: isActive }, errorClass]">
```

### 綁定內聯樣式

```html
<!-- CSS 屬性名可以用駝峰式(camelCase)或名短橫分隔命(kebab-case) -->
<div v-bind:style="{ color: activeColor, 'font-size': fontSize + 'px' }"></div>

data: {
activeColor: 'red',
fontSize: 30
}

<!-- 直接綁定到一個樣式對象 -->
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}

<!-- v-bind:style 的數組語法可以將多個樣式對象應用到一個元素上 -->
<div v-bind:style="[baseStyles, overridingStyles]">
```

---

## 條件渲染

### v-if-else-elseif

```html
<!-- 基本用法 -->
<h1 v-if="ok">Yes</h1>

<!--
通過 template 包裝多個元素,渲染結果不包含 template
v-else 元素必須緊跟在 v-if 或者 v-else-if 元素的後面——否則它將不會被識別。
-->
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

<!-- 使用 v-else 指令來表示 v-if 的“else 塊” -->
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>

<!--
v-else-if,顧名思義,充當 v-if 的“else-if 塊”。可以鏈式地使用多次:
v-else,,v-else-if 必須緊跟在 v-if 或者 v-else-if 元素之後
-->
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
```

---

## 列表渲染

---

## 事件處理器

---

## 表單控制項綁定

---

## 組件

組件是 Vue.js 最強大的功能,組件可以擴展自定義 HTML 元素,封裝可重用代碼。

### 組件的命名

- 如果一個單詞就只寫一個單詞即可
- 如果是多個單片語成的名字
+ 建議使用短橫杠的方式
- 如果使用的是駝峰命名
+ 則在 DOM 模板中必須將 駝峰命名的組件名改為短橫杠的方式
+ 在 字元串模板中,無論是駝峰還是短橫杠都行

### 組件基礎

- 註冊全局組件
- 註冊局部組件
- 組件的模板
- 組件的 data

#### 註冊全局組件:`Vue.component(tagName, options)`

註冊:

```js
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
```

使用:

```html
<div id="example">
<my-component></my-component>
</div>
```

渲染為:

```html
<div id="example">
<div>A custom component!</div>
</div>
```

#### 組件的 template

組件的 template 必須具有一個根節點,否則,模板編譯報錯。

- 可以是內聯模板
- 可以是 script 標簽模板
- 可以是 .vue 模板

#### 局部註冊組件:實例選項的 `components`

不必在全局註冊每個組件。
通過使用組件實例選項註冊,可以使組件僅在另一個實例/組件的作用域中可用:

```html
<body>
<div id="app">
<!-- 渲染為 <div>局部組件</div> -->
<my-component></my-component>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
components: {
'my-component': {
template: '<div>局部組件</div>'
}
},
data: {
},
})
</script>
</body>
```

#### 在 DOM 模板中使用組件註意事項

當使用 DOM 作為模版時(例如,將 `el` 選項掛載到一個已存在的元素上),
你會受到 HTML 的一些限制,
因為 Vue 只有在瀏覽器解析和標準化 HTML 後才能獲取模版內容。
尤其像這些元素 `<ul>` , `<ol>`, `<table>` , `<select>` 限制了能被它包裹的元素,
`<option>` 只能出現在其它元素內部。

在自定義組件中使用這些受限制的元素時會導致一些問題,例如:

```html
<table>
<my-row>...</my-row>
</table>
```

自定義組件 `<my-row>` 被認為是無效的內容,因此在渲染的時候會導致錯誤。
變通的方案是使用特殊的 `is` 屬性:

```html
<table>
<tr is="my-row"></tr>
</table>
```

**應當註意,如果您使用來自以下來源之一的字元串模板,這些限制將不適用:**

- `<script type="text/x-template">`
- JavaScript內聯模版字元串
- `.vue` 組件

因此,推薦使用字元串模板。

#### 組件的 `data` 必須是函數

在組件中,data 必須是函數,下麵是錯誤的方式:

```js
Vue.component('my-component', {
template: '<span>{{ message }}</span>',
data: {
message: 'hello'
}
})
```

正確的方式:

```js
data: function () {
return {
message: '組件的 data 必須是函數返回一個json字面量對象'
}
}
```

### 組件通信

- 使用 prop 傳遞數據
- props 命名規則
+ camelCase 和 kebab-case
- 動態 prop
+ v-bind
- 字面量語法 vs 動態語法
- 單向數據流

組件意味著協同工作,通常父子組件會是這樣的關係:組件 A 在它的模版中使用了組件 B 。
它們之間必然需要相互通信:

- 父組件要給子組件傳遞數據
- 子組件需要將它內部發生的事情告知給父組件

然而,在一個良好定義的介面中儘可能將父子組件解耦是很重要的。
這保證了每個組件可以在相對隔離的環境中書寫和理解,也大幅提高了組件的可維護性和可重用性。

在 Vue.js 中,父子組件的關係可以總結為 `props down, events up`:

- 父組件通過 `props` 向下傳遞數據給子組件
- 子組件通過 `events` 給父組件發送消息

![img/props-events.png](img/props-events.png)

#### 使用 prop 傳遞數據

組件實例的作用域是孤立的。
這意味著不能(也不應該)在子組件的模板內直接引用父組件的數據。
要讓子組件使用父組件的數據,我們需要通過子組件的props選項。

子組件要顯式地用 `props` 選項聲明它期待獲得的數據:

```js
Vue.component('child', {
// 聲明 props
props: ['message'],
// 就像 data 一樣,prop 可以用在模板內
// 同樣也可以在 vm 實例中像 “this.message” 這樣使用
template: '<span>{{ message }}</span>'
})
```

然後我們可以這樣向它傳入一個普通字元串:

```html
<child message="hello!"></child>
```

#### camelCase 和 kabab-case 命名規則

#### 動態 prop

#### 字面量語法 vs 動態語法

#### 單向數據流

prop 是單向綁定的:

- 當父組件的屬性發生變化時,將傳導給子組件
+ 子組件動態綁定的 prop,當父組件更新,子組件所有的 prop 都會得到更新
- 但是不會反過來
+ 也就是說,在子組件內部修改 prop 數據
* 子組件內部會響應更新
* 更新不會傳導給父組件
* 同時 Vue 會在控制台發出警告
* 對象和數組除外
* 如果 prop 是一個對象或數組,在子組件內部修改它會影響父組件的狀態
* 如果直接給 prop 中的對象或數組類型數據重新賦值,父組件也不會得到更新
+ 這是為了防止子組件無意間修改了父組件的狀態
+ 這會讓數據流的走向變得混亂而難以理解

為什麼我們會有修改 prop 中數據的衝動呢?

1. prop 作為初始值傳入後,子組件想要把它當作局部數據來用
2. prop 作為初始值傳入後,由子組件處理成其它數據輸出

對於這兩種原因,正確的方式是:

1. 定義一個局部變數,並用 prop 的值初始化它

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

2. 定義一個計算屬性,處理 prop 的值並返回

```js
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
```

#### Prop 驗證

我們可以為組件的 props 指定驗證規格。
如果傳入的數據不符合規格,Vue 會發出警告。
當組件給其他人使用時,這就很有用了。

要指定驗證規格,需要使用對象的形式,而不能用字元串數組:

```js
Vue.component('example', {
props: {
// 基礎類型檢測 (`null` 意思是任何類型都可以)
propA: Number,
// 多種類型
propB: [String, Number],
// 必傳且是字元串
propC: {
type: String,
required: true
},
// 數字,有預設值
propD: {
type: Number,
default: 100
},
// 數組/對象的預設值應當由一個工廠函數返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函數
propF: {
validator: function (value) {
return value > 10
}
}
}
})
```

`type` 可以是下麵原生的數據類型:

- String
- Number
- Boolean
- Function
- Object
- Array

`type` 也可以是一個自定義構造器函數(例如 Person),
Vue 會使用 `instanceof` 對數據進行檢測。

當 prop 驗證失敗,Vue會在拋出警告 (如果使用的是開發版本)。

### 自定義事件(父子通信)

- 使用 v-on 綁定自定義事件
- 使用自定義事件的表單輸入組件
- 非父子組件通信

父組件是使用 props 傳遞數據給子組件,
但如果子組件要把數據傳遞迴去,應該怎樣做?
那就是自定義事件!

#### 使用 v-on 綁定自定義事件

每個 Vue 實例都實現了事件介面:

- 使用 $on(eventName) 監聽事件
- 使用 $emit(eventName) 觸發事件

父組件可以在使用子組件的地方直接使用 `v-on` 監聽子組件發射的事件。
註意:不能使用 `$on` 偵聽子組件拋出的事件,而必須在模板里直接使用 `v-on` 綁定。

```html
<body>
<div id="app">
<p>{{ total }}</p>
<child v-on:increment="incrementTotal"></child>
<child v-on:increment="incrementTotal"></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('child', {
template: `
<div>
<span>{{ counter }}</span>
<button @click="increment">increment</button>
</div>`,
data () {
return {
counter: 0
}
},
methods: {
increment () {
this.counter += 1
this.$emit('increment')
}
}
})
new Vue({
el: '#app',
data: {
total: 0
},
methods: {
incrementTotal () {
this.total += 1
}
}
})
</script>
</body>
```

在本示例中,子組件已經和它外部完全解耦了。
它所做的只是報告自己的內部事件,至於父組件是否關心則與它無關。

有時候,可能想要在某個組件的根元素上綁定一個原生事件。
可以使用 `.native` 修飾 `v-on`。例如:

```html
<my-component v-on:click.native="doTheThing"></my-component>
```

#### 非父子組件通信

有時候兩個組件也需要通信(非父子關係)。
在簡單的場景下,可以使用一個空的 Vue 實例作為中央事件匯流排:

```html
<body>
<div id="app">
<child-a></child-a>
<child-b></child-b>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
const bus = new Vue()
Vue.component('child-a', {
template: `
<div>
<p>組件 child A</p>
<button @click="emitDataToB">發射數據到</button>
</div>
`,
methods: {
emitDataToB() {
// 在組件 A 中通過 $emit 發射 data 事件,組件 B 中的鉤子監聽了 data 事件
bus.$emit('data', '組件a傳遞過來的數據')
}
}
})
Vue.component('child-b', {
template: `
<div>
<p>組件 child B</p>
<p>{{ message }}</p>
</div>
`,
created() {
const vm = this
// 在組件 B 的鉤子中通過 bud 的 $on 監聽事件
bus.$on('data', function (data) {
vm.message = data
})
},
data() {
return {
message: 'hello child b'
}
}
})
new Vue({
el: '#app',
data: {},
})
</script>
</body>
```

### 使用 Slot 分發內容

- 編譯作用域
- 單個 Slot
- 具名 Slot
- 作用域插槽

在使用組件的時候,我們常常要像這樣組合它們:

```html
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
```

註意兩點:

1. `<app>` 組件不知道它的掛載點會有什麼內容。掛載點的內容是由 `<app>` 的父組件決定的
2. `<app>` 組件很可能有它自己的模板

為了讓組件可以組合,我們需要一種方式來混合父組件的內容和子組件自己的模板。
這個過程被稱為 **內容分發**(或 “transclusion”)。

Vue.js 實現了一個內容分發 API,參照了當前 Web 組件規範草案,
使用特殊的 `<slot>` 元素作為原始內容的插槽。

#### 編譯作用域

#### 單個 Slot

- 如果子組件沒有 `<slot>` 插口,否則父組件的內容會被丟棄
- 當子組件模板只有一個沒有屬性的 slot 時
+ 父組件整個內容片段都將插入到 slot 所在的 DOM 位置
+ 並替換掉 slot 標簽本身
+ 在 slot 標簽中的任何內容都被視為 備用內容
+ 備用內容在子組件的作用域內編譯
+ 並且只有宿主元素為空的時候,備用內容才會被編譯顯示出來

示例如下:

```html
<body>
<div id="app">
<bs-panel title="面板1">
面板1的內容
</bs-panel>
<bs-panel title="面板2">
面板2的內容
</bs-panel>
<bs-panel title="沒有分發內容的面板">
</bs-panel>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('bs-panel', {
template: `
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ title }}</h3>
</div>
<div class="panel-body">
<slot>
只有才沒有分發的內容時才會顯示
</slot>
</div>
</div>
`,
props: {
title: { type: String, required: true }
}
})
new Vue({
el: '#app',
data: {},
})
</script>
</body>
```

#### 具名 slot

- 在組合組件時,內容分發 API 是非常有用的機制
- `<slot>` 元素可以用一個特殊的屬性 `name` 來配置如何分發內容
- 多個 slot 可以有不同的名字。
- 具名 slot 將匹配內容片段中有對應 slot 特性的元素
- 仍然可以有一個匿名 slot,它是預設 slot
+ 作為找不到匹配的內容片段的備用插槽
+ 如果沒有預設的 slot,這些找不到插槽的內容片段將被拋棄

```html
<body>
<div id="app">
<app-layout>
<h1 slot="header">頂部</h1>
<p>內容段落</p>
<p>內容段落</p>
<p slot="footer">底部信息</p>
</app-layout>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('app-layout', {
template: `
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
})
new Vue({
el: '#app',
data: {},
})
</script>
</body>
```

#### 作用域插槽

- 目的:作用域插槽的目的就是可以將子組件內部的數據傳遞到外部
- 在子組件中,在 `slot` 標簽上通過屬性的方式將 prop 數據傳遞到外部
- 在父組件中,通過具有特殊屬性 `scope` 的 `<template>` 元素,表示它是作用域插槽的模板
+ `scope` 的值對應一個臨時變數名
+ 該變數接收來自子組件中通過 `slot` 元素屬性傳遞的 prop 數據

示例如下:

```html
<body>
<div id="app">
<child>
<template scope="props">
<p>hello from parent</p>
<p>{{ props.text }}</p>
<p>{{ props.message }}</p>
</template>
</child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('child', {
template: `
<div class="child">
<input v-model="message" />
<slot text="hello from child" :message="message"></slot>
</div>
`,
data () {
return {
message: 'child message'
}
}
})
new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```


作用域插槽更具代表性的用例是列表組件,允許組件自定義應該如何渲染列表每一項:

```html
<body>
<div id="app">
<my-awesome-list :todos="todos">
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('my-awesome-list', {
props: ['todos'],
template: `
<ul>
<slot name="item"
v-for="item in todos"
:text="item.title">
<!-- fallback content here -->
</slot>
</ul>
`
})
new Vue({
el: '#app',
data: {
todos: [
{ id: 1, title: '吃飯' },
{ id: 2, title: '睡覺' },
{ id: 3, title: '打豆豆' },
]
},
})
</script>
</body>
```

### 動態組件

通過保留的 `<component>` 元素,動態的綁定到它的 is 特性,
我們讓多個組件可以使用同一個掛載點:

簡單示例

```html
<body>
<div id="app">
<select v-model="currentView">
<option value="home">home</option>
<option value="posts">posts</option>
<option value="archive">archive</option>
</select>
<component v-bind:is="currentView"></component>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
currentView: 'home'
},
components: {
home: {
template: '<div>home</div>',
},
posts: {
template: '<div>posts</div>',
},
archive: {
template: '<div>archive</div>',
}
}
})
</script>
</body>
```

登陸註冊示例:

```html
<body>
<div id="app">
<ul>
<li><a href="JavaScript:void(0)" @click="defaultView = 'register'">註冊</a></li>
<li><a href="JavaScript:void(0)" @click="defaultView = 'login'">登陸</a></li>
</ul>
<div>
<component :is="defaultView"></component>
</div>
<hr><hr><hr><hr>
<div>
<!-- 可以使用 keep-alive 保持組件狀態 -->
<keep-alive>
<component :is="defaultView"></component>
</keep-alive>
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
components: {
login: {
template: `
<form action="">
<div>
<label for="">用戶名</label>
<input type="text" />
</div>
<div>
<label for="">密碼</label>
<input type="password" />
</div>
<div>
<input type="submit" value="點擊登陸" />
</div>
</form>
`
},
register: {
template: `
<form action="">
<div>
<label for="">用戶名</label>
<input type="text" />
</div>
<div>
<label for="">密碼</label>
<input type="password" />
</div>
<div>
<label for="">確認密碼</label>
<input type="password" />
</div>
<div>
<label for="">驗證碼</label>
<input type="password" />
</div>
<div>
<input type="submit" value="點擊註冊" />
</div>
</form>
`
}
},
data: {
defaultView: 'login'
},
})
</script>
</body>
```

 

 

 

 

 

 

 

---

## 使用 Vue 的一些經驗

### 調試 Vue

- https://github.com/vuejs/vue-devtools
- https://github.com/MiCottOn/DejaVue


### 解決表達式閃爍

1. 將所有 `{{}}` 通過 `v-text` 替換
2. 使用 `v-cloak` 解決

第一,在要渲染處理的 DOM 節點上添加一個指令 `v-cloak`:

```html
<div id="app" ng-cloak>
{{ message }}
</div>
```

第二,在 style 中加入一個屬性選擇器樣式:

```css
[v-cloak] {
display: none;
}
```

第三,解析執行機制:

1. 當瀏覽器解析處理到添加了 `v-cloak` 屬性的節點的時候,屬性樣式被作用上來,也就是說預設這個容器就是隱藏著的
2. 然後當 Vue 程式解析渲染完HTML模板的時候,自動將容器上的 `v-cloak` 屬性移除

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Vue.js 使用了基於 HTML 的模版語法,允許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循規範的瀏覽器和 HTML 解析器解析。 在底層的實現上, Vue 將模板編譯成虛擬 DOM 渲染函數。結合響應系統,在應用狀態改 ...
  • 英文原文:http://net.tutsplus.com/tutorials/html-css-techniques/sass-vs-less-vs-stylus-a-preprocessor-shootout 原文鏈接:http://www.w3cplus.com/css/sass-vs-less ...
  • 講述js中的on事件與addEventListener的用法與不同 ...
  • 事件,就是網頁中某個特別值得關註的瞬間。事件經常由用戶操作或通過其他瀏覽器功能來觸發。 但很少有人知道,也可以使用JavaScript 在任意時刻來觸發特定的事件,而此時的事件就如同瀏覽器創建的事件一樣。也就是說,這些事件該冒泡還會冒泡,而且照樣能夠導致瀏覽器執行已經指定的處理它們的事件處理程式。在 ...
  • 1.字元串截取substr(str,length)返回從指定位置開始,截取length長度的子字元串。substring(start,end)返回從start開始到end結束的字元串。end不寫就到結尾。區別,前者截取長度,後者截取位置 2.頁面跳轉window.history.back();返回的 ...
  • 近期做一高校項目,招生計劃發佈需要實現同一專業在不同省份計劃招生的人數,即一對多以及多對多,就應用了批量填寫表單,由於整體UI框架用了bootstrap,那bootstrap-select的使用自然不在話下,於是使用了clone,但無奈發現bootstrap-select被clone,和after追 ...
  • 啥都不說了,直接上 1.Html結構代碼 2.css樣式代碼 3.javascript代碼 4.主要: a.字體豎排垂直:字體大小font-size = 包含字體標簽寬度width b.js中stop()方法:stop()表示結束動畫有過渡 stop(true)表示暫停動畫, stop(true,t ...
  • 又是輪播?沒錯,換個樣式玩輪播。 HTML: CSS: JS: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...