--- tags: VUE 3 課程筆記 --- # VUE 3 課程筆記 (六) 進階 API ## 操作 DOM 元素技巧 refs ### 使用 ref 定義元素 ```htmlembedded= <input type="text" ref="自定義名稱"> ``` 然後在 mounted 裏面 可以用一個 "this.$refs" 就會取得所有的 ref 節點內容。 如果在元件上定義一個 ref 也是可以在父層用 $refs 找到 子層元件的所有資訊,並且直接操作調整,不過在大多數情況下,由於並不確定元件內是否都有相對應的屬性,所以不太會這樣去進行操作 ### 使用 ref 操作 bootstrap 元件 可以在 modal 的 methods 裡面找到下面這個東西 ![](https://i.imgur.com/zwUF3Ov.png) 在 modal 元件加入屬性 ```htmlembedded= <div class="modal" tabindex="-1" ref="自定義名稱modal"> </div> //加入 ref 屬性 ``` 在 js 加入定義 modal 以及操作方法 可以在文件中找到打開 modal 的方法 ![](https://i.imgur.com/170nrK0.png) 調整 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 新增的東西 ![](https://i.imgur.com/BIV8kJZ.png) ## 跨層級資料傳遞 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 } } }) ```