mvvm
model: 模型
view:视图
viewmodel :与视图对应的model
模型和视图并没有直接关联,而是通过viewmodel来处理的。model和viewmodel是双向绑定的,viewmodel和view也是双向绑定的,所以当model中数据发生变化时,会同步更新view中,反之view中界面数据发生变化时也会更新model。
vue双向绑定原理
采用数据劫持结合发布订阅模式的设计模式,通过Object.definedproperty来劫持数据各个属性的setter、getter,在数据发生变动时
发布消息给订阅者,触发相应的监听回调,从而更新数据。
主要分为3部分:
1. observer监听数据更新,遍历所有对象属性,给每个属性添加getter、setter。当给数据发生改变时就会触发setter方法,就会监听到数据的改变
2. compile 将魔板中的变量替换成数据,将每个节点绑定更新函数,添加监听数据的订阅者,当数据有变动就会收到通知更新视图。
3. watcher 将observer和complie结合起来,当有数据改变时,发布通知给订阅者,订阅者通过执行compile的更新函数来改变视图。
什么是vue
Vue是用于构建用户界面的渐进式框架,Vue的核心库只关注视图层,更易上手。
Vue的指令
v-text 用来将数据填充到标签中
<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>
v-html 类似v-text,将html填充到标签中
v-show 显示或者隐藏元素,相当于css的display: none
v-if / v-else-if / v-else 效果等同于v-show,不同点是v-if条件为真时才会渲染。
v-for 循环渲染。当和v-if一起使用时,v-for优先级更高。常常需要绑定key值。
v-on 绑定事件监听。缩写@
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
/************************************************/
// 在子组件上绑定事件,当子组件调用my-event时触发
<my-component @my-event="handleThis"></my-component>
v-bind 属性绑定
<!-- 绑定一个 attribute -->
<img v-bind:src="imageSrc">
<!-- 动态 attribute 名 (2.6.0+) -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc">
v-model 双向绑定,仅限在input / select / textarea / components中使用
v-pre 跳过这个节点和他的子节点,不需要编译。
v-solt 提供具名插槽
v-once 只渲染一次
自定义指令
Vue.directive
key的作用
给每个项添加一个key,以便跟踪每个节点的变化,让数据更高效的渲染。如果不添加key,那么如果数据顺序发生改变,vue不会移动数据项的顺序,而是就地更新每个元素,确保在每个索引位置正确渲染。
计算属性computed
当模板中不再是简单的声明,需要复杂的表达式时可以用计算属性。
计算属性是基于它们的响应式依赖进行缓存的,只有相关响应式依赖发生改变时,才会重新求值,所以多次访问计算属性时,如果数据没有改变会立马返回值。
<div id="app">
<div>{{ reverse }}</div>
</div>
<script>
new Vue({
el: '#app',
data: {
name: 'zhangsan'
},
computed: {
reverse() {
return this.name.split('').reverse().join('')
}
}
})
</script>
watch
监听数据变化,有两个参数,第一个是新值,第二个是旧值。一般用于异步操作。
<script>
new Vue({
el: '#app',
data: {
name: 'zhangsan',
msg: ''
},
computed: {
reverse() {
return this.name.split('').reverse().join('')
}
},
watch: {
msg: function(newmsg, oldmsg) {
console.log('新值:'+ newmsg)
console.log('旧值:'+ oldmsg)
}
}
})
</script>
过滤器
生命周期钩子函数
beforeCreate 此时data,methods,页面dom都还没有初始化
created data,methods初始化完毕了可以调用了。
beforeMount 创建$el实例替换el之前调用,此时还无法使用dom
mounted $el已成功挂载,可以操作dom了
beforeUpdate 更新数据之前,此时数据还是旧的
updated 页面数据已经更新
beforeDestroy 实例销毁之前调用
destroyed 实例已经销毁
组件基础使用
<div id="app">
<mycomponent></mycomponent>
<child></child>
</div>
<script>
// 全局注册
Vue.component('mycomponent', {
data() {
return {
msg: 'mycomponent'
}
},
template: `<div>{{ msg }}</div>`
})
// 局部注册
var child = {
data() {
return {
msg: 'child-component'
}
},
template: '<div>{{ msg }}</div>'
}
new Vue({
el:'#app',
components: {
child
}
})
</script>
组件传值
父 > 子 传值,在子组件上绑定一个属性,将值传给子组件,子组件通过props 接收这个属性,
<div id="app" >
<child :title='name'></child>
</div>
<script>
// 子组件
var child = {
props: ['title'],
data() {
return {
}
},
template: `<div>{{ title }}</div>`,
}
new Vue({
el:'#app',
data: {
name: '我是父组件的name'
},
components: {
child
}
})
</script>
子 > 父 传值,子组件通过$emit 方法提交一个事件,父组件在子组件上绑定一个事件来监听。
<div id="app" >
<child @childsendevent='receive'></child>
</div>
<script>
// 子组件
var child = {
data() {
return {
name: 'my is child component'
}
},
template: `<div><div>点击向父组件发送信息</div><button @click='cli'>发送</button></div>`,
methods: {
cli() {
this.$emit('childsendevent', this.name)
}
}
}
new Vue({
el:'#app',
data: {
name: '我是父组件的name'
},
components: {
child
},
methods: {
receive(e) {
console.log(e)
}
}
})
</script>
兄弟组件传值
A.最好用的是用vuex统一管理。
B.也可以用eventBus.js 实现。
eventBus.js
import Vue from 'Vue'
export default new Vue()
要传值的组件
EventBus.$emit("editName", this.name) // 触发全局事件,并且把改变后的值传入事件函数
接收的组件
EventBus.$on('editName', (name) => {this.name = name})
C. rovide / inject
export default {
components: {
},
provide() {
return {
reload: this.reload
}
},
data(){
return {
// 通过isRouterAlive结合provide 和 inject来刷新当前路由
isRouterAlive: true
}
},
methods: {
reload() {
this.isRouterAlive = false
this.$nextTick(()=>{
this.isRouterAlive = true
})
}
}
}
// Inject : [‘reload’] 接收数据
插槽slot
作用: 最大的作用就是提高组件的复用性。例如2个页面功能一致名称不一样,一个叫编辑一个叫新增,这个时候可以通过插槽传入不同的名称来调用组件。
<div id="app" >
<child>我就是那个插槽</child-one>
<bus>
<template v-slot:default>
<div>
默认插槽
</div>
</template>
<template v-slot:footer>
<div>
我是footer
</div>
</template>
<template v-slot:header>
<div>
我是header
</div>
</template>
</bus>
</div>
<script>
var eventBus = new Vue()
// 插槽基础用法
var child = {
data() {
return {
name: 'my is child component'
}
},
template: `<div>我是子组件 <slot>我是默认值</slot></div>`,
methods: {
}
}
// 具名插槽
var bus = {
template: `<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div> `
}
new Vue({
el:'#app',
data: {
name: '我是父组件的name'
},
components: {
child,
bus
},
methods: {
}
})
</script>
插槽prop
<div id="app" >
<child>我就是那个插槽</child-one>
<bus>
<template v-slot='user'>
<div>
{{ user.user }}
</div>
</template>
<template v-slot:footer>
<div>
我是footer
</div>
</template>
<!-- 插槽props -->
<template v-slot:header="age" >
<div>
我是header
{{ age.age }}
</div>
</template>
</bus>
</div>
<script>
var eventBus = new Vue()
// 插槽基础用法
var child = {
data() {
return {
name: 'my is child component'
}
},
template: `<div>我是子组件 <slot>我是默认值</slot></div>`,
methods: {
}
}
// 具名插槽
var bus = {
data() {
return {
name: '张三',
age: 23
}
},
// 父组件在插槽中使用子组件数据,
// 1.在子组件中绑定一个属性,传要传出的值
// 2.在父组件使用插槽时,用v-slot绑定一个名称(命名随意)
template: `<div>
<header>
<slot name='header' :age="age"></slot>
</header>
<main>
<slot :user="name"></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div> `
}
new Vue({
el:'#app',
data: {
name: '我是父组件的name'
},
components: {
child,
bus
},
methods: {
}
})
</script>