Vue系列之『滿足多數(shù)需求的基本使用』

首先創(chuàng)建一個簡單的vue應用

# 全局安裝 vue-cli
$ npm i -g vue-cli
# 創(chuàng)建一個簡單的vue倉庫
$ vue init webpack-simple learnvue
# 安裝依賴
$ cd learnvue
$ npm i
$ npm run dev

class綁定——動態(tài)地切換class

  • 在:class上綁定一個對象
  • 在:class上綁定一個對象名
  • 在:class上綁定一個返回對象的計算屬性
  • 在:class上綁定一個數(shù)組,數(shù)組語法中也可以使用對象語法
  • 三元表達式:根據(jù)條件切換列表中的class

注意:普通class和綁定class可以共存

<template>
  <div id="app">
    {{msg}}
    <!--  1.在在:class上綁定一個對象  -->
    <div :class="{active:isActive}" class="header">
      我是header,我有一個普通class和一個可能存在的class對象
    </div>
    <div :class="{bigger:isBigger,'text-danger':hasError}" class="main">
      我是main,我有一個普通class和一個可能存在的class對象
    </div>

    <!--  在:class上綁定一個對象名  -->
    <div :class="classObj1">
      我是小字,我有背景色
    </div>

    <!-- 在:class上綁定一個返回對象的計算屬性 -->
    <div :class="classObj2">
      我是大字,沒有背景色
    </div>

    <!--在:class上綁定一個數(shù)組,可使用三元表達式,數(shù)組語法中也可以使用對象語法  -->
    <div :class="['text-danger',classObj1]">我始終是紅色字,可能是大字也可能是小字,有或者沒有綠色背景</div>
    <div :class="[classObj2,{'text-danger':hasError}]">我現(xiàn)在是大字且為紅色,沒有背景色</div>
    <div :class="[isActive? '' :'text-danger']">我是紅字</div>

  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      msg: 'Welcome to Your Vue.js App',
      isActive: 0,
      isBigger: '',
      hasError: true,
      classObj1: {
        active: !this.isActive,
        bigger: this.isBigger
      }
    }
  },
  computed: {
    classObj2() {
      return {
        active: this.isActive && this.hasError,
        bigger: !this.isBigger && this.hasError
      }
    }
  }
}
</script>

<style>
.active {
  background: green;
}

.header {
  font-size: 2em;
}

.bigger {
  font-size: 4em;
}

.text-danger {
  color: red;
}

.main {
  font-style: italic;
}
</style>

style綁定——綁定內聯(lián)樣式

  • 看著很像css內聯(lián)樣式,屬性名可用駝峰式或用短橫線分隔,短橫線分隔要用單引號引起來
  • 直接綁定到一個樣式對象
  • 結合計算屬性使用
  • 數(shù)組語法

v-bind:style 使用需要添加瀏覽器引擎前綴的 CSS 屬性時,如 transform,Vue.js 會自動偵測并添加相應的前綴。

<template>
  <div id="app">
    {{msg}}
    <div :style="{color:activeColor,fontSize:fontSize+'px'}">
      hello world
    </div>

    <div :style="styleObj1">
      hello dot
    </div>

    <div :style="styleObj2">
      hello dolby
    </div>

    <div :style="[styleObj1,styleObj2]">
      hello dolby
    </div>

  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      msg: 'Welcome to Your Vue.js App',
      activeColor: 'red',
      fontSize: 12,
      styleObj1: {
        background: 'yellow',
        color: 'pink'
      }
    }
  },
  computed: {
    styleObj2() {
      return {
        fontStyle: 'italic',
        fontWeight: 'bolder'
      }
    }
  }
}
</script>

vue不得不說的生命周期

當一個vue實例被創(chuàng)建時,它向 Vue 的響應式系統(tǒng)中加入了其 data 對象中能找到的所有的屬性。當這些屬性的值發(fā)生改變時,視圖將會產生“響應”,即匹配更新為新的值。

看下圖:

圖源Vue.js教程

一個實例的生命周期分為creat、mount、update、destroy四個階段,它們的分工各有不同:

  • create:創(chuàng)建
  • mount:掛載DOM
  • update:數(shù)據(jù)更新
  • destroy:銷毀(一般為手動清理事件和定時器)

同時在這些過程中也會運行一些叫做生命周期鉤子的函數(shù),這給了用戶在不同階段添加自己的代碼的機會。對應生命周期來看,又有以下8個鉤子:

beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。

看一個例子:

<template>
  <div id="app">
    {{msg}}
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      msg: 'Welcome to Your Vue.js App',
    }
  },
  beforeCreate() {
    console.log('beforeCreate')
  },
  created() {
    console.log('created')
    setTimeout(() => {
      this.msg = 'change msg'
    }, 1000);
  },
  beforeMount() {
    console.log('beforeMount')
  },
  mounted() {
    console.log('mounted')
  },
  beforeUpdate() {
    console.log('beforeUpdate')
  },
  updated() {
    console.log('updated')
  },
  beforeDestroy() {
    console.log('beforeDestroy')
  },
  destroyed() {
    console.log('destroyed')
  },
  methods: {
    clickBtn() {
      alert('hello')
    }
  },
  watch: {
    msg() {
      console.log('hello')
    }
  }
}
</script>

