Vue.js起手式+Vue小作品實戰

本文是小羊根據Vue.js文檔進行解讀的第一篇文章,主要內容涵蓋Vue.js的基礎部分的知識的,文章順序基本按照官方文檔的順序,每個知識點現附上代碼,然後根據代碼給予個人的一些理解,最後還放上在線編輯的代碼以供練習和測試之用;

在最後,我參考SegmentFault上的一篇技博,對Vue進行初入的實戰,目的是將新鮮學到的知識立即派上用場;

如果你還是前端的小白,相信這篇文章可能會對產生一些幫助和引起思想的碰撞,因為大家的學習歷程是相似的,遇到的困惑也有一定的共通性,如果文章出現謬誤之處,歡迎各位童鞋及時指正;

目錄

1.Vue.js是什麼

2.Vue.js的基本語法

3.Vue.js的小作品

1. Vue.js是什麼

Vue.js(讀音 /vju?/, 類似於 view) 是一套構建用戶界面的 漸進式框架。與其他重量級框架不同的是Vue 的核心庫只關注視圖層。

Vue.js 的目標是通過儘可能簡單的 API 實現響應的數據綁定組合的視圖組件

Vue.js是一種MVVM框架,其中html是view層,js是model層,通過vue.js(使用v-model這個指令)完成中間的底層邏輯,實現綁定的效果。改變其中的任何一層,另外一層都會改變;

2.Vue的基本語法

2.1 Vue構造函數開啟Vue之旅

通過構造函數Vue創建一個Vue的根實例

<div id=#el></div>n---nvar vm = new Vue({n //optionsn el:#el,n data:{},n methods:{}n})n---n//擴展Vue構造器nvar MyComponent = Vue.extend({n //擴展選項n})nvar vm = new MyComponent({})n

解讀:

  • 使用Vue構造函數創建一個Vue實例,然後通過Vue實例的el介面實現和HTML元素的掛載;
  • 構造函數Vue需要傳入一個選項對象,可包含掛載元素、數據、方法和生命周期鉤子等;
  • 構造函數Vue可以通過extend方法實現擴展,從而可以用預定義的選項創建可復用的組件構造函數,但是構建組件的常用方法是使用Vue.component()介面去實現;

2.2 Vue實例的屬性和方法

Vue實例將代理data對象的所有屬性,也就是說部署在data對象上的所有屬性和方法都將直接成為Vue實例的屬性和方法

<div id="app">{{message}}n <button v-on:click="sayHello">click me</button>n</div>n---nvar app = new Vue({n el:#app,n data:{n message:hello world!,n sayHello:function(){n console.log(1)n }n }n})n---n//如果想要獲取到app這一實例中選項的對象,Vue提供$進行獲取napp.$el === document.getElementById(app)//truenapp.$data.message//hello worldn

【demo】

【TIP】

Vue實例所代理data對象上的屬性只有在實例創建的同時進行初始化才具有響應式更新,若在實例創建之後添加是不會觸發視圖更新的;

2.3數據綁定操作

綁定文本和HTML

<div id = "app">n {{msg}}n <div v-html="hi"></div>n</div>n---nvar app = new Vue({n el: #app,n data:{n msg: hello world!,n hi:<h1>hi</h1>n }n})n

解讀:

  • HTML部分實現數據的動態綁定,這個數據是vue實例的屬性值;
  • JS部分的語法可以從jQuery角度去理解,相當於創建一個Vue實例,這個實例指向#app,並在Vue提供的固定介面data上定義Vue實例的屬性;
  • 使用{{message}}的mustache語法只能將數據解釋為純文本,為了輸出HTML,可以使用v-html指令;

綁定數據在元素的屬性

<div id="app" v-bind:title=message v-bind:stylex=red v-once>n {{message}}n </div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n red: color:redn }n})n

解讀:

  • 定義在Vue實例的data介面上的數據的綁定靈活的,可以綁定在DOM節點內部,也可以綁在屬性上;
  • 綁定數據到節點屬性上時,需要使用v-bind指令,這個元素節點的 title屬性和 Vue 實例的 message屬性綁定到一起,從而建立數據與該屬性值的綁定,也可以使用v-bind:href="url"的縮寫方式:href="url";
  • v-once指令能夠讓你執行一次性的插值,當數據改變時,插值處的內容不會更新;

    【demo】

使用JS表達式處理數據

<div id=#app>n <p v-once>{{num + 10 }}</p>n <p v-if=seen>{{message + jirengu}}</p>n</div>n---nvar app = new Vue({n el: #app,n data:{n num:10,n message: hello world!,n seen:truen }n})n

【demo】

使用過濾器來格式化數據

<div id="app" >n <p v-if=seen>{{message | capitalize}}</p>n </div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n seen:true,n },n filters:{n capitalize:function(value){n if(!value) return n value = value.toString()n return value.charAt(0).toUpperCase() + value.slice(1)n }n }n})n

