一、安裝與項(xiàng)目初始化
安裝 @vue/cli:$ npm install -g @vue/cli
安裝 git:sudo apt-get install git
創(chuàng)建項(xiàng)目:$ vue create todo --default
項(xiàng)目結(jié)構(gòu)
$ tree todo -I node_modules
todo
├── babel.config.js
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ └── index.html
├── README.md
└── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ └── HelloWorld.vue
└── main.js
| 文件 | 功能 |
|---|---|
public/index.html |
瀏覽器加載的 HTML 文件。其中包含用于顯示 Vue 應(yīng)用的標(biāo)簽和加載應(yīng)用文件的 <script>
|
src/main.js |
負(fù)責(zé) Vue 應(yīng)用的基本配置,通常還用于注冊應(yīng)用依賴的第三方組件 |
src/App.vue |
Vue 組件,即 Vue 應(yīng)用的主要構(gòu)成部分。如需要顯示給用戶的 HTML 頁面、Javascript 代碼和 CSS 等 |
public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>todo</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
src/main.js:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
App.vue:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</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>
開啟測試服務(wù)器:$ npm run serve

二、數(shù)據(jù)綁定與指示器
準(zhǔn)備工作,添加 CSS 庫:
$ npm install bootstrap@4.0.0
修改 src/main.js 導(dǎo)入 Bootstrap:
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.min.css";
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
1. 數(shù)據(jù)綁定
展示數(shù)據(jù)給用戶是 web 組件最重要的工作之一,數(shù)據(jù)綁定可以將 <script> 下定義的數(shù)據(jù)對象關(guān)聯(lián)給 <template> 中的特定元素進(jìn)行顯示。
顯示數(shù)據(jù)
修改 src/App.vue 如下:
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak"
}
}
}
</script>
通過 JavaScript 模塊中的 data 屬性定義了可以被綁定的數(shù)據(jù)值:
data: function () {
return {
name: "Kayak"
}
}
再借助數(shù)據(jù)綁定機(jī)制,使用 {{ name }} 等語法將 <script> 中定義的數(shù)據(jù)關(guān)聯(lián)到 template 模塊的 HTML 元素中。這種形式的綁定也稱為文本注入(text interpolation binding)。

綁定中使用表達(dá)式
實(shí)際上數(shù)據(jù)綁定不僅僅可以通過預(yù)先定義的變量替換 HTML 元素中的文本,還可以直接嵌入復(fù)雜的表達(dá)式,具體代碼如下:
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
<h3>Price: ${{ (price + (price * (taxRate / 100))).toFixed(2) }}</h3>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak",
price: 275,
taxRate: 12
}
}
}
</script>

Computed Properties
在數(shù)據(jù)綁定中使用過于復(fù)雜的表達(dá)式并不利于代碼的閱讀、維護(hù)和復(fù)用。為了使 template 組件可以足夠簡單,Vue 提供了計(jì)算屬性模塊,可以從 data 屬性提供的數(shù)據(jù)中生成需要的值,從而減少數(shù)據(jù)綁定中復(fù)雜表達(dá)式的使用。
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
<h3>Price: ${{ totalPrice.toFixed(2) }}</h3>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak",
price: 275,
taxRate: 12
}
},
computed: {
totalPrice: function() {
return this.price + (this.price * (this.taxRate / 100));
}
}
}
</script>
關(guān)鍵代碼:
<h3>Price: ${{ totalPrice.toFixed(2) }}</h3>
...
computed: {
totalPrice: function() {
return this.price + (this.price * (this.taxRate / 100));
}
}

Methods
方法相對于計(jì)算屬性則更加靈活,可以定義自己的參數(shù)。
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
<h4>Price: ${{ lowTotalPrice.toFixed(2) }} (Low Rate)</h4>
<h4>Price: ${{ highTotalPrice.toFixed(2) }} (High Rate)</h4>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak",
price: 275,
lowTaxRate: 12,
highTaxRate: 20
}
},
computed: {
lowTotalPrice: function () {
return this.getTotalPrice(this.lowTaxRate);
},
highTotalPrice: function () {
return this.getTotalPrice(this.highTaxRate);
}
},
methods: {
getTotalPrice(taxRate) {
return this.price + (this.price * (taxRate / 100));
}
}
}
</script>

也可以省略掉上面代碼中對計(jì)算屬性的定義,直接在數(shù)據(jù)綁定時(shí)調(diào)用方法。這顯示了各組件之間組織的靈活性,當(dāng)然此舉也會增加 template 中代碼的復(fù)雜度:
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
<h4>Price: ${{ getTotalPrice(lowTaxRate).toFixed(2) }} (Low Rate)</h4>
<h4>Price: ${{ getTotalPrice(highTaxRate).toFixed(2) }} (High Rate)</h4>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak",
price: 275,
lowTaxRate: 12,
highTaxRate: 20
}
},
methods: {
getTotalPrice(taxRate) {
return this.price + (this.price * (taxRate / 100));
}
}
}
</script>