打開localhost:8080,頁面顯示初始msg內容,控制臺打印beforeCreate、created、beforeMount、mounted

大約1s之后,頁面內容發(fā)生變化,緊接著watch生效,控制臺一次打印出hello,beforeUpdate,updated,這里沒有打印出beforeDestroy和destroyed是因為頁面上沒有事件。

v-model

單行文本

v-model 指令用于在表單 <input><textarea> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會根據(jù)控件類型自動選取正確的方法來更新元素,但 v-model 本質上不過是語法糖。它負責監(jiān)聽用戶的輸入事件以更新數(shù)據(jù),并對一些極端場景進行一些特殊處理。

<template>
  <div id="app">
    <input type="text" placeholder="edit me" v-value="hello">
  </div>
</template>

以上代碼的效果是頁面上會出現(xiàn)一個input輸入框且有初始值value

當我們在input中使用v-model用于綁定數(shù)據(jù)時,value值不生效

<template>
  <div id="app">
    <input type="text" placeholder="edit me" v-model="message" value="hello">
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: '',
    }
  },
}
</script>

v-model 會忽略所有表單元素的 value、checked、selected 特性的初始值而總是將 Vue 實例的數(shù)據(jù)作為數(shù)據(jù)來源。如果有需要,我們應在組件的 data 選項中聲明初始值。

<template>
  <div id="app">
    <input type="text" placeholder="edit me" v-model="value">
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: '',
      value: 'init',
    }
  },
}
</script>

現(xiàn)在結合前面學到的看一段代碼:

<template>
  <div id="app">
    <input type="text" placeholder="edit me" v-model="message">
    <button @click="clickBtn">Click me</button>
    <p :style="styleObj">Message is: {{message}}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: '',
      isClick: ''
    }
  },
  computed: {
    styleObj() {
      return {
        color: this.isClick && this.message ? 'red' : '',
        background: this.isClick && this.message ? 'yellow' : ''
      }
    }
  },
  methods: {
    clickBtn() {
      if (this.message) {
        this.isClick = true
      }
    }
  },
}
</script>

我們?yōu)閕nput定義了一個placeholder值為'edit me'并綁定了一個message屬性

首先看input里綁定的message和p中message之間的關系,前面說v-model雙向綁定不過是語法糖,實際上還是單向綁定。
當input中的內容發(fā)生變化,也就是input中綁定的message——來自于data中的message屬性值發(fā)生了變化,p中的message也會隨之改變,所以數(shù)據(jù)改變是單向的從data中的message屬性到頁面上的message屬性變換的過程。所以p中的文本會隨著input中的內容改變而改變。

我們再來看看data中的另一個屬性'isClick',我們給它賦值為一個空的字符串,即布爾值false

繼續(xù)看代碼,在button按鈕上定義了一個單擊事件'clickBtn',在p元素上綁定了一個內聯(lián)樣式'styleObj',我們把樣式對象放在computed中,這樣就會返回計算后的屬性對象。當用戶點擊按鈕時,如果input中有輸入,我們將'isClick'值設為true,反應在頁面上就是輸入不為空的情況下點擊按鈕之后p元素有了黃色背景,字色變?yōu)榧t色。

多行文本
<template>
  <div id="app">
    <span>Multiline message is:</span>
    <p style="white-space: pre-line;">{{ message }}</p>
    <br>
    <textarea v-model="message" placeholder="add multiple lines"></textarea>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: '',
    }
  },
}
</script>

在文本區(qū)域插值 (<textarea></textarea>) 并不會生效,應用 v-model 來代替。

復選框
  • 單個復選框綁定到布爾值
<template>
  <div id="app">
    <input type="checkbox" v-model="checked">
    <label for="checkbox">{{checked}}</label>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      checked: false,
    }
  },
}
</script>
  • 多個復選框綁定到同一數(shù)組
<template>
  <div id="app">
    <input type="checkbox" value="Jack" v-model="checkedArr">
    <label for="jack">Jack</label>
    <input type="checkbox" value="John" v-model="checkedArr">
    <label for="john">John</label>
    <input type="checkbox" value="Mike" v-model="checkedArr">
    <label for="mike">Mike</label>
    <br>
    <span>Checked names: {{ checkedArr }}</span>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      checkedArr: [],
    }
  },
}
</script>

綁定到checkedArr中的值是input中的value

單選按鈕
<template>
  <div id="app">
    <input type="radio" value="Jack" v-model="picked">
    <label for="jack">Jack</label>
    <input type="radio" value="John" v-model="picked">
    <label for="john">John</label>
    <input type="radio" value="Mike" v-model="picked">
    <label for="mike">Mike</label>
    <br>
    <span>picked names: {{ picked }}</span>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      picked: '',
    }
  },
}
</script>
選擇框
  • 單選時
<template>
  <div id="app">
    <select v-model="selected">
      <option disabled value="">請選擇</option>
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
    <span>Selected: {{ selected }}</span>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      selected: '',
    }
  },
}
</script>

