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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...