实例方法
vm.$mount:将实例挂载在指定节点上
let vm = new Vue({
// el:"#app",
template: '<div>{{msg}}</div>',
data() {
return {
msg: {a: 1},
arr: [1,23,3, {c: 5}],
}
}
})
vm.$mount('#app'); // 可以在挂载在页面的任何地方 是替换
vm.$watch:自定义watcher
给同一属性进行多次赋值时,只会生效最后一次 原理:内部有个queueWatcher,它将所有更新对应的watcher进行存储,一定时间后一起更新;相同的watcher只有一个,所以只会更新一次
vm.$watch('msg', (newValue, oldValue) => {
console.log(newValue)
})
vm.msg = 'world'
vm.msg = 123
vm.msg = 456
vm.$options:用户传入的所有属性
vm.$data:实例上的data
- v2.0中,通过defineProperty添加getter和setter重新定义属性在data上,实现响应式;所以,data数据应尽量少的嵌套,影响性能;但是在vue3.0中,通过proxy实现响应式,性能更好
- 数组只能通过七个方法改变时,才能实现响应式:push shift unshift pop slice...
vm.$nextTick:异步触发
- 保证页面渲染完毕后获取最新的dom元素
vm.$set:添加新的数据,对新的数据进行劫持
- 不存在的属性,如果新增的话,不会渲染视图;可以通过$set实现响应式;
- $set原理:如果是对象属性,通过defineProperty添加getter和setter重新定义属性在data上;如果是数组,内部通过splice实现
vm.$delete:删除某个数据
指令
template:无意义标签;不会被渲染,但是可以写指令;key不能添加在上面
v-html、v-text、{{}}
<div id="app">
<p>{{message}}</p> <!-- 输出:<span>通过双括号绑定</span> -->
<p v-html="html"></p> <!-- 输出:html标签在渲染的时候被解析 -->
<p v-text="text"></p> <!-- 输出:<span>html标签在渲染的时候被源码输出</span> -->
</div>
let app = new Vue({
el: "#app",
data: {
message: "<span>通过双括号绑定</span>",
html: "<span>html标签在渲染的时候被解析</span>",
text: "<span>html标签在渲染的时候被源码输出</span>",
}
});
- v-html:值是一个变量,相当于innerHTML;插入的内容会被转为标签,可能导致xss攻击,所有尽量采用可信任内容 会覆盖子元素
- v-text: 等价于{{}}
v-once: 静态节点,只会渲染一次 数据改变不会出现重新渲染
v-if和v-show
-
v-if:控制dom是否存在;内部原理:被编译成render函数,函数中是一个三元表达式;可配合template使用(vue-template-compiler插件将dom编译)

