# VUE ###### tags: `PID` # setup ## CDN(開發版本) ``` <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> ``` ## CDN(生產版本) ``` <script src="https://cdn.jsdelivr.net/npm/vue"></script> ``` # 基本教學 ## 呼叫預設資料內容 ### html ``` <div id="app"> {{ message }} </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) ``` ## 用VUE影響標籤屬性, 可以簡寫成 :title ### html ``` <div id="app-2"> <span v-bind:title="message"> 鼠标悬停几秒钟查看此处动态绑定的提示信息! </span> </div> ``` ### js ``` var app2 = new Vue({ el: '#app-2', data: { message: '页面加载于 ' + new Date().toLocaleString() } }) ``` ## 條件式, 由seen(資料名稱)的布林值控制顯示 ### html ``` <div id="app-3"> <p v-if="seen">现在你看到我了</p> </div> ``` ### js ``` var app3 = new Vue({ el: '#app-3', data: { seen: true // false } }) ``` ## 迴圈 ### html ``` <div id="app-4"> <ol> <li v-for="todo in todos"> {{ todo.text }} </li> </ol> </div> ``` ### js ``` var app4 = new Vue({ el: '#app-4', data: { todos: [ { text: '学习 JavaScript' }, { text: '学习 Vue' }, { text: '整个牛项目' } ] } }) ``` ## DOM事件處理, 可以簡寫成 @click ### html ``` <div id="app-5"> <p>{{ message }}</p> <button v-on:click="reverseMessage">反转消息</button> </div> ``` ### js ``` var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } }) ``` ## 顯示端(html)影響資料顯示 ### html ``` <div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div> ``` ### js ``` var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } }) ``` ## 組建化應用 ### 定義新標籤(組建)-->todo-item ### html ``` <ol> <todo-item></todo-item> </ol> ``` ### js ``` Vue.component('todo-item', { template: '<li>这是个待办项</li>' }) var app7 = new Vue(...) ``` ### 定義新標籤的屬性-->todo ### js ``` Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) ``` ### 利用 v-bind 新增新屬性 ### html ``` <div id="app-7"> <ol> <todo-item v-for="item in groceryList" v-bind:todo="item" v-bind:key="item.id" ></todo-item> </ol> </div> ``` ### 輸入需要循環顯示的資料 ### js ``` Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { id: 0, text: '蔬菜' }, { id: 1, text: '奶酪' }, { id: 2, text: '随便其它什么人吃的东西' } ] } }) ``` ### 結果會等於以下 ### html ``` <div id="app-7"> <ul> <li v-for="todo in groceryList"> {{ todo.text }} </li> </ul> </div> ``` ### js ``` var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { text: '蔬菜' }, { text: '奶酪' }, { text: '随便其它什么人吃的东西' } ] } }) ``` # 數據應用 ## 前置 ### html ``` <div id="app"> {{ a }} {{ b }} </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> ``` ### js ``` // 通常用vm(Vue Model)來代表一個Vue的實例, 可以在Vue裡面撈外面先設定好的資料 var vm = new Vue({ el: '#app', data: data }) ``` ## 在Vue外面先設定好資料 ### js ``` var data = { a: 1 } ``` ## 更改先宣告的資料會影響到Vue裡面的資料 ### js ``` // 也可以先宣告後更改(a: 1-->2) data.a = 2; ``` ## 也可以直接更改Vue的資料 ### js ``` // 或是更改Vue裡面的資料(a: 2-->3) vm.a = 3; ``` ## 但是不能在原有的宣告裡新增來顯示 ### js ``` // 但是不能再加入其他資料(畫面上不會顯示新增的 b 資料, 但是直接更改Vue裡面的可以) vm.b = 1; ``` ## Object.freeze() ### js ``` // Object.freeze(data): 在此函式過後就無法再被更改(指的是外在因素, 例如: v-on) Object.freeze(data); ``` ## Vue內建功能 ### js ``` // Vue 也有許多功能是以 $ 作為開頭, 以區分使用者自定義 // vm.$data: 可以看現在vm裡的data // vm.$el: 可以看Vue運用在哪個位置 // vm.$watch: 查詢資料變化 vm.$watch('a', function(newVal, oldVal){ console.log(newVal,oldVal); }) // console結果 3,4 vm.a = 4; ``` # 生命週期(鉤子) ## vue可以在每個步驟中進行動作, 例如: 監聽, 編譯 ## created: 在一個實例被創建後執行 ### js ``` var app = new Vue({ el: '#app', data: { a: 'hello' }, created: function(){ console.log('a is:' + this.a) } }) ``` ## 不可以用箭頭函示, 因為箭頭函式沒有this的概念 ## 其他的例如: mounted(元素掛載, 有el), updated(DOM更新完成), destroyed(銷毀) ## 也可以在前面加上before, 字首大寫, 不用加ed # Vue語法 ## v-once: 有這個標籤屬性的不會被更改 ### html ``` <div id="app"> <span>{{ a }}</span><br> <span v-once>{{ a }}</span> </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { a: 'hello' }, created: function(){ console.log('a is:' + this.a) } }) app.a = 'change'; ``` ## 如果要透過Vue加入HTML的話, 必須要再需加入的地方加入標籤屬性 v-html ### html ``` <div id="app1"> <p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p> </div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { rawHtml: '<span style="color: red">This should be red</span>' } }) ``` ## html 中的 VUE 可以加入JS的單向運算 ### html ``` <div id="app2"> <span>{{ a + 2 }}</span> <span>{{ ok ? "YES" : "NO" }}</span> <span>{{ message.split('').reverse().join('') }}</span> <div v-bind:id="'list' + id">id = list1</div> </div> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { a: 3, ok: true, message: 'It will be reverse', id: 1 } }) ``` ## 由VUE來控制html的顯示 ### html ``` <div id="app3"> <p>如果v-if裡面的值是true就會看到<span v-if="seen">這句話</span></p> </div> ``` ### js ``` var app3 = new Vue({ el: '#app3', data: { seen: true } }) ``` # 計算和偵聽器 ## Vue可以通過cpmputed 來設定要計算的函示並且return 顯示 ### html ``` <div id="app"> <p>原本的句子 {{ message }}</p> <p>計算後句子 {{ reverseMessage }}</p> </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { message: 'Hello' }, computed:{ reverseMessage: function(){ return this.message.split('').reverse().join('') } } }) ``` ## 也可以用表達式(方法)來達到一樣的效果 ### html ``` <div id="app1"> <p>原本的句子 {{ message }}</p> <p>表達式句子 {{ reverseMessage() }}</p> </div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { message: 'Hello' }, methods: { reverseMessage: function(){ return this.message.split('').reverse().join('') } } }) ``` ## 計算和方法最大的差異在於: 計算式儲存於緩存, 所以不會再更動, 除非有響應式依賴發生變化 ## 實際應用 ### html ``` <div id="app2">{{ fullName }}</div> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) ``` ## computed中每個計算方法都是預設為getter, 但是其實有另一個setter需要自行建立, setter可以設定該計算方法的設定 ### js ``` var app3 = new Vue({ el: '#app3', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: { get: function(){ return this.firstName + ' ' + this.lastName }, set: function (newValue) { var names = newValue.split('') this.firstName = names[0] this.lastName = names[names.length - 1] } } } }) ``` ## watch可以觀察資料的變化 ### html ``` <div id="app4"> <p> ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> ``` ### js ``` var app4 = new Vue({ el: '#app4', data: { question: '', answer: 'Ask some question' }, watch: { question: function (newQ, oldQ) { this.answer = 'Waiting...' } } }) ``` # class&style ## 利用布林值控制是否添加class ### html ``` <div id="app" v-bind:class="{ active: isActive }">this GREEN if isActive is true</div> ``` ### js ``` var app = new Vue({ el: '#app', data: { isActive: true } }) ``` ## 可以對class傳送一個陣列來增加多的類別 ### html ``` <div id="app1" v-bind:class="[active1, active2]">There are two class</div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { active1: 'done', active2: 'box' } }) ``` ## 可以設定style樣式再帶入 ### html ``` <div id="app2" :style="styleObject">TEXT</div> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { styleObject: { color: 'red', fontSize: '13px' } } }) ``` # 條件式 ## v-else可以顯示如果不通過要顯示的 ### html ``` <div id="app"> <h1 v-if="awsome">True it</h1> <h1 v-else>False it</h1> </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { awsome: true } }) ``` ## 也可以利用v-else-if來做更細部的篩選 ### html ``` <div id="app1"> <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>NOT A,B,C</div> </div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { type: 'D' } }) ``` ## 可以用key來獨立重複元素, 避免復用 ## 與v-if相似的是v-show, 兩者差別在v-if只有在true才會運作(善用於不太會一直更改的地方), v-show則是會直接運作(善用於頻繁更改的地方) # 列表 ## v-for 可以做迴圈顯示 ### html ``` <ul id="app"> <li v-for="item in items" :key="item.message"> {{ item.message }} </li> </ul> ``` ### js ``` var app = new Vue({ el: '#app', data: { items: [ {message: 'test1'}, {message: 'test2'} ] } }) ``` ## v-for也有父子層關係 ### html ``` <ul id="app1"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { parentMessage: 'parent', items: [ {message: 'test1'}, {message: 'test2'} ] } }) ``` ## in 可以替換成 of ## 也可以對類別做迴圈 ### html ``` <ul id="app2"> <li v-for="(value, name) in object"> {{ name }} : {{ value }} </li> </ul> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { object: { title: 'How to do lists in Vue', author: 'Jane Done', public: '2020-08-10' } } }) ``` ## 嘗試在工作管理區域打入 app.items.push({message: 'test3'}) ## 函式篩選 ### html ``` <ul id="app3"> <li v-for="n in evenNumbers"> {{ n }} </li> </ul> ``` ### js ``` var app3 = new Vue({ el: '#app3', data: { numbers: [1, 2, 3, 4, 5, 6] }, computed: { evenNumbers: function(){ return this.numbers.filter(function (number){ return number % 2 == 0; }) } } }) ``` ## 此方法與上面相同 ### html ``` <ul id="app4"> <li v-for="set in sets" v-if="set % 2 == 0"> {{ set }} </li> </ul> ``` ### js ``` var app4 = new Vue({ el: '#app4', data: { sets: [1, 2, 3, 4, 5, 6, 7, 8] } }) ``` ## 可以在html中使用template等待js啟用 ### html ``` <ul id="app5"> <template v-for="set in sets"> <li>{{ set }}</li> </template> </ul> ``` ### js ``` var app5 = new Vue({ el: '#app5', data: { sets: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } }) ``` # DOM事件, v-on ## 更改data ### html ``` <div id="app"> <button v-on:click="count+=1">Add 1</button> <p>The button click {{ count }} times</p> </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { count: 0 } }) ``` ## 呼叫函式 ### html ``` <div id="app1"> <button v-on:click="greet">Greet</button> </div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { name: 'Tommy' }, methods: { greet: function (){ alert('Hello ' + this.name) } } }) ``` ## 事件也有修飾 ## .once 事件只會處理一次 ### html ``` div id="app4"> <button v-on:click.once="alert">Just Once</button> </div> ``` ### js ``` var app4 = new Vue({ el: '#app4', methods: { alert: function(){ alert('Just this time') } } }) ``` ## .stop 防止事件冒泡 ### html ``` <div id="app5"> <div @click="outer"> <div @click="middle"> <button @click.stop="inner">click me</button> </div> </div> </div> ``` ### js ``` var app5 = new Vue({ el: '#app5', methods: { outer: function(){ alert('這是外面的div'); }, middle: function(){ alert('這是中間的div'); }, inner: function(){ alert('這是裡面的button'); } } }) ``` ## .prevent 不會發生預設事件 ### html ``` <div id="app6"> <a href="https://tw.yahoo.com/" @click.prevent="changeMsg">Click me</a> <p>{{ msg }}</p> </div> ``` ### js ``` var app6 = new Vue({ el: '#app6', data: { msg: 'Hi' }, methods: { changeMsg: function(){ this.msg = 'Cant go to yahoo'; } } }) ``` ## .capture 冒泡捕捉, 類似反向冒泡, 許要每一層都加上 ### html ``` <div id="app7"> <div @click.capture="outer"> <div @click.capture="middle"> <button @click.capture="inner">click me</button> </div> </div> </div> ``` ### js ``` var app7 = new Vue({ el: '#app7', methods: { outer: function(){ alert('這是外面的div'); }, middle: function(){ alert('這是中間的div'); }, inner: function(){ alert('這是裡面的button'); } } }) ``` ## .self 只會發生在自己的範圍, 不包含子元素 # 表單 ## v-model 可以用表單來控制資料 ### html ``` <div id="app"> <input type="text" v-model="msg"> <p>Message is: {{ msg }}</p> </div> ``` ### js ``` var app = new Vue({ el: '#app', data: { msg: 'Hello' } }) ``` ## 可以利用checkbox控制布林值 ### html ``` <div id="app1"> <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> </div> ``` ### js ``` var app1 = new Vue({ el: '#app1', data: { checked: false } }) ``` ## 利用多個checkbox控制資料 ### html ``` <div id="app2"> <input type="checkbox" id="check1" value="apple" v-model="buyList"> <label for="check1">apple</label> <input type="checkbox" id="check2" value="milk" v-model="buyList"> <label for="check2">milk</label> <input type="checkbox" id="check3" value="egg" v-model="buyList"> <label for="check3">egg</label> <ul> <li v-for="item in buyList">{{ item }}</li> </ul> </div> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { buyList: [] } }) ``` ## 利用單選選項控制資料 ### html ``` <div id="app3"> <input type="radio" id="A" value="A" v-model="checkOne"> <label for="A">A</label> <input type="radio" id="B" value="B" v-model="checkOne"> <label for="B">B</label> <p>Checked: {{ checkOne }}</p> </div> ``` ### js ``` var app3 = new Vue({ el: '#app3', data: { checkOne: '' } }) ``` ## 由下拉式選單控制 ### html ``` <div id="app4"> <select name="" id="" v-model="selected"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <span>SELECT: {{ selected }}</span> </div> ``` ### js ``` var app4 = new Vue({ el: '#app4', data: { selected: '' } }) ``` ## 可以在html中設定預設的布林值轉化 ### html ``` <div id="app5"> <input type="checkbox" v-model="check" true-value="YES" false-value="NO"> <span>{{ check }}</span> </div> ``` ### js ``` var app5 = new Vue({ el: '#app5', data: { check: '' } }) ``` ## 表單的事件綁定也有修飾符, .lazy: 需要點擊非作用區域才會發生 ### html ``` <div id="app6"> <input type="text" v-model.lazy="msg"> <p>{{ msg }}</p> </div> ``` ### js ``` var app6 = new Vue({ el: '#app6', data: { msg: 'Hello' } }) ``` ## .number: 把數字變成字串格式 ### html ``` <div id="app7"> <input type="text" v-model.number="num"> <p>{{ num }}</p> </div> ``` ### js ``` var app7 = new Vue({ el: '#app7', data: { num: 0 } }) ``` ## .trim: 去除首尾的空白字元 ### html ``` <div id="app8"> <input type="text" v-model.trim="num"> <p>{{ num }}</p> </div> ``` ### js ``` var app8 = new Vue({ el: '#app8', data: { num: '' } }) ``` # 組建 ## 初始建立 ### html ``` <div id="app"> <button-counter></button-counter> </div> ``` ### js ``` Vue.component('button-counter', { data: function(){ return { count: 0 } }, template: '<button v-on:click="count++">{{ count }} clicked</button>' }) var app = new Vue({ el: '#app' }) ``` ## 組建可以重複使用, 但是都是獨立的, 資料不會互相影響 ## 組建中的data必須是函式, 除非要組建之間互相影響 ## 可以用props來設定屬性 ### html ``` <div id="app1"> <blog-post title="Hello"></blog-post> <blog-post title="Vue"></blog-post> <blog-post title="Hi"></blog-post> </div> ``` ### js ``` Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' }) var app1 = new Vue({ el: '#app1' }) ``` ## 以下效果與上面一樣 ### html ``` <div id="app2"> <blog-post v-for="post in posts" :key="post.id" :title="post.title"></blog-post> </div> ``` ### js ``` var app2 = new Vue({ el: '#app2', data: { posts: [ {id: 1, title: 'Hello'}, {id: 2, title: 'Vue'}, {id: 3, title: 'Hi'} ] } }) ```