# Vue 2 > 使用 Options API [TOC] # 第三節-基礎例子 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=3 ## 聲明式渲染 Vue.js 的核心是一個允許採用簡潔的模板語法來聲明式地將數據渲染進DOM 的系統: ```htmlmixed <div id="app"> {{ message }} </div> ``` ```javascript var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) ``` :::success **output** Hello Vue! ::: ***:memo: 所有東西都是<font color="#f00">響應式</font>的。只要修改app.message的值,上例也會相應地更新。*** ## Vue 實例 每個Vue應用都是通過用`Vue`函數創建一個新的Vue實例開始的: ```javascript var vm = new Vue({ // 選項 }) ``` ## 數據和方法 當一個Vue實例被創建時,它將<font color="#f00">`data`</font>對像中的所有的屬性加入到Vue的**響應式系統**中。當這些屬性的值發生改變時,視圖將會產生“響應”,即匹配更新為新的值。 ```javascript // 我们的数据对象 var data = { a: 1 } // 该对象被加入到一个 Vue 实例中 var vm = new Vue({ data: data }) // 获得这个实例上的 屬性 // 返回源数据中对应的字段 //vm.a == data.a // 设置 屬性 也会影响到原始数据 vm.a = 2 data.a // => 2 // ……反之亦然 data.a = 3 vm.a // => 3 ``` ***:memo: 只要修改值,view也會被改變*** 當這些數據改變時,視圖會進行重渲染。值得注意的是只有當實例被創建時就已經存在於data中的屬性才是響應式的。也就是說如果你添加一個新的屬性,比如: ```javascript vm.b = 'hi' ``` 那麼對b的改動將不會觸發任何視圖的更新。 ***:memo: 變量必須先宣告才可以使用*** 如果你知道你會在晚些時候需要一個屬性,但是一開始它為空或不存在,那麼你僅需要設置一些初始值。比如: ```javascript data: { newTodoText: '', visitCount: 0, hideCompletedTodos: false, todos: [], error: null } ``` ### `Object.freeze()` 這裡唯一的例外是使用<font color="#f00">`Object.freeze()`</font>,這會阻止修改現有的屬性,也意味著響應系統無法再追踪變化。 ```javascript var obj = { foo: 'bar' } Object.freeze(obj) new Vue({ el: '#app', data: obj }) ``` ```htmlmixed <div id="app"> <p>{{ foo }}</p> <!-- 这里的 `foo` 不会更新! --> <button v-on:click="foo = 'baz'">Change it</button> </div> ``` ### 前綴`$` Vue實例還暴露了一些有用的實例屬性與方法。它們都有前綴<font color="#f00">`$`</font>,以便與用戶定義的屬性區分開來。例如: ```javascript var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // => true vm.$el === document.getElementById('example') // => true ``` 前綴`$`和無前綴`$` ```javascript! var data = { a: 1, data: {a: 1000}} var vm = new Vue({ el: '#example', data: data }) console.log("1. vm.$data.a = " + vm.$data.a) console.log("2. vm.$data.data.a = " + vm.$data.data.a) console.log("3. vm.data.a = " + vm.data.a) console.log("4. vm.data.data = " + vm.data.data) console.log("5. vm.a = " + vm.a) ``` :::success **output_console** 1\. vm.\$data.a = 1 2\. vm.\$data.data.a = 1000 3\. vm.data.a = 1000 4\. vm.data.data = undefined 5\. vm.a = 1 ::: :::info :bulb: **關於 `$` in Vue.js** 變數前的 prefix 符號純粹是 Vue.js 的一種撰寫風格,主要是用來辨識該變數對於實體來說是屬於私有屬性還是公開屬性,如果沒弄好可能會意外覆蓋到實體中的屬性。 ``` $ is for public instance properties: _ is for private instance properties: ``` https://stackoverflow.com/questions/56881724/vue-js-meaning-of-the-dollar-prefix 若要自己命名私有屬性,官方風格寫作建議使用 $_ prefix 來命名。 https://vuejs.org/v2/style-guide/#Private-屬性-names-essential 另一例是好比變數命名風格中,如果變數名稱均採大寫,一般我們會視為是一個常數值,我們看到這個變數名稱時自然不會去隨意更改他。 如:<font color="#f00">`const PI = 3.1415936`</font> ::: ### `vm.$watch()` 查看變量改變前後的值 ```javascript! var data = { a: 1} var vm = new Vue({ el: '#example', data: data }) vm.a = 2; // $watch 是一个实例方法 vm.$watch('a', function (newValue, oldValue) { console.log(newValue,oldValue);// 會輸出a改變前後的值,如a本來為1,將它改為2,則輸出2 1(new old) }) vm.$data.a = 3 ``` :::success **output_console** 3 2 ::: # 第四節-生命週期 >[color=#00FF00] Vue.js_API參考自:https://cn.vuejs.org/v2/api/index.html#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=4 ## 生命週期 每個Vue實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到DOM並在數據變化時更新DOM等。同時在這個過程中也會運行一些叫做生命週期鉤子的函數,這給了用戶在不同階段添加自己的代碼的機會。 ## 生命週期圖 ![](https://i.imgur.com/gMAEILJ.png) ## 生命週期鉤子 所有的生命週期鉤子自動綁定<font color="#f00">`this`</font>上下文到實例中,因此你可以訪問數據,對屬性和方法進行運算。這意味著你不能使用箭頭函數來定義一個生命週期方法 (例如<font color="#f00">`created: () => this.fetchTodos()`</font>)。這是因為箭頭函數綁定了父上下文。 ```javascript var vm = new Vue({ el: "#app", data: { msg: "Hi Vue", }, //在實例初始化之後,數據觀測(data observer) 和event/watcher 事件配置之前被調用 beforeCreate:function(){ }, //在實例創建完成後被立即調用。 //在這一步,實例已完成以下的配置:數據觀測(data observer),屬性和方法的運算, //watch/event事件回調。然而,掛載階段還沒開始,$el屬性目前不可見 created:function(){ }, //在掛載開始之前被調用:相關的render函數首次被調用。 beforeMount:function(){ }, //el被新創建的vm.$el替換,掛載成功 mounted:function(){ }, //數據被更新時調用 beforeUpdate:function(){ }, //組件 DOM 已更新,組件更新完畢 updated:function(){ }, }) ``` ### `beforeCreate` ***:memo: 整個頁面創建之前*** * 類型:<font color="#f00">`Function`</font> * 詳細:在實例初始化之後,數據觀測(data observer) 和event/watcher 事件配置<font color="#f00">之前</font>被調用。 ### `created` ***:memo: 相對於<font color="#f00">`beforeCreate`</font>,<font color="#f00">`$el`</font>屬性還不可見;在模板渲染成html前調用,即通常初始化某些屬性值,然後再渲染成視圖*** * 類型:<font color="#f00">`Function`</font> * 詳細:在實例創建完成後被立即調用。在這一步,實例已完成以下的配置:數據觀測(data observer),屬性和方法的運算,watch/event事件回調。然而,掛載階段還沒開始,<font color="#f00">`$el`</font>屬性目前尚不可用。 ### `beforeMount` ***:memo: 在掛載之前被調用: 相關的渲染函數首次被調用*** * 類型:<font color="#f00">`Function`</font> * 詳細:在掛載開始<font color="#f00">之前</font>被調用:相關的<font color="#f00">`render`</font>函數首次被調用。 **該鉤子在服務器端渲染期間不被調用。** ### `mounted` ***:memo: <font color="#f00">`el`</font>被新創建的<font color="#f00">`vm.$el`</font>替換,掛載成功;在模板渲染成html後調用,通常是初始化頁面完成後,再對html的dom節點進行一些需要的操作*** * 類型:<font color="#f00">`Function`</font> * 詳細:實例被掛載後調用,這時<font color="#f00">`el`</font>被新創建的<font color="#f00">`vm.$el`</font>替換了。如果根實例掛載到了一個文檔內的元素上,當<font color="#f00">`mounted`</font>被調用時<font color="#f00">`vm.$el`</font>也在文檔內。 注意<font color="#f00">`mounted` </font>不會保證所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以在<font color="#f00">`mounted`</font>內部使用<font color="#228b22">`vm.$nextTick`</font>: ```javascript mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) } ``` **該鉤子在服務器端渲染期間不被調用。** ### `beforeUpdate` ***:memo: 數據被更新時調用*** * 類型:<font color="#f00">`Function`</font> * 詳細:數據更新時調用,發生在虛擬DOM 打補丁之前。這裡適合在更新之前訪問現有的DOM,比如手動移除已添加的事件監聽器。 **該鉤子在服務器端渲染期間不被調用,因為只有初次渲染會在服務端進行。** ### `updated` ***:memo: 組件 DOM 已更新,組件更新完畢*** * 類型:<font color="#f00">`Function`</font> * 詳細:由於數據更改導致的虛擬DOM 重新渲染和打補丁,在這之後會調用該鉤子。 當這個鉤子被調用時,組件DOM已經更新,所以你現在可以執行依賴於DOM的操作。然而在大多數情況下,你應該避免在此期間更改狀態。如果要相應狀態改變,通常最好使用<font color="#228b22">計算屬性</font>或<font color="#228b22">watcher</font>取而代之。 注意<font color="#f00">`updated`</font>不會保證所有的子組件也都一起被重繪。如果你希望等到整個視圖都重繪完畢,可以在<font color="#f00">`updated`</font>裡使用<font color="#228b22">`vm.$nextTick`</font>: ```javascript updated: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been re-rendered }) } ``` **該鉤子在服務器端渲染期間不被調用。** :::info :bulb: **其他生命週期鉤子** ### `activated` * 類型:<font color="#f00">`Function`</font> * 詳細:被keep-alive 緩存的組件激活時調用。 **該鉤子在服務器端渲染期間不被調用。** ### `deactivated` * 類型:<font color="#f00">`Function`</font> * 詳細:被keep-alive 緩存的組件停用時調用。 **該鉤子在服務器端渲染期間不被調用。** ### `beforeDestroy` * 類型:<font color="#f00">`Function`</font> * 詳細:實例銷毀之前調用。在這一步,實例仍然完全可用。 **該鉤子在服務器端渲染期間不被調用。** ### `destroyed` * 類型:<font color="#f00">`Function`</font> * 詳細:實例銷毀後調用。該鉤子被調用後,對應Vue 實例的所有指令都被解綁,所有的事件監聽器被移除,所有的子實例也都被銷毀。 **該鉤子在服務器端渲染期間不被調用。** ### `errorCaptured` > <font color="#228b22">2.5.0+ 新增</font> > [color=#228b22] * 類型:<font color="#f00">`(err: Error, vm: Component, info: string) => ?boolean`</font> * 詳細:當捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。此鉤子可以返回<font color="#f00">`false`</font>以阻止該錯誤繼續向上傳播。 ><font color="#f00">錯誤傳播規則</font> >[color=red] > * 默認情況下,如果全局的<font color="#f00">`config.errorHandler`</font>被定義,所有的錯誤仍會發送它,因此這些錯誤仍然會向單一的分析服務的地方進行匯報。 > > * 如果一個組件的繼承或父級從屬鏈路中存在多個<font color="#f00">`errorCaptured`</font>鉤子,則它們將會被相同的錯誤逐個喚起。 > > * 如果此<font color="#f00">`errorCaptured`</font>鉤子自身拋出了一個錯誤,則這個新錯誤和原本被捕獲的錯誤都會發送給全局的<font color="#f00">`config.errorHandler`</font>。 > > * 一個errorCaptured鉤子能夠返回<font color="#f00">`false`</font>以阻止錯誤繼續向上傳播。本質上是說“這個錯誤已經被搞定了且應該被忽略”。它會阻止其它任何會被這個錯誤喚起的<font color="#f00">`errorCaptured`</font>鉤子和全局的<font color="#f00">`config.errorHandler`</font>。 ::: # 第五節-模板語法_插值 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=5 ## 插值 數據綁定最常見的形式就是使用"Mustache"語法(雙大括號) 的文本插值: ```htmlmixed <span>Message: {{ msg }}</span> ``` Mustache標籤將會被替代為對應數據對像上<font color="#f00">`msg`</font>屬性的值。綁定的數據對像上<font color="#f00">`msg`</font>屬性發生了改變,插值處的內容都會更新。 ### :pushpin: `v-once`一次性插值 通過使用<font color="#228b22">v-once</font>指令,執行一次性地插值,當數據改變時,插值處的內容不會更新。但要小心可能會影響到該節點上的其它數據綁定: ```htmlmixed <div id="app" v-once> {{ msg }} </div> <script> var vm = new Vue({ el: "#app", data: { msg: "hi vue" } }); vm.msg = "hi..."; </script> ``` :::success **output** hi vue // not "hi..." ::: ### :pushpin: `v-html`原始HTML 雙大括號會將數據解釋為普通文本,而非HTML代碼。為了輸出真正的HTML,你需要使用<font color="#f00">`v-html`</font> ```htmlmixed <div id="app"> <p>未使用v-html: {{ rawHtml }}</p> <p>使用v-html: <span v-html="rawHtml"></span></p> <p v-html="rawHtml"></p> </div> <script> var vm = new Vue ({ el: "#app", data: { msg: "hi vue", rawHtml: '<span style="color:red">this is should be red</span>' } }); </script> ``` :::success **output** 未使用v-html: \<span style="color:red">this is should be red</span> 使用v-html: <font color="#f00">This is should be red</font> ::: ### :pushpin: `v-bind`綁定屬性 Mustache語法不能作用在HTML屬性上,遇到這種情況應該使用<font color="#f00">`v-bind`</font>指令: ```htmlmixed <div v-bind:id="dynamicId"></div> ``` :::info :bulb: **v-bind語法:** **v-bind:屬性名稱="屬性內容"** ::: 就是將屬性對應到變數名稱的值 ```htmlmixed <div v-bind:class="color"> hi vue </div> <!-- 會變成 <div class="red">hi vue</div> --> <script> var vm = new Vue({ el: ".color", data: { color: red } }) </script> <style> .red { color: red; } </style> ``` :::success **output** <font color="#f00">hi vue</font> // class名稱被改變為"red"再被css改變顏色 ::: ## 使用 JavaScript 表達式 對於所有的數據綁定,Vue.js 都提供了完全的JavaScript 表達式支持。 ### :pushpin: 算式 ```htmlmixed <div id="app"> {{ number + 1 }} </div> <script> var vm = new Vue({ el: "#app", data: { number: 10 } }) </script> ``` :::success **output** 11 ::: ### :pushpin: 二元表達式 ```htmlmixed <div id="app"> <p>{{ ok ? 'YES' : 'NO' }}</p> <p>{{ 1 == 1 ? 'YES' : 'NO' }}</p> <p>{{ msg ? 'YES' : 'NO' }}</p> <p>{{ test > 50 ? 'YES' : 'NO' }}</p> </div> <script> var vm = new Vue({ el: "#app", data: { ok: false, test: 100 } }) </script> ``` :::success **output** NO YES NO YES ::: ### :pushpin: 複雜函數運算 ```htmlmixed <div id="app"> <p>{{ message.split('').reverse().join('') }}</p> </div> <script> var vm = new Vue({ el: "#app", data: { message: "vue", } }) </script> ``` :::success **output** euv ::: :::info :bulb: **函式** **split(str):字串拆分 reverse():反序 join(str):結合** ::: # 第六節-模板語法_指令 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=6 ## 指令 指令(Directives)是帶有<font color="#f00">`v-`</font>前綴的特殊attribute。指令attribute的值預期是單個**JavaScript表達式** (<font color="#f00">`v-for`</font>是例外情況)。指令的職責是,當表達式的值改變時,將其產生的連帶影響,響應式地作用於DOM。 ```htmlmixed <div id="app"> <p>看到我了嗎</p> <p v-if="seen">現在你看到我了</p> </div> <script> var vm = new Vue({ el: "#app", data: { seen: false, } }) </script> ``` :::success **output** 看到我了嗎 // seen為false所以不會顯示第二行 ::: ## 參數 一些指令能夠接收一個"參數",在指令名稱之後以冒號表示。例如,<font color="#f00">`v-bind`</font>指令可以用於響應式地更新HTML屬性: ```htmlmixed <div id="app"> <p><a v-bind:href="url">Vue.js</a></p> </div> <script> var vm = new Vue({ el: "#app", data: { url: "https://cn.vuejs.org/v2/guide/syntax.html#%E6%8C%87%E4%BB%A4", } }) </script> ``` :::success **output** [Vue.js](https:////cn.vuejs.org/v2/guide/syntax.html#%E6%8C%87%E4%BB%A4) ::: 另一個例子是<font color="#f00">`v-on`</font>指令,它用於監聽DOM事件: ```javascript <a v-on:click="doSomething">...</a> ``` 在這裡參數是監聽的事件名。 ## <font color="#f00">*</font>動態參數 > <font color="#228b22">2.6.0 新增</font> > [color=#228b22] :::info :bulb: **動態參數** 從2.6.0 開始,可以用方括號括起來的JavaScript 表達式作為一個指令的參數: ```htmlmixed <!-- 注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。 --> <a v-bind:[attributeName]="url"> ... </a> ``` 這裡的<font color="#f00">`attributeName`</font>會被作為一個JavaScript表達式進行動態求值,求得的值將會作為最終的參數來使用。例如,如果你的Vue實例有一個<font color="#f00">`data`</font>屬性<font color="#f00">`attributeName`</font>,其值為<font color="#f00">`"href"`</font>,那麼這個綁定將等價於<font color="#f00">`v-bind:href`</font>。 同樣地,你可以使用動態參數為一個動態的事件名綁定處理函數: ```htmlmixed <a v-on:[eventName]="doSomething"> ... </a> ``` 在這個示例中,當<font color="#f00">`eventName`</font>的值為<font color="#f00">`"focus"`</font>時,<font color="#f00">`v-on:[eventName]`</font>將等價於<font color="#f00">`v-on:focus`</font>。 **對動態參數的值的約束** 動態參數預期會求出一個字符串,異常情況下值為<font color="#f00">`null`</font>。這個特殊的<font color="#f00">`null`</font>值可以被顯性地用於移除綁定。任何其它非字符串類型的值都將會觸發一個警告。 **對動態參數表達式的約束** 動態參數表達式有一些語法約束,因為某些字符,如空格和引號,放在HTML attribute 名里是無效的。例如: ```htmlmixed <!-- 这会触发一个编译警告 --> <a v-bind:['foo' + bar]="value"> ... </a> ``` 變通的辦法是使用沒有空格或引號的表達式,或用計算屬性替代這種複雜表達式。 在DOM 中使用模板時(直接在一個HTML 文件裡撰寫模板),還需要避免使用大寫字符來命名鍵名,因為瀏覽器會把attribute 名全部強制轉為小寫: ```htmlmixed <!-- 在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。 除非在实例中有一个名为“someattr”的 屬性,否则代码不会工作。 --> <a v-bind:[someAttr]="value"> ... </a> ``` ::: ## 修飾符 修飾符(modifier)是以半角句號.指明的特殊後綴,用於指出一個指令應該以特殊方式綁定。例如,<font color="#f00">`.prevent`</font>修飾符告訴<font color="#f00">`v-on`</font>指令對於觸發的事件調用<font color="#f00">`event.preventDefault()`</font>: ```htmlmixed <div id="app"> <div @click="click1"> <div @click="click2"> <p>click me</p> </div> </div> </div> <script> var vm = new Vue({ el: "#app", methods: { click1: function() { console.log("click1..."); }, click2: function() { console.log("click2..."); }, } }) </script> ``` :::success **output_console.log** click2... click1... ::: <font color="#f00">`.stop`</font>就是Vue.js的一個修飾符,指當前的click事件執行完畢後就停下而不執行父集的點擊事件 ```htmlmixed <div id="app"> <div @click="click1"> <div @click.stop="click2"> <p>click me</p> </div> </div> </div> <script> var vm = new Vue({ el: "#app", methods: { click1: function() { console.log("click1..."); }, click2: function() { console.log("click2..."); }, } }) </script> ``` :::success **output_console.log** click2... ::: ## 縮寫 Vue為v-bind和v-on這兩個最常用的指令,提供了特定簡寫: ### :pushpin: `v-bind` 縮寫 <font color="#f00">`:`</font> ```htmlmixed <!-- 完整语法 --> <a v-bind:href="url">...</a> <!-- 缩写 --> <a :href="url">...</a> <!-- 动态参数的缩写 (2.6.0+) --> <a :[key]="url"> ... </a> ``` ### :pushpin: `v-on` 縮寫 <font color="#f00">`@`</font> ```htmlmixed <!-- 完整语法 --> <a v-on:click="doSomething">...</a> <!-- 缩写 --> <a @click="doSomething">...</a> <!-- 动态参数的缩写 (2.6.0+) --> <a @[event]="doSomething"> ... </a> ``` # 第七節-Class 與Style 綁定 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=7 ## 綁定HTML Class 我們可以傳給<font color="#f00">`v-bind:class`</font>一個對象,以<font color="#f00">**動態**</font>地切換class: ```htmlmixed <div id="app"> <div v-bind:class = "{ active: isActive}" style = "width:200px; height:200px; text-align:center; line-height:200px"> hi vue </div> </div> <script> new Vue({ el: '#app', data: { isActive: true } }) </script> <style> .active { background: #FF0000; } </style> ``` :::success **output** <div style="width: 200px; height: 200px; text-align: center; line-height: 200px; background:#FF0000;"> hi vue </div> ::: 亦可以綁定多個class ```htmlmixed <div id="app"> <div class = "test" v-bind:class = "{ active: isActive, green: isGreen }" style = "width:200px; height:200px; text-align:center; line-height:200px"> hi vue </div> </div> <script> new Vue({ el: '#app', data: { isActive: true, isGreen: true } }) </script> <style> .active { background:#FF0000; } .test { font-size:30px; } .green { color:#00FF00; } </style> ``` :::success **output** <div style="width: 200px; height: 200px; text-align: center; line-height: 200px; background:#FF0000; font-size:30px; color:#00FF00;"> hi vue </div> ::: :pushpin: 使用數組形式 ```htmlmixed <div id="app"> <div class = "test" v-bind:class = "[ 'active', 'green' ]" style = "width:200px; height:200px; text-align:center; line-height:200px"> hi vue </div> </div> ``` :pushpin: 使用二元表達式 ```htmlmixed <div id="app"> <div class = "test" v-bind:class = "[ isActive ? 'active': '', isGreen ? 'green' : '' ]" style = "width:200px; height:200px; text-align:center; line-height:200px"> hi vue </div> </div> ``` ```htmlmixed <script> new Vue({ el: '#app', data: { isActive: true, isGreen: true } }) </script> <style> .active{background:#FF0000;} .test{font-size:30px;} .green{color:#00FF00;} </style> ``` :::success **output** <div style="width: 200px; height: 200px; text-align: center; line-height: 200px; background:#FF0000; font-size:30px; color:#00FF00;"> hi vue </div> ::: ```htmlmixed <div id="app"> <div :style = "{ color : color, fontSize : size , background : isRed ? '#FF0000' : ''}"> hi vue </div> </div> <script> new Vue({ el: '#app', data: { color: #FFFFFF, size : '50px', isRed : true } }) </script> ``` :::success **output** <div style="color: #FFFFFF; font-size: 50px; background: #FF0000;">hi vue</div> ::: # 第八節-條件渲染 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=8 ## :pushpin: `v-if`、`v-else-if`、`v-else` <font color="#f00">`v-if`</font>指令用於條件性地渲染一塊內容。這塊內容只會在指令的表達式返回truthy值的時候被渲染。 也可以用<font color="#f00">`v-else`</font>添加一個"else塊": ```htmlmixed <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>NO</div> </div> <script> var vm = new Vue({ el : '#app', data: { type : '' }, }); </script> ``` :::success **output** NO ::: ```javascript var vm = new Vue({ el : '#app', data: { type : 'B' }, }); ``` :::success **output** B ::: ## :pushpin: `v-show` 另一個用於根據條件展示元素的選項是<font color="#f00">`v-show`</font>指令。用法大致一樣 不同的是帶有<font color="#f00">`v-show`</font>的元素始終會被渲染並保留在DOM中。<font color="#f00">`v-show`</font>只是簡單地切換元素的CSS屬性<font color="#f00">`display`</font>。 ```htmlmixed <div id='app'> <h1 v-show="ok">Hello!</h1> </div> <script> var vm = new Vue({ el : '#app', data: { ok : false }, }); </script> ``` :::success **output_html** \<div style="display: none;">Hello!</div> ::: ### `v-if`和`v-show` <font color="#f00">`v-if`</font>是"真正"的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建。 <font color="#f00">`v-if`</font>也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。 相比之下,<font color="#f00">`v-show`</font>就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於CSS進行切換。 一般來說,<font color="#f00">`v-if`</font>有更高的切換開銷,而v-show有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用<font color="#f00">`v-show`</font>較好;如果在運行時條件很少改變,則使用<font color="#f00">`v-if`</font>較好。 # 第九節-列表渲染 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=9 ## :pushpin: 用`v-for`把一個數組對應為一組元素 可以用<font color="#f00">`v-for`</font>指令基於一個數組來渲染一個列表。<font color="#f00">`v-for`</font>指令需要使用<font color="#f00">`item in items`</font>形式的特殊語法,其中<font color="#f00">`items`</font>是源數據數組,而<font color="#f00">`item`</font>則是被迭代的數組元素的別名。 ```htmlmixed <div id='app'> <ul> <li v-for="item in items"> {{ item.message }} </li> </ul> </div> <script> var vm = new Vue({ el : '#app', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] }, }); </script> ``` :::success **output** <ul><li>Foo</li><li>Bar</li></ul> ::: 在<font color="#f00">`v-for`</font>塊中,我們可以訪問所有父作用域的屬性。<font color="#f00">`v-for`</font>還支持一個可選的第二個參數,即當前項的索引。 ```htmlmixed <ul id="app"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> ``` ```javascript var vm = new Vue({ el : '#app', data: { parentMessage: 'Parent', testMessage: 'this is test', items: [ { message: 'Foo' ,test: 'a'}, { message: 'Bar' ,test: 'b'}, { message: 'just test'} ] }, }); ``` :::success **output** <ul><li> this is test - a - Foo - 0 </li><li> this is test - b - Bar - 1 </li><li> this is test - - just test - 2 </li></ul> ::: 可以用<font color="#f00">`of`</font>替代<font color="#f00">`in`</font>作為分隔符,因為它更接近JavaScript迭代器的語法: ```htmlmixed <div v-for="item of items"></div> ``` :::info :bulb: **v-for語法:v-for="(item,index) in items"** ::: ## :pushpin:在`v-for`裡使用對象 可以用<font color="#f00">`v-for`</font>來遍歷一個object的屬性。 可使用三個參數(值,鍵名,索引) ```htmlmixed <div id='app'> <ul> <li v-for="(value,name,index) in object"> {{ name }} : {{ value }} - {{ index }} </li> </ul> </div> <script> var vm = new Vue({ el : '#app', data: { object : { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' } }, }); </script> ``` :::success **output** <ul><li> title : How to do lists in Vue - 0 </li><li> author : Jane Doe - 1 </li><li> publishedAt : 2016-04-10 - 2 </li></ul> ::: :::info :paperclip: **語法:v-for="(value,key,index) in object"** ::: ## 維護狀態 當Vue正在更新使用<font color="#f00">`v-for`</font>渲染的元素列表時,它默認使用“就地更新”的策略。 這個默認的模式是高效的,但是**只適用於不依賴子組件狀態或臨時DOM狀態(例如:表單輸入值)的列表渲染輸出。** 為了給Vue一個提示,以便它能跟踪每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一<font color="#f00">`key`</font>屬性: ```htmlmixed <div v-for="item in items" v-bind:key="item.id"> <!-- 内容 --> </div> <div v-for="(item,index) in items" v-bind:key="index"> <!-- 内容 --> </div> <div v-for="(name,key,index) in object" v-bind:key="key"> <!-- 内容 --> </div> ``` # 第十節-事件處理 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=10 ## 監聽事件 可以用<font color="#f00">`v-on`</font>指令監聽DOM事件,並在觸發時運行一些JavaScript代碼。 ```htmlmixed <div id="app"> <button v-on:click="counter += 1"> 數值: {{ counter }} </button> </div> <script> new Vue({ el: '#app', data: { counter: 0 } }) </script> ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/qBbxWrV" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: ## 事件處理方法 通常許多事件處理邏輯會更為複雜,直接把JavaScript代碼寫在<font color="#f00">`v-on`</font>指令中是不可行的。因此<font color="#f00">`v-on`</font>還可以接收一個需要調用的<font color="#f00">**方法**</font>名稱。 ```htmlmixed <div id="app"> <button v-on:click="greet">Greet</button> </div> ``` ```javascript var vm = new Vue({ el: "#app", data: { counter: 0, name: "hello world!" }, methods: { greet: function () { alert("hi"); alert(this.name); } } }); // 也可以用 JavaScript 直接调用方法 vm.greet(); // => "hi" & "hello world!" ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/BajYBZe" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: ## 內聯處理器中的方法 除了直接綁定到一個方法,也可以在內聯JavaScript 語句中調用方法: ```htmlmixed <div id="app"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div> ``` ```javascript new Vue({ el: '#app', methods: { say: function (message) { alert(message) } } }) ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/OJMQLxG" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: 有時也需要在內聯語句處理器中訪問原始的DOM事件。可以用特殊變量<font color="#f00">`$event`</font>把它傳入方法: ```htmlmixed <button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> ``` ```javascript // ... methods: { warn: function (message, e) { // 现在我们可以访问原生事件对象 if (e) { e.preventDefault() } alert(message) } } ``` :::info :bulb: **event.preventDefault()** 停止事件的默認動作,例如有時候我們會利用連結的<a>來當作按鈕,他本身DOM就擁有連結的功能,但是有時候我們會為他新增類似onclick的事件,而只要在該<a>觸發的事件中加入event.preventDefault(),就不會在執行他默認的動作,也就是不會再執行「連結到某個網址」這個動作 ::: ## 事件修飾符 在事件處理程序中調用<font color="#f00">`event.preventDefault()`</font>或<font color="#f00">`event.stopPropagation()`</font>是非常常見的需求。儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理DOM事件細節。 為了解決這個問題,Vue.js為<font color="#f00">`v-on`</font>提供了**事件修飾符**。之前提過,修飾符是由點開頭的指令後綴來表示的。 <font color="#f00"> * `.stop` * `.prevent` * `.capture` * `.self` * `.once` * `.passive` </font> ```htmlmixed <!-- 阻止单击事件继续传播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件监听器时使用事件捕获模式 --> <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 --> <div v-on:click.capture="doThis">...</div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div v-on:click.self="doThat">...</div> ``` :::warning :warning: 使用修飾符時,順序很重要;相應的代碼會以同樣的順序產生。因此,用 <font color="#f00">`v-on:click.prevent.self`</font>會阻止**所有的點擊**,而<font color="#f00">`v-on:click.self.prevent`</font>只會阻止對元素自身的點擊 ::: :::info > [color=#00FF00]<font color="#228b22">2.1.4 新增</font> 不像其它只能對原生的DOM事件起作用的修飾符,<font color="#f00">`.once`</font>修飾符還能被用到自定義的<font color="#228b22">**組件事件**</font>上。如果你還沒有閱讀關於組件的文檔,現在大可不必擔心。 ```htmlmixed <!-- 点击事件将只会触发一次 --> <a v-on:click.once="doThis"></a> ``` > [color=#00FF00]<font color="#228b22">2.3.0 新增</font> Vue還對應<font color="#f00">`addEventListener`</font>中的<font color="#f00">`passive`</font>選項提供了<font color="#f00">`.passive`</font>修飾符。 ```htmlmixed <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> <!-- 而不会等待 `onScroll` 完成 --> <!-- 这其中包含 `event.preventDefault()` 的情况 --> <div v-on:scroll.passive="onScroll">...</div> ``` 這個<font color="#f00">`.passive`</font>修飾符尤其能夠提升移動端的性能。 :::warning :warning: 不要把<font color="#f00">`.passive`</font>和<font color="#f00">`.prevent`</font>一起使用,因為<font color="#f00">`.prevent`</font>將會被忽略,同時瀏覽器可能會向你展示一個警告。請記住,<font color="#f00">`.passive`</font>會告訴瀏覽器你不想阻止事件的默認行為。 ::: # 第十一節-表單輸入綁定 >[color=#00FF00]本節教學影片:https://learning.dcloud.io/#/?vid=11 ## :pushpin:`v-model` 用<font color="#f00">`v-model`</font>指令在表單<font color="#f00">`<input>`</font>、<font color="#f00">`<textarea>`</font>及<font color="#f00">`<select>`</font>元素上創建<font color="#f00">**雙向數據綁定**</font>。 <font color="#f00">`v-model`</font>在內部為不同的輸入元素使用不同的屬性 並拋出不同的事件: * text和textarea元素使<font color="#f00">`value`</font>屬性和<font color="#f00">`input`</font>事件; * checkbox和radio使用<font color="#f00">`checked`</font>屬性和<font color="#f00">`change`</font>事件; * select字段將<font color="#f00">`value`</font>作為prop並將<font color="#f00">`change`</font>作為事件。 ```htmlmixed <div id="app"> <!--輸入框--> <p><input v-model="message" placeholder="edit me"></p> <p><textarea v-model="message2" placeholder="add multiple lines"></textarea> <p>Message is {{ message }}</p> <p>Message2 is {{ message2 }}</p> <!--單選--> <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> <br> <!--複選--> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <p>Checked names: {{ checkedNames }}</p> <!--radio--> <input type="radio" id="one" value="One" v-model="picked"> <label for="one">One</label> <br> <input type="radio" id="two" value="Two" v-model="picked"> <label for="two">Two</label> <br> <p>Picked: {{ picked }}</p> <button type="button" @click="submit">提交</button> 請F12打開控制台查看 </div> ``` ```javascript new Vue({ el: "#app", data: { message: "", message2: "", checked: "", checkedNames: [], picked: "" }, methods: { submit: function () { var postobj = { msg1: this.message, msg2: this.message2, checkedVal: this.checked, checkedArray: this.checkedNames, pickedVal: this.picked }; console.log(postobj); console.log(this.$data); } } }); ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/xxZYKmW" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: :::info :bulb: **複選框使用數組**checkNames: [] ::: 用 <font color="#f00">`v-for`</font>渲染的動態選項: ```htmlmixed <div id="app"> <select v-model="selected"> <option v-for="(option,name,index) in options" v-bind:value="option.value" v-key="index"> {{ option.text }} - {{ option.value }} - {{ name }} - {{ index }} </option> </select> <span>Selected: {{ selected }}</span> </div> ``` ```javascript new Vue({ el: "#app", data: { selected: "A", options: { 甲: { text: "One", value: "A" }, 乙: { text: "Two", value: "B" }, 丙: { text: "Three", value: "C" } } } }); ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/vYLdYOw" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: ## 值綁定 對於單選按鈕,複選框及選擇框的選項,<font color="#f00">`v-model`</font>綁定的值通常是**靜態字符串**(對於復選框也可以是boolean值): ```htmlmixed <!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 当选中第一个选项时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select> ``` 但是有時我們可能想把值綁定到Vue實例的一個動態property上,這時可以用<font color="#f00">`v-bind`</font>實現,並且這個property的值可以不是字符串。 ### :pushpin:複選框 ```htmlmixed <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" > ``` ```javascript // 当选中时 vm.toggle === 'yes' // 当没有选中时 vm.toggle === 'no' ``` :::warning :warning: 這裡的<font color="#f00">`true-value`</font>和<font color="#f00">`false-value`</font>attribute並不會影響輸入控件的<font color="#f00">`value`</font>attribute,因為瀏覽器在提交表單時並不會包含未被選中的複選框。如果要確保表單中這兩個值中的一個能夠被提交,(即“yes”或“no”),請換用單選按鈕。 ::: ### :pushpin:單選按鈕 ```htmlmixed <input type="radio" v-model="pick" v-bind:value="a"> ``` ```javascript // 当选中时 vm.pick === vm.a ``` ### :pushpin:選擇框的選項 ```htmlmixed <select v-model="selected"> <!-- 内联对象字面量 --> <option v-bind:value="{ number: 123 }">123</option> </select> ``` ```javascript // 当选中时 typeof vm.selected // => 'object' vm.selected.number // => 123 ``` ## 修飾符 ### <font color="#f00">`.lazy`</font> 在默認情況下,<font color="#f00">`v-model`</font>在每次<font color="#f00">`input`</font>事件觸發後將輸入框的值與數據進行同步(除了上述輸入法組合文字時)。你可以添加<font color="#f00">`lazy`</font>修飾符,從而轉為在<font color="#f00">`change`</font>事件之後進行同步: ```htmlmixed <!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg"> ``` ### <font color="#f00">`.number`</font> 自動將用戶的輸入值轉為數值類型,可以給<font color="#f00">`v-model`</font>添加<font color="#f00">`number`</font>修飾符: ```htmlmixed <input v-model.number="age" type="number"> ``` 即使在<font color="#f00">`type="number"`</font>時,HTML輸入元素的值也總會返回字符串。如果這個值無法被<font color="#f00">`parseFloat()`</font>解析,則會返回原始的值。 ### <font color="#f00">`.trim`</font> 自動過濾用戶輸入的首尾空白字符,可以給<font color="#f00">`v-model`</font>添加<font color="#f00">`trim`</font>修飾符: ```htmlmixed <input v-model.trim="msg"> ``` ## `v-model`取得選項的text 在<font color="#f00">`:value`</font>後面用物件形式使<font color="#f00">`v-model`</font>可以讀取該鍵位的值 ```htmlmixed <div id="app"> <select v-model="selected_table"> <option v-for="(value, index) in tables" v-bind:value="{ text: value, id: index }"> {{ value }} </option> </select> <h1> selected_table.text:value= {{ selected_table.text }} <br> selected_table.text:index= {{ selected_table.id }}</h1> </div> ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/mdVKepa" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: # 第十二節-組件 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=12 ## 基本示例 組件是可複用的Vue實例,且帶有一個名字。我們可以在一個通過<font color="#f00">`new Vue`</font>創建的Vue根實例中,把這個組件作為自定義元素來使用: ```htmlmixed <div id="app"> <button-counter title="title1: "></button-counter> <button-counter title="title2: "></button-counter> <button-counter title="title3: "></button-counter> </div> ``` ```javascript Vue.component('button-counter', { props: ['title'], data: function () { return { count: 0 } }, template: '<button v-on:click="count++">{{ title }}You clicked me {{ count }} times.</button>' }) new Vue({ el: '#app', data: { message: 'Hello Vue.js!' } }) ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/BajYqaO" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: 因為組件是可複用的Vue實例,所以它們與<font color="#f00">`new Vue`</font>接收相同的選項,例如<font color="#f00">`data`</font>、<font color="#f00">`computed`</font>、<font color="#f00">`watch`</font>、<font color="#f00">`methods`</font>以及生命週期鉤子等。僅有的例外是像<font color="#f00">`el`</font>這樣根實例特有的選項。 :::info :bulb: **若要實現多組html標籤,則將其放在\<div>裡** ex:\<div>\<h1>\</h1>\<button v-on:click="count++">\</button>\<div> ::: :::warning :warning: <font color="#f00">**`data`</font>必須是一個<font color="#f00">函數**</font> 當我們定義這個<font color="#f00">`<button-counter>`</font>組件時,你可能會發現它的<font color="#f00">`data`</font>並不是像這樣直接提供一個對象: ```javascript data: { count: 0 } ``` 取而代之的是,**一個組件的<font color="#f00">`data`</font>選項必須是一個函數**,因此每個實例可以維護一份被返回對象的獨立的拷貝: ```javascript data: function () { return { count: 0 } } ``` ::: ## 父元件和子元件互動:<font color="#f00">`props`</font> & <font color="#f00">`$emit`</font> >[color=#00FF00] VUE中關於$emit的用法 >https://www.itread01.com/content/1546224676.html >Vue一下 13日:傳遞資料的跳台 props & emit >https://ithelp.ithome.com.tw/articles/10205981 ### **<font color="#f00">`props`</font>** ***:memo: 父元件可以使用 <font color="#f00">`props`</font> 把資料傳給子元件。*** ![](https://i.imgur.com/kU0xwsm.png) <font color="#f00">`props`</font>是寫在<font color="#f00">`component`</font>裡面的,是**單向數據流**的傳遞,為避免內部元件改變外部元件。 ```htmlmixed <div id="app"> <blog-post // Vue.component('blog-post',...) v-for="post in posts" // posts來自 data v-bind:key="post.id" // 注意動態資料要 bind, key跟title來自props宣告 v-bind:title="post.title"> </blog-post> </div> ``` ```javascript Vue.component("blog-post", { props: ["title"], template: "<h3>{{ title }}</h3>" }); new Vue({ el: "#app", data: { posts: [ { id: 1, title: "My journey with Vue" }, { id: 2, title: "Blogging with Vue" }, { id: 3, title: "Why Vue is so fun" } ] } }); ``` :::success **output** <h3>My journey with Vue</h3><h3>Blogging with Vue</h3><h3>Why Vue is so fun</h3> ::: 在<font color="#f00">`component`</font>裡面的資料要用<font color="#f00">`function return`</font>出來,透過<font color="#f00">`props`</font>來連結<font color="#f00">`data`</font>,讓資料更集中,管理上更便利。 ### <font color="#f00">`props`</font>的駝峰命名 在html中,attribute對大小寫不敏感,<font color="#f00">`props`</font>中的駝峰命名若要在html使用,必須全部使用小寫,且用dash(<font color="#f00">`-`</font>)隔開:例如 <font color="#f00">`backgroundPng`</font> 要寫作 <font color="#f00">`background-png`</font> ### <font color="#f00">`props`</font>的型別 可以為<font color="#f00">`props`</font>傳遞的資料指定型別,數值在**靜態屬性**是傳入**字串**,但我們可能需要用來運算,所以指定成<font color="#f00">`Number`</font> ```javascript props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object } ``` 另外還能指定多個可能的型別、若沒有預先傳入資料的預設值,參考官網:[Prop 验证](https://cn.vuejs.org/v2/guide/components-props.html#Prop-%E9%AA%8C%E8%AF%81) ### 靜態傳遞與動態傳遞 最簡單的判別就在於我們自定義的attribute在html使用時前面有沒有加冒號(<font color="#f00">`v-bind:`</font>) ```javascript Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' }) ``` ```htmlmixed <blog-post title="My journey with Vue"></blog-post> ``` 這樣是**靜態** ```htmlmixed <blog-post v-bind:title="post.title"></blog-post> ``` 這樣是**動態** :::warning :warning: **如果沒有使用動態,那就會全部視為<font color="#f00">`string`</font>** ::: ### <font color="#f00">`$emit`</font> ***:memo: 子元件可以使用 <font color="#f00">`$emit`</font> 觸發父元件的自定義事件。*** 從內部向外傳遞。 vm.$emit( eventName, […args] ) ```htmlmixed <div id="app"> <button-counter title="title1: " @give-advice="showAdvice"></button-counter> <button-counter title="title2: "></button-counter> <button-counter title="title3: "></button-counter> </div> ``` ```javascript Vue.component("button-counter", { props: ["title"], data: function () { return { count: 0 }; }, template: '<button v-on:click="giveAdvice();son();" >{{ title }}You clicked me {{ count }} times.</button>', methods: { giveAdvice: function () { this.count++; this.$emit("give-advice", this.count); alert("giveAdvice()"); }, son: function () { alert("son()"); } } }); var vm = new Vue({ el: "#app", data: { message: "Hello Vue.js!", count: 0 }, methods: { showAdvice: function (e) { alert("showAdvice()"); alert("count = " + e); } } }); ``` :::success **output** <iframe height="265" style="width: 100%;" scrolling="yes" title="Button Hover Effects | Html CSS" src="https://codepen.io/LeeGeorgeBye/pen/jOWZeaO" frameborder="no" allowtransparency="true" allowfullscreen="true"> </iframe> ::: :::info :bulb: 也就是說<font color="#f00">`template @click`</font>觸發<font color="#f00">`giveAdvice()`</font>, <font color="#f00">`giveAdvice()`</font>帶著參數 <font color="#f00">`emit`</font> 給<font color="#f00">`give-advice`</font>, <font color="#f00">`give-advice`</font>連著參數給予並觸發<font color="#f00">`showAdvice()`</font> ::: ## 在組件上使用 <font color="#f00">`v-model`</font> 自定義事件也可以用於創建支持v-model的自定義輸入組件。 ```htmlmixed <input v-model="searchText"> ``` 等價於 ```htmlmixed <input v-bind:value="searchText" v-on:input="searchText = $event.target.value" > ``` 當用在組件上時,<font color="#f00">`v-model`</font>則會這樣: ```htmlmixed <custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input> ``` 為了讓它正常工作,這個組件內的`<input>`必須: * 將其<font color="#f00">`value`</font>attribute綁定到一個名叫<font color="#f00">`value`</font>的prop上 * 在其<font color="#f00">`input`</font>事件被觸發時,將新的值通過自定義的<font color="#f00">`input`</font>事件拋出 ```javascript Vue.component('custom-input', { props: ['value'], template: '<input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" >' }) ``` 現在<font color="#f00">`v-model`</font>就應該可以在這個組件上完美地工作起來了: ```htmlmixed <custom-input v-model="searchText"></custom-input> ``` ## 組件局部註冊 >[color=#00FF00] 教學影片:https://learning.dcloud.io/#/?vid=13 可以簡單的註冊一個組件而不需要全局註冊 ```htmlmixed <div id="app"> <button-counter></button-counter> <test></test> </div> ``` ```javascript Vue.component("button-counter", { props: ["title"], data: function () { return {}; }, template: "<div><h1>hi...</h1></div>", methods: {} }); new Vue({ el: "#app", data: { message: "Hello Vue.js!" }, components: { test: { template: "<h2>h2...</h2>", methods: {} } } }); ``` :::success **output** <div id="app"><div><h1>hi...</h1></div> <h2>h2...</h2></div> ::: # 第十三節-單文件組件 >[color=#00FF00] 本節教學影片:https://learning.dcloud.io/#/?vid=14 # Vue.js: 計算屬性 Computed >[color=#00FF00] 參考: https://cythilya.github.io/2017/04/15/vue-computed/ <font color="#228b22">計算屬性 Computed</font> 如果在模版內加入太多的邏輯運算,不但顯得雜亂也難以維護。 例如:在顯示預設的字串「Hello World!」之下,再顯示反轉後的字串。 ``` 原始訊息: Hello World! 反轉訊息: !dlroW olleH ``` ```htmlmixed <div id="app"> <p>原始訊息:${ message }</p> <p>反轉訊息:${ message.split('').reverse().join('') }</p> </div> ``` ```javascript var vm = new Vue({ el: '#app', delimiters: ['${', '}'], data: { message: 'Hello World!', }, }); ``` 如程式碼所示,將反轉的邏輯寫在樣版裡面。這看起來似乎把樣版弄得很髒亂,基於凡是要切乾淨,未來才好維護的理由,應該要把邏輯寫在一個可以純粹做計算的地方-計算屬性 (computed)。 改寫成 ```htmlmixed <div id="app"> <p>原始訊息:${ message }</p> <p>反轉訊息:${ reversedMessage }</p> </div> ``` ```javascript var vm = new Vue({ el: '#app', delimiters: ['${', '}'], data: { message: 'Hello World!', }, computed: { reversedMessage: function() { return this.message .split('') .reverse() .join(''); }, }, }); ``` computed 內的 reversedMessage 的 function 內容,將成為 reversedMessage 的 getter method,每當 message 的值有變化時,便重新計算 reversedMessage。 :bulb: ***computed 會將結果暫存起來,當參考到的資料改變時,computed 才會重新計算。*** :::info :paperclip: **delimiters** Vue.js 改變分隔符的符號 Change Custom Delimiters ```javascript Vue.config.delimiters = ['%%%', '%%%']; new Vue({ el: '#app', data: { name: 'WINWU' } }); ``` ```htmlmixed <div id="app"> my name is %%%name%%% </div> ``` 要注意這一改,就是原本有用到 vue 的 {{ }} expression 的地方都要換成 %%%,不能某地方還是維持 {{ }}。 ::: # Vue-resource:Vue.js的ajax >[color=#00FF00] 參考: https://ithelp.ithome.com.tw/articles/10188447 >參考: https://blog.csdn.net/ZZY1078689276/article/details/83657466 vue-resource語法 ```javascript /* 支援 Promise 的優點 */ { this.$http.get('/someUrl') .then((response) => { /* 成功拿到資料,然後... */ .catch((response) => { /* 失敗,發生錯誤,然後...*/ }) .finally(() => { /* 不論成功失敗,都做些事 */ }); } { this.$http.get('/someUrl', [options]).then(successCallback, errorCallback); this.$http.post('/someUrl', [body], [options]).then(successCallback, errorCallback); } ``` POST請求需要在報文體中添加<font color="#f00">`Content-Type: application/json`</font>字樣,但在這要達到該效果,需要這麼寫: ```javascript {emulateJSON:true} ``` 範例 ```javascript //example var posts = { action : "login", account : this.account, password : this.password }; //發送POST請求 this.$http.post('../model/getdata.php', posts, {emulateJSON:true}) .then(function(res){ console.log(res.data); var status = res.data this.loginStatus = "帳號密碼錯誤,登入失敗"; },function(res) { alert('error'); }); ``` # Vue.$refs:Vue 父組件直接操作子組件 > 參考 https://blog.johnsonlu.org/vue-refs/