Object.defineProperty
有缺陷,就是无法监控到对象属性临时添加的属性,必须使用VUE.set($set)
属性去添加,这个方法原理就是重新设定一个Object.defineProperty
监听新属性,所以VUE3.0改为使用proxy去监听,没有这种缺陷数组在添加过程中也是会无法监听的,所以在VUE中写了
push,pop,shift ...
这类直接修改数组方法(因为不修改原数组的方法是会返回新数组的,可以通过监听新数组去触发)的变异方法,通过改写这类方法进行数组值的监听,例:
let push = Array.prototype.push
Array.prototype.push = function(...args) {
// 中间做些类似中间件的代码,获取新增的数据,进行监听
....
push.call(this, ...args)
}
3.v-once只渲染一次
4.v-pre忽略这个元素和它子元素内容的编译
5.v-model修饰符
v-model.lazy,取代change方法,等输入完之后再进行输出
v-model.number,只能输入数字
v-model.trim,收尾空格去除
6.鼠标修饰符
.stop,阻止冒泡
.self,点击事件绑定元素与当前元素一致时才会触发
//stop和self分别什么情况下使用,当子元素不想产生冒泡触发父级事件时使用stop,
//而self则时在很多嵌套元素情况下,只想触发当前元素的时候使用
.prevent,阻止默认事件
.once,事件只绑定一次
.passive,告诉浏览器没有阻止默认事件
//每次触发事件,浏览器都要等线程执行到事件监听器对应的代码时,才能查询代码是否有阻止默认事件这个行为,而这个查询过程会导致在使用scroll,手势,移动等事件出现卡顿,
//添加上这个修饰符后就是直接告诉浏览器没有做这个行为,就不用去查询了
//原生写法:el.addEventer('click', function() {}, {once:true,passive:true})
7.键盘修饰符
.exact,精确键盘事件
//@keyDown.ctrl.exact="fn" 表示只能在单独按ctrl键的时候才能触发
8.watch
new Vue({
el: '#app',
data: {
a: {
b: {
c: 1
}
}
},
watch: {
'a.b.c': {
handler: function(new, old) { console.log() }, //对于多层数据的监听,可以使用字符串+点语法
immediate: true, //当第一次绑定值时就直接监听
deep: true //深层监听,最多监听5层
}
}
//
})
9.v-model在除input元素外的使用
<div id="app">
<child v-model="val"></child>
</div>
const child= {
model: {
// 指定v-model绑定的prop属性,需要和下方props的属性名对应
prop: 'checked',
// 执行内部触发的哪个事件会修改指定的prop
event: 'check'
},
props: ['checked'],
data() {
return {
status: this.checked
}
},
template: `
<div class="kkb-radio" :class="{'checked': status}" @click="changeStatus"></div>
`,
methods: {
changeStatus() {
this.status = !this.status;
this.$emit('check', this.status);
}
}
};
let vm = new Vue({
el: '#app',
data: {
val: true
},
components: {
'kkb-radio': kkbRadio,
'kkb-plane': kkbPlane
},
methods: {
change(status) {
this.val = status;
}
}
});
10.作用域插槽
<div id="app">
<dialog :datas="datas">
<template v-slot:title="data">
<h1>{{data.title}}</h1>
</template>
<template v-slot:default="data">
<ul>
<li v-for="user of data.users">
{{user.username}}
</li>
</ul>
</template>
</dialog>
</div>
<script>
const Dialog = {
props: ['datas'],
template: `
<div class="dialog">
<i class="dialog_close_btn"></i>
<div class="dialog_header">
<slot :title="datas.type" name="title"></slot>
</div>
<div class="dialog_content">
<slot :users="datas.users"></slot>
</div>
</div>
`
};
new Vue({
el: '#app',
data() {
return {
datas: {
type: '学员',
users: [{
id: 1,
username: 'baogege',
gender: '男',
checked: false
},
{
id: 2,
username: 'mt',
gender: '男',
checked: false
},
{
id: 3,
username: 'haigege',
gender: '男',
checked: false
},
{
id: 4,
username: 'zMouse',
gender: '男',
checked: false
},
{
id: 5,
username: 'reci',
gender: '女',
checked: false
},
{
id: 6,
username: 'lisi',
gender: '女',
checked: false
}
]
}
}
},
components: {
'dialog': Dialog
}
});
</script>
11.生命周期
//捕获子孙组件错误
new Vue({
.....
errorCaptured(err, vm, info) {
//err: 错误信息
//vm: 错误的组件对象
//info: 错误类型
}
})
12.动态加载组件,keep-alive组件
<div id="app">
<button @click="goto('InBox')" :class="{'current': currentComponent==='InBox'}">收邮件</button>
<button @click="goto('PostMail')" :class="{'current': currentComponent==='PostMail'}">发邮件</button>
<button @click="goto('RecycleBin')" :class="{'current': currentComponent==='RecycleBin'}">垃圾箱</button>
<hr>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
<script>
const InBox = {
data() {
return {
items: [
'111111',
'22222222222',
'aaaaaaaa',
'3333333'
]
}
},
template: `
<div>
<ul>
<li v-for="item of items">
<input type="checkbox" />
{{item}}
</li>
</ul>
</div>
`,
created() {
console.log('InBox:created');
},
destroyed() {
console.log('InBox:destroyed');
},
activated() { //添加keep-live后新增的生命周期,启用的时候触发
console.log('InBox:activated');
},
deactivated() { //添加keep-live后新增的生命周期,冻结的时候触发
console.log('InBox:deactivated');
}
}
const PostMail = {
template: `
<div>PostMail</div>
`,
created() {
console.log('PostMail:created');
},
destroyed() {
console.log('PostMail:destroyed');
},
activated() { //添加keep-live后新增的生命周期,启用的时候触发
console.log('PostMail:activated');
},
deactivated() { //添加keep-live后新增的生命周期,冻结的时候触发
console.log('PostMail:deactivated');
}
}
const RecycleBin = {
template: `
<div>RecycleBin</div>
`,
created() {
console.log('RecycleBin:created');
},
destroyed() {
console.log('RecycleBin:destroyed');
},
activated() { //添加keep-live后新增的生命周期,启用的时候触发
console.log('RecycleBin:activated');
},
deactivated() { //添加keep-live后新增的生命周期,冻结的时候触发
console.log('RecycleBin:deactivated');
}
}
let app = new Vue({
el: '#app',
data: {
currentComponent: 'InBox'
},
components: {
InBox,
PostMail,
RecycleBin
},
methods: {
goto(target) {
this.currentComponent = target;
}
}
});
</script>
13.自定义组件
new Vue({
el: '#app',
directives: {
'指令名称' : {指令配置}
}
})
### 指令生命周期
- bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
- update : 所在组件更新的时候调用
- componentUpdated : 所在组件更新完成后调用
- unbind : 只调用一次,指令与元素解绑时调用
### 例子
<script>
Vue.directive('drag', {
bind(el, {modifiers,value}) {
let isDragStart = false;
let disX = 0;
let disY = 0;
el.canDrag = value;
el.addEventListener('mousedown', e => {
if (!el.canDrag) return;
disX = e.clientX - el.offsetLeft;
disY = e.clientY - el.offsetTop;
isDragStart = true;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (isDragStart) {
let x = e.clientX - disX;
let y = e.clientY - disY;
if (modifiers.limit) {
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
}
el.style.left = x + 'px';
el.style.top = y + 'px';
}
});
document.addEventListener('mouseup', e => {
isDragStart = false;
});
},
componentUpdated(el, {value}) {
console.log('componentUpdated', value);
el.canDrag = value;
}
});
let app = new Vue({
el: '#app',
data: {
canDrag: false
}
});
</script>
14.路由守卫
### 组件内路由
beforeRouteEnter(to, from, next) { //路由组件进入前
//这个阶段不能访问this,组件未创建
//实在需要访问的话
next(vm => { console.log(vm) })
}
beforeRouteUpdate(to, form, next) {} //当前路由变化,但该组件属于复用时调用
beforeRouteLeave(to, form, next) {} //离开对应路由时调用
### 路由独享守卫
beforeEnter(to, from, next) {} //直接使用在路由中, 如:
const router = new VueRouter({
routes: [
{
path: '/foo',
conponent: Foo,
beforeEnter: (to, from, next) => {}
}
]
})
### 全局路由守卫
router.beforeEach((to, from, next) => {})
router.beforeResolve((to, from, next) => {})
router.beforeEach((to, from) => {}) //导航被确认后调用,就是在整个路由完成最后调用的生命周期
15.route组件的props
//路由中可以定义props属性,值为`true/false`。
//router.js
const router = new VueRouter({
...
routers: [
{
path: '/home/:itemId',
name: 'home',
compoment: Home,
props: true
}
]
})
`使用了props属性后,那么在组件中就不需要使用this.$route.params.itemId进行获取,可以直接在组件的props:['itemId']中获取到`
// child.vue
export default {
...
props:['itemId'],
created() { console.log(this.itemId) }
}
`PS: vue在处理props传值时,如果在组件中的props没有定义所传的属性名,将会保存在this.$attrs中,如:
//parent.vue
<div id="app">
<child a="1" b="2" c="3"></child>
</div>
//child.vue
export default {
props: ['a'],
data() {
return {}
},
created() { console.log(this.$attrs) } //{b:2, c:3}
}
`
`路由props有三种处理模式`
//1.默认处理
//直接将route.params中的数据和组件中props的数据进行合并
//2.对象模式
//有选择的返回props
{
path: '/home/:itemId',
compoment: home,
props: {a:1, b:2}
}
//3.回调函数模式
{
path: '/home/:itemId',
compoment: home,
props: r => ({itemId: Number(r.params.itemId)})
}
16.切换路由时,页面滚动条保持/归顶
//必须使用浏览器前进回退功能(使用js触发前进回退也行)
const router = new VueRouter({
scrollBehavior(to, from, savedPosition) {
if(savedPosition) {
return savedPosition //保存上一页的x、y坐标
} else {
return {x: 0, y: 0}
}
},
routes: [...]
})