# vue 指令 ## v-once 只做一次,只處理初始化的 減少效能 ## v-html , v-text (會有XSS問題) {{}}只能把資料編譯回來,他不能把html語法Rander畫面, 因此需要v-html (除非有html 不然就用{{}}) 可以用{{}} template取代就取代 v-html , v-text 都會取代原本的內容 用樣板可以把資料跟html結合 XSS 最好防禦是用自訂規則 ex 大型論壇都會自訂 [] 是圖片還是怎 (主要是在後台要確認這個是我家的東西) laravel也是 {{}} 他會把這個 鬍子語法編譯 ## v-on 或 @縮寫 如果不需要傳值 可以不寫() 另外簡單的表達式就在html用,那邊可以直接取data,不用在下面要this再取 ![](https://i.imgur.com/5aqyqaS.png) **拆解v-on** ![](https://i.imgur.com/x51LC4B.png) ## 參數 **沒參數** `@input="change()"`等於 `@input="change"` 不用傳even進去, 下面可以直接取。 **有參數** 有參數 要傳 **event** 注意要加$字號 `@input="change('good',$event)"` ### 綁定方法 ``` v-on:事件=執行事件 <button type="button" v-on:click="greet">數字加1</button> <script> const vm = new Vue({ el:'#vm', data:{ userName:'Celeste' }, methods:{ greet(){ alert(`Hello ${this.userName}`) } } }) </script> ``` methods 裡,在讓 v-on 接收這個方法,如 greet ,在接收時,該方法並不需要加上 ()。 **當事件(click)發生時,執行的方法預設有 event 物件,所以無須將 event 設為參數。** ### 調用方法 Vue 提供 $event 屬性,可以用來取得 DOM event 物件的值。 ``` v-on:事件=執行事件 <button type="button" v-on:click="greet2('Celeste',$event)">數字加1</button> <script> const vm = new Vue({ el:'#vm', methods:{ greet2(name,event){ alert(`Hello ${name}`) console.log(event) } } }) </script> ``` ### 加入事件修飾符的 v-on 指令 在 JS 中,可以透過一些方法來阻止一些預設行為,像是 event.preventDefault() 、 event.stopPropagation() 或是改變事件傳遞的階段,而 Vue 透過加入修飾符也能達到相同的作用。 **Vue 提供的修飾符:** #### .stop .stop 跟 JS 裡 event.stopPropagation() 目的相同,阻止後續的事件傳遞。 ``` <div class="changeTarget" v-on:click.stop="changeTarget('div')"> <button type="button" v-on:click.stop="changeTarget('button')">changeTarget</button> </div> ``` 在沒有 .stop 的情況,如果點擊 `<button>` ,則上層 `<div>` 跟 `<button>`皆會觸發 changeTarget(),這是因為事件的傳遞預設為 Bubbling ,所以在觸發 changeTarget('button') 後,會接續觸發 changeTarget('div') ,而修飾符 .stop 就是當第一個事件執行後,後續的冒泡事件又或是捕獲事件就會被阻止。 #### .prevent .prevent 跟 JS 裡 event.preventDefault() 目的相同,阻止元素的預設行為,像是` <a> `跟 `<button>` 的預設行為會導致頁面跳轉或重整。 `<a href="https://www.google.com/" target="_blank" v-on:click.prevent="changeTarget('a')" >changeTarget</a>` #### .capture 在 JS 中, addEventListener 事件傳遞預設是 Bubbling,如果要變成 Capturing ,就需要將第三個參數改成 true。 在 Vue 裡,如果想把觸發時機點改成 Capturing ,可以直接使用修飾符 .capture。 ``` <div class="changeTarget" v-on:click.capture="changeTarget('div')"> <button type="button" v-on:click.stop="changeTarget('button')">changeTarget</button> </div> ``` #### .self 只当在 event.target 是当前元素自身时触发处理函数 即事件不是从内部元素触发的 `<div v-on:click.self="doThat">...</div>` #### .once 使用修飾符 .once ,該事件只能被執行1次。 #### .passive 看了 MDN 對於參數 passive 的介紹,主要是針對 touch 某些事件在 scrolling 事件發生時,會有停頓延遲的情況,參數 passive 預設是 false ,當參數 passive 改為 true 時,可以改善scrolling 事件停頓延遲這個問題。 **不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。** ##### 按鍵修飾符 透過加入按鍵修飾符,以決定使用哪個按鍵來執行事件。 常見的幾個按鍵修飾符如下: * .enter * .tab * .delete (“删除”和“退格”鍵) * .esc * .space * .up * .down * .left * .right * .ctrl * .alt * .shift ### 單一按鍵修飾符(只能在key事件) `<input type="text" v-on:keyup.enter="addTodo" v-model="newTodo">` 監聽鍵盤中 enter 按鍵的 keyup 事件,當 enter 按鍵被按下再鬆開後就會去執行所綁定的函式 addTodo ##### 多個按鍵修飾符 `<input type="text" v-on:keyup.alt.67="clear" v-model="newTodo">` 監聽鍵盤中 alt 跟 C 按鍵的 keyup 事件,必須特別注意的是當 .alt、.ctrl、shift 等修飾鍵與其他按鍵或事件組合時,事件要觸發的話, .alt、.ctrl、shift 等修飾鍵是必須處於按下的狀態的,例如同時按下 alt 跟 C 按鍵,並不會觸發 clear 事件,按下 alt 跟 C 按鍵後僅放開 alt 按鍵,也不會觸發事件,只有在按下 alt 跟 C 按鍵後放開 C 按鍵( alt 按鍵仍須持續按著)才會觸發事件。 ##### 滑鼠修飾符 加入滑鼠按鈕修飾符的狀況下,只有那個滑鼠按鈕才能觸發事件。 * .left * .right * .middle `<button type="button" v-on:click.right="addTodo">addTodo</button>` ## v-model 雙向綁定 input 都是會把轉成string,所以要數字都記得要轉 **v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值** text 和 textarea 元素使用 value property 和 input 事件; checkbox 和 radio 使用 checked property 和 change 事件; select 字段将 value 作为 prop 并将 change 作为事件。 **对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。** ``` 小注意 Vue裡面 多選用ckeckbox radio是單選 ``` **form表元素 value預設都是string 請用修飾詞 .number轉回數字** ### 單行(input)及多行(textarea)文本 ``` value="edit me"不會顯示在畫面上 <input type="text" v-model="message" value="edit me"> 可以使用屬性placeholder作為沒有空值的替代文字 <textarea v-model="message" placeholder="add multiple lines"></textarea> <script> const vm = new Vue({ el:'#vm', data:{ //畫面上的輸入欄位顯示的值↓ message:'編輯' } }) </script> ``` **在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替。** ### 單選(type="radio")、複選(type="checkbox")按鈕及下拉式選單(select) #### 單多選提醒事項 radio vs checkbox **原生** name屬性值來組成一組。 表單提交時送出的值為選擇項目的value屬性的值 **vue** 在原生都可以用 單多選自主決定 一定要在 input 中設定 value 屬性,因為 Vue 是利用 value 屬性的值做響應的。也就是說被點選的按鈕,會將 value 屬性的值傳送到 v-model 綁定的資料裡。 同樣 v-model = 'a' radio **單選** checkbox**多選** ``` 如果checkbox要單選 data:{ a:[] } ``` 把綁定資料改成array,且自動有排序 #### <input> 單選(type="radio") ``` <label for="male"><input type="radio" id="male" value="male" v-model="gender" name="gender">Male</label> <label for="female"><input type="radio" id="female" value="female" v-model="gender">Female</label> <script> const vm = new Vue({ el:'#vm', data:{ gender: '' } }) </script> ``` 如上例,利用 v-model="gender" 讓 Vue 實例的資料跟畫面顯示的值做響應,當點選 Male 時, value 的值 male 就會連動到 Vue 實例的資料(gender)中,當改點選 Female 時, gender 的資料值也會同時改變。 #### `<input>` 複選(type="checkbox") **沒有 value 屬性** 布林直判斷 0 1 在沒有 value 屬性的狀況下,複選按鈕的資料會呈現布林值(true或false)。 ``` <label for="contract"><input type="checkbox" id="contract" v-model="contract" name="contract">Agree Contract</label> <script> const vm = new Vue({ el:'#vm', data:{ // 預設值設為false contract: false } }) </script> ``` **有 value 屬性** 選出三個先後順序的(**有順序**) 作為複選按鈕,要將這些被選取的資料連動到同一筆資料時,那麼綁定的資料要是陣列,如此才能夠容納多筆 value 值。 v-model = "a" ``` data:{ a:[] } ``` 如果要用原本排序 跑x-for 然後v-model綁 a讓他記錄排序 ``` data:{ a:[] } ``` #### 下拉式選單(select) 如果屬性 value 被省略,那麼 `<option>` 的文字內容就會被當成 value 值送出去 ``` <select v-model="cellphone"> <!--有value傳value,沒有value就傳包含的文字--> <option disabled value="">請選擇</option> <option>三星</option> <option>蘋果</option> <option>oppo</option> </select> <script> const vm = new Vue({ el:'#vm', data:{ // 預設的值要能跟上面其中一個的 value 值吻合,否則畫面的下拉選單初始值會是空白的 // 如果預設要是第一個請選擇的話,可以把 value 值設為空值 cellphone:'' } }) </script> ``` **複選** `<select>` 要加上屬性 multiple才能變成複選。 點選時,需透過 ctrl 加滑鼠點擊才能成功變成複選。 響應的資料會是陣列型別。 不像上面的 `<input type="checkbox">` 複選,響應式資料的排序結果會按照點選的順序,而複選的下拉式選單的資料響應式資料的排序結果則是依據畫面上資料的的排序。 ``` 下拉式選單要是複選要加上屬性multiple <select v-model="watch" multiple> <option disabled value="">請選擇</option> <option>Omega</option> <option>蘋果</option> <option>Garmin</option> </select> <script> const vm = new Vue({ el:'#vm', data:{ // 複選的響應資料要是陣列 //3個都點選的結果,資料一定是[ "Omega", "蘋果", "Garmin" ] watch:[] } }) </script> ``` #### 修飾符 ##### .lazy 將表單的 input 事件變成 change 事件。 事件發生的時機點從輸入變成焦點移開。 針對 `<input type="text">` 發生時機: change:沒有 focus 在表單元素上時(當滑鼠焦點移開時)才發生。 input:輸入時即發生。 ##### .number 在使用 <input> 傳送響應式資料時,資料的型別會是字串,即使是使用 <input type="number"> 。 如果希望從 <input> 傳送過來的資料就是數字型別的話,就必須使用修飾符 .number ,即可直接傳送數字而非字串給響應式資料。 Vue 是透過 parseFloat() 來解析數值的,如果無法解析會返回原值(JS 中的parseFloat()無法解析會返回NaN)。 ##### .trim ## v-show display:none 指令後面的都會解析成布林值 `v-show="只要是表達式都可以"` **注意,v-show 不支持` <template>` 元素,也不支持 v-else。** v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 **不用擔心 key的問題** ## v-if if迴圈跑出來的可以用在自己 ex `<div v-for=" bird in birds" :class="bird"></div>` 跟show差別在 if是**操縱dom元素** 直接刪除增加 對於性能消耗比較大 常用來顯示就用show **v-if 跟 v-show 的使用時機** 當有頻繁切換需要時,使用 v-show ,如果沒有的話,就使用 v-if,這是因為 v-show 不論初始值是真值(Truthy)還是假值(Falsy)都一定會先渲染出來,但是 v-if 初始值如果是假值,則不會去渲染畫面,但在切換的過程則需要不斷的重建或銷毀,也因此 v-show 會有較高的初始渲染效能,而 v-if 則是在切換的過程,使用較多的效能。 if - else -if ``` <h3 v-if="Num>9">First</h3> <h3 v-else-if="Num>5">Second</h3> <h3 v-else>Third</h3> ``` v-if 、 v-else-if跟 v-else 這幾個指令要緊緊相依,中間不能插入其他的不相干的元素,另外在有 v-else 的情況下,就不會出現佔位標籤 <!----> ,這是因為在 v-if 、 v-else-if跟 v-else 是一種切換的關係,一定會有其中一個顯示在畫面上,所以不像是單純只有 v-if 時,當 v-if 不成立時,HTML 就沒有留位置給它,所以需要靠佔位標籤 <!----> 來留位置。 ### 利用 template 標籤來將條件分組 利用template 標籤分組的好處是甚麼? 簡單來說就是畫面上不會出現太多的 `<div>` 來為要呈現的區塊分組,又或是在 HTML 使用太多的 v-if 跟 v-else 可以比較一下這幾種用法 第一種寫法,運用很多div ``` <div v-if="templateReveal"> <h1>利用div</h1> <p>Paragraph 1</p> <p>Paragraph 1</p> </div> <div v-else> <h1>利用div</h1> <p>Paragraph 2</p> <p>Paragraph 2</p> </div> 第二種寫法,都加上v-if <h1 v-if="templateReveal">全都加上v-if</h1> <h1 v-else>全都加上v-else</h1> <p v-if="templateReveal">Paragraph 1</p> <p v-else>全都加上v-else</p> <p v-if="templateReveal">Paragraph 1</p> <p v-else>全都加上v-else</p> 第三種寫法,使用template <template v-if="templateReveal"> <h1>使用template</h1> <p>Paragraph 1</p> <p>Paragraph 1</p> </template> <template v-else> <h1>使用template</h1> <p>Paragraph 2</p> <p>Paragraph 2</p> </template> ``` ![](https://i.imgur.com/d4pYHes.png) 使用` <template>` 可以製作一個群組,但是在 HTML 上不會顯示出 `<template> `標籤,使用 `<div>` 同樣可以做出一個群組,但是在 HTML 上會顯示出` <div>` 標籤,單純只看包裹群組的的標籤是否還有額外的作用,有的話就須使用` <div> `標籤,例如為群組加上 class 樣式,如果使用 `<template>` 標籤的話,因為會消失,所以會導致群組無法吃到 class 樣式。 有class用 div 沒class用template 不會破版 ### key Atttribute ``` <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template> ``` 在切換的過程中,輸入在 `<input> `中的文字,並不會隨著 loginType 的不同而有所改變(即使切換還是存在剛剛輸入的文字)。如同上面所說的 Vue 為了快速渲染畫面,針對幾乎相同的元素模板將重複使用。 為了解決上面的問題, Vue 提供了 key Atttribute 來表達這2個元素是不同的,不要去複用。 資料切換而模板重複使用造成問題的通常是表單元素` <input> `,所以可以在 `<input> `上加入 key Atttribute 來解決複用的問題。 ``` <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template> ``` **注意,`<label>` 元素仍然会被高效地复用,因为它们没有添加 key attribute** ## v-bind ### style class https://www.youtube.com/watch?v=D6JHwVGqg84 https://v3.cn.vuejs.org/guide/class-and-style.html#%E5%AF%B9%E8%B1%A1%E8%AF%AD%E6%B3%95 **css** :style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名: 正常等於右邊不能加入表達氏,也不能{{}},用v-bind把右邊變成可以表達式 v-bind:屬性名稱 = 表達式 :class = " a? 'active' : '' " 這邊注意,在v裡面都會去找下面 所以String記得加 '' ### 物件語法 記住這邊的都是js 你看background-color才是css寫法 但在這邊是js寫法 ![](https://i.imgur.com/rTWeE7L.png) 避免混淆最好這樣寫 ![](https://i.imgur.com/WeTQm1E.png) ture false用法 ![](https://i.imgur.com/EDcTrVb.png) ture的情況會 ![](https://i.imgur.com/sJa3qil.png) ``` v-bind:被綁定的HTML Attribute = "Vue使用的資料 <div v-bind:class="{ active: isActive }"></div> ``` -bind 指令告訴了 Vue 這一段的 class 屬性要使用到 Vue 實例裡的 data 資料, { active: isActive } ,前面的 active 是 className,而後面的 isActive 是 Vue 實例裡的 data 資料,決定前面的 active 是否要啟用,是 true 就啟用,否則就不啟用。 ### 陣列語法 ` <div v-bind:class="[active,right]"></div>` 同樣的 v-bind 指令告訴了 Vue 這一段的 class 屬性要使用到 Vue 實例裡的 data 資料,利用陣列語法把要加入的 class 加入進去,加入的方法如下: ``` const vm = new Vue({ el:'#app', data:{ // 自訂屬性名稱: 'className' active: 'active', right: 'btn-white' } }) ``` 當然 物件不只能綁定data 其他一樣可以 **第一個data 第二個computed** ![](https://i.imgur.com/yj9jm42.png) v-bind:style 樣式綁定 ### 物件語法(ture false控制) (能用input的ckeckbok 沒有value去控制) 其實跟上面的 v-bind:class 寫法是很像的。 但是要注意前面的屬性是 CSS 屬性,後面的值是自訂的 JS 屬性名。 而當我們在寫 CSS 屬性名稱時,記得使用小駝峰式或短橫線分隔,例如: fontSize 或是 'font-size' ``` <div v-bind:style="{ color: activeColor, fontSize: fontSize }"></div> <script> const vm = new Vue({ el:'#app', data:{ activeColor: 'red', fontSize: '30px' } }) </script> ``` **HTML 的任何屬性如果要套用到 Vue 實例 data 資料的話,就必須使用 v-bind 指令,即使是透過 data 資料產生出來的資料也是一樣(例如 v-for 資料要綁到 HTML Attribute 上時也一樣要使用 v-bind 指令)。** ## x-for(都要加:key) 有key他就不會每次更動就重新渲染 會友判斷依據 然後你有key的情況 去改動 dom不會重新渲染 0 1 2 會變成 0 2 他只會把dom拔起來放到要移動的位置 資料那邊的index還是會重新跑過 這兩邊是不同的 ### array objects array " (bird,index) in birds" object " (value, key, index) in birds" 命名最好這樣 比較好記 另外object有三個參數 ![](https://i.imgur.com/N42hBI2.png) ### 小知識 **for...of** 用於可迭代物件,如字串、陣列、DOM NodeList 等等 如果要排序 最好用id 再來用index ``` const arr = ['a', 'b', 'c'] for(const value of arr){ // value 是陣列值 console.log(value) //a b c } ``` **for...in** 主要是為遍歷物件屬性 ``` const obj = {a:1, b:2, c:3} for(const prop in obj){ // prop 是物件屬性名 console.log(prop) //a b c console.log(obj[prop]) // 1 2 3 } ``` **想成 for of 和 for in 就好了 因為v-for可以物件跟array** ### 迭代陣列 ``` item是從items代入的資料(從索引值0開始一筆一筆的代入),index是代入資料(item)的索引值 <div v-for="(item,index) in items">{{ index }}-{{ item.type }}</div> ``` 對於陣列資料的渲染,更推薦使用 v-for="item of items" ,因為在 JS 中 for...of 比 for...in 更適合用在迭代中,因此使用 v-for="item of items" 會更接近 JS 的語法。 ### 迭代物件 ``` obj:{ alex:'male', sara:'female' } ``` ``` value是物件屬性值,key是物件屬性名,而index是指該key-value pair 是第幾筆資料 <div v-for="(value,key,index) in obj">{{ key }}-{{ value }}</div> ``` 記法 第一參數 是item 要index再加一個,但你是item是key 在從裡面拿value所以在左邊加入參數 ### 迭代數字 `<div v-for="num in 10">{{ num }}</div>` **須注意 num 的值是從 1 開始** ### 解決 v-for 元素複用 v-for 跟 v-if 有相同的問題, Vue 為了更快速的渲染畫面,在資料改變的狀況下,不會移動已經被渲染過的 DOM ,而是直接更新資料(把已經存在的 DOM 元素資料修改成新的值)。 所以可以透過在 HTML 中使用 v-for 的元素加入 key Attribute(值須為唯一,類似 id ),通知 Vue 這個部分資料如果改變,對於已經存在的元素是需要重新排序或是重用一個元素的 ``` 有key Attribute <div v-for="(user,index) of users"> <label>{{user.label}}</label> <input :placeholder="user.placeholder" :key="user.label"> </div> ``` ### 過濾 當需要一個經過濾後的陣列去渲染時,可以創建 computed 去執行過濾,再透過 v-for 渲染對 computed 執行結果去做渲染。 ``` 對oddNums的值做渲染 <div v-for="odd in oddNums">{{ odd }}</div> <script> const vm = new Vue({ el:'#vm', data:{ numbers: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] }, computed:{ oddNums(){ // 返回 [1, 3, 5, 7, 9] return this.numbers.filter(num => num%2 === 1) } } }) </script> ``` ### 陣列更新如何是響應(如何更動array資料) 在 Vue 文件中有說明,只有 push() 、 pop() 、 shift() 、 unshift() 、 splice() 、 sort() 、 reverse() 等 Vue 會知道陣列有變動。 所以當使用 array[index] 執行更新時,是沒辦法觸發頁面更新的。 ``` const vm = new Vue({ el:'#vm', data:{ numbers: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] }, methods:{ changeNum(){ // 網頁不會更新 this.numbers[2] = 13 this.numbers[10] = 11 // 網頁會更新 // this.numbers.splice(2,1,13) // this.numbers.push(11) // this.numbers = [...this.numbers,11] } } }) ``` ### 注意事項(v-for 及 v-if 不要同時出現在同一個元素上) 在 Vue 執行的過程中, v-for 的層級較高,所以會在執行完 v-for 才去執行 v-if 的判斷,也因此會造成 v-for 跑完後,才去一個一個執行判斷,之後決定不執行,這是非常多此一舉的。 如果真的需要 v-for 及 v-if 一起使用的話,建議可以在 v-for 外層(可以是 template )元素上加上 v-if ,先跑完判斷,再決定是否要迭代。 ``` 當show是truthy時,下面迭代才會執行 <template v-if="show"> <div v-for="item in items"> <h3>{{ item.type }}</h3> <p>{{ item.made }}</p> </div> </template> 不建議使用下面的做法 會先跑迭代,才執行判斷 <div v-for="item in items" v-if="show"> <h3>{{ item.type }}</h3> <p>{{ item.made }}</p> </div> ``` ### 標籤 利用 template 為 v-for 做分組 template 做分組的最大好處減少 `<div> `標籤的出現。 ``` <template v-for="item in items"> <h3>{{ item.type }}</h3> <p>{{ item.made }}</p> </template> ``` ###### tags: `Vue`