# Vue JS初探(上) ## 目錄 [toc] ## What is framework 在網頁的發展歷史中,一開始大家都是直接透過原本的原生函式來撰寫網頁應用程式,因為當時的專案都不大,網路以及電腦也沒辦法負荷太多的程式碼,所以沒有太大影響。後來隨著專案越來越大,數量也越來越多,就出現了許多重複出現的需求,例如說針對一個有特定屬性的物件操作。秉持的「不要重複造輪子」的想法,大家會把這些常出現的東西寫再一起,變成所謂的「函式庫」,之後需要功能時就直接去呼叫這些已經寫好的函式,不用再重複撰寫。這樣一來降低了維護的成本,也提高的開發的效率。這樣的東西也衍伸出「黑盒子」的概念。 隨著專案更加龐大,現有的函式庫已經沒辦法有效地解決一些問題,例如說如果要開發一個網站動畫,函式庫能直接執行的功能有限,就變回當初用原生函式撰寫的狀況差不多,同時對於物件的概念日益盛行。於是比函式庫更加龐大,架構也更加完整的東西出現了:「框架」。 ### 使用框架的好處 - 方便管理,由資料決定畫面,將資料從介面中抽取出來,每個畫面都是對資料處理過後的呈現,尤其是在動態JS中有關DOM的操作 - 將腳本和程式模組化,使得各組件只需要處理組件內的事,外部引用的組件來決定怎麼使用、提供什麼資料給組件,藉由簡單的切分權責,加上前述的由資料決定畫面,就能讓各個組件的任務單一,並且能被重複使用。 - 提高server和browser間資料存取的效率 ## Then, what is Vue? - Front-end, JavaScript/TypeScript framework - Vue是個發展中的前端框架,可以輕易的和其他library或現有專案整合 - 像是把Vue和既有的HTML做整合,這讓你可以像使用插入性替換的函式庫一樣來使用Vue - Used to **dynamic** & **data-driven** wedsites(SPA's) - Easy to let user use DOM and manage data - Vue是一套前端框架,目的是讓網頁可以動態Load資料,有別於傳統靜態的頁面將內容寫死在source code裡面 - Used to create stand-alone widgets - Vue提供漸進式的方法來撰寫網頁內容。就像大部分的框架,Vue讓創造可以重複使用的網頁內容區塊(稱為widget元件) ![](https://i.imgur.com/8KHATjy.png) ## The characterists of Vue websites ### Makw a comparison - between the progressive JavaScript framework and the imperative programming |type|differnece| |----|----------| |the progressive JavaScript framework(渲染式框架)|透過簡單的語法,完成對目標的綁定,對於更動的內容更易修改| |the imperative programming(指令式編成)|像是JavaScript的DOM,一個指令對應一個物件| ### MVVM模式 ![](https://i.imgur.com/EmnflwQ.png) - 將DOM監聽的事件狀態與狀態(Model)綁定,由JS的物件管理。使用者在畫面(View)上所做的更動,ViewModel層上的Vue.js就會回傳到有JS的物件表式的Model,Model修改後同步更新View上對影的內容 - 操作的最小單位從DOM中的節點變為一個個component的組合,而每個components都有其模板與樣式JS code - DOM Tree --> Components Tree ### The routing between browser and server - 前端模擬路由,用於實現SPA;that is,換頁時不需向後端發出請求 - All routing is done in the browser instead on the sever - When we interact with the components, the website doesn't go to the server and instead Vue handles in the browser(much faster and smoother)--Single Page Application #### Don't use Vue or something similar like react ![](https://i.imgur.com/wcyfFtv.png) - the browser is constantly making a request to the server for every new pages(a bit slow) #### Use Vue ![](https://i.imgur.com/ppZWhnJ.png) - Vue can be injected to different websites - Vue bundles take control of the website in the browser, and it renders the different Vue components needed for the page and take over the link of it ### Single Page Application(SPA) - Only a single HTML page sent to the browser - Vue intercept(攔截) subsequent(後來的) requests and handles "page" changes in the browser by swapping what components are shown on the page - Result is in much faster and smoother ### Instal the extension "Vetur" ![](https://i.imgur.com/a2fRIw7.png) - bring some features for Vue appication ## Basic template - `<script src="https://unpkg.com/vue@3.0.2">`是一種對於Vue形式的引用,在此我所使用的版本為3.0.2 - 也能以`<script src="https://unpkg.com/vue@next">`表示使用最新版的Vue ### Example ![](https://i.imgur.com/kjoiso6.jpg) #### HTML的操作 1. 設立讓HTML掛載的節點,像是id,class,tag 2. 需綁定的內容以{{}}表示 - name of the property we want to output use double curly braces - We can't use those variables outside the scope of Vue "root" #### 建立Vue的框架 3. 利用Vue.createApp()這個method,建立實體物件(以root為例) - pass in the object and represent something called "root component" in view - controlling the section in the web page 4. data()函式能將JavaScript建立的對應object資料,用於HTML中綁定 5. return a data object - it can be dynamic changing value 6. 將Vue的實體物件掛載至節點之上 - this method tells the app at what point or where in the DOM to mount this application - any dynamic data we want to output inside this app element or any events(like click,mousemove....). All of that will be controlled by our view - "root"物件內能夠回傳給html一物件,也能在JS檔中使用,作為動態操作value的依據 #### 補充 -- template ```htmlembedded= const app = Vue.createApp({ template:`<h2>I am the template</h2>` }); app.mount('#app'); ``` <hr> ### methods - how to make our website be active #### Methods and computed - 監聽事件觸發後,透過函式的手法,對資料進行操作 - 若要使用data()內的屬性,要用this去做存取,也能用帶有參數的形式 - reference to the component itself (後續再詳細說明,下面以methods為例) #### Events--試問DOM中的AddEventListener和getElementById等method的靈壓? `v-on:click="age++"`作為JavaScript中的物件綁定,語法為`v-on:使用者行為=執行程式` - v-on is a directive which allow us to react to different type of events - update the data() in the script `@click` 其中的@為v-on的簡寫 `v-if`像是JavaScript中的條件判斷作用於DOM之上(這說法有點怪?舉例!!) - In this case, v-if evalute what is in the quote,if it's true,then show this `v-for`用於連續生成目標相近的element - we use v-for to cycle through an array of the data and output a bit of template for each items or output to users some kind of list `v-bind`用來動態新增或綁定一個或多個HTML屬性(src、class、style) #### Example 1 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"> <p>{{commodities}}-{{price}}</p> <button v-on:click="price++">rise</button> <button @click="commodities = 'nothing'">nothing</button> <br> <button @click="changegoods">Change goods </button> <br> <button @click="changegoods_re('Ten days wonder')">Change goods 2</button> <div v-if="true"> <p>{{commodities}} || {{price}} </p> </div> </div> <script> const app = Vue.createApp({ data(){ return { commodities:'The final Banpire', price:54 } }, methods: { changegoods(){ console.log('you click me'); this.commodities = 'Boyce ODE'; }, changegoods_re(commodities){ this.commodities = commodities; } } }); app.mount('#root'); </script> </body> </html> ``` #### Example 2 ```htmlembedded= <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Learning Vue</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <div v-if="showBooks"> <p>{{ books[0].title }} - by {{ books[0].author }}</p> <p>{{ books[1].title }} - by {{ books[1].author }}</p> </div> <div v-else> <p>Click the button below to show books</p> </div> <button @click="toggleShowBooks"> <span v-if="showBooks">Hide books</span> <span v-else>Show books</span> </button> <ul> <li v-for="book in books"> <h3>{{book.title}}</h3> <h3>{{book.author}}</h3> </li> </ul> </div> <script> const root = Vue.createApp({ data(){ return{ showBooks:true, books: [ {title:'The final Banpire',author:'Duncan',display:true},{title:'JavaScript Eloquent',author:'Johnny',display:true},{title:'Boyce ODE',author:'Fanny',display:true},{title:'Duncan is handsome',author:'Duncan',display:false} ] } }, methods:{ toggleShowBooks(){ this.showBooks=!this.showBooks; } } }) root.mount('#app'); </script> </body> </html> ``` ### v-bind - 目的:用於和HTML與CSS之間的資料綁定 - 寫法為`v-bind:html屬性="對應的object"`,其中,`v-bind:`可簡化為`:` - 複習一下html的屬性列表 - `<a href="">` `<img src="">` `<div class="">`等等 #### Example 3 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="style.css" /> <script src="https://unpkg.com/vue@next"></script> <title>Vue Learn</title> </head> <body> <div id="app"> <a href="https://www.youtube.com/c/%E5%A4%9C%E6%A9%98%E5%AF%A6%E6%B3%81">夜橘YT</a> <hr> <a v-bind:href="link">豆漿YT</a> </div> <script> const app = { data() { return { link: "https://www.youtube.com/user/Qiyoudaoyi", }; }, }; Vue.createApp(app).mount("#app"); </script> </body> </html> ``` #### Example 4 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script> <style> body{ background-color: white; max-width: 960px; margin: 20px auto; } p, h3, ul{ margin: 0; padding: 0; } li{ list-style-type: none; background: #fff; margin: 20px auto; padding: 10px 20px; border-radius: 10px; display: flex; align-items: center; justify-content: space-between; } li.fav{ background: #ff9ed2; color: white; } img{ width:400px; height:200px; } </style> </head> <body> <div id="app"> <p>{{filterBooks}}</p> <div> <!-- v-bind用於在Vue框架開發時,綁定HTML屬性 --> <a v-bind:title="hint">把滑鼠移過來</a> </div> <div v-if="showBooks"> <ul> <li v-for="book in filterBooks" v-bind:class="{fav: book.isFav}" @click="toggleFav(book)"> <!-- If the value is true, then we can apply this style --> <img :src="book.img" v-bind:alter="book.owner"> <h3 style="font-size:100px">{{book.title}}</h3> </li> </ul> </div> <div v-else> <p>Click the button below to show books</p> </div> <button @click="toggleShowBooks"> <span v-if="showBooks">Hide books</span> <span v-else>Show books</span> </button> </div> <script> const app = Vue.createApp({ data(){ return{ hint:'哈哈笑死', showBooks:true, books: [ // Note that the property like "img" is a kind of HTML property // The purpose to use the property "isFav" due to what we want to display {title:'The final Banpire',owner:'Setsuna',img:'picture/Setsuna.png',isFav:true}, {title:'JavaScript Eloquent',owner:'Shioriko',img:'picture/Shioriko.png',isFav:false}, {title:'Boyce ODE',owner:'Ayumu',img:'picture/Ayumu.png',isFav:true} ] } }, methods:{ toggleShowBooks(){ this.showBooks=!this.showBooks }, toggleFav(book){ book.isFav = !book.isFav } }, computed:{ filterBooks(){ return this.books.filter((book)=> book.isFav) } } }) app.mount('#app'); </script> </body> </html> ``` ### v-modal - 一種雙向綁定 - v-model常用於表單及元素來做雙向數據綁定,結合v-bind跟v-on一樣,用v-bind初始綁定與呈現資料,用v-on監聽事件來做資料更新。 #### Example 5 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/vue@next"></script> <title>Vue Learn</title> </head> <body> <div id="app"> <input type="text" v-model="text" /> <div>{{ text }}</div> </div> <script> const app = { data() { return { text: "", }; }, }; Vue.createApp(app).mount("#app"); </script> </body> </html> ``` ### Computed 和 Methods 比較 - 兩者的差異在於, computed 會把計算後的結果暫存起來,如果裡面使用的資料沒有變動,就不會重複執行,以避免過度調用methods;反之,methods 只要在 HTML 裡面被使用幾次就會執行幾次。 - 在實際使用上,computed較為簡潔,尤其在重複使用相同的計算重複使用,但須注意computed需要return出一個值 - computed 的缺點是無法帶入參數,所以在設計時需考量,如果有有帶入參數的需求,就應使用 methods。 #### Example 6 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/vue@next"></script> <title>Vue Learn</title> </head> <body> <div id="app"> <input type="number" v-model="Num" /> <div>{{ Num }}</div> <div>{{ time }}</div> <div>{{ output }}</div> <button v-on:click="calculate()">Calculate</button> <button @click="calculate()">Calculate</button> <div>{{ times }}</div> </div> <script> const app = { data() { return { time: 10, Num: 0, output: 0, }; }, methods: { calculate() { this.output = this.time * this.Num; }, }, computed: { times() { return this.Num * this.time; }, }, }; Vue.createApp(app).mount("#app"); </script> </body> </html> ``` ## Vue CLI ![](https://i.imgur.com/sozIV0x.png) - 全稱為Vue.js Command-Line Interface - 由Vue.js核心團隊開發,提供開發者快速建置 Vue.js 專案並整合相關工具鍊的一套指令列 (command-line) 工具 - 它的特色是提供開發者在短短的幾分鐘時間內,即可快速建置一個立即可用的 Vue.js (含 2.x/3.x) 示範專案,這個專案同時也內建了 Router、Hot-Reload、ES Lint 與 dev-Server 等功能。 - a tool to boilerplate full vue applications which come along to the configuration and structure that we need to create full vue website ### setup Vue CLI 1. 到[官網](https://nodejs.org/zh-tw/)安裝Node.js,建議安裝長期維護版 ![Node.js官網圖片](https://i.imgur.com/SJ1BkVI.png) 2. 打開command prompt,並輸入`node -v`,透過顯示版本的手段,以確認是否安裝Node.js 3. 打開terminal,輸入`npm install -g @vue/cli`(下圖為示意圖) ![](https://i.imgur.com/7UpwWYR.png) 4. 先創一個資料夾,用以放入專案,並在terminal輸入`vue create [專案名稱]` ![](https://i.imgur.com/5bUd2tJ.png) 5. 選擇Vue 3 或 Manually select features(下面以後者作為操作) 6. features目前只需要Babel,透過空白鍵來選擇(之後提到Router會在建立一次cli,到時操作會在說明) ![](https://i.imgur.com/Dc8SJzZ.png) 7. 其他選項 ![](https://i.imgur.com/qRp3oHC.png) 8. 安裝完後,進入那專案並由vscode打開即可 ![](https://i.imgur.com/XkJwGyl.png =50%x) #### 簡單相關的file介紹 - `node_modules`: library --> if we install another library/package, they will be put in there - `public`:contain the index.html file, which is sent to the browser initially - `src`:the place where we write the source code - contain main.js, which kickstarts our applications - we use the method `createApp()`, which directly import "createApp" function from 'vue' library - import a component from .vue file - 載入整個專案中所使用到的所有JavaScript檔案 ```htmlembedded= import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app') ``` #### To preview our vue application in a browser - 打開terminal,並輸入`npm run serve`,然後會得到一個連結`http://localhost:8080/`,打開即可(之後能從控制台觀察一下) - 打開console後,all inside in the empty div with '#app' ## Vue元件檔-SFC(Single File Component) 單一元件檔 ### What is this? 一開始我們的專案都是透過Javascript去做相對應的渲染,但是當專案逐漸龐大的時候,很多可以重複用的元件都必須重複撰寫,這不僅會提升專案維護的難度,更會讓專案的架構變得複雜。 為了解決這樣的問題,Vue提供一個新的選項:SFC(Single File Component),作為單一元件的檔案。這樣的好處是當我們在組織程式碼的結構的時候, 可以很清楚地看出整個專案的架構與元件的分割關係,進而達到更高的可讀性及可重用性 ### How to do? 以剛剛生成的app.vue為例 ```htmlembedded= <template> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App" /> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style> ``` - 一個vue檔內會有三個部分,分別是<template\>、<script\>與<style\>。下面將逐一介紹 備註:components 中的.vue檔,檔名必須用大寫名稱,為了在<template\>不會和其他html tag 搞混 ### template - 這個部分類似html,就是撰寫這個元件長什麼樣子,寫法類似html,可以使用像是{{}}、v-if等vue的指令與語法 - With a view to not being in mess, we spilt those parts into different components. Then, when we render(打底) the components into the DOM, its template will be injected into the DOM. - use the way of "ref" to store a refernece to a DOM element inside a variable, then, we can use regular JS way to manipulate it - Just like the way of queryselector of DOM ### script - 這個部分的寫法比較特殊,原則上是寫Javascript,也就是這個元件會怎麼動,但是由於這個是元件檔,所以會需要有匯出元件的部分。 - 在匯出的部分,會需要多提供這個元件的名稱(name: string),還有這個元件所使用到的元件(object list),剩下的部分就是與前面所提及的相似,例如data,computed等等。 - represent the root components as an object - components屬性中,用於registering any extra components we use inside this component ### style - 就是CSS,不過要注意CSS汙染,例如底下這樣的寫法,會導致整個元件的h1都被設定大小為100px,包括引用的元件。 ```javascript= <style> h1 { font-size:100px; } </style> ``` 而這樣的問題可以透過增加scoped標籤解決 - `<style scoped>` - we can add "scoped" attribute to limit CSS to this component only - if we use "scoped" attribite on, it means that these styles will now only applay to whatever in the component - it's not working and it won't apply this style to anything outside the components ```javascript= <style scoped> h1 { font-size:100px; } </style> ``` ### multiple components--to nest other components as html tags ![](https://i.imgur.com/WnMlv0t.png) - 將內含的HelloWorld.vue改為下面的Modal.vue ```htmlembedded= <template> <div class="backdrop"> <div class="modal"> <h1>Modal title</h1> <p>modal content</p> </div> </div> </template> <style> .modal{ width:400px; padding:20px; margin: 100px auto; background:white; } .backdrop{ top:0; position:fixed; background:rgb(0, 0, 0, 0.5); width:100%; height: 100%; } </style> <style scoped> h1{ color:aqua; font-size: 100px; } </style> ``` 然後在App.vue中引入Modal這個component ```htmlembedded= <template> <div> <h1>{{title}}</h1> <Modal/> <h1>we can write any html components here!!</h1> <input type="text" ref="name"> <button @click="handleclick">click me</button> </div> </template> <script> import Modal from './components/Modal.vue' export default { name: 'App', components: {Modal}, data(){ return{ title:'My first Vue app :)' } }, methods:{ handleclick(){ console.log(this.$refs.name); this.$refs.name.classList.add('active'); this.$refs.name.focus(); } } } </script> <style> /* global --> inside the component file they apply to any element on the page*/ #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } h1{ border: 1px solid red; display: inline-block; padding-bottom: 10px; } .active{ color:blueviolet; } </style> ``` - 在`<template>`中為該component插入的位置,在此以`<Modal/>`表示 - 在`<script>`中引入該.vue的位置,在此以 `import Modal from './components/Modal.vue'`表示 - 也要在default物件中裡的components物件註冊child component - for `$refs`, we can access on the refs object "any ref" that we name in the template #### 附註:`<template>`出現error,顯示出The template root requires exactly one element.的狀況 - 原因:vue的模板只容許一個element,因此出現多個element便會報錯(這問題多出現於設定為Vue2的情況) - 解決方法: - 將`<template>`中的內容**以一個`<div>`包裝** - 到`settings`中,輸入`eslint`,並取消勾選`Validate vue-html in using eslint-plugin-vue - eslint-plugin-vue: 提供 Vue Style Guide ## The interaction between Parent Components and Child Components - 從`<style scoped>`這最簡單的例子便能看出,Vue.js每個components都必須以互為獨立的形式,that is,我們不能從child components中修改parent components或其他components的資料 - 因此,我們需要透過某種屬性或是方法,完成child componens和 parent components 之間的資料傳遞 在撰寫Vue元件檔的時候,各位可能有注意到一行程式碼 ```javascript= <HelloWorld msg="Welcome to Your Vue.js App" /> ``` 這裡的msg就是Props。==Props就是父元件將資料傳遞給子元件的方式==,或稱元件間溝通的方式,他是一個陣列型態 父元件寫法: ```javascript= <component :props="data" /> ``` 其中:props為名稱,data為值 子元件中的寫法 ```javascript= props:[ "props1", "props2" ] ``` 在app.vue與helloeorld.vue可以看到範例,使用方法就是直接將它當作資料使用 ```htmlembedded <div>{{ props1 }}</div> <p>{{ props2 }}</p> ``` - 之前曾提及過Vue能將網頁所需的內容切割成多個components,並將之重複利用;然而,在不能直接取用的前提下,若有從外部引進資料的需求,就需要透過 props 屬性來引用外部的狀態。 - for those html elements, such as`<p>` `<h1>`, we can pass those html datas into the components from App.vue (the parent elements) to child components - advantage: 1. make the components reusable && dynamic 2. there are multiple components using the same data that may be a text or an array of items, exc. We have to define that data in one single place - which is called "single source of truth/data" <hr> ### emit語法 - props屬性由parent component傳遞資料給child component,從上到下傳遞。反之,自然也會有下到上的溝通方式 - `emit` is what we use to **customize** events which can be fired from a component and **can be listened from the parent component** - 語法: ```htmlembedded= this.$emit('the_customized_event',{title:'this is a title'}) ``` 其中,emit能接收第二個參數作為想傳遞的資料(也可將之省略) 接著,我們看看下面兩個例子 - 父元件 ```htmlmixed= <template> <div id="app"> <child v-on:childMethod="parentMethod"></child> </div> </template> <script> import Child from './components/Child'; export default { name: 'App', components: { Child, }, methods: { parentMethod() { console.log('Hello World'); }, }, }; </script> ``` - 子元件 ```htmlmixed= <template> <button @click="handleClick">Emit</button> </template> <script> export default { methods: { handleClick() { this.$emit('childMethod'); }, }, }; </script> ``` 也可以帶入參數 - 父元件 ```htmlmixed= <template> <div id="app"> <child v-on:childMethod="parentMethod"></child> </div> </template> <script> import Child from './components/Child'; export default { name: 'App', components: { Child, }, methods: { parentMethod(val) { console.log(val); }, }, }; </script> ``` - 子元件 ```htmlmixed= <template> <button @click="handleClick">Emit</button> </template> <script> export default { methods: { handleClick() { this.$emit('childMethod', "Hello world!"); }, }, }; </script> ``` 透過在後方加入參數的方式,可以將值往上傳到父元件 ### Example 7 - App.vue ```htmlembedded= <template> <div> <h1>{{title}}</h1> <div v-if="showModal"> <ThisModal header="This is header" :text="text" theme="sale" @close="ToggleModal"/> </div> <button @click="ToggleModal">open</button> </div> </template> <script> import ThisModal from './components/ThisModal.vue' export default { name: 'App', components: {ThisModal}, data(){ return{ title:'My first Vue app :)', text:"This is a text", showModal:false } }, methods:{ ToggleModal() { this.showModal = !this.showModal; } } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } h1{ border: 1px solid red; display: inline-block; padding-bottom: 10px; } .active{ color:blueviolet; } </style> ``` - ThisModel.vue ```htmlembedded= <template> <div class="backdrop" @click.self="closeModal"> <!-- 'self' means that only when we click on this and this is the target of the event, it will fire this function--> <div class="modal" :class="{sale: theme === 'sale'}"> <h1>{{header}}</h1> <p>{{text}}</p> </div> </div> </template> <script> export default{ props:['header','text','theme'], methods:{ closeModal(){ this.$emit('close'); } } } </script> <style> .modal{ width:400px; padding:20px; margin: 100px auto; background:white; } .backdrop{ top:0; position:fixed; background:rgb(0, 0, 0, 0.5); width:100%; height: 100%; } .modal h1{ /* make the style more specific */ color:red; } .modal p{ font-style: normal; /* override the global */ } .modal.sale { background-color: brown; color:wheat; } .modal.sale h1{ color:white; } </style> ``` #### We will illustrate the code step by step - recall: **data-binded** 1. class屬性作為data-binded,這裡的例子也是很直覺,在`:class="{sale: theme === 'sale'}"`之中,如果sale的value為true(也就是===成立),則會有css渲染(也就是下方的.madal .sale) 2. 承上,我們能觀察到一個很有趣的比較,就是從`class="modal"`和 `:class="{sale: theme === 'sale'}"`之中,能發現前著的綁定呈 static,而後者呈dynamic的key vlaue pass <hr> - first, we pay attention on the property of "**props**" 1. "props"屬性的value透過array表示,將想操作的attribute以**string**的形式放入 2. 將我們所要從parent component的資料,註冊於child component之中,放入child component所插入的位置,像是`<Modal/>` 之中 3. 以"header"為例:在App.vue中,我們不需要在裡面定義和"header"本身有關的資料,因為我們已經在ThisModal.vue中註冊進props屬性綁定的array中;因此,我們能在ThisModal.vue中,直接以{{}}攫取 4. 如果不是用data-binded的形式(也就是:或v-bind:),操作的內容只能以string的形式;反之,像是數字、陣列、物件等便能進一步操作 <hr> - second, how to control whether we want to inject the `<ThisModal/>` (that is, the child components) - 以此為例,我們希望按下open的按鈕來讓`<ThisModal/>`component 顯示,而**只**按下背景使該component關閉 1. we use property "showModal" to control, and use the way of `v-if` 2. ThisModal.vue中做為App.vue的child component,利用` closeModal(){ this.$emit('close'); }`的函式,返回parent component中`<ThisModal header="This is header" :text="text" theme="sale" @close="ToggleModal"/>`v-on所監聽的自訂'close' event,並執行其中的ToggleModal()函式