---
tags: VUE 3 課程筆記
---
# VUE 3 課程筆記 (六) 進階 API
## 操作 DOM 元素技巧 refs
### 使用 ref 定義元素
```htmlembedded=
<input type="text" ref="自定義名稱">
```
然後在 mounted 裏面 可以用一個 "this.$refs" 就會取得所有的 ref 節點內容。
如果在元件上定義一個 ref
也是可以在父層用 $refs 找到 子層元件的所有資訊,並且直接操作調整,不過在大多數情況下,由於並不確定元件內是否都有相對應的屬性,所以不太會這樣去進行操作
### 使用 ref 操作 bootstrap 元件
可以在 modal 的 methods 裡面找到下面這個東西

在 modal 元件加入屬性
```htmlembedded=
<div class="modal" tabindex="-1" ref="自定義名稱modal">
</div>
//加入 ref 屬性
```
在 js 加入定義 modal 以及操作方法
可以在文件中找到打開 modal 的方法

調整 js
```javascript=
data(){
return{
bsModal:'',
// 1. 定義一個準備被操作的 bsModal 變數
}
},
methods:{
openModal(){
this.bsModal.show();
// 3. 加入打開 bsModal 的方法
}
},
mounted(){
this.bsModal = new bootstrap.Modal(this.$refs.自定義名稱modal)
// 2. 將 data 定義的變數 bsModal 建立實體,指向 $refs.自定義名稱 modal ,這邊要注意,因為已經有在 data 裡面定義了 bsModal,所以要記得把官方文件當中的 var myModal 調整成用 this 指向的 this.bsModal
}
```
## 自訂元件生成位置 teleport
teleport 可以在 template 裏面運用
```javascript=
template:`<teleport to="DOM元素">
要改變的東西
</teleport>`
```
可以直接指向 html 標籤,對標籤進行調整,甚至是```<title></title>```都可以調整到~
但要注意,如果是要調整 #app 之後的元件,由於 js 作動區域的關係,會抓不到在 #app 後面的 html 標籤就會顯示 teleport 要抓的東西是一個 null
另外,teleport 也可以這樣用
```javascript=
app.component('new-title', {
template: `
<teleport to="title"> - 新增的標題片段</teleport>
<teleport to="h1"> - 新增的文字片段</teleport>`
})
```
去調整 title 以及 h1 等等的,但調整起來會變成顯示 原本的title + teleport 新增的東西

