# Vue基礎知識
[TOC]
### computed屬性
#### **為什么要使用計算屬性**
模板內的表達式非常便利,但是設計它們的初衷是用于簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護,對于任何復雜邏輯,你都應當使用**計算屬性**。
計算屬性是根據data中已有的屬性,計算得到一個新的屬性,創建一個計算屬性要通過computed對象來創建
```html
<div id='app'>
<p>{{ hi }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message1: 'hello',
message2: 'world'
},
computed: {
// hi 就是一個計算屬性,hi雖然是一個函數形式,但是在使用的時候,是通過屬性的方式使用,如果通過函數調用方式的使用話,會報這個 計算屬性不是一個函數。
hi() {
return this.message1 + this.message2;
}
}
})
</script>
```
#### computed屬性 vs `methods`方法
我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是**計算屬性是基于它們的依賴進行緩存的**,而方法每當觸發重新渲染時,調用方法將**總會**再次執行函數,不會依賴緩存。
#### 計算屬性 vs 偵聽屬性 `watch`
Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變動:**偵聽屬性**。當你有一些數據需要隨著其它數據變動而變動時,你很容易濫用 `watch`然而,通常更好的做法是使用計算屬性而不是使用 `watch`屬性。
`watch`屬性用來偵聽data屬性中的數據,只要被偵聽的數據發生變化,就能觸發相應的函數,被觸發的函數,就是需要偵聽data數據中的屬性命名的函數,這個函數包含兩個參數一個是新值,一個是舊值。
```html
<body>
<div id='app'>
<h3>computed</h3>
姓: <input type="text" v-model="firstName"> 名: <input type="text" v-model="secondName">
<br>
<p> 全名:{{ hi }}</p>
<h3>watch</h3>
<p> 全名:{{ fullName }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: '',
secondName: '',
fullName: ''
},
computed: {
hi() {
return this.firstName + this.secondName;
}
},
watch: {
// firstName函數名稱就是data屬性中firstName屬性名
// newVal 新值 oldVal 舊值
firstName(newVal, oldVal) {
// console.log(newVal, oldVal);
this.fullName = newVal + this.secondName;
},
// 上同
secondName: function(newVal, oldVal) {
this.fullName = this.firstName + newVal;
}
}
})
</script>
</body>
```
兩種方式的最終結果確實是完全相同的,但是什么時候使用計算屬性,什么時候使用watch屬性呢?
> 能夠使用computed屬性實現的結果,就使用computed屬性,不要使用`watch`,因為`watch`是一直在偵聽數據的變化,對內存性能開銷比較大,而**計算屬性是基于它們的依賴進行緩存的**,所以從性能上比watch開銷要小很多。
那什么時候使用`watch`屬性呢?
> 當需要執行異步操作的時候,就要使用watch屬性,這個是computed屬性無法做到的。
#### 深度偵聽
> 如果是要偵聽data中的一個對象的屬性,那么這時候就不能使用普通的(淺)偵聽模式了,需要進行深度偵聽
```html
<body>
<div id='app'>
<h3>深度watch</h3>
姓名:<input type="text" v-model="userInfo.name">
<!-- <p> 用戶姓名為:{{ userInfo.name }}</p> -->
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
userInfo: {
name: 'xz'
}
},
watch: {
// 如果是要偵聽data中的一個對象的屬性,那么這時候以下寫法不能被執行,需要進行深度偵聽。
// userInfo(newVal, oldVal) {
// console.log(newVal, oldVal);
// },
// 深度偵聽
// 被深度偵聽data屬性中的對象
userInfo: {
// 深度偵聽的處理函數
handler(val, oldVal) {
console.log(val.name);
},
// 深度偵聽開啟
deep: true
}
}
})
</script>
</body>
```
### axios(愛咳嗽絲)
### 組件
#### 組件的創建方式
```html
<body>
<!-- 模板的要定義在vue的view視圖之外 -->
<template id="tmplt">
<div>
我是componentD
</div>
</template>
<div id='app'>
<!-- 采用駝峰命名創建組件,在使用組件的時候,要加上短橫杠 - 。 -->
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
<component-d></component-d>
</div>
<script>
// 組建的創建方式:共三種
// 第一種(使用extend和component共同創建組件)
var componentOne = Vue.extend({
// 注意這里的模板template中只能有一個根節點,不能存在多個根節點。
template: "<div>我是componentA<p>我是p標簽中的內容</p></div>"
// 以下是錯誤的
// template: "<div>我是componentA</div><p>我是p標簽中的內容</p>"
});
// 采用駝峰命名創建組件,在使用組件的時候,要加上短橫杠 - 。
Vue.component('componentA', componentOne);
// 等價于
Vue.component('componentB', Vue.extend({
template: "<div>我是componentB</div>"
}));
// 第二種方式(直接使用component創建,通過這里可以看出,Vue.component方法的本質上還是調用了Vue.extend方法了)
Vue.component('componentC', {
template: "<div>我是componentC</div>"
});
// 第三種方式(使用指定的模板創建, 模板的要定義在vue的view視圖之外)
Vue.component('componentD', {
template: '#tmplt'
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```
#### 父子組件的創建
```html
<body>
<div id='app'>
<father></father>
</div>
<script>
Vue.component('father', {
template: `
<div>
<div>我是父組件</div>
<son></son>
</div>
`,
components: {
son: {
template: `
<div>我是子組件</div>
`
}
}
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```
#### 父組件向子組件傳值
```html
<body>
<div id='app'>
<father></father>
</div>
<script>
Vue.component('father', {
template: `
<div>
<div>我是父組件,我的兒子叫{{mySonName}}</div>
<son :myName="mySonName"></son>
</div>
`,
data() {
return {
mySonName: '小宏'
}
},
components: {
son: {
props: ['myName'], // props 用來接收從父組件傳遞過來的值
template: `
<div>我是子組件,我叫{{myName}}</div>
`
}
}
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```
#### 子組件向父組件傳值
```html
<body>
<div id='app'>
<father></father>
</div>
<script>
Vue.component('father', {
template: `
<div>
<div>我是父組件,我的兒子叫{{mySonName}}</div>
<son @sendFatherName="getSonName"></son>
</div>
`,
data() {
return {
mySonName: ''
}
},
methods: {
getSonName(val) {
console.log(val);
this.mySonName = val;
}
},
components: {
son: {
template: `
<div>
<div>我是子組件,我的名字叫{{myName}}</div>
<button @click="sendData">點擊把我的名字告訴給父組件</button>
</div>
`,
data() {
return {
myName: '小明'
}
},
methods: {
// 子組件向父組件傳值,需要通過事件向父組件傳遞值,在事件中需要用到$emit()方法,這個方法有兩個參數,第一個參數是事件的名稱,第二個參數是需要傳遞的數據。
sendData() {
this.$emit('sendFatherName', this.myName)
}
}
}
}
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```
#### 兄弟組件之間的傳值
```html
<body>
<div id='app'>
<father></father>
</div>
<script>
// 創建一個事件總線(相當于中間人)
var bus = new Vue();
Vue.component('father', {
template: `
<div>
<son></son>
<brother></brother>
</div>
`,
components: {
son: {
template: `
<div>
我的兄弟叫{{ myBrotherName }}
</div>
`,
data() {
return {
myBrotherName: ''
}
},
// 頁面加載完成后
mounted() {
// 通過事件總線中的$on()方法監聽兄弟組件傳遞過來的事件和值
bus.$on('sendBrotherName', val => {
// console.log(val);
this.myBrotherName = val;
})
}
},
brother: {
template: `
<div>
我叫{{ myName }}
<button @click="snedMyName">點擊告訴我哥哥我的名字</button>
</div>
`,
data() {
return {
myName: '小明'
}
},
methods: {
snedMyName() {
// 通過事件總線,觸發$emit()方法,兄弟組件之間可以傳遞值
bus.$emit('sendBrotherName', this.myName);
}
}
}
}
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
```
#### 動態創建組件
```html
<body>
<div id='app'>
<ul>
<li @click="active='index'"><a href="#">首頁</a></li>
<li @click="active='fe'"><a href="#">前端</a></li>
<li @click="active='back'"><a href="#">后端</a></li>
<li @click="active='ui'"><a href="#">UI</a></li>
</ul>
<!-- 通過component 動態創建組件 -->
<component :is="active"></component>
</div>
<script>
Vue.component('index', {
template: `
<div>
我是首頁
</div>
`
});
Vue.component('fe', {
template: `
<div>
我是前端
</div>
`
});
Vue.component('back', {
template: `
<div>
我是后端
</div>
`
});
Vue.component('ui', {
template: `
<div>
我是UI
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
active: ''
},
})
</script>
</body>
```
### 路由
#### 路由和路由參數
```html
<body>
<div id='app'>
<div>
<ul>
<li>
<router-link to="/index">首頁</router-link>
</li>
<li>
<router-link to="/fe">前端</router-link>
</li>
<li>
<router-link to="/back/90">后端</router-link>
</li>
</ul>
</div>
<!-- 4、使用 router-view組件 把路由對應的組件進行渲染 -->
<router-view></router-view>
</div>
<script>
// 1、定義(路由)組件(準備路由需要的組件)
var index = Vue.component('index', {
template: `
<div>
首頁
</div>
`
});
var fe = {
template: `<div>前端</div>`
}
var back = {
template: `<div>后端{{$route.params.id}}</div>`,
mounted() {
console.log(this.$route.params.id); // => 90
}
};
// 2、創建路由對象,在這個對象中會配置路由規則,每個路由會映射(對應)一個組件
var router = new VueRouter({
routes: [{
name: 'index',
path: '/index',
component: index
}, {
name: 'fe',
path: '/fe',
component: fe
}, {
name: 'back',
path: '/back',
component: back
}]
})
console.log(router);
var vm = new Vue({
el: '#app',
data: {
},
// 3、在vue實例中注入路由
router
})
</script>
</body>
```
#### 響應(監聽)路由參數變化
```html
<body>
<div id='app'>
<div>
<ul>
<li>
<router-link to="/index">首頁</router-link>
</li>
<li>
<router-link to="/fe">前端</router-link>
</li>
<li>
<router-link to="/back/90">后端</router-link>
</li>
<li>
<router-link to="/back/100">后端</router-link>
</li>
</ul>
</div>
<!-- 4、使用路由把路由組件渲染出來 -->
<router-view></router-view>
</div>
<script>
// 1、定義路由組件
var index = Vue.component('index', {
template: `
<div>首頁</div>
`
});
var fe = {
template: `<div>前端</div>`
};
var back = {
template: `<div>后端{{$route.params.id}} -- {{ info }}</div>`,
// mounted() {
// console.log(this.$route.params.id); // => 90
// },
data() {
return {
info: ''
}
},
// 當使用路由參數時,例如從 /user/foo 導航到 /user/bar,原來的組件實例會被復用。
// 因為兩個路由都渲染同個組件,比起銷毀再創建,復用則顯得更加高效。不過,這也意味著組件的生命周期鉤子不會再被調用。
// 復用組件時, 想對路由參數的變化作出響應的話, 你可以簡單地 watch(監測變化) $route 對象:
watch: {
// 響應路由參數變化
'$route' (to, from) {
// console.log(to);
// console.log(from);
if (to.params.id === '90') {
this.info = 'Docker'
} else if (to.params.id === '100') {
this.info = 'jdbc'
}
}
}
};
// 2、創建路由對象,在路由對象中配置路由規則
var router = new VueRouter({
// 2、1 定義路由
routes: [{
name: 'index',
path: '/index',
component: index
}, {
name: 'fe',
path: '/fe',
component: fe
}, {
name: 'back',
path: '/back/:id',
component: back
}, ]
})
var vm = new Vue({
el: '#app',
data: {
},
// 3、在vue實例中注入路由
router,
})
</script>
</body>
```
#### 嵌套路由和編程式導航
```html
<body>
<div id='app'>
<div>
<ul>
<li>
<router-link to="/index">首頁</router-link>
</li>
<li>
<router-link to="/fe">前端</router-link>
</li>
<li>
<router-link to="/back/90">后端</router-link>
</li>
<li>
<router-link to="/back/100">后端</router-link>
</li>
</ul>
</div>
<!-- 4、使用路由把路由組件渲染出來 -->
<router-view></router-view>
</div>
<script>
// 1、定義路由組件
var index = Vue.component('index', {
template: `
<div>首頁</div>
`
});
var fe = {
template: `<div>前端</div>`
};
var back = {
template: `<div>
<p>后端{{$route.params.id}}</p>
<p>{{ info }}</p>
<button @click="goTo">點擊查看更多技術點</button>
<router-view></router-view>
</div>`,
// mounted() {
// console.log(this.$route.params.id); // => 90
// },
data() {
return {
info: ''
}
},
// 當使用路由參數時,例如從 /user/foo 導航到 /user/bar,原來的組件實例會被復用。
// 因為兩個路由都渲染同個組件,比起銷毀再創建,復用則顯得更加高效。不過,這也意味著組件的生命周期鉤子不會再被調用。
// 復用組件時, 想對路由參數的變化作出響應的話, 你可以簡單地 watch(監測變化) $route 對象:
watch: {
// 響應路由參數變化
'$route' (to, from) {
// console.log(to);
// console.log(from);
if (to.params.id === '90') {
this.info = 'Docker'
} else if (to.params.id === '100') {
this.info = 'jdbc'
}
}
},
methods: {
goTo() {
this.$router.push({
name: 'skill'
})
console.log(this.$router);
}
}
};
var skill = {
template: `
<div> 這里有很多的技術點哈</div>
`
};
// 2、創建路由對象,在路由對象中配置路由規則
var router = new VueRouter({
// 2、1 定義路由
routes: [{
name: 'index',
path: '/index',
component: index
}, {
name: 'fe',
path: '/fe',
component: fe
}, {
name: 'back',
path: '/back/:id',
component: back,
children: [{
name: 'skill',
path: 'skill',
component: skill
}]
}, ]
})
var vm = new Vue({
el: '#app',
data: {
},
// 3、在vue實例中注入路由
// router: router, // 等價于下面
router,
})
</script>
</body>
```
### vue項目
## 將原生事件綁定到組件
你可能有很多次想要在一個組件的根元素上直接監聽一個原生事件。這時,你可以使用 `v-on` 的 `.native` 修飾符:
```vue
<base-input v-on:focus.native="onFocus"></base-input>
```
```html
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="logOUt">登出</el-dropdown-item>
</el-dropdown-menu>
```