一、 v-on的修饰符
<body>
<div id='app'>
<div @click='divHandle'>
<button @click.stop='buttonHandle'>按钮</button>
</div>
</div>
</body>
@click.stop => 修饰符.阻止冒泡
@click.prevent => 修饰符.阻止默认事件
@click.prevent.stop => 修饰符.阻止默认事件和冒泡
<script src="js/vue.js"></script>
<script>
new Vue({
el: "#app",
methods: {
buttonHandle(ev) {
// 阻止冒泡
// ev.stopPropagation();
console.log('按钮被点')
},
divHandle() {
console.log('div被点')
}
}
})
</script>
二、 v-model的修饰符
<body>
<div id='app'>
<!-- <input type="text" :value='msg' @input='setMsg'> -->
<!-- <input type="text" v-model='msg'> -->
<hr>
<!-- <input type="text" :value='msg' @change='setMsg'> -->
<input type="text" v-model.lazy.trim.number='msg'>
<div>{{msg}}</div>
</div>
</body>
:value='msg' => 数据变,视图也跟着变
@input='setMsg' => 视图变,数据也跟着变.
v-model实际上是上面两个操作的语法糖.
lazy => change事件触发时才会修改vue数据.(输入完成之后,再修改vue数据msg)
<script>
new Vue({
el: "#app",
data: {msg: ''},
methods: {
setMsg(ev) {
this.msg = ev.target.value
}
}
})
</script>
三、 v-for
<body>
<div id='app'>
<ul>
<li v-for='item in 3'>{{item}}</li>
</ul>
<ul>
<li v-for='item in "abcd"'>{{item}}</li>
</ul>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
})
</script>
四、 v-once
<body>
<div id='app'>
<button @click='fn'>按钮</button>
<div v-once>{{msg}}</div>
<div>{{msg}}</div>
</div>
</body>
v-once => 视图只更新一次,后续绑定的数据变化,不会更新当前的视图
v-once没有指令的值.
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 10
},
methods: {
fn() {
this.msg = Math.random();
}
}
})
</script>
五、 v-html
<body>
<div id='app'>
<div>{{str}}</div>
<div v-text='str'></div>
<div v-html='str'></div>
</div>
</body>
v-html => 可以用于实现插入一个标签. => 它有安全问题.容易手动xss攻击.=> 必须保证内容是安全的
xss => 脚步注入攻击.v-text => 根插值表达式的功能一模一样
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
str: '<p>你好</p>'
}
})
</script>
六、v-pre和v-cloak
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div v-pre>{{msg}}</div>
<div v-cloak>{{strrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr}}</div>
</div>
</body>
v-pre => 让插值表达式不会求值
v-cloak => 为了cdn引入Vue的用户准备的.
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 10000,
strrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr: 20000
}
})
</script>
七、 created
<body>
<div id='app'>
<button @click='fn'>按钮</button>
<div>{{msg1}}</div>
<div>{{msg2}}</div>
<div>{{num}}</div>
<div>{{msg1 + msg2}}</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
let arr = [1,2,3,4,5]
new Vue({
el: '#app',
data: {
msg1: 0,
msg2: 0,
num: 0
},
// 用于给data的数据进行初始化的.
// 程序运行,created会默认触发一次.
// created => 生命周期钩子函数.
created() {
this.msg1 = Math.random();
this.msg2 = Math.random();
this.num = this.msg1 + this.msg2
// for (let i = 0; i < arr.length; i++) {
// this.msg += arr[i]
// }
},
methods: {
fn() {
this.msg1 = Math.random();
this.msg2 = Math.random();
// this.num = this.msg1 + this.msg2;
}
}
})
</script>
八、视图更新发生了什么事情
<body>
<div id='app'>
<button @click='fn'>按钮</button>
<div>{{msg1}}</div>
<div>{{msg2}}</div>
<div>{{msg1 + msg2}}</div>
</div>
</body>
视图更新后,视图内的所有逻辑(插值表达式和指令)都会重新运行和求值.
msg1和msg2变化,会导致视图更新,视图更新,会导致msg1 + msg2重新求值.
<script src="js/vue.js"></script>
<script>
// new Vue({
// el: '#app',
// data: {
// msg1: Math.random(),
// msg2: Math.random(),
// },
// methods: {
// fn() {
// this.msg1 = Math.random();
// this.msg2 = Math.random();
// }
// }
// })
// let [oBtn] = document.querySelectorAll('button');
// let msg1 = Math.random();
// let msg2 = Math.random();
oBtn.onclick = function() {
// 修改数据
msg1 = Math.random();
msg2 = Math.random();
// 修改数据后更新视图
renderView();
}
// function renderView() {
// console.log(msg1 + msg2);
// oDiv1.innText = msg1;
// oDiv2.innText = msg2;
// oDiv3.innText = msg1 + msg2;
// }
</script>
九、视图更新的后的指令
<body>
<div id='app'>
<button @click='fn'>按钮</button>
<div :style='{backgroundColor: msg < 5 ? "red" : "green"}'>1222</div>
<div :style='{backgroundColor: msg > 5 ? "blue" : "yellow"}'>1222</div>
</div>
</body>
视图更新后,视图内的所有逻辑(插值表达式和指令)都会重新运行和求值.
msg变化就会导致视图更新,视图更新会导致 msg < 5 ? "red" : "green"重新运行.
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 0
},
methods: {
fn() {
this.msg = Math.floor(Math.random() * 10) + 1;
}
}
})
// let [oBtn] = document.querySelectorAll('button');
// let msg = Math.random();
// oBtn.onclick = function() {
// 修改数据
// msg = Math.floor(Math.random() * 10) + 1;
// 修改数据后更新视图
// renderView();
// }
// :style='{backgroundColor: msg < 5 ? "red" : "green"}' 的对应DOM操作逻辑
// 视图每次更新,都会运行下面的DOM逻辑.
// function renderView() {
// if (msg < 5) {
// oDiv.style.backgroundColor = 'red'
// } else {
// oDiv.style.backgroundColor = 'green'
// }
// }
</script>
十、高亮
<body>
<div id='app'>
<ul>
<li
v-for='(d, i) in 5'
:style='{backgroundColor: index===i ? "red" : ""}'
@click='toRed(i)' >{{d}}</li>
</ul>
</div>
<ul id='wrap'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</body>
<script src="js/vue.js"></script>
<script>
// index改变 => 视图更新 => 让v-for和v-bind对应的逻辑重新运行.
new Vue({
el: '#app',
data: {
index:2
},
methods: {
toRed(i) {
this.index = i;
}
}
});
</script>
<script src="js/vue.js"></script>
<script>
let aLi = document.querySelectorAll('#wrap>li');
let oUl = document.querySelectorAll('#wrap')[0];
被点击的li的下标。
let index = 0;
aLi.forEach(function(item, i) {
item.onclick = function() {
记录下标
index = i;
根据index的值来渲染视图.
renderView();
}
});
index改变之后的DOM操作逻辑
function renderView() {
for (let i = 0; i < 5; i++) {
if (index === i) {
aLi[i].style.backgroundColor = 'red'
} else {
aLi[i].style.backgroundColor = ''
}
}
}
</script>
十一、视图更新
<body>
<div id='app'>
<div>{{msg}}</div>
<div>{{arr[0]}}</div>
<ul>
<li
v-for='(item, i) in colors'
:style='{backgroundColor: colors[i]}'
@click='toRed(i)'
>1111</li>
</ul>
</div>
</body>
msg => 会导致视图更新 => msg和arr[0]会重新求值
只修改arr[0]视图不更新,但是数据是改变了
后续通过修改msg来驱动视图更新,视图更新又会显示最新的arr[0]的值.
<script src="js/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: 0,
arr: [1,2,3],
colors: ['', '', '']
},
methods: {
toRed(i) {
// 这里for循环内的操作不会导致视图更新.但是数据变了
for (let i = 0; i < this.colors.length; i++) {
this.colors[i] = '';
}
// 这个操作会导致视图更新,也会显示for循环的结果.
this.colors.splice(i, 1, 'red');
}
}
})
</script>
十二、defineProperty
js对象的属性都有4种描述
1:是否可以修改 writable: true
2:是否可以删除 configurable: true
3:是否可以枚举 enumerable: true => 是否可以for in 检测
4:值是什么 value
<script>
const oYm = {
name: '幂幂'
}
// 获取oYm对象的name属性的4中描述
const desc = Object.getOwnPropertyDescriptor(oYm, 'name');
console.log(desc);
// 修改oYm的name属性的描述.
Object.defineProperty(oYm, 'name', {
value: '超越',
enumerable: false
});
</script>
十三、defineProperty
<body>
<div id='app'></div>
</body>
defineProperty是Vue的数据驱动的底层核心方法.
当一个监听的数据发生变化时,可以通过视图更新.(DOM操作)如何监听一个数据,能捕获到这个数据的变化呢?
用defineProperty来设置监听
<script>
const data = { msg: 100 };
const [oDiv] = document.querySelectorAll('div');
let val = data.msg;
// 设置了set和get方法的属性,它的值,只能通过get返回.
Object.defineProperty(data, 'msg', {
// msg被访问时触发
get() {
console.log('msg被访问');
return val
},
// msg被修改时触发(可以在这里做DOM操作)
// 参数就是新修改的值.
set(newVal) {
val = newVal;
console.log('msg被修改');
oDiv.innerText = newVal;
}
});
</script>
十四、模拟vue监听数据
<body>
<div id='app'>
<div>{{str}}</div>
<div>{{msg}}</div>
<div>{{person}}</div>
<div>{{person.name}}</div>
<div>{{arr[0]}}</div>
</div>
</body>
实例化Vue时,Vue会给data内的所有字段(属性)进行数据劫持.
vm.$data => vue的实例监听的data数据
<script src="js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 100,
str: 200,
person: {
name: '幂幂'
},
arr: [1,2,3, {name: '超越'}]
}
});
</script>
const data = {
msg: 100,
str: 200,
}
myVue({
data: data
});
function myVue({ data }) {
// 循环data对象,进行数据监听(劫持).
for (let key in data) {
let val = data[key];
Object.defineProperty(data, key, {
get() {
return val;
},
set(newVal) {
val = newVal;
}
})
}
}
十五、set方法
<body>
<div id='app'>
<button @click='fn'>按钮</button>
<div>{{obj.name}}</div>
</div>
</body>
obj没有name属性,因此实例化时,没办法给这个name属性设置监听.
因此后续对name的修改,视图不会更新.
<script src="js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
obj:{}
},
methods: {
fn() {
// 新增属性name.(不算修改)
// this.obj.name = 1000;
// 如何在新增一个属性的同时去给它添加数据劫持?
// 用实例方法添加初始值和数据劫持
// this.$set(this.obj, 'name', 1000);
// 通过Vue的静态方法添加.
// Vue.set(this.obj, 'name', 1000);
// 访问实例的默认属性data.
// console.log(this.$data);
}
}
})
</script>
十六、视图更新后的重新收集依赖
<body>
<div id='app'>
<div>{{obj.name}}</div>
<p>{{fn()}}</p>
</div>
</body>
数据劫持 => 还需要视图订阅 => 必须在视图上有对应的劫持数据.
要实现响应式的条件:
1:数据要有劫持.
2:视图上必须出现对应的数据.
满足以上两个条件的数据 => Watcher的依赖.
这个例子中,有3个字段被劫持,但是只有obj叫Watcher的依赖。
当obj变化时,产生了新的属性name,则在obj导致的视图更新后,Vue会自动给这个新生的name属性添加数据劫持.
如何给一个一开始不存在的属性添加数据劫持?
1:手动用$set添加.
2:通过修改这个属性所在的对象(把它变成新的对象).
<script src="js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
obj:{},
msg: 100,
str: 200
},
methods: {
fn() {
console.log('视图更新了');
}
}
})
</script>
十七、计算属性
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
</div>
</body>
Vue的数据
1:data中的数据
2:计算属性数据.
一个Vue的数据应该写在data中,还是应该写在computed中?
一个数据写在data中后就不能写在computed中,反之亦然.
计算属性的值,依赖别的数据的值变化而变化.(身不由己).
<script src="../js/vue.js"></script>
<script>
let vm = new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
},
// 计算属性.可以简写成一个函数.return最终的值.
// 这个计算属性的函数,在num1或者num2变化时,都会自动触发返回新的值.
// 函数名就是我们需要使用的数据名.
computed: {
num3() {
console.log('num3函数触发了');
return this.num1 + this.num2
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>
十八、计算属性
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
<input type="text" v-model='num3'>
</div>
</body>
- 计算属性默认是不能手动修改的.(不能主动改变值).
- 如果需要手动修改计算属性,需要给计算属性设置setter
- 在setter中修改计算属性依赖的数据.
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
},
// 计算属性的函数简写,实际上是全写的只有get的形式.
computed: {
num3: {
get() {
return this.num1 + this.num2
},
set(newVal) {
this.num1 = newVal - this.num2
}
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>
十九、watch
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
num3: 0
},
// 侦测属性(监听)
watch: {
// 当num1变化时,就触发这个函数,
num1() {
this.num3 = this.num1 + this.num2;
},
// 当num2变化时,就触发这个函数
num2() {
this.num3 = this.num1 + this.num2;
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>
二十、watch的immediate属性
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
num3: 0
},
// 侦测属性(监听)
watch: {
num1: {
// 立即,默认触发一次handler.
immediate: true,
// handler在每次num1变化时都会触发.
handler() {
this.num3 = this.num1 + this.num2;
}
},
// 当num2变化时,就触发这个函数
num2: {
immediate: true,
// handler在每次num2变化时都会触发.
handler() {
this.num3 = this.num1 + this.num2;
}
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>
二十一、计算属性和watch的区别
vue 数据更新流程
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
</div>
</body>
计算属性是watch的一种特例.
计算属性能够实现的功能,watch都能实现.
watch能够实现的功能,计算属性不一定能够实现.例如异步操作.
计算属性内部不能包含异步操作.计算属性有缓存,watch没有.
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
num3: 0
},
// computed: {
// num3() {
// // 计算属性内部能包含异步操作.
// let total = 0;
// setTimeout(() => {
// total = this.num1 + this.num2
// }, 3000);
// return total
// }
// },
watch: {
num1: {
immediate: true,
handler() {
setTimeout(() => {
this.num3 = this.num1 + this.num2
}, 3000);
}
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>
二十二、计算属性的缓存
<body>
<div id='div'>
<input type="button" value="随机取数" @click='fn'>
<span>第一个随机数:{{num1}}</span>
<span>第二个随机数:{{num2}}</span>
<span>两个数的和是:{{num3}}</span>
<h3>{{msg}}</h3>
</div>
</body>
- 计算属性的缓存 => 如有闭包,尽可能使用前一次的计算结果。
- 这样做是问了避免不必要的函数调用.
<script src="../js/vue.js"></script>
<script>
let vm = new Vue({
el: '#div',
data: {
num1: Math.floor(Math.random() * 10) + 1,
num2: Math.floor(Math.random() * 10) + 1,
msg: 0
},
computed: {
num3: {
// 关闭计算属性的缓存功能.
// 这样做会导致每次视图更新,计算属性的get都会重复调用.返回计算属性的值.
cache: false,
get() {
console.log('num3计算属性函数触发了')
return this.num1 + this.num2
}
}
},
methods: {
fn() {
this.num1 = Math.floor(Math.random() * 10) + 1;
this.num2 = Math.floor(Math.random() * 10) + 1;
}
}
});
</script>