--- title: 第三堂:Vue 起步走,指令學起來 tags: 2022 Vue 作品實戰班, 六角學院 date: 20220121 image: --- # 第三堂:Vue 起步走,指令學起來 # 最終作業主題 - 做自已有興趣的 - 浪浪動物 - 服飾 - 先收集素材 - 賣酒,你的產品有哪些 - 台灣啤酒,unsplash 可能沒有 - 換個方向,選擇國外高檔葡萄酒 - 撰寫文案 - 作品牆 - 健身廚房 - 麵包 - 感受到點麵包的愛 - 配色一致 - 誇張系 - 注意授權 - 小魔女諾貝塔雜貨工坊 - 使用免授權圖片 - 台中行李箱 - local 生活感 - api 限制 - NoSql - 試著突破極限 - 延伸加入更多的欄位 - 同學自行發揮 - 求職網![](https://i.imgur.com/iKhP66L.png) - 先做電商主題 - 熟悉 api 可以做到什麼程度 - 亞洲媒體藝術節 - 預約功能 - 調整時間 - 新建了web server - Nuxt - middle ware![](https://i.imgur.com/aTxv5Bb.png) - 鼓勵玩自已想玩的東西 # Vue 起步走 ## 物件傳參考 - 物件傳參考的特性:https://youtu.be/y1odVMpi6dU ## JavaScript 型別知識 - 原始型別:字串、布林、數字、undefined、null... - 物件型別:物件、陣列、函式 > Javascript 有沒有"function" 型別 > 沒有 - 物件型別有傳參考的問題 ## 物件參考特性 #### ex:one: ```javascript= 物件:function, array, object var text = '一段文字'; var text2 = text; text2 = '更新文字'; // 純值原始型別 console.log(text === text2); ``` - `false` - text2 重新賦值後,和原來的值沒有關聯性 #### ex:two: ```javascript= var a = { name: 'a', } var b = { name: 'a', } console.log(a === b); ``` - `false` - 2個一模一樣的物件 - 宣告物件時,每一組 `{}` 都會產生新的記憶體空間![](https://i.imgur.com/dRkwKaO.png) - 比對的時候,是比對記憶體位置 #### ex:three: ```javascript= var a = { name: 'a', } var b = a; b.name = 'b'; console.log(a.name); ``` - b 指向 a 物件 - 把 b 中的 name 改成 b - ` console.log(a.name);` = **b** #### ex:four: ```javascript= var a = { name: 'a', } var b = a; b.name = 'b'; console.log(a === b); ``` - true - 只有宣告一個物件 - 2個變數都指向同一個物件 - 修改同一個記憶體空間的 nmae![](https://i.imgur.com/yWDTNuD.png) #### ex:five: ```javascript= var a = { name: 'a', } function changeData(param) { param.name = 'b' return param; } var b = changeData(a); console.log(a === b); ``` - 程式執行流程 ![](https://i.imgur.com/CkOj3b8.png) --- ![](https://i.imgur.com/cw2ksVe.png) --- - 實戰中可能會踩到的雷點 - true - **ESLint**建議不要調整傳入的物件屬性,會改到原始值 - 透過參數傳遞,傳參考特性不會變,這幾個是一樣的![](https://i.imgur.com/Jzgvj18.png) - 從頭到尾只有建立一次物件 :::success 所有的函式執行一定會 return,沒有加 return,也會 return undefined ::: #### ex:six: ```javascript= var a = { name: 'a', } function changeData(param) { return { name: param.name } } var b = changeData(a); console.log(a === b); ``` - 把參數帶進物件,return 物件 ![](https://i.imgur.com/eIdKdDj.png) - 實戰應用面 - fasle - 有大括號就會建立新物件 ![](https://i.imgur.com/5q83BZz.png) - 這2個物件沒有關聯性 ## 複製物件 ### 淺層複製 ```javascript= var a = { name: 'a', } function changeData(param) { // 方法一:Object assign // const newData = Object.assign( {}, param); // console.log('Object.assign', newData); // 淺層複製 // 方法二:展開 const newData = { ...param }; console.log('Spread syntax', newData); // 淺層複製 return newData; } var b = changeData(a); console.log(a === b); ``` #### `Object.assign()` - 宣告一個新物件,把第2個參數的值展開過來![](https://i.imgur.com/5g7FnEI.png) #### `const newData = { ...param };` - 寫法比較簡潔 - 和 `Object.assign()` 的結果一樣 ### 淺層拷貝觀念 - 展開背後做了什麼 ![](https://i.imgur.com/8ipfrWI.png) - 第二層的物件並沒有複製,只有指向而已 ![](https://i.imgur.com/BSRGlcd.png) - 實戰中很常遇到 - 記憶體空間不相同時,修改值才不會誤改 - 第一層物件,a,b 不相同 ![](https://i.imgur.com/an9Jdm6.png) ![](https://i.imgur.com/WbfTZqi.png) --- - 深層物件是指向的,並沒有複製![](https://i.imgur.com/RqVkszj.png) - 深層物件比對,得到相等的結果 ![](https://i.imgur.com/b8RLPAc.png) ![](https://i.imgur.com/ms53uuk.png) --- - 修改會改到被參考的物件 :::success 陣列、函式都是物件 物件要有幾層,就有幾層 ::: ### 深層複制 #### 原生 JS 寫法 ```javascript= var a = { name: 'a', } function changeData(param) { const newData = JSON.parse( JSON.stringify(param) ); console.log('stringify', newData); // 淺層複製 return newData; } var b = changeData(a); console.log(a === b); ``` #### 超雷無限深層範例 ```javascript= const z = { z: '123' } z.z = z; console.log(z); ``` - `console.log()` ![](https://i.imgur.com/nfCL3M3.png) - 試著用記憶體指向的方式解釋無限層的原因,加深物件傳參考的觀念 ### 深層拷貝觀念 #### 先轉成字串,再轉成物件 ```javascript= const newData = JSON.parse( JSON.stringify(param) ); ``` * 下圖為淺層拷貝 ![淺層拷貝](https://i.imgur.com/SXHmLWp.png) - 字串轉成物件時,會宣告新的記憶體位置 - 為什麼會有淺層拷貝,因為淺層的 code 比較好寫 - 淺層、深層都會用到 - 如果沒有深層物件,用淺層拷貝就可以 ### 物件製範例 #### ex0 ```javascript= <script> const person1 = { name: '小明', } const person2 = person1; person2.name = '小美'; console.log(person1.name); // 值為 小明 or 小美 ``` - 小美 #### ex1 ```javascript= const person1 = { name: '小明', } const person2 = person1; person2 = { name: '小美', } console.log(person1.name); // 值為 小明 or 小美 ``` - error - 只要建立新物件,參考的位置就不一樣, const 就會出錯![](https://i.imgur.com/khKSjTp.png) --- ```javascript= const person1 = { name: '小明', }; const person2 = person1; person2.name = '小美'; console.log(person1.name); // 值為 小明 or 小美 ``` - 不會出錯 - 小美![](https://i.imgur.com/l0cT1UN.png) - 參考值不變,可以使用 const --- ```javascript= let person1 = { name: '小明', } let person2 = person1; person2 = { name: '小美', } console.log(person1.name); // 值為 小明 or 小美 ``` - 用 `let` 宣告,eslint 無法通過,值沒有重新指派,要用 `const` #### ex2 ```javascript= const person1 = { name: '小明', } function fn(item) { item.name = '小美'; } fn(person1); console.log(person1.name); // 值為 小明 or 小美 ``` - 和上一題一樣 - 透過參數改變值 - 小美 #### ex3(機車題) ```javascript= const person1 = { name: '小明', } function fn(item) { item = {}; item.name = '小美'; } fn(person1); console.log(person1.name); // 值為 小明 or 小美 ``` - 小明 - 從頭到尾只有1個 person1,先把參數傳進來,再把參數指向新物件,再把物件裡面的 name 換掉![](https://i.imgur.com/pepxHAo.png) - 指向 `{}`,就和原來的 `person1` 沒有關係了,所以不會改到原來的 `person1` #### ex4(小樂高) ```javascript= let person1 = { name: '小明', } let person2; function fn(item){ person2 = person1; //此段如何修改,可以避免最後的值變成小美 person2.name = '小美'; } fn(person1); console.log(person1.name); ``` - `person2 = JSON.parse(JSON.stringify(person1));` - `person2 = { ...person1 };` - 使用展開或深拷貝 #### ⚡⚡ex大樂高(複製物件時可能會遇到) ```javascript= const people = [ { name: '卡斯伯', like: '鍋燒意麵', price: 95, imageUrl: 'https://images.unsplash.com/photo-1569562211093-4ed0d0758f12?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80' }, { name: '瑞', like: '炒麵', price: 80, imageUrl: 'https://images.unsplash.com/photo-1612929633738-8fe44f7ec841?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NHx8ZnJpZWQlMjBub29kbGVzfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=400&q=60' }, { name: '小明', like: '黑胡椒燴飯', price: 120, imageUrl: 'https://images.unsplash.com/photo-1637362520022-81292a4bff4b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80' }, { name: '喬伊', like: '生菜沙拉', price: 80, imageUrl: 'https://images.unsplash.com/photo-1505253716362-afaea1d3d1af?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80' } ]; people.forEach((person) => { if (person.name === '卡斯伯') { person = { name: '杰倫', like: '香菜', price: 180, } } }); console.log(people); // Q:請問卡斯伯是否有被替換? ``` - 沒有被替換,因為 person 指向新物件 - 流程示意圖![](https://i.imgur.com/6HNk3bw.png) - 只要看到大括號,就會產生新的參考位置 - 正確寫法 ```javascript= people.forEach((person, key) => { if (person.name === '卡斯伯') { people[key] = { name: '杰倫', like: '香菜', price: 180, }; // person = { // name: '杰倫', // like: '香菜', // price: 180, // }; } }); ``` ![](https://i.imgur.com/i7o9IcL.png) # MVVM ## Vue 起手式 <iframe src="https://codesandbox.io/embed/v-init-2022-dgyjy?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="v-init 2022" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> - option api,和 vue2 幾乎一模一樣 - data, function return - methods, 物件 - mounted,函式 - ⚡⚡常見結構,Vue 初始化 ```javascript= const app = Vue.createApp({ data() { return { ...data }; }, methods: {}, mounted() {} }); const data = {...}; ``` - 實體化,`app.mount("#app");` - 生命週期,`mounted` 最常用 #### Option Api - 簡單、易學 - 資料、方法、生命週期已經分類做好了 - 較好理解 #### Composition Api - 進階使用 - 需要熟悉 ES6 - 沒有分類,code 內容靠經驗分類 - 官方 doc 提到不適合初學者 #### 加入元件的方式 - `app.component();` ## Vue 藍圖方法 ![](https://i.imgur.com/yKWaagp.png) - Vue 用藍圖方法建立元件 - 元件內有資料、方法、生命週期…等等 - 使用 `createApp()` 把元件建立起來 - 使用 `createApp()` 建立的元件稱為==根元件== - 網頁上有許多區塊,Vue 通常會掛載在 `#app` (最外層的 dom 元素)上,用 `mount` 的方法掛載上來 - 根元件內可以插入許多子元件 ### 雙向綁定 #### 在 `mounted` 中觀察 `this` ```javascript= mounted() { console.log(this); } ``` ![](https://i.imgur.com/MEk82Gz.png) - `Proxy`,Vue3 的新機制,效能非常好,會對內容的資料監控,當值有變動時,會重新渲染到畫面上 #### 專注在資料渲染 ![](https://i.imgur.com/vvGCtok.png) - 在方法或生命週期中調整資料 - 資料變動,就會重新渲染 - MVVM概念 - 當資料變動時,能直接渲染至html畫面上 - 資料的調整可透過「方法」、「生命週期」 - 使用Vue時,可專注在資料處理,不需要花太多時間解決資料渲染 #### 從畫面調整資料 - 透過指令v-model雙向綁定後,可直接調整資料內容 ![](https://i.imgur.com/cG2fP9q.png) :::success #### ⚡⚡學習等級 - 第1級:僅有閱讀 - 第2級:動手操作 - 第3級:自已寫一次 - 第4級:用自已的方式做成筆記 - 小錯誤 ok der - 沒有時間,之後會更沒有時間 - 儘量做到 3、4 ::: ### 指令 <iframe src="https://codesandbox.io/embed/v-init-2022-dgyjy?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="v-init 2022" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> #### Vue 常用指令 - [常用指令](https://hackmd.io/@hexschool/S1DJeKTdL/%2FRhud3_1PR9qv1RJyMfwUmA) ![](https://i.imgur.com/z0wRWkU.png) #### `v-model`,雙向綁定 - vue-devtools 如果沒出現,重新開一個分頁 - ⚡⚡養成習慣,預先定義資料,避免一開始就報錯 #### 方法寫在 `methods` 裡 - 在 `methods` 定義方法 - 在 button `v-on:click` 觸發方法,修改資料 #### 元件和指令之間的關聯性 ![](https://i.imgur.com/Lf5lr5j.png) - 全部的集合都是物件,只有 data 是 function return,因為 Vue 要做元件化 - 方法會立刻執行 - 生命週期符合條件,會立刻觸發,只會觸發一次 #### 指令分三大類 ![](https://i.imgur.com/rfcyi6E.png) - 雙向綁定,`v-model`,可以讀出、寫入資料 - 渲染方法,把資料讀出來,呈現到畫面上 - `{{...}}` - `v-for`,`v-show`,`v-text` - 事件綁定,`v-on` - 觸發方法,修改資料,重新渲染畫面 ### 取出多筆資料,呈現在 `ul` 上 ```javascript= <ul> <li v-for="(item,i) in list" v-bind:key="item.name"> {{i}} / {{item}} </li> </ul> ``` - v-for 沒綁 key 的 error 看 [常用指令](https://hackmd.io/@hexschool/S1DJeKTdL/%2FRhud3_1PR9qv1RJyMfwUmA) - `v-bind:key` 不建議帶 `v-for` 的第2個參數 - 通常帶 id,data 裡沒 id ,可以這樣做,想辦法 bind 獨一無二的值![](https://i.imgur.com/s9bDFBi.png) ### html 屬性運用 - `v-bind:src=""` #### 指令縮寫 - `v-bind` → `:` - `v-on:` → `@` ### 判斷式 - button,記得加上 `type="button"` - ask:事件觸發要用什麼方法 - `v-on` - 修改 `isShow` 的 true、false 可以怎麼做 - `@click` 後面可以加入表達式 - `==` 是判斷式 - 指令儘量練習縮寫 #### `v-if`,toggle 效果 ```javascript= <button type="button" @click="isShow=!isShow">{{isShow}}</button> <div v-if="isShow">這段要顯示</div> <div v-else>這段不要顯示</div> ``` #### 指令不熟 - 去玩一下每日任務 - 表單應用滿重要的 # Options API 簡述 ## opening - 我理解很多人沒有時間 - 畢竟,業界不會管你有沒有時間 - 作業 Lv1:補上註解 - 參考範例 code - 我們尾牙要取消了 - 其實我們對員工不錯吧, array - 每人一台 mac - 獎金暑假、年尾各一次,加年終 - 每週三全遠端👀 - 實際上有一點血汗 ## computed & watch ### 如何區分 computed & watch > 除了生命週期外,其它(data、methods)都是集合 * computed 監聽多個資料 * watch 監聽單一資料 ### :one: computed ![](https://i.imgur.com/hcMAXnZ.png) - 把資料撈出來,重新運算,回傳到畫面上 - 監聽多個資料 - computed 不會更動到資料,只撈資料、渲染到畫面上。 - 優點:自動監聽data()內值的變動,自動運行 #### computed sample <iframe src="https://codesandbox.io/embed/computed-sample-g60yg?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="computed-sample" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> - .sort()會帶出前一個值與後一個值用年齡或價格排序 - 主要調整的內容,people - 目的:把 people 讀出來,重新運算,渲染到畫面上 - 閒聊歸閒聊,苓膏龜苓膏,課程還是要上 #### 加入升降冪 - 用 computed 中的 `sortPeople` 取代原本 data 的 `people` - 在 `sortPeople` 中重新排序 - 排序的陣列方法,`.sort()` - 使用三元運算子,`return ascending ? 真的結果 : 假的結果` - 取資料時記得加 `this`![](https://i.imgur.com/i1KXARj.png) #### 加入價格、年齡排序 - 用 `this.sortBy` 取代原本的 `name` - 修改 `data` 中的 `sortBy` ```javascript= <thead> <tr> <th>姓名</th> <th @click="sortBy = 'age'">年齡</th> <th>喜好</th> <th @click="sortBy = 'price'">價格</th> </tr> </thead> ``` - `computed` 只有把資料讀出來,沒有寫入 - `computed` 會對使用到的變數進行監聽,監聽到變動會再觸發函式,只要有 `this` 都是會被監聽的 ```javascript= computed: { // #1 先完成價格排序 // #2 加入 升降冪 // #3 加入價格、年齡的排序 sortPeople() { const newPeople = this.people.sort((a, b) => { return this.ascending ? b[this.sortBy] - a[this.sortBy] : a[this.sortBy] - b[this.sortBy]; }); return this.people; } } ``` - `mounted` 只會觸發一次,`computed` 只要變數有變動就會觸發 - 監聽是針對資料監聽 #### :A: getter - 一偵測到資料集的內容產生更動,就同步觸發 computed 內的函式一次。 - 因為上述特性,computed 很容易拿來做搜尋功能。 #### :B: setter > 待補 ### :two: watch ![](https://i.imgur.com/UTymwsp.png) - 重新調整資料內容,並渲染到畫面上 - 能更動資料內容 - 主要監聽data()資料中**單一的**值,當值有變化,就觸發 methods。 - 觸發 methods 後,watch 可**修改 Data 資料內容**,再把資料渲染到畫面上,或重新取得遠端的值。 - watch 不會產生新的值。 #### watch example <iframe src="https://codesandbox.io/embed/watch-sample-l1jzk?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="watch sample" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> - watch內的 `方法名稱` 和 `data()資料` 同名,就會被監聽 ![](https://i.imgur.com/Jn9yfa6.png) - [random user](https://randomuser.me/) - [seeds](https://randomuser.me/documentation#seeds) - 固定的 `seed` 會取出固定的結果 - 第1個參數是 **當前值**,第2個參數是 **前一個值** - seed 值變動的時候會去取遠端的資料 ### debounce 技巧 與 watch ⚡⚡防止 watch 時 call爆 api ###### tags: debounce lodash,為老牌函式庫 [lodash - debounce](https://lodash.com/docs/4.17.15#debounce) - 會有持續監聽的行為,但不會立即做反應 (連續觸發時不會有作用,有一個延遲反映的時間) - JS中加入 lodash 的 debounce `import { debounce } from "https://cdn.jsdelivr.net/npm/@esm-bundle/lodash@4.17.21/esm/index.js";` - 有的搜尋工具會分2層,第1層先找 localstorage,debounce 時間到才去打 api - 💡debounce 裡改傳統函式,`this` 才能正確指向 ```javascript= watch: { text(current, old) { // #1 使用 Watch 取得當前值及舊有值 console.log(current, old); }, // debounce seed: debounce(function (current) { console.log(current); axios .get(`https://randomuser.me/api/?seed=${current}`) .then((res) => { console.log(res.data); this.people = res.data; }); }, 1000) ``` - https://stackoverflow.com/questions/45178621/how-to-correctly-use-vue-js-watch-with-lodash-debounce #### watch test1 如何在 text 文字更新時,觸發 fn 函式 <iframe src="https://codesandbox.io/embed/watch-test-1-n9ikv?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="watch test 1" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> ### :three: methods methods 本身是一個大物件,內層可以包很多函式,是主動觸發的事件。 #### 觸發方式: - 指令觸發 (點擊) - 由其他 options API 觸發 methods `備註` 以上三者皆為 options API,在操作中會大量使用 this,因此盡量不要在 options API 中直接使用箭頭函式,this 的指向會出錯。 ## 卡老師叮嚀 ![](https://i.imgur.com/Bu7wZpx.png) - 儘量參與作業討論 - 用3~4個月,抵過1年 - 第4周滿難的 - 過年把第4周做完 - 第4周以後難度會上升很多 - 熟悉 github pages - 裝 nodejs - 建議裝 14版 - 16版移除了一些語法,有一些套件不支援 ### nodejs 相關 - [安裝 nvm 環境](https://wcc723.github.io/development/2022/01/10/install-nvm/) - [無法辨識 'XXX' 詞彙是否為 Cmdlet、函數、指令檔或可執行程式的名稱](https://hsiangfeng.github.io/nodejs/20190801/449913843/) - [nvm安裝](https://www.796t.com/article.php?id=418016) - [顯示 Mac 隱藏檔案 的三個方法](https://macuknow.com/2017/08/26/1428/%e4%b8%89%e6%8b%9b%e8%ae%93-mac-%e9%a1%af%e7%a4%ba%e5%87%ba%e9%9a%b1%e8%97%8f%e6%aa%94%e6%a1%88/) - 前端必裝工具 - JavaScript 環境 - 可以跑後端 - `nove -v` - iterm2 主題![](https://i.imgur.com/lRI3iHC.png) # Bootstrap JS ## Alerts Dismissing - [Component Alerts Dismissing](https://getbootstrap.com/docs/5.0/components/alerts/#dismissing) - [Component Alerts Dismissing(中文)](https://bootstrap5.hexschool.com/docs/5.1/components/alerts/#dismissing) - alert 元件可以透過叉叉關閉 - `data-bs` 開頭的屬性,bootstrap 專屬的屬性 - `data-bs-dismiss="alert"`,移除 alert ## Modal - [Modal live demo](https://bootstrap5.hexschool.com/docs/5.1/components/modal/#live-demo) - `data-bs-toggle="modal"`,切換 modal![](https://i.imgur.com/QE0neQ5.png) - `data-bs-target="#exampleModal"`,用 `id` 選擇目標![](https://i.imgur.com/umd2bJp.png) ## 如何使用 JS 操作元件 <iframe src="https://codesandbox.io/embed/vue-bootstrap5-001-bdxqu?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="Vue Bootstrap5 001" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> - 拿掉 button 中的 `data-bs` 屬性 ```javascript= <button type="button" class="btn btn-primary" id="modalBtn"> Launch demo modal </button> ``` - [modal usuage](https://bootstrap5.hexschool.com/docs/5.1/components/modal/#usage) - 透過資料屬性 - 透過 JavaScript - `var myModal = new bootstrap.Modal(document.getElementById('myModal'), options)` - modal 實體化 - 選擇 dom 元素 - [options](https://bootstrap5.hexschool.com/docs/5.1/components/modal/#options),相關選項 - [modal 相關方法](https://bootstrap5.hexschool.com/docs/5.1/components/modal/#methods) - show - hide ## 在 Vue 中使用 bootstrap modal <iframe src="https://codesandbox.io/embed/vue-bootstrap5-003-zxii4?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="Vue Bootstrap5 003" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> - 在 `mounted` 擷取 dom 元素 ## 討論串 - [討論串](https://discord.com/channels/801807326054055996/905656987583397908)