如果 v-model 表達式的初始值未能匹配任何選項,<select> 元素將被渲染為“未選中”狀態(tài)。這種情況下iOS 不會觸發(fā) change 事件,這會使用戶無法選擇第一個選項。因此推薦像上面這樣提供一個值為空的禁用選項。

  • 多選時
<template>
  <div id="app">
    <select v-model="selectedArr" multiple >
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
    <span>SelectedArr: {{ selectedArr }}</span>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      selectedArr: [],
    }
  },
}
</script>

用v-for渲染的動態(tài)項

<template>
  <div id="app">
    <select v-model="selected">
      <option v-for="option in options" :value="option.value">{{option.text}}</option>
    </select>
    <span>Selected: {{ selected }}</span>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      selected: 'A',
      options: [
        { value: 'A', text: 'One' },
        { value: 'B', text: 'Two' },
        { value: 'C', text: 'Three' },
      ]
    }
  }
}

computed

接下來我們來說一個前面反復提到的計算屬性對象computed,計算屬性可以應用于各種復雜的邏輯,使代碼更加輕便容易維護。
語法:computed: {xxx: function () { return /* */}}

<template>
  <div id="app">
    <p>Original message: "{{ message }}"</p>
    <p>Computed reversed message: "{{ reversedMessage }}"</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: 'hello'
    }
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  },
}

可以像綁定普通屬性一樣在模板中綁定計算屬性。Vue 知道 reversedMessage 依賴于message,因此當 message 發(fā)生改變時,所有依賴 reversedMessage 的綁定也會更新。而且最妙的是用計算屬性沒有副作用,更易于測試和理解。

methods

看了上面computed的例子你會發(fā)現(xiàn),我們在methods中使用方法可達到同樣的效果:

<template>
  <div id="app">
    <p>Original message: "{{ message }}"</p>
    <p>Methods reversed message: "{{ reversedMessage() }}"</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      message: 'hello'
    }
  },
  methods: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  },
}

我們可以將同一函數(shù)定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。不同的是計算屬性是基于它們的依賴進行緩存的,只有在它的相關依賴發(fā)生改變時才會重新求值。這就意味著只要 message 還沒有發(fā)生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結果,而不必再次執(zhí)行函數(shù)。

這也同樣意味著下面的計算屬性將不再更新,因為 Date.now() 不是響應式依賴:

<template>
  <div id="app">
    <p>{{ now }}</p>
    <p>{{ now }}</p>
    <p>{{ now }}</p>
    <p>{{ now }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  computed: {
    now() {
      return Date.now()
    }
  },
}

相比之下,每當觸發(fā)重新渲染時,調用方法總會再次執(zhí)行函數(shù)。

<template>
  <div id="app">
    <p>{{ now() }}</p>
    <p>{{ now() }}</p>
    <p>{{ now() }}</p>
    <p>{{ now() }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  methods: {
    now() {
      return Date.now()
    }
  },
}

我們?yōu)槭裁葱枰彺??假設我們有一個性能開銷比較大的的計算屬性 A,它需要遍歷一個巨大的數(shù)組并做大量的計算。然后我們可能有其他的計算屬性依賴于 A 。如果沒有緩存,我們將不可避免的多次執(zhí)行 A 的 getter函數(shù),如果你不希望有緩存,請用方法來替代。

前面說過了computed計算屬性與methods方法的比較,現(xiàn)在說說計算屬性和偵聽屬性watch的比較。

<template>
  <div id="app">
    {{ fullName }}
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      firstName: 'Foo',
      lastName: 'Bar',
      fullName: 'Foo Bar'
    }
  },
  watch: {
    firstName: function(val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function(val) {
      this.fullName = this.firstName + ' ' + val
    }
  },
}

以上做法是命令式的watch回調,而且代碼重復,也沒辦法做到響應式,沒有任何意義??碿omputed如何實現(xiàn)

<template>
  <div id="app">
    {{ fullName }}
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      firstName: 'Foo',
      lastName: 'Bar',
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  },
}

代碼簡潔直觀,且當firstName或lastName改變時,fullName也會隨之改變并反映到頁面上。
雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的偵聽器,于是就有了watch。

watch

當需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時,watch是最有用響應數(shù)據(jù)變化的方法,一般用于監(jiān)聽input等有輸入操作的場景。

<template>
  <div id="app">
    {{msg}}
    <input v-model="question">
    <div>{{answer}}</div>
  </div>
</template>

<script>
export default {
  name: 'app',
  created() {
    console.log('')
    setTimeout(() => {
      this.msg = '12121212'
    }, 1000);
  },
  data() {
    return {
      msg: 'Welcome to Your Vue.js App',
      question: '',
      answer: 'I cannot give you an answei until you ask a question'
    }
  },
  watch: {
    question() {
      this.answer = 'Waiting for you to stop typing...'
    },
    msg() {
      alert('hello')
    }
  }
}
</script>

以上代碼在http://localhost:8080/中打開后,頁面正常顯示數(shù)據(jù),接著先彈出hello

點擊確定后msg值變?yōu)閟etTimeout中的12121212

在輸入框中輸入任意內容,answer值變?yōu)槲覀冊O定的字符串

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容