【demo】

條件指令控制DOM元素的顯示操作

<div id="app" >n <p v-if=seen>{{message}}</p>n </div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n seen:truen }n})n

解讀:

  • v-if指令可以綁定一個屬性值為布爾型的屬性,當值為真時,元素將顯示,反之則消失;

循環指令實現數據的遍歷

<div id="app">n <ol>n <li v-for=item in items>n {{ item.text }}n </li>n </ol>n</div>n---nvar app = new Vue({n el: #app,n data:{n items:[n {text:Vue},n {text:React},n {text:Angular}n ]n }n})n

解讀:

  • v-for可以綁定數組型數據進行綁定,並使用item in items形式,從而數據的遍歷操作;

【demo】

事件綁定指令可以實現事件監聽

<div id=app>n <p>{{message}}</p>n <button v-on:click=reverseMessage>reverseMesssage</button>n</div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!n },n methods:{n reverseMessage:function(){n this.message = this.message.split().reverse().join()n }n }n})n

解讀:

  • v-on指令用於監聽事件操作,click="reverseMessage"定義點擊事件後執行的回調函數;
  • v-on指令也可以採用縮寫方式:@click="method"
  • 在Vue實例中,提供methods介面用於統一定義函數;

【demo】

小結

本章涉及Vue的基礎的數據綁定操作,內容包括:

  • {{message}}實現文本數據的綁定,並且文本數據可以使用JS表達式和過濾器進行進一步處理;

    -v-html="hi"實現HTML數據的綁定;
  • v-bind:href="url"實現屬性數據的綁定;
  • v-if="seen"和v-for="item in items"指令實現流程式控制制;
  • v-on:click="method"指令實現事件監聽

2.4計算屬性

使用計算屬性完成一些數據計算操作

<div id="app" >n <p>Original message : {{message}}</p>n <p>Reversed message : {{ReversedMessage}}</p>n </div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n },n computed:{n ReversedMessage:function(){n return this.message.split().reverse().join()n }n }n})n

解讀:

  • Vue實例提供computed對象,我們可以在對象內部定義需要進行計算的屬性ReverseMessage,而提供的函數將作為屬性的getter,即獲取器;
  • 上述的代碼使得app.ReverseMessage依賴於app.message;
  • 與先前直接在{{message.split().reverse().join() }}使用表達式相比,它讓模板過重並且難以維護代碼;

計算屬性 VS Methods

<div id="app" >n <p>Original message : {{message}}</p>n <p>Reversed message : {{ReversedMessage}}</p>n <p>Reversed message:{{reversedMessage()}}</p> n </div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n },n computed:{n ReversedMessage:function(){n return this.message.split().reverse().join()n }n },n methods:{n reversedMessage:function(){n return this.message.split().reverse().join()n }n }n})n

解讀:

  • 通過Vue實例的methods介面,我們在模板中調用reversedMessage函數同樣實現需求;
  • methods與computed方法的區別在於:computed的數據依賴於app.message,只要message未變,則訪問ReverseMessage計算屬性將立即返回之前的計算結果,而methods則每次重新渲染時總是執行函數;
  • 如果有緩存需要,請使用computed方法,否則使用methods替代;