-
v-show: 控制dom的样式;不能用在template上
当用户频繁切换显示隐藏时,使用v-show;当控制是否产生时用v-if;v-if还可以阻止后续逻辑的发生
v-for
循环字符串、对象、数组、数字,如果使用在template上,key不能使用,key必须放在真实的dom元素上
- 尽量避免使用索引作为key,影响性能 原因:如果用index,内容交换时,可能操作两次dom;如果是唯一值,只需要位置交换 解决办法:可以为index增加唯一的标识,如添加前缀,避免渲染冲突
<div v-for='(a, index) in 3' :key='`a_${index}`'>{{a}}</div>
<div v-for='(a, index) in 3' :key='`b_${index}`'>{{a}}</div>
- v-for和v-if不能连用,两者有优先级问题,同时使用会引起冲突;先执行v-for循环,再执行v-if判断是否渲染,有性能问题;解决办法:在元素的外层或里层添加template,使用v-if
// 大致原理如下
arr.map(item => { // 先执行v-for循环,再执行v-if判断
return true ? item : ''
})
// 解决办法:
<div v-for='(a, index) in 3' :key='`d_${index}`' >
<template v-if='index%2'>
<div>{{a}}</div>
</template>
</div>
v-on:动态绑定事件
等同于@;内部为原生事件div.addEventListener;如果添加(),需要手动添加事件源$event
<div @clicl='fn($event)'>事件绑定</div>
v-bind:动态绑定数据,等同于:
指令修饰符:
.stop冒泡 .prevent阻止默认行为 .self当前元素生效 .once只执行一次 .passive 提高滚动事件效率 .lazy失去焦点时更新 .trim去除空格
v-model:双向绑定的语法糖
- 可用于input|textarea|select|checkbox|radio,等同于value+input|checked+change;
- 对于input,监控输入事件,将输入的值赋值给变量;但是,v-model不完全等同于value+input,因为,在输入时,v-model会对输入法进行处理,例如,进行汉字输入时,当输入过程中,选中汉字时,汉字内容才会被填入输入框中
// input
<!-- <input type="text" :value='msg' @input='(e) => msg = e.target.value'> -->
<input type="text" v-model='msg'/>
// select
<select v-model='selected' multiple='true'>
<option value="" disabled>请选择</option>
<option v-for='o in opts' :key='o.value'
:value="o.value">{{o.name}}</option>
</select>
// checkbox
游泳<input type="checkbox" v-model='checked' value='游泳'>
洗澡<input type="checkbox" v-model='checked' value='洗澡'>
睡觉<input type="checkbox" v-model='checked' value='睡觉'>
let vm = new Vue({
el:"#app",
data() {
return {
red: 'back',
s: {fontSize: '20px'},
msg: "q",
opts: [{ name: "a", value: 1 }, { name: 'b',value: 2 }],
selected: [],
checked: ['游泳']
}
},
})
监听属性-watch
let vm = new Vue({
el:"#app",
data() {
return {
msg: {a: '游泳'},
msg2: 'haha',
}
},
watch: {
// 1.函数方式
msg2: function (newVau, oldVal) {
console.log(newVau, oldVal)
},
msg: [{ // 3. 数组方式:如果一个msg有多个handler,可以放在数组中;如果msg为对象类型,只执行第一个handler
handler(newVau, oldVal) {
console.log(newVau, oldVal)
},
deep: true // 2. 对对象深度监控;如果是对象类型,无法获取oldValue;因为引用类型数据指向同一个数据对象
}, {
handler(newVau, oldVal) {
console.log(newVau, oldVal)
},
immdiate: true // 4.在 wacth 里声明msg后,立即执行里面的handler
},
'fn' // 5.调用methods中的方法
]
},
methods: {
fn() {
console.log('watch调用methods中的函数')
}
}
})
setTimeout(() => {
vm.msg.a = 100
// vm.msg = 200
}, 1000);
计算属性-computed
<!-- 全选和反选--点击子,会触发总的get;点击总,触发set-->
<input type="checkbox" v-model='checkAll'>
<input type="checkbox" v-for='checkbox in checks' v-model='checkbox.check'>
let vm = new Vue({
el:"#app",
data() {
return {
checks: [{check: true}, {check: true}, {check: true}]
}
},
computed: {
checkAll: {
get() { // 取值执行get方法
return this.checks.every(item => item.check)
},
set(newVal) { // 计算属性很少用set 只有使用v-model时可能用到 不能用于改自己 会死循环
this.checks.forEach(item => {
item.check = newVal
});
}
}
},
})
computed和watch区别
两者都是watcher
computed不会立马获取值,只有取值时执行;有缓存,如果依赖的结果不变,则不进行更新
watch默认会在内部先执行,算出一个老值,数据变化执行回调
如果计算一个结果 不会使用methods,如果使用methods,每次渲染时都会重新执行,他不具有缓存
过滤器-filter
将元数据格式化显示,而不改变原数据;用于时间格式化、货币符号添加等
{{timer | format('YYYY:MM:DD')}}
// 1.全局过滤器
Vue.filter('format', function(timer, formatter) { // filter中没有this
return moment(timer).format(formatter)
})
let vm = new Vue({
el:"#app",
data() {
return { timer: 123123123123 }
},
filters: { // 2.局部过滤器 }
})
自定义指令-directive
指令主要用于操作dom;分类:全局指令和局部指令
生命周期:
bind:指令绑定到指定元素时执行
insert:指令元素插入父节点时调用
update:被绑定元素所在模板更新时调用,而且无论绑定值是否有变化;通过比较更新前后的绑定值,忽略不必要的模板更新
componentUpdate:被绑定的元素所在模板完成一次更新时调用
unbind:指令与元素解绑时调用
eg:面板显示|隐藏
<div v-click-outside1='hide'>
<input type="text" @focus='show'>
<div v-if='isShow'>
显示面板
</div>
</div>
let vm = new Vue({
el:"#app",
data() {
return { isShow: false }
},
methods: {
show() { this.isShow = true },
hide() { this.isShow = false }
}
})
指令的函数写法
函数写法 = bind + update
directives: {
clickOutside1(el, bindings, vnode) {
// 参数:绑定指令的元素el 相关属性bindings 虚拟dom vnode
document.addEventListener('click', function(e){
if (!el.contains(e.target)) { // el是否包含目标元素--原生
let method = bindings.expression;
vnode.context[method](); // vnode.context为当前vm实例
}
})
console.log(el, bindings, vnode)
},
}
指定的对象写法
directives: {
clickOutside2: {
bind(el, bindings, vnode) {
el.handler = function(e){
if (!el.contains(e.target)) {
let method = bindings.expression;
vnode.context[method]();
}
}
document.addEventListener('click', el.handler)
},
unbind(el) {
document.removeEventListener('click', el.handler)
}
},
}
进入页面时,获取焦点
<input type="text" v-focus>
let vm = new Vue({
el:"#app",
directives: { // 局部指令
focus: {
/* inserted(el, bindings, vnode) { // 当指令元素插入到dom后获取焦点
el.focus()
} */
bind(el, bindings, vnode) { // 当指令绑定到元素上时获取焦点,注意使用nextTick
Vue.nextTick(() => {// 页面更新后获取焦点
el.focus()
})
}
}
}
})
生命周期
beforeCreate:初始化父子关系和内部事件
emit等;此时还没有进行数据观测;可以混入公共逻辑,vue.mixin
Vue.mixin({ // 原理:内部将生命周期函数放在一个数组中,mixin中的生命周期先放在数组中,生命周期被调用时一次执行数组中方法
beforeCreate() {
console.log('初始化前的公共逻辑')
},
})
created:实例创建完成
- 可以访问data、computed、watch、methods上的方法和数据;可进行初始化数据源,但是没有真实挂载元素,无法获取dom
beforeMount:实例挂载dom前
render:渲染元素
mounted:实例挂载在dom上(真实的dom替换老的节点,vm.$el替换el)
- 可获取dom,进行ajax请求
beforeUpdate:响应式数据更新时调用
- 可进行更新前dom访问,移除事件监听
updated:虚拟dom重新渲染后执行
- 不能再次更新数据,否则导致死循环;可执行依赖于DOM的操作
beforeDestroy:实例销毁前调用
- 可进行自定义事件的解绑,取消dom的事件绑定,定时器清理
destroyed:实例销毁后调用
- 实例上说有的东西都被解绑,事件监听被移除,子实例被销毁
注:
- 生命周期中的this指向当前实例;生命周期为同步执行;如果需要所有组件挂载完成执行,使用vm.$nextTick
- ajax在哪发请求? created beforeMount mounted都可以;但是如果操作dom,只能在mounted ;异步请求一定是在mounted后执行,因为生命周期是同步的;服务端渲染不支持mounted,使用created
动画-transition
- 当dom显示或隐藏时,vue可以管理动画,如v-if、v-show
- transition中,如果没指定name,统一v-开头;如果指定name为xx,则以xx-开头
<transition name='fade'>
<div class="box" style='width: 100px;height: 100px;' v-if='isShow'></div>
</transition>
<button @click='change'>点我</button>
let vm = new Vue({
el:"#app",
data() { return { isShow: false } },
methods: {
change() { this.isShow = !this.isShow; },
}
})
.box {
background: red;
}
.fade-enter {/* 进入动画时的样式 */
background: blue;
}
.fade-enter-active {
transition: all 2s linear;
}
.fade-enter-to {/* 进入动画结束时的样式 */
background: yellow;
}
.fade-leave { /*离开动画开始时样式*/
background: purple;
}
.fade-leave-active {
transition: all 2s linear;
}
.fade-leave-to {/*离开动画结束时样式*/
background: blue;
}
- transform-group:动画元素必须添加animated样式;推荐使用animate.css动画库
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<input type="text" v-model='content'>
<transition-group
enter-active-class='animated bounceInLeft'
leave-active-class='animated bounceOutRight'
>
<li class='arrItem' v-for='(arr, index) in computedArr' :key='index'>{{arr}}</li>
</transition-group>
data() {
return {
content: '',
arrs:['abc', 'bsd', 'aab', 'bbc']
}
},
computed: {
computedArr() {
return this.arrs.filter(item => item.includes(this.content))
}
},
.arrItem {
background: blue;
color: #fff;
line-height: 35px;
width: 200px;
}
通过js实现动画,eg:购物车
购物车原理:定义一个动画元素,默认隐藏;当点击某个商品的添加购物车时,将动画元素定位在当前商品上,开始动画,动画元素从被定为商品移动到购物车
<div v-for='(p, index) in products' ref='lists'>
<img :src="p" alt="">
<button @click='addCar(index)'>添加购物车</button>
</div>
<transition @enter='enter' @after-enter='afterEnter'>
<div class="animated" v-if='isShow'></div>
</transition>
<div class="cart" ref='cart'></div>
data() {
return {
isShow: false,
currentIndex: -1,
products: ['https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg', 'https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg']
}
},
methods: {
addCar(index) {
this.isShow = true
this.currentIndex = index
},
enter(el, done) {// 获取当前点击的元素的坐标,将动画元素定位到被点击元素上,设置它的显示样式;获取购物车的位置,设置动画元素的动画样式;动画移除后将动画元素归位
let div = this.$refs.lists[this.currentIndex];
let {x, y} = div.getBoundingClientRect();
el.style.left = x + 'px'
el.style.top = x + 'px';
el.style.background = `url(${this.products[this.currentIndex]})`;
el.style.backgroundSize = '100% 100%';
let {x: cartX, y: cartY} = this.$refs.cart.getBoundingClientRect();
el.style.transform = `translate3d(${cartX - x}px, ${cartY - y}px, 0) scale(0,0)`
el.addEventListener('transitionend', done); // 动画结束后进行重置
},
afterEnter(el){
this.isShow = false
}
}