## 跨層級資料傳遞 provide
可以從祖父層直接將資料傳遞到孫子層
在祖父層使用 provide(提供) 在子孫層使用 inject(注入)翻譯起來怎麼有點怪怪的 (X
provide 方式有兩種:
第一種是直接 provide 一個物件:
```javascript=
const app = Vue.createApp({
data() {
return {
user: {
name: '小明',
uuid: 78163
}
}
},
provide:{
user: {
name: '小明',
uuid: 78163
}
},
})
```
一種是用函式的方式進行 provide
```javascript=
const app = Vue.createApp({
data() {
return {
user: {
name: '小明',
uuid: 78163
}
}
},
provide(){
return{
user : this.user,
}
},
})
```
祖父層提供完之後就可以在子孫層加上 inject ,然後, inject 是一個陣列哦
```javascript=
const userComponent = {
template: `<div>
userComponent 元件:<br />
{{ user.name }}, <br />
{{ user.uuid }}
</div>`,
inject:['user'],
created() {
console.log(this.user);
},
}
app.component('card', {
data() {
return {
title: '文件標題',
content: '文件內文',
toggle: false
}
},
components: {
userComponent
},
template: `
<div class="card" style="width: 18rem;">
<div className="card-header">card 元件</div>
<div class="card-body">
<div> {{user.name}} </div>
<userComponent></userComponent>
</div>
</div>
`,
});
```
這樣就可以跳過 card 這個父層,直接將資料 inject 到子孫層的 userComponent 了。
另外,如果想要在父層也接收到來自祖父層的 provide ,
只要在父層也加上```inject:["user"]```就可以了
## 元件直接加入 v-model
基本上就是一個使用 v-model 搭配 props & $emit 的方式對父子層進行雙向綁定
HTML 部分:
```htmlembedded=
{{name}}
<custom-input v-model:text="name"></custom-input>
// 有一個顯示 name 的地方,在子層的 html 標籤上綁上 v-model + props 的混合 v-model:text="name" text 是子層 name 是父層,依然遵照“前內後外”原則。
```
JavaScript 部分:
```javascript=
// 父層(根元件)
const app = Vue.createApp({
data() {
return {
name: '小明',
}
}
});
// 子層
app.component('custom-input', {
// 一個對應父層的props
props: ['text'],
// 在input 裡面使用 :value="text" 以及 $emit ,要注意,這邊的 'update:text' 的 update 是固定的,不能自定義。
template: `<input type="text"
:value="text"
@input="$emit('update:text', $event.target.value)"
class="form-control">`
});
```
## 混合元件方法 mixins
把元件混合進其他元件的方式
```javascript=
// 要混合進去的元件 1
const mixComponent1 = {
data() {
return {
name: '混合的元件',
number: 6
}
},
created() {
console.log('混合的元件生命週期');
}
};
// 要混合進去的元件 2
const mixComponent2 = {
data() {
return {
name: '混合的元件 2',
number: 2
}
},
created() {
console.log('混合的元件生命週期 2');
}
};
// 混合主體的根元件
const app = Vue.createApp({});
app.component('card', {
data(){
return{
number: 10,
}
},
template: `<div class="card">
<div class="card-body">{{ name }} <br /> {{number}} </div>
</div>`,
// 建立一個 mixins 的陣列,把要混合進去的元件名稱帶入陣列裡面。
mixins:[mixComponent1,mixComponent2],
created() {
console.log('card 的元件生命週期')
}
})
app.mount('#app');
```
mixins 是可以重複混合兩個以上的元件的,而每個混合元件的生命週期都可以重複被觸發,不同生命週期的話會依照生命週期的順序觸發,如果是同樣的生命週期時間點,則會以『混合元件1』->『混合元件2』->『混合主體的元件』這樣的順序進行觸發,還是每一個都會觸發到。
不過同名的變數、方法等,則會依照 mixins 的順序,後者會覆蓋前者哦,所以變數、方法等如果同名的話,最後覆蓋結果就會是混合主體的元件裡面定義的東西。
## 擴展元件方法 extends
跟 mixins 的用法挺像的,不過 extends 一次只能擴展一個,所以在擴展的方式如下
```javascript=
app.component('card', {
template: `<div class="card">
<div class="card-body">{{ name }}</div>
</div>`,
extends: extendComponent1 ,
created() {
console.log('card 的元件生命週期')
}
});
```
由於 extends 是單一擴展,所以在進行 extends 的時候是直接加入一個物件的方式```extends: extendComponent1 ,```而不是像 mixins 是寫一整個陣列做多個混合。
extends 以及 mixins 的生命週期是可以重複觸發的,如果有 extends 也有 mixins 的話,兩者的生命週期都會觸發到哦,最後在 主元件還有 mixins extends 是有權重的,權重是『主元件』>『mixins』>『extend』,如果三者有同名的變數與方法,則會依照權重決定最後顯示的是誰,前面提到的生命週期觸發則是依照權重倒過來觸發。
## 自定義指令 directive
自定義指令的方式跟元件有點相似
```javascript=
app.directive('validator', {
// directive 生命週期
mounted(el, binding) {
el.focus();
// 將外部的值改為
el.className = binding.value
},
updated: function(el, binding, vnode) {
// el 元素本體
// binding 綁定的資源狀態
// vnode 虛擬 DOM 節點
console.log('update', el, binding, vnode);
const className = binding.value;
// 尋找當前的 model 名稱(取得 key 值,並帶入第一個)
const currentModel = Object.keys(binding.instance)[0];
// 從當前 Model 取值
const value = binding.instance[currentModel];
console.log(currentModel, value)
// Email validate 那一大坨是一個驗證 e-mail 的正規表達式
const re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
if (!re.test(value)) {
el.className = `${className} is-invalid`
} else {
el.className = `${className} is-valid`
}
},
})
```
其實我看得有點吃力,大概知道在幹嘛而已 XDDDDDDD
## 擴充插件 plugins
plugins 擴充插件的引入方式,可使用 CDN 以及 NPM 兩種方式
CDN 就是直接 CDN
npm 則可以參考套件文件例如:
```javascript=
npm install --save axios vue-axios
```
在引入之後則有兩種啟用方式:
app.use():(vue-axios 為例)
```javascript=
const app = Vue.createApp(...);
app.use(套件名稱,套件名稱);
// 這部分都可以參考套件的文件,當中都會有引入的操作說明
```
元件形式載入:(vee-validate 為例)
```javascript=
// 使用 import 載入元件
import {Fidle, Form, ErrorMessage} from 'vee-validate';
const app = Vue.createApp({
components: {
// VeeValidate 是 vee-validate 載入進來之後的名字
// 所以可以從 VeeValidate.XXX 找到要載入的套件
// 把它變成一個物件載入的原因是因為 Form 的名稱
// 跟原本 HTML 的 form 名字是重複的
// 所以改成 VForm(v-form) 比較好辨別
VForm: VeeValidate.Form,
VField: VeeValidate.Field,
ErrorMessage: VeeValidate.ErrorMessage,
}
})
```
套件元件載入
將原本的 html 標籤轉換成 vue 元件:
```htmlembedded=
<form @submit="onSubmit">
<input name="name" type="text" placeholder="Who are you">
<span>請填寫此欄位</span>
<button>Submit</button>
</form>
```
改成:
```htmlembedded=
// v-slot 是要把元件資源向外匯出給外部使用的部分
<v-form @submit="onSubmit" v-slot="{errors}">
<v-field name="name" type="text" placeholder="Who are you" :rules="isRequired"></v-field>
<span>請填寫此欄位</span>
<button>Submit</button>
</v-form>
```
加入驗證內容:
```javascript=
// 使用 import 載入元件
import {Fidle, Form, ErrorMessage} from 'vee-validate';
const app = Vue.createApp({
components: {
// VeeValidate 是 vee-validate 載入進來之後的名字
// 所以可以從 VeeValidate.XXX 找到要載入的套件
// 把它變成一個物件載入的原因是因為 Form 的名稱
// 跟原本 HTML 的 form 名字是重複的
// 所以改成 VForm(v-form) 比較好辨別
VForm: VeeValidate.Form,
VField: VeeValidate.Field,
ErrorMessage: VeeValidate.ErrorMessage,
},
methods:{
// 對應到 html 上的 :rules
isRequired(value){
// 驗證此欄位有無填寫
if (!vlaue){
return "此欄位必填啊"
}
return true
}
}
})
```