> 如果有記憶吐司就好了... # Vue 學習筆記 使用Vue可以讓我們用更簡易的方式,將程式渲染到畫面上。 ```htmlembedded= <div id="counter"> <!--使用兩個大括號包住要顯示的屬性名稱--> Counter: {{ counter }} </div> ``` ```js= const Counter = { data() { return { counter: 0 // ←屬性名稱 } } } // 綁定Vue.createApp(變數名稱).mount('DOM') Vue.createApp(Counter).mount('#counter') ``` 畫面如下: ![](https://i.imgur.com/cLOOgiu.jpg) --- ## Vue起手式 兩種方法: CDN(整包加入) / import esm (需要的加入) ### CDN ```htmlembedded= <script src="https://unpkg.com/vue@next"></script> ``` 基本寫法 ```js= Vue.createApp({ data() { return{ ... } }, methods:{ ... }, mounted: function(){ ... }, }).mount('DOM元素') ``` or ```js= const 變數 = { data() { return{ ... } } } Vue.createApp(變數).mount('DOM元素') ``` ### ESM ```JS= import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.26/vue.esm-browser.min.js' ``` 基本寫法 ```htmlembedded= <!-- 載入js時需加 type="module" --> <script type="module" src="index.js"></script> ``` ```js= // {要載入的模組} import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.1.4/dist/vue.esm-browser.min.js'; createApp({ data() { return{ ... } }, methods: { clickAlert() { alert('我被觸發了'); }, mounted: function(){ ... }, }).mount('DOM元素') ``` --- ## v-html / v-text / {{ }} JS(共用) ```js= var app = new Vue({ el: '#app', data: { name:`<a herf="#">Jasper</a>` } }) ``` ### 1. `{{ }}` 僅顯示字串 ```htmlembedded= <div id="app"> <p>1.祝{{ name }}早日成為工程師</p> </div> </div> ``` 如下: ![](https://i.imgur.com/k77IK3E.jpg) ### 2. `v-html` 會帶上標籤的效果一同渲染到畫面上。 官網上有特別聲明,簡單來說就是使用v-html有安全性的問題,只建議在安全領域下使用,不建議用於客戶端或任何不安全的環境下。 > WARNING > > Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS attacks (opens new window). Only use v-html on trusted content and never on user-provided content. ```htmlembedded= <div id="app"> <p>2.祝<span v-html="name"></span>早日成為工程師</p> </div> ``` 如下: ![](https://i.imgur.com/qQC1Mqv.jpg) ### 3. `v-text` 僅顯示字串 ```htmlembedded= <div id="app"> <p>3.祝<span v-text="name"></span>早日成為工程師</p> </div> ``` 如下: ![](https://i.imgur.com/DdABMdV.jpg) **所以...** * `v-text` 與 `{{ }}`一樣都僅顯示字串,不會將標籤效果渲染出來。 * `v-html` 會將整個結構一同渲染至畫面上 --- ## v-cloak 我們使用vue.js的時候,若遇到網速比較慢,在畫面還沒渲染出來之前通常會顯示`{{ message }}`之類的資料裸露在畫面上,此時就可以使用`v-cloak`的方式將它隱藏,讓使用者體驗更好。 ```htmlembedded= <!--html帶上屬性即可--> <div id="app" v-cloak> {{ message }} </div> ``` ```js= var app = new Vue({ el:'#app', data:{ message: 'HelloWorld' } }) ``` v-clock的關鍵語法是加在css內,如下: ```css= [v-colak] { display: none; } ``` ## v-bind 屬性綁定 使用`v-bind`可以綁定屬性,ex:title、src、class...等 ```htmlembedded= <div id="bind-attribute"> <span v-bind:title="message"> 將指標停留在我身上 </span> </div> ``` ```js= const AttributeBinding = { data() { return { message: '我指到你了 ' + new Date().toLocaleString() } } } Vue.createApp(AttributeBinding).mount('#bind-attribute') ``` 效果如下: ![](https://i.imgur.com/xLpZsNA.gif) --- ## v-on 綁定事件監聽 使用`v-on`與使用者互動,透過click、change、submit...等 ```htmlembedded= <div id="event-handling"> <p>{{ message }}</p> <!-- 綁定點擊事件(click)--> <button v-on:click="reverseMessage">Reverse Message</button> </div> ``` ```js= const EventHandling = { data() { return { message: '123456' } }, methods: { reverseMessage() { this.message = this.message .split('') //["1","2","3","4","5","6"] .reverse() //["6","5","4","3","2","1"] .join('') //'654321' } } } Vue.createApp(EventHandling).mount('#event-handling') ``` 效果如下: ![](https://i.imgur.com/sFYDbUp.gif) --- ## v-model 雙向綁定 使用`v-model`讓DOM與畫面可以同步進行 ```htmlembedded= <div id="two-way-binding"> <p>{{ message }}</p> <input v-model="message" /> </div> ``` ```js= const TwoWayBinding = { data() { return { message: 'Hello Vue!' } } } Vue.createApp(TwoWayBinding).mount('#two-way-binding') ``` 效果如下: ![](https://i.imgur.com/ggO6Pqj.gif) --- ## v-if / v-else / v-else-if 條件式綁定 ### v-if ```htmlembedded= <div id="conditional-rendering"> <span v-if="seen">Now you see me</span> </div> ``` ```js= const ConditionalRendering = { data() { return { seen: true //判斷為true才顯示 } } } Vue.createApp(ConditionalRendering).mount('#conditional-rendering') ``` 效果如下: ![](https://i.imgur.com/LmWE4rS.gif) ### v-else 同層必須要有`v-if`才能使用 ```htmlembedded= <div id="app"> <!--若 num>0 成立顯示Now you see me--> <div v-if="num > 0"> Now you see me </div> <!-- 若不成立顯示Now you don't--> <div v-else> Now you don't </div> </div> ``` ```js= var app = new Vue({ el:'#app', data:{ num: 9 // 因為9>0所以畫面顯示Now you see me } }) ``` ### v-else-if 同層必須要有`v-if` 或` v-else if`才能使用 ```htmlembedded= <div id="app"> <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> 以上皆非 </div> </div> ``` ```js= var app = new Vue({ el:'#app', data:{ type: "C" //顯示C } }) ``` --- ## v-for 迴圈綁定 ### 陣列 使用`v-for`可以將陣列的資料,一筆一筆渲染在畫面上,若參數兩個或以上必須加()。 陣列中的參數(item,index,array) 第一個位置:通常為item,可自定義名稱,代表的是陣列中每一筆物件 第二個位置:通常為index,可自定義名稱,為索引值,從0開始 第三個位置:很少用到,通常為array,指的是陣列本身 ```htmlembedded= <div id="app"> <ol> <!-- v-for="參數 in 陣列名稱" --> <li v-for="item in todos"> {{ item.text }} </li> </ol> </div> ``` ```js= const app = { data() { return { todos: [ // ←陣列 { text: 'Learn JavaScript' }, { text: 'Learn Vue' }, { text: 'Build something awesome' } ] } } } Vue.createApp(app).mount('#app') ``` 畫面如下: ![](https://i.imgur.com/7agS6Yy.jpg) ### 物件 v-for也可以使用在物件上,所帶的參數與陣列略有不同。 物件中的參數(item, key) 第一個位置:通常為item,可自定義名稱,指的是物件family內的每一整筆物件如jasper:{...}、nini:{...}、max:{...}。 第二個位置:通常為key,可自定義名稱,指的是物件內的屬性,如下方程式碼中的jasper、nini、max。 ```htmlembedded= <div id="app"> <ol> <!-- v-for="參數 in 物件名稱" --> <li v-for="(item,key) in family"> {{ key }} 是 {{ item.title }}, 今年{{ item.age }}歲, 身高{{ height }}, 體重{{ weight }} </li> </ol> </div> ``` ```js= const app = { data() { return { family: { jasper: { title: '爸爸', age: 32, height: 168, weight: 60 }, nini: { title: '媽媽', age: 29, height: 158, weight: 50 }, max: { title: '兒子', age: 0, height: '未知', weight: '未知' } } } } } Vue.createApp(app).mount('#app') ``` 呈現如下: ![](https://i.imgur.com/J2fzOA3.jpg) --- ## v-show 方法跟`v-if`類似,差別於判斷後`v-show`牽動的是css的元素`display`,意思是若值為`false`,則`display:none`將`v-show`綁定的dom隱藏起來,就不會顯示在畫面上。 ```htmlembedded= <div id="app"> <!--當status為1時顯示123 --> <p v-show="status === 1">123</p> </div> ``` ```js= <script> var app = new Vue({ el:'#app', data:{ status: 2 } </script> // 因為status是2,所以畫面不會顯示 // 查看開發者工具內的element // <p style="display: none;">123</p> ``` 效果如下: ![](https://i.imgur.com/JgHAV9j.gif) --- 其他還有很多指令,之後再來補充 v-once v-memo v-pre v-is v-slot --- ## 事件修飾符 Vue.js 藉由事件修飾符 (Event Modifiers) 處理了許多 DOM 事件的細節,讓我們能專注於程式邏輯的撰寫。 * .stop:等同於event.stopPropagation(),防止事件冒泡。 * .capture:與事件冒泡的方向相反,事件捕獲 (event capturing) 是由外而內的。 * .prevent:等同於event.preventDefault(),防止執行預設的行為。 * .self:只會觸發自己範圍內的事件,不包含子元素。 * .once:只會觸發一次 ### .stop 在預設的情況下,當我按下按鈕之後,會由內而外的執行函式中的alert,此現象稱為冒泡現象,stop就可以來阻止冒泡現象發生。 冒泡線上的執行順序: inner → middle → outer 以下範例為預設的冒泡現象: ```htmlembedded= <div id="app"> <div @click="outer"> <div @click="middle"> <button @click="inner">按我!</button> </div> </div> </div> ``` ```js= // 使用CDN的方式 const app = ({ methods: { inner() { alert('這是裡面的 button'); }, middle() { alert('這是中間的 div'); }, outer() { alert('這是外面的 div'); } } }); Vue.createApp(app).mount('#app') ``` 呈現如下: ![](https://i.imgur.com/A1qhwgL.gif) 加上.stop後: ```htmlembedded= <div id="app"> <div @click="outer"> <div @click="middle"> <button @click.stop="inner">按我!</button> </div> </div> </div> ``` 呈現如下: ![](https://i.imgur.com/A0ENzBM.gif) 可以看到加上.stop後的click事件,執行後就會停止不會向外執行,我知道有人應該會好奇,那如果加在中間層的click事件呢? ```htmlembedded= <div id="app"> <div @click="outer"> <div @click.stop="middle"> <button @click="inner">按我!</button> </div> </div> </div> ``` 呈現如下: ![](https://i.imgur.com/ahTiRIi.gif) 沒錯!!當你加在中間層,他依然是依照預設的冒泡現象執行,但是執行到你有加stop的click事件後就會停止了。 ### .capture 與冒泡現象相反 ```htmlembedded= <div id="app"> <div @click.capture="outer"> <div @click.capture="middle"> <button @click.capture="inner">按我!</button> </div> </div> </div> ``` ```js= // 使用CDN的方式 const app = ({ methods: { inner() { alert('這是裡面的 button'); }, middle() { alert('這是中間的 div'); }, outer() { alert('這是外面的 div'); } } }); Vue.createApp(app).mount('#app') ``` 呈現如下: ![](https://i.imgur.com/hEPr3Rr.gif) --- ## 關於生命週期 我們都知道`v-if`與`v-show`差異在於,使用`v-if`時,當你隱藏又重啟時`v-if`會重新跑一次完整的生命週期,`v-show`則是當他跑過一次生命週期之後就會生成,即使你隱藏他,他只是`display:none`,當你重起他就會直接顯示不會重新跑生命週期。 常用的情境: 1. 若你希望每次戳api不要讓他重新跑一次生命週期,因為跑一次就算一次,此時可以使用標籤`<keep-alive>`去包覆v-if,這樣重啟始只會跑activated與deactivated。 2. 當切換元件時,上一個元件沒有正確卸載時會導致切換過去的元件出錯,此時可以在生命週期unmounted做卸載,ex:Event Bus。 --- ## Props 將外層的資料透過html(前內後外)為平台,拿來內層使用。 ```htmlembedded= <div id="app" class="p-3"> <!-- 直接帶入文字 --> <line-msg msg="Hello World"></line-msg> <!-- 引用外層的message --> <line-msg :msg="message"></line-msg> <!-- 若遇到小駝峰,則改為小寫中間加符號- --> <line-msg2 :next-msg="message"></line-msg2> </div> ``` ```js= const app = Vue.createApp({ data() { return { message: 'Hello World' } } }) app.component('line-msg', { template: `<div>請正確呈現訊息:{{ msg }}</div>`, props: ['msg'] }) app.component('line-msg2', { template: `<div>請正確呈現訊息:{{ nextMsg }}</div>`, props: ['nextMsg'] }) app.mount('#app') ``` 畫面如下: ![](https://i.imgur.com/gwNiWF8.png) ### 也可以一次傳遞多筆資料 ```htmlembedded= <div id="app" class="p-3"> <line-msg :msg1="message1" :msg2="message2" :arraymsg="msgObj"></line-msg> </div> ``` ```js= const app = Vue.createApp({ data() { return { message1: '有看到我的訊息嗎?', message2: '這個工具可以傳遞 2 句話以上', msgObj: ['也是', '可以', '傳遞陣列資料'] } } }) app.component('line-msg', { template: `<div> <span>請正確呈現訊息:{{ msg1 }}</span> <br> <span>請正確呈現訊息:{{ msg2 }}</span> <br> <p>請正確呈現訊息:<span v-for="item in arraymsg">{{ item }}</span></p> </div>`, props: ['msg1','msg2','arraymsg'] }) app.mount('#app') ``` 畫面如下: ![](https://i.imgur.com/kHvJ5Ro.png) ### 如何使用props進行簡單驗證 若是 props 傳入值的型別,與設定不同,雖然仍可使用,但會跳出警告,以方便開發時 debug。 ```htmlembedded= <div id="app"> <props-com :child-string="string" :child-object="object" > </props-com> </div> ``` ```js= // 根元件 const app = Vue.createApp({ data() { return { string: 'test', object:{ name:'Ryder', gender:'man'}, } }, }) // 內層元件 app.component('props-com', { props:{ childString:{ type: String, // 型別須為字串 }, childNum:{ type: Number, // 型別須為數字 default: 100 // 預設值100 }, childObject:{ type: Object // 型別須為物件 } }, template: `<div>{{ childString }}</div> <div>{{ childNum }}</div> <div>{{ childObject }}</div> `, }); app.mount('#app'); ``` --- ## Emit ```htmlembedded= <div id="app" class="p-3"> {{ message }} <mobile-phone @push-data="getMessage"></mobile-phone> </div> ``` ```js= const app = Vue.createApp({ data() { return { message: '' } }, methods: { getMessage(msg) { this.message = msg; } } }) app.component('mobile-phone', { template: `<div> <input class="me-3" v-model="innerText"> <button @click="push">傳送訊息</button> </div>`, data: function() { return { innerText: '需要向外傳的訊息' } }, methods: { push() { this.$emit('pushData', this.innerText); } } }) app.mount('#app') ``` 畫面如下: ![](https://i.imgur.com/rWNNI51.gif) --- ## Props + Emit ```htmlembedded= <div id="app" class="p-3"> {{ newMessage }} <mobile-phone :msg="message" @push-data="getMessage"></mobile-phone> </div> ``` ```js= const app = Vue.createApp({ data() { return { message: '晚餐吃什麼~,請回覆:「」', newMessage: '', // 5 } }, methods: { getMessage(msg) { // 4 this.newMessage = msg; } } }) app.component('mobile-phone', { template: `<div> <input class="me-3" v-model="message"> <button @click="push">傳送訊息</button> </div>`, props: ['msg'], // 1 data() { return { message: this.msg // 2 } }, methods: { push() { // 3 this.$emit('push-data', this.message); } } }) app.mount('#app') ``` 畫面如下: ![](https://i.imgur.com/Fyrh2WV.gif) --- ## 同層元件之間的傳遞可使用mitt套件 CDN ```htmlembedded= <script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script> ``` ```htmlembedded= <div id="app" class="p-3"> <h3>小明的手機</h3> <ming-phone class="mb-3"></ming-phone> <h3>老媽的手機</h3> <mom-phone></mom-phone> </div> ``` ```js= const emitter = mitt() // 將套件載入 const app = Vue.createApp({}) app.component('ming-phone', { template: `<div><strong>老媽傳來的訊息:</strong> {{ momMsg }}</div>`, data() { return { momMsg: '' } }, created() { emitter.on('passMsg', res => { this.momMsg = res; }); // 接收 } }) app.component('mom-phone', { template: `<div> <input class="me-3" v-model="msg"> <button @click="pushData">傳送訊息</button> </div>`, data() { return { msg: '快回家,晚上煮好料 der' } }, methods: { pushData() { emitter.emit('passMsg', this.msg); // 傳送 } } }) app.mount('#app') ``` 畫面如下: ![](https://i.imgur.com/knjPQrK.gif) --- ## 跨層級資料傳遞provide 一般兩層的元件,我們會使用emit、props來引用或取得內外層的資料與方法,而provide則是可以運用在三層以上的元件來做資料的傳遞。 ```htmlembedded= <div id="app"> <ming-mom></ming-mom> </div> ``` ```js= // 最三層 const ming = { template: `<div> 小明拿到了 {{ this.cash }} 為零用錢 </div>`, } // 第一層 const app = Vue.createApp({ data() { return { cash:1000, } }, }) // 第二層 app.component('ming-mom', { template: `<ming></ming>`, components: { ming }, }); app.mount('#app'); ``` 我們預期希望從第一層傳遞資料跨過第二層到傳遞到第三層來使用。 第一層新增一個privide的屬性(物件or函式),放入要傳遞的資料或方法 第三層新增一個inject的屬性載入資料或方法 ```js= // 第三層 const ming = { template: `<div> 小明拿到了 {{ this.cash }} 元零用錢 </div>`, inject:['cash'] //對應provide內的屬性名稱 } // 第一層 const app = Vue.createApp({ data() { return { cash:1000, } }, provide:{ cash:1000, } }) // 第二層 app.component('ming-mom', { template: `<ming></ming>`, components: { ming }, }); app.mount('#app'); ``` 畫面呈現: 小明拿到了 1000 元零用錢 --- ## 混和元件方法mixins 使用屬性mixins將元件混入另一個元件內 ```htmlembedded= <div id="app"> <nike></nike> <adidas></adidas> <reebok></reebok> </div> ``` ```js= const filterMix = { created(){ this.cash = this.dollarSign(this.cash) }, methods:{ dollarSign(dollar) { return `$${dollar}`; } }, }; const app = Vue.createApp({ data() { return { } }, }) app.component('nike', { data() { return { cash:3000, title:'nike 球鞋', img:'https://i.imgur.com/fiUT2Sx.jpg' } }, template: `<div class="card" style="width: 18rem;"> <img :src="img" class="card-img-top"> <div class="card-body"> <h5 class="card-title">{{title}}</h5> <div>售價: <span>{{cash}}</span></div> </div> </div>`, mixins:[filterMix] //混入元件 }); app.component('adidas', { data() { return { cash:2500, title:'adidas 球鞋', img:'https://i.imgur.com/6A15UOE.jpg' } }, template: `<div class="card" style="width: 18rem;"> <img :src="img" class="card-img-top"> <div class="card-body"> <h5 class="card-title">{{title}}</h5> <div>售價: <span>{{cash}}</span></div> </div> </div>`, mixins:[filterMix] // 混入元件 }); app.component('reebok', { data() { return { cash:2299, title:'reebok 球鞋', img:'https://i.imgur.com/2GkIleC.jpg' } }, template: `<div class="card" style="width: 18rem;"> <img :src="img" class="card-img-top"> <div class="card-body"> <h5 class="card-title">{{title}}</h5> <div>售價: <span>{{cash}}</span></div> </div> </div>`, mixins:[filterMix] //混入元件 }); app.mount('#app'); ``` 預期將filterMix元件混入nike、addidas、reebok三個元件內,將cash內的金額補上$。 畫面如下: ![](https://i.imgur.com/eX3FpJw.png) --- ## teleport傳送 使用標籤teleport+屬性to來指定要傳送的地方。 ```htmlembedded= <div id="app"> <div class="minHome">小明家:</div> <div id="huaHome">小華家:</div> <span>小王家:</span> <home></home> </div> ``` ```js= const app = Vue.createApp({ data() { return { } }, }) app.component('home', { template: `<teleport to=".minHome">小明</teleport> <teleport to="#huaHome">小華</teleport> <teleport to="span">小王</teleport> `, }); app.mount('#app'); ``` 畫面如下: 小明家:小明 小華家:小華 小王家:小王 --- ## 使用 pathMatch 進行換頁404頁面設定 ```htmlembedded= <div id="app"> <router-link to="/pageOne">1</router-link> <router-link to="/pageTwo">2</router-link> <router-view></router-view> </div> ``` ```js= const app = Vue.createApp({}); const pageOne = { template: `<div>第一頁</div>` }; const pageTwo = { template: `<div>404</div>`, }; const router = VueRouter.createRouter({ history: VueRouter.createWebHashHistory(), routes:[ { path: '/pageOne', component: pageOne }, { // 若要指向404不存在 // router 需設定 path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*', component: pageTwo } ] }) app.use(router); app.mount('#app'); ``` 畫面如下: ![](https://i.imgur.com/Pcpl7Sc.gif) --- ## 使用 methods 切換路由頁面 運用this.$router.push()的方法指向指定的路徑 ```htmlembedded= <div id="app"> <router-link class="p-3" to="/pageOne">1</router-link>| <router-link class="p-3" to="/pageTwo">2</router-link> <router-view></router-view> </div> ``` ```js= const app = Vue.createApp({}); const pageOne = { template: `<div>第一頁</div>` }; const pageTwo = { template: `<div>第二頁 <button type="button" @click="changePage">換頁</button></div>`, methods: { changePage() { this.$router.push('/pageOne') //指定的路徑 } } }; const router = VueRouter.createRouter({ history: VueRouter.createWebHashHistory(), routes: [ { path: '/pageOne', component: pageOne }, { path: '/pageTwo', component: pageTwo } ] }) app.use(router); app.mount('#app'); ``` --- ## refs 呼叫元件方法 使用$refs找到指定的元件,並運用該元件的方法。 ```htmlembedded= <div id="app"> <button type="button" @click="callComponent">按我</button> <br> <alert-com ref="alert"></alert-com> </div> ``` ```js= const app = Vue.createApp({ methods: { callComponent() { // 調用該元件的方法 this.$refs.alert.callAlert(); }, } }) app.component('alert-com', { template: `<div>我裡面藏了 Alert</div>`, methods: { callAlert() { alert('跳出警告了') } } }); app.mount('#app'); ``` --- ## 建置computed運算後回傳該值 ```htmlembedded= <div id="app"> 收入: <input type="number" v-model="cash"> <br> 今年要繳的稅金為 {{ taxes }} 元 </div> ``` ```js= const app = Vue.createApp({ data() { return { cash: 0, } }, computed: { taxes(){ return Number.parseInt(this.cash*0.2) } } }).mount('#app'); ``` 畫面如下: ![](https://i.imgur.com/FSirSAx.gif) --- ## 使用 watch 監控 ```htmlembedded= <div id="app"> 收入: <input type="number" v-model="cash"> <br> 今年要繳的稅金為 {{ Taxes }} 元 </div> ``` ```js= const app = Vue.createApp({ data() { return { cash: 0, Taxes: 0, } }, watch: { cash() { this.Taxes = Number.parseInt(this.cash*0.2) } } }).mount('#app'); ``` 畫面如下: ![](https://i.imgur.com/fkXpFQs.png) ### 監控 props ```htmlembedded= <div id="app"> 收入: <input type="number" v-model="cash"> <br> <new-input :prop-Dash="cash"></new-input> </div> ``` ```js= const app = Vue.createApp({ data() { return { cash: 0, } }, }) app.component('new-input', { props: ['propCash'], // 對應的是根元件的cash data() { return { newcash: 0, } }, template: `<input type="number" v-model="newcash"/>`, watch: { propCash() { // 監聽對象propCash this.newcash = this.propCash; } }, }); app.mount('#app'); ``` 畫面如下: ![](https://i.imgur.com/zCOBgto.gif) --- ## 運用 methods 作為 filter 使用return將結果傳回函式本身。 ```htmlembedded= <div id="app"> {{ reverseText(text) }} <br> {{ dollarSign(cash) }} </div> ``` ```js= const app = Vue.createApp({ data() { return { text: '棒棒伯斯卡', cash: 1000 } }, methods: { reverseText(someText) { console.log(someText); return someText.split('').reverse().join(''); }, dollarSign(dollar) { return `$${dollar}`; } } }).mount('#app') ``` 畫面如下: 卡斯伯棒棒 $1000 --- 參考文獻: [Vue.js](https://v3.cn.vuejs.org/) [[Vue.js] 使用 v-cloak 解決 Vue Instance 顯示變數的問題](https://tools.wingzero.tw/article/sn/131) [[Vue學習筆記](三)Vue指令(上) — v-text, v-html, v-if, v-show, v-for](https://medium.com/andy%E7%9A%84%E8%B6%A3%E5%91%B3%E7%A8%8B%E5%BC%8F%E7%B7%B4%E5%8A%9F%E5%9D%8A/vue%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-%E4%B8%89-vue%E6%8C%87%E4%BB%A4-%E4%B8%8A-v-text-v-html-v-if-v-show-v-for-4eb6cd994359) [Vue.js: Methods 與事件處理 (Event Handling)](https://cythilya.github.io/2017/04/17/vue-methods-and-event-handling/)