Filters
過濾器是一種在 filter 模塊下定義的函數(shù),可以對需要顯示的表達(dá)式進(jìn)行格式化操作:
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name }}</h3>
<h4>Price: {{ getTotalPrice(lowTaxRate) | currency }} (Low Rate)</h4>
<h4>Price: {{ getTotalPrice(highTaxRate) | currency }} (High Rate)</h4>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Kayak",
price: 275,
lowTaxRate: 12,
highTaxRate: 20
}
},
methods: {
getTotalPrice(taxRate) {
return this.price + (this.price * (taxRate / 100));
}
},
filters: {
currency(value) {
return new Intl.NumberFormat("en-US",
{ style: "currency", currency: "USD" }).format(value);
}
}
}
</script>

復(fù)雜 Filter
// src/App.vue
<template>
<div id="app" class="bg-primary text-white text-center m-2 p-3">
<h3>Product: {{ name | reverse | capitalize }}</h3>
<h4>Price: {{ getTotalPrice(lowTaxRate) | currency(3) }} (Low Rate)</h4>
<h4>Price: {{ getTotalPrice(highTaxRate) | currency }} (High Rate)</h4>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
price: 48.95,
lowTaxRate: 12,
highTaxRate: 20
}
},
methods: {
getTotalPrice(taxRate) {
return this.price + (this.price * (taxRate / 100));
}
},
filters: {
currency(value, places) {
return new Intl.NumberFormat("en-US",
{
style: "currency", currency: "USD",
minimumFractionDigits: places || 2,
maximumFractionDigits: places || 2
}).format(value);
},
capitalize(value) {
return value[0].toUpperCase() + value.slice(1);
},
reverse(value) {
return value.split("").reverse().join("");
}
}
}
</script>

2. Directives
指示器 是 template 中可以為 HTML 元素添加 Vue.js 功能的一些特殊屬性。比如 v-on:click 指示器可以為 <button> 元素添加對鼠標(biāo)單擊事件的監(jiān)聽,v-text 指示器可以設(shè)置某個 HTML 元素的文字內(nèi)容。

有選擇地顯示元素
使用 v-if 指示器控制 HTML 元素的顯示與隱藏:
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<div class="bg-primary text-white m-2 p-3">
<h3>Product: <span v-text="name"></span></h3>
<h4 v-if="showElements">{{ price }}</h4>
</div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
price: 275,
showElements: true
}
},
methods: {
handleClick() {
this.showElements = !this.showElements;
}
},
}
</script>
關(guān)鍵代碼:
<h4 v-if="showElements">{{ price }}</h4>
<button v-on:click="handleClick" class="btn btn-primary">
...
methods: {
handleClick() {
this.showElements = !this.showElements;
}
}


v-if-else
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<div class="bg-primary text-white m-2 p-3">
<h3 v-if="counter % 3 == 0">Product: {{name}}</h3>
<h3 v-else-if="counter % 3 == 1">Price: {{price}}</h3>
<h3 v-else>Category: {{category}}</h3>
</div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
price: 275,
category: "Waterspots",
counter: 0
}
},
methods: {
handleClick() {
this.counter++;
}
},
}
</script>


設(shè)置元素的 Class 屬性

通過 v-bind:class 設(shè)置 HTML 元素的 Class 屬性:
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<div class="bg-primary text-white m-2 p-3">
<h3 v-bind:class="elemClasses">Product: {{name}}</h3>
</div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
highlight: false
}
},
computed: {
elemClasses() {
return this.highlight
? ["bg-light", "text-dark", "display-4"]
: ["bg-dark", "text-light", "p-2"];
}
},
methods: {
handleClick() {
this.highlight = !this.highlight;
}
},
}
</script>


設(shè)置多個屬性
v-bind 指示器可以同時(shí)設(shè)置 HTML 元素的 class、style 等屬性,甚至還可以包括原本不存在的由用戶自行定義的屬性(如下面代碼中的 data-size)。
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<div class="bg-primary text-white m-2 p-3">
<h3 v-bind="attrValues">Product: {{name}}</h3>
</div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
highlight: false
}
},
computed: {
attrValues() {
return {
class: this.highlight ? ["bg-light", "text-dark"] : [],
style: {
border: this.highlight ? "5px solid red" : ""
},
"data-size": this.highlight ? "big" : "small"
}
}
},
methods: {
handleClick() {
this.highlight = !this.highlight;
}
},
}
</script>
<style>
[data-size=big] { font-size: 40pt; }
[data-size=small] { font-size: 20pt; }
</style>


