目录
(1)回顾Object.defineProperty(双向数据传输原理)
一、第一次使用Vue
1.script引入式
输出hello 狗蛋
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- {{}}引用vue实例里的元素 -->
<h1>hello {{name}}!</h1>
</div>
<script>
// 关闭启动时的生产提示 在官网api全局配置可查
Vue.config.productionTip = false
// 创建启动vue实例
const vm = new Vue({
el: '#root', //元素名 与div语句连接
data: {
name: '狗蛋'
}
})
</script>
</body>
el两种写法
(1)写在vue实例内:el: '#root'
(2)写在vue实例外:vue实例.$mount('#root')
$mount() 挂载
data两种写法
(1)对象式(不建议)
data: {
name: '狗蛋'
}
(2)函数式
data(){
return {
name:'狗蛋'
{
}
data()函数必须有返回值,该函数的this指向Vue实例对象
注意:不建议使用箭头函数,这样this会指向window,导致后续操作报错!
二、模板语法
1.插值语法{{}}
用于解析标签体内容
2.指令语法
用于解析标签(包括标签属性、标签体内容、事件绑定等)
格式:v-XXXX 如:v-bind,v-if等
<!-- html部分 -->
<a v-bind:href="website.url">点我去网站</a>
<!-- 缩写v-bind:变为: -->
<a :href="website.url">点我还去这个网站</a>
<!-- script部分 -->
// 创建启动vue实例
const vm = new Vue({
el: '#root', //元素名 与div语句连接
data: {
name: '狗蛋',
website: {
name: '狗蛋网站',
url: 'https://www.bilibili.com/'
}
}
})
注意:v-bind:可缩写为 “:”
href="url"中,url必须是js表达式
三、数据绑定
(1)单向数据绑定:v-bind
vue实例中的数据->表单的值
(2)双向数据绑定:v-model(只能用于表单元素)
vue实例中的数据->表单的值
表单的值修改->vue实例中的数据也被修改
缩写方法:v-model:value="name"简写为 v-model="name"
<div id="root">
单向数据绑定:<input type="text" v-bind:value="name"> <br>
双向数据绑定:<input type="text" v-model:value="name">
</div>
<script>
new Vue({
el: '#root',
data: {
name: '狗蛋'
}
})
</script>
单项数据修改value值,实例中的值不会改变
双向数据修改value值,实例中的值也会跟着改变
四、MVVM模型
五、数据代理
(1)回顾Object.defineProperty(双向数据传输原理)
<script>
let num = 18
const person = {
name: 'csq'
}
// 向对象person添加一个属性
Object.defineProperty(person, 'age', {
// value: 18,
// enumerable: true, //可枚举、可遍历
// writable: true, //可修改
// configurable: true, //可删除
// 访问age属性数据时,getter会被调用,触发返回年龄数据
get() {
console.log('age属性被访问!')
return num
},
// 修改age属性数据时,setter会被调用,触发修改年龄数据
//同时,修改age后会触发get(),使得属性和数值连接起来,修改属性后,后台数值也被修改
set(value) {
console.log('age属性被修改!')
num = value
}
})
</script>
访问age属性
修改age属性
从后台修改num可以改变age属性的值,从控制台修改age属性的值可以改变num的值,实现了双向数据传输的基本原理
(2)数据代理
基本结构:
//最简 数据代理结构
let obj1 = {
x: 100
}
let obj2 = {
y: 10
}
Object.defineProperty(obj2, 'x', {
get() {
return obj1.x
},
set(value) {
obj1.x = value
}
})
(3)Vue中的数据代理
通过vm对象来代理data对象中属性的操作,更加方便对data的操作
基本原理:通过Object.defineObject()把data对象中的所有属性添加到vm中,为每一个添加到vm中的属性指定一个getter和setter,在getter和setter中去操作(读、写)data中对应的属性
<div class="root">
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}</h1>
</div>
<script>
//Vue 数据代理 最简结构
const vm = new Vue({
el: '.root',
data() {
return {
name: 'csq',
age: 18
}
}
})
</script>
读取vm.name触发getter,修改vm.name触发setter,符合数据代理
注意:Vue实例vm的数据data存储在vm._data对象中,vm._data.name=vm.name
六、事件处理*
v-on:事件类型="函数" 缩写为@事件类型="函数" (该函数只能写在vm实例中才能被访问到)
函数方法写在methods对象中,不能写在data对象中
原因:函数在最开始内容就被指定,后期不必更改,放在data对象中会被数据代理,添加函数方法的getter/setter功能(实际不会用到)
(1)事件绑定
<div class="root">
<h2>我是谁?</h2>
<button v-on:click="showInfo">点我提示信息</button>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
name: 'csq'
},
methods: {
showInfo() {
alert("my name is csq")
}
}
})
</script>
点击按钮事件,alert信息
(2)函数传参
v-on:事件类型="函数",引号中的语句会被解析为js语句,故需要传参的时候在函数名后写上 括号和实参即可
关键字$event,用于给event对象占位
<button @click="showInfo2($event,77)">点我提示信息2</button>
methods: {
showInfo(event) {
alert("my name is csq")
},
showInfo2(event, num) {
console.log(event)
console.log(num)
}
}
(3)事件修饰符
- prevent 阻止默认事件
- stop 阻止事件冒泡
- once 事件只能触发一次
- capture 使用事件的捕获机制
- self 只有event.target是当前操作的元素才会触发事件
- passive 事件的默认行为立即执行,无需等待回调事件结束
<div class="root">
<!-- prevent 阻止默认事件 -->
<a @click.prevent="showInfor" href="https://www.bilibili.com">点我</a> <br>
<div @click="showInfor1" class="son">
<!-- stop 阻止事件冒泡 -->
<button @click.stop="showInfor">点我</button>
</div>
<!-- once 事件只能触发一次 -->
<button @click.once="showInfor">点我只能点一次</button>
<!-- capture 使用事件的捕获机制 -->
<div @click.capture="showInfor1" class="son">
<button @click.stop="showInfor">点我</button>
</div>
<!-- self 只有event.target是当前操作的元素才会触发事件 -->
<div @click.self="showInfor1" class="son">
<button @click.stop="showInfor">点我</button>
</div>
<!-- passive 事件的默认行为立即执行,无需等待回调事件结束 -->
<!-- 不添加passive时,ul先执行鼠标滚动的回调事件循环输出i,再完成默认行为:滚动 -->
<ul v-on:wheel.passive="move">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '.root',
methods: {
showInfor() {
alert("点我")
},
showInfor1() {
alert("点我爸爸")
},
move() {
for (let i = 0; i < 10000; i++)
console.log(i)
}
}
})
</script>
(4)键盘事件
按回车响应事件(不使用关键字)
<div class="root">
<input @keyup="showInfor" type="text">
</div>
<script>
const vm = new Vue({
el: '.root',
methods: {
showInfor(e) {
if (e.keyCode == 13)
console.log('你按了回车')
}
}
})
</script>
按回车响应事件(使用关键字enter)
<!-- 使用关键字 -->
<div class="root">
<input @keyup.enter="showInfor" type="text">
</div>
<script>
const vm = new Vue({
el: '.root',
methods: {
showInfor(e) {
console.log('你按了回车')
}
}
})
</script>
键盘关键字,按下对应按键响应事件
七、computed和watch*
1.计算属性computed
根据一个简单的案例来看看计算属性的高级之处:分别输入姓和名来拼接出全名并显示在屏幕中
(1)直接在标签中用插值语句拼接
<!-- 1.插值方法 -->
<div class="root">
<span>姓:</span><input v-model="xing" type="text"> <br>
<span>名:</span><input v-model="ming" type="text"> <br>
<p>全名:{{xing}}-{{ming}}</p>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
xing: '',
ming: ''
}
})
</script>
弊端:直接在标签中插值连接,不便于后期有更多需求时的修改,在标签中添加更多不利于可读性
(2) 在methods中写一个方法
<!-- 2.methods方法 -->
<div class="root">
<span>姓:</span><input v-model="xing" type="text"> <br>
<span>名:</span><input v-model="ming" type="text"> <br>
<p>全名:{{fullname()}}</p>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
xing: '',
ming: ''
},
methods: {
fullname() {
return this.xing + '-' + this.ming
}
}
})
</script>
弊端:每次修改姓或名后,fullname()方法会被重新调用,且若多次调用函数,会大大增加系统的压力,不利于Vue轻便减负的特点
(3)计算属性方法*
添加计算属性fullname,通过get读取调用,计算属性具有缓存特点,即,在该属性值不变动时,将该属性存在缓存中,以后每次用到该属性直接拿出来用即可,不需要再向Vue实例调用属性,大大减负。显而易见,将需要计算的值保存在计算属性中更加高效
<!-- 计算属性 -->
<div class="root">
<span>姓:</span><input v-model="xing" type="text"> <br>
<span>名:</span><input v-model="ming" type="text"> <br>
<p>全名:{{fullname}}</p>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
xing: '',
ming: ''
},
computed: {
fullname: {
// get在什么时候调用?(缓存机制,减少调用)
//1.初次访问fullname属性时; 2.fullname内数据更新时
get() {
return this.xing + '-' + this.ming
},
// set在fullname被修改时调用
//确定该计算属性只用于显示计算结果就不必写set函数
set(value) {
const arr = value.split('-')
this.xing = arr[0]
this.ming = arr[1]
}
}
}
})
</script>
大部分情况下,计算属性不需要修改值,即不需要set()函数,故将计算属性简写:
computed: {
// 简写:只使用get()
fullname() {
return this.xing + '-' + this.ming
}
}
2.监视属性watch
用于监视属性是否被修改
根据案例修改天气来看看具体效果
(1)监视属性
在watch属性中写入需要监视的属性a,handler函数用于在修改时自动调用,可包含两个参数,新值和旧值
监视属性中还有别的函数和属性可以添加,例如immediate:true,在网页打开时就进行一次监视,旧值为undefined
<div class="root">
<p>今天天气很{{show}}</p>
<button @click="changeWeather">点我切换天气</button>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
a: true
},
computed: {
show() {
return this.a ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.a = !this.a
}
},
watch: {
a: {
// handler函数在a被修改时调用
handler(newValue, oldValue) {
console.log('a被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
}
}
}
})
</script>
另一种写法:写在Vue实例外
vm.$watch('a',{
immediate: true,
handler(newValue, oldValue) {
console.log('a被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
}
})
(2)深度监视
不使用深度监视只能这样监视深层的属性a.b
<div class="root">
<p>b={{a.b}}</p>
<button @click="changeB">点我b+1</button>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
a: {
b: 0,
c: 0
}
},
computed: {
},
methods: {
changeB() {
this.a.b = this.a.b + 1
}
},
watch: {
'a.b': {
// handler函数在a被修改时调用
handler(newValue, oldValue) {
console.log('b被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
}
}
}
})
</script>
使用深度监视deep:true
// 深度监视 可以监视属性a内的多层属性
a: {
deep: true,
handler(newValue, oldValue) {
console.log('b被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
}
}
监视a属性,也可以监视到a属性内的多层属性;若不添加深层监视,则只能监视到属性a本身的改变,看不到内层属性的改变
(3)监视属性的简写(前提是监视属性只需要handler()函数)
写在实例内的简写:
watch: {
a(newValue, oldValue) {
console.log('a被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
}
}
写在实例外的简写:
vm.$watch('a', function (newValue, oldValue) {
console.log('a被修改了');
console.log('新值:', newValue, '旧值:', oldValue)
})
注意:需要指向vm实例的函数不要使用箭头函数,否则this指针会指向外层,如window
3.计算属性和监听属性的比较
上文中的全名案例:用watch来写
可见,上面代码是命令式且重复的,不如computed计算属性来写更加简单明了
监视属性watch可以进行异步操作,用computed是无法替代的,如下,计时器1s后再写入全名,再computed属性中不可实现
const vm = new Vue({
el: '.root',
data: {
xing: '',
ming: '',
fullname: ''
},
watch: {
xing(newValue) {
// 1s后再返回全名
setTimeout(() => {
this.fullname = newValue + '-' + this.ming
}, 1000)
},
ming(newValue) {
setTimeout(() => {
this.fullname = this.xing + '-' + newValue
}, 1000)
}
}
})