# 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>
```