設(shè)置 HTMLElement 屬性(不常用)
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<div class="bg-primary text-white m-2 p-3">
<h3 v-bind:text-content.prop="textContent"></h3>
</div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
name: "Lifejacket",
highlight: false
}
},
computed: {
textContent() {
return this.highlight ? "Highlight!" : `Product: ${this.name}`;
}
},
methods: {
handleClick() {
this.highlight = !this.highlight;
}
},
}
</script>


3. Repeater Directive
Vue.js 中的 v-for 指示器可以用來操作數(shù)組格式的數(shù)據(jù)、生成表格和 Grid 布局等。
遍歷數(shù)組
v-for 指示器可以遍歷數(shù)組結(jié)構(gòu)中的數(shù)據(jù)對象并以循環(huán)的方式綁定給多個 HTML 元素。
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<h2 class="bg-primary text-while text-center p-3">Products</h2>
<table class="table table-sm table-bordered table-striped text-left">
<tr><th>Index</th><th>Name</th><th>Price</th></tr>
<tbody>
<tr v-for="(p, i) in products" v-bind:key="p.name">
<td>{{ i + 1 }}</td>
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
</tr>
</tbody>
</table>
<div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</div>
</template>
<script>
export default {
name: "app",
data: function () {
return {
products: [
{ name: "Kayak", price: 275 },
{ name: "Lifejacket", price: 48.95 },
{ name: "Soccer Ball", price: 19.50 },
]
}
},
methods: {
handleClick() {
this.products.push(this.products.shift());
}
},
}
</script>
關(guān)鍵代碼:
<tr v-for="(p, i) in products" v-bind:key="p.name">
<td>{{ i + 1 }}</td>
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
</tr>

遍歷對象
注意此處遍歷的數(shù)據(jù)結(jié)構(gòu)是 JavaScript 對象而不是上面代碼中的數(shù)組。
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<h2 class="bg-primary text-while text-center p-3">Products</h2>
<table class="table table-sm table-bordered table-striped text-left">
<tr><th>Index</th><th>Key</th><th>Name</th><th>Price</th></tr>
<tbody>
<tr v-for="(p, key, i) in products" v-bind:key="p.name">
<td>{{ i + 1 }}</td>
<td>{{ key }}</td>
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
</tr>
</tbody>
</table>
<div>
<button v-on:click="handleClick" class="btn btn-primary">
Press Me
</button>
</div>
</div>
</template>
<script>
import Vue from "vue";
export default {
name: "app",
data: function () {
return {
products: {
"kayak": { name: "Kayak", price: 275 },
22: { name: "Lifejacket", price: 48.95 },
3: { name: "Soccer Ball", price: 19.50 },
4: { name: "Corner Flags", price: 39.95 }
}
}
},
methods: {
handleClick() {
Vue.set(this.products, 5, { name: "Running Shoes", price: 100 });
}
},
}
</script>
PS:更新表格數(shù)據(jù)應(yīng)使用 Vue.set(),不要使用類似 this.products[index] = xx 這樣的形式。
Vue.set(this.products, 5, { name: "Running Shoes", price: 100 });


v-for 與 Computed Properties
v-for 指示器可以搭配計(jì)算屬性和方法等一起使用,參考(細(xì)品)下面的分頁示例:
// src/App.vue
<template>
<div id="app" class="container-fluid text-center">
<h2 class="bg-primary text-while text-center p-3">Products</h2>
<table class="table table-sm table-bordered table-striped text-left">
<tr><th>Name</th><th>Price</th></tr>
<tbody>
<tr v-for="p in pageItems" v-bind:key="p.name">
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
</tr>
</tbody>
</table>
<div>
<!-- eslint-disable-next-line vue/require-v-for-key -->
<button v-for="i in pageCount" v-on:click="selectPage(i)"
class="btn btn-secondary m-1"
v-bind:class="{'bg-primary': currentPage == i}">
{{ i }}
</button>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
pageSize: 3,
currentPage: 1,
products: [
{ name: "Kayak", price: 275 },
{ name: "Lifejacket", price: 48.95 },
{ name: "Soccer Ball", price: 19.50 },
{ name: "Corner Flags", price: 39.95 },
{ name: "Stadium", price: 79500 },
{ name: "Thinking Cap", price: 16 },
{ name: "Unsteady Chair", price: 29.95 },
{ name: "Human Chess Board", price: 75 },
{ name: "Bling Bling King", price: 1200 }
]
}
},
computed: {
pageCount() {
return Math.ceil(this.products.length / this.pageSize);
},
pageItems() {
let start = (this.currentPage - 1) * this.pageSize;
return this.products.slice(start, start + this.pageSize);
}
},
methods: {
selectPage(page) {
this.currentPage = page;
}
}
}
</script>