計算屬性的setter

Vue實例的computed對象默認只有getter,如果你要設置數據,可以提供一個setter,即設置器;

<div id="app" > n <p>Hi,Im{{fullName}}</p>n</div>n---nvar app = new Vue({n el: #app,n data:{n message: hello world!,n name:Terenn },n computed:{n fullName:{n get:function(){n return this.name n },n set:function(value){n this.name = valuen }n }n }n})n

2.5Class與Style的綁定

綁定Class

<div id="app" >n <!-- 直接綁定對象的內容 -->n <p class=static v-bind:class="{active:isActive,error:hasError}">Hello world!</p>n <!-- 綁定對象 -->n <p v-bind:class="classObj">こんにちは </p> n <p v-bind:class=style >你好</p>n <!-- 綁定數組 -->n <p v-bind:class="[staticClass,activeClass,errorClass]">nOlá</p>n <button @click=changeColor>click me</button>n </div>n---n//cssn.static{n width: 200px;n height: 100px;n background: #ccc;n}n.active{n color:red;n}n.error{n font-weight: 800;n}n---nvar app = new Vue({n el: #app,n data:{n isActive:true,n hasError:true,n classObj:{n static:true,n active:true,n error:true,n },n staticClass:static,n activeClass:active,n errorClass:error,nn },n computed:{n style:function(){n return {n active: this.isActive,n static:true,n error:this.hasErrorn }n }n },n methods:{n changeColor:function(){n this.isActive = !this.isActiven }n }n})n

解讀:

  • 通過v-bind:class="{}"或v-bind:class=[]方式為模板綁定class
  • 通過v-bind:class="{active:isActive,error:hasError}"綁定class,首先要在css中設置.active和,error,然後在Vue實例的data對象中設置isActive和hasError的布爾值;也可以直接傳一個對象給class,即v-bind:class="classObj,再在data對象上直接賦值:

data:{n classObj:{n static:true,n active:true,n error:true,n }n

  • 你也可以通過傳遞數組的方式為class賦值v-bind:class="[staticClass,activeClass,errorClass]",此時你要在data對象上為數組的元素的屬性賦值:

data:{n staticClass:static,n activeClass:active,n errorClass:error,n }n

【TIP】無論是哪種方式,前提都是css中的class要先設定

【demo】

綁定style

<div id="app" >n <p v-bind:stylex=styleObj>Hello World!</p>n <p v-bind:stylex=[styleObj,bgObj]>你好</p>n </div>n---nvar app = new Vue({n el: #app,n data:{n styleObj:{n fontWeight:800,n color:redn },n bgObj:{n width:100px,n height:80px,n background:#cccn }n },n})n

解讀:

  • 綁定style到模板的方法有兩種,一是v-bind:stylex="styleObj",然後在data對象上定義styleObj;而是可以通過數組方式為style傳入多個樣式對象

【demo】

2.6條件渲染和列表渲染

前面簡單介紹了一下v-if、v-for和v-on指令,下面的部分將詳細介紹以上3個指令;

條件渲染

<div id="app" >n <p v-if=ok>Hello World!</p>n <p v-else>Hello Universal</p>n <template v-if=motto>n <h1>Steve Jobs</h1>n <p>motto:stay hungry ! stay foolish</p>n </template>n <p v-show=ok>Show Me</p>n </div>n---nvar app = new Vue({n el: #app,n data:{n ok:true,n motto:true,n },n})n

解讀:

  • 通過v-if和v-else指令實現條件渲染,其中v-if="value"的valuey

    要在data對象中賦布爾值,v-if支持<template>語法
  • v-show="value"是另一種條件渲染的方法;

【TIP】 v-if和v-show的區別

  • v-if是真實的條件渲染,當進行條件切換時,它會銷毀和重建條件塊的內容,並且它支持<template>語法;
  • v-show的條件切換時基於css的display屬性,所以不會銷毀和重建條件塊的內容;
  • 當你頻繁需要切換條件時,推薦使用v-show;否則使用v-if;

【demo】

列表渲染

<div id="app" >n <ol>n <li v-for=car in cars>n {{car.name}}n </li>n </ol>n <ul>n <li v-for=(food,index) in foods>n {{index}}---{{food}}---{{delicious}}n </li>n </ul>n <ul>n <li v-for=(value,key,index) in object>n {{index}}.{{key}}.{{value}}n </li>n </ul>n <div>n <span v-for=n in 10 stylex="margin-left:5px">{{n}}</span>n </div>n <span v-for=n in evenNumbers stylex="margin-left:5px">{{n}}</span>n </div> n <!-- <div>n <span v-for=n in odd(counts) stylex="margin-left:5px">{{n}}</span> n </div> -->n </div>n---nvar app = new Vue({n el: #app,n data:{n delicious:delicious,n cars:[n {name:Benz},n {name:BMW}n ],n foods:[n tomato,n potato,n ice creamn ],n object :{n name:Benz,n age:18n },n numbers:[1,2,3,4,5,6,7,8,9,10],n counts:[1,2,3,4,5]n },n computed:{n evenNumbers:function(){n return this.numbers.filter(function(number){n return number%2 === 0n })n }n },n methods:{n odd:function(counts){n return counts.filter(function(count){n return count%2 === 1;n })n }n }n})n

解讀:

  • v-for指令能夠讓我們循環渲染列表型數據,數據放在data對象中,類型可以如下:

data:{n//數字數組n numbers:[1,2,3,4,5,6,7,8,9,10],n counts:[1,2,3,4,5]n//字元串數組n foods:[n tomato,n potato,n ice creamn ],n//對象數組n cars:[n {name:Benz},n {name:BMW}n ],n//對象n object :{n name:Benz,n age:18n },n}n

  • 根據不同類型的數據,v-for指令在模板中具體採用的語法如下:

//數據為數字數組n<div>n <span v-for="n in numbers">{{n}}</span>n</div>n---n//數據為字元數組n<ul>n <ol v-for=food in foods>{{food}}</ol>n</ul>n---n//數據為對象n<ul>n <ol v-for="value in object">{{value}}</ol>n</ul>n//或者n<ul>n <ol v-for="(value,key,index) in object">{{index}}.{{key}}.{{value}}</ol>n</ul>n---n//數據為對象數組n<ul>n <ol v-for="car in cars">{{car.name}}</ol>n</ul>n

  • 在 v-for塊中,我們擁有對父作用域屬性的完全訪問許可權;

【demo】

2.7 事件監聽

簡單的事件監聽——直接在指令上處理數據

<div id="#app">n <p v-on:click="counter+=1">{{counter}}</p>n</div>n---nvar app = new Vue({n el: "#app",n data:{n counter: 0,n }n})n

複雜的事件監聽——在methods對象定義回調函數

<div id="#app">n <p v-on:click="greet">{{vue}</p>n</div>n---nvar app = new Vue({n el: "#app",n data:{n vue:"hello Vue.js"n },nmethods:{n greet:function(event){n console.log(this.vue)n }n }n})n

事件修飾符——調用事件對象函數的快捷方式

<div v-on:click.prevent="greet">1</div>//等價於event.preventDefault()n <div v-on:click.stop="greet">2</div>//等價於event.stopPropagation()n <div v-on:click.capture="greet">3</div>//等價於事件回調函數採用捕獲階段監聽事件n <div v-on:click.self="greet">4</div>//等價於event.targetn

按鍵修飾符——按鍵事件的快捷方式

常見按鍵別名包括:n- entern- tabn- deleten- escn- spacen- upn- downn- leftn- rightn

【demo】

2.8 表單控制項綁定

文本控制項

<div id="app">n <p>{{message}}</p>n <input type="text" v-model=message>n </div>n---nvar app = new Vue({n el:#app,n data:{n message:Hello World!n },n})n

解讀:

  • 通過v-model指令可以實現數據的雙向綁定,即View層的數據變化可以直接改變Model層的數據,而Model層的數據改變也可以直接反映在View層;
  • 上述代碼v-model="message"使得input的value屬性和message屬性綁定,在輸入框輸入值,即改變value同時也改變message;

單選控制項

<input id="man" value="man" type="radio" v-model=picked>n <label for="man">Man</label>n <br>n <input id="woman" value="woman" type="radio" v-model=picked>n <label for="woman">Woman</label>n <div stylex="margin-left:10px">{{picked}}</div>n---nvar app = new Vue({n el:#app,n data:{n message:Hello World!,n picked:mann },n})n

解讀:

  • v-model指令綁定data對象的picked屬性,該屬性默認指向type=radio的input的value;

複選框

<input type="checkbox" id="Benz" v-model=checked value=Benz>n <label for="Benz">Benz</label>n <input type="checkbox" id="BMW" v-model=checked value="BMW">n <label for="BMW">BMW</label>n <div>Checked Name:{{checked}}</div>n---nvar app = new Vue({n el:#app,n data:{n message:Hello World!,n picked:man,n selected:"A",n checked:[],n },n})n

【demo】

2.9 組件

組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素;

通過Vue.component()介面將大型應用拆分為各個組件,從而使代碼更好具有維護性、復用性以及可讀性

註冊組件

<div id="app">n <my-component></my-component>n </div>n---nnVue.component(my-component,{n template:<div>my-first-component</div>n})nnvar app = new Vue({n el:#app,n data:{n }n})n

解讀:

  • 註冊行為必須在創建實例之前;
  • component的template介面定義組件的html元素;

局部註冊組件

<div id="app">n <my-component>n <heading></heading>n </my-component>n </div>n---nVue.component(my-component,{n template:<div>my-first-component</div>n})nnvar Child = {n template: <h3>Hello World</h3>n}nnvar app = new Vue({n el:#app,n components:{n my-component:Childn }n})n

解讀:

  • 可以定義一個子組件,在實例的components介面中將子組件掛載到父組件上,子組件只在父組件的作用域下有效;

特殊DOM模板將會限制組件的渲染

像這些包含固定樣式的元素<ul>, <ol>, <table>, <select>,

自定義組件中使用這些受限制的元素時會導致渲染失敗;

通的方案是使用特殊的 is屬性:

<table>n <tr is="my-component">n </table>n

創建組件的data對象必須是函數

<counter></counter>n <counter></counter>n <counter></counter>n---nVue.component(counter,{n template:<button @click="count+=1">{{count}}</button>,n data:function(){n return {n count: 0n }n }n})n

解讀:

  • 在組件當中定義的數據count必須以函數的形式返回;

使用Props實現父組件向子組件傳遞數據

<child some-text=hello></child>n <br>n <child v-bind:some-text=message> </child>n---nVue.component(child,{n template:<div>{{someText}}</div>,n props:[someText]n})nvar app = new Vue({n el:#app,n components:{n my-component:Childn },n data:{n message:"你好"n }n})n

解讀:

  • 組件實例的作用域是孤立的。這意味著不能並且不應該在子組件的模板內直接引用父組件的數據。可以使用 props 把數據傳給子組件;

  • 可以用 v-bind動態綁定 props 的值到父組件的數據中。每當父組件的數據變化時,該變化也會傳導給子組件,注意這種綁定方式是單向綁定;

【demo】

父組件是使用 props 傳遞數據給子組件,但如果子組件要把數據傳遞迴去則使用自定義事件!

<div id="app">n <p>{{total}}</p>n <button-counter v-on:increment=incrementTotal></button-counter>n <button-counter @increment=incrementTotal></button-counter>n </div>n---nVue.component(button-counter,{n template:<button v-on:click="increment">{{counter}}</button>,n data:function(){n return {n counter:0n }n },n methods:{n increment:function(){n this.counter +=1;n this.$emit(increment)n }n }n})nnvar app = new Vue({n el:#app,n data:{n total:0n },n methods:{n incrementTotal:function(){n this.total += 1;n }n }n})n

解讀:

  • 父組件可以通過監聽子組件的自定義事件,從而改變父組件的數據;
  • 子組件每點擊一次,觸發increment函數,該函數在執行過程中通過$emit(increment)發出increment事件;
  • button控制項同時監聽increment事件,每次發出該事件就改變父組件的total值;

    【demo】

使用Slots分發內容

內容分發指的是混合父組件的內容與子組件自己的模板;

<div id="app">n <h1>Im the parent title</h1>n <my-component>n <p>This is some original content</p>n <p>This is some more original content</p>n </my-component>n <hr>n </div>n---nVue.component(my-component,{n template:"<div><h2>Im the child title</h2><slot>如果沒有分發內容則顯示我。</slot></div>"n})nvar app = new Vue({n el:#app,n data:{n }.n})n

解讀:

  • 如果子組件模板一個<slot>都不包含,則父組件內容將會被丟棄;
  • 當子組件模板只有一個沒有屬性的 slot 時,父組件整個內容片段將插入到 slot 所在的 DOM 位置,並替換掉 slot 標籤本身;
  • 只有在宿主元素為空,且沒有要插入的內容時才顯示備用內容;

//子組件app-layout模板n<div class="container">n<header>n<slot name="header"></slot>n</header>n<main>n<slot></slot>n</main>n<footer>n<slot name="footer"></slot>n</footer>n</div>n//父組件模板n<app-layout>n<h1 slot="header">Here might be a page title</h1>nn<p>A paragraph for the main content.</p>n<p>And another one.</p>nn<p slot="footer">Heres some contact info</p>n</app-layout>n//渲染結果n<div class="container">n<header>n<h1>Here might be a page title</h1>n</header>n<main>n<p>A paragraph for the main content.</p>n<p>And another one.</p>n</main>n<footer>n<p>Heres some contact info</p>n</footer>n</div>n

解讀:

  • 具名slot相當於給slot設置標識符,只要在父組件的元素上設置<div slot="name"></div>就可以把該元素插入子組件定義的模板;

【TIP】關於組件的命名規範

  • 當註冊組件(或者 props)時,可以使用 kebab-case ,camelCase ,或 TitleCase

// 在組件定義中ncomponents: {n// 使用 camelCase 形式註冊nkebab-cased-component: { /* ... */ },ncamelCasedComponent: { /* ... */ },nTitleCasedComponent: { /* ... */ }n}n

  • 在 HTML 模版中,請使用 kebab-case 形式:

<kebab-cased-component></kebab-cased-component>n<camel-cased-component></camel-cased-component>n<title-cased-component></title-cased-component>n

  • 為了記憶方便,建議統一使用kebab-case形式;

【demo】

2.10 vue-resource插件

使用vue-rescource實現前後端的通信

在vue實例中新增ready對象,當頁面完成載入時發出請求

new Vue({n el: #app,n ready: function() {n this.$http.get(book.json, function(data) {n this.$set(books, data);n }).error(function(data, status, request) {n console.log(fail + status + "," + request);n })n //post方法:this.$http.post(url,postdata,function callback)n },n data: {n ....n books:n },n .....n

【TIP】

這個$http請求和jquery的ajax還是有點區別,這裡的post的data默認不是以form data的形式,而是request payload。解決起來也很簡單:在vue實例中添加headers欄位:

http: { headers: {Content-Type: application/x-www-form-urlencoded} }n

3. 實戰之Vue小作品

【Hello Vue.js】

上面的Vue小作品是小羊仿照SegmentFault的一篇技博的練手之作,建議各位對照源碼親手練習一次,以便初步熟悉Vue的使用;

【注】版權歸Teren所有,轉載請聯繫作者。

參考資料:

  • Vue.js Docs
  • Vue.js 快速入門

推薦閱讀:

Angular Articles 2017-01
AntV - 我認為這是一個不嚴謹的錯誤
面向未來的前端數據流框架 - dob
遺世獨立的組件——Angular應用中的單組件構建

TAG:Vuejs | 前端框架 |