一、Vue监测数据原理
1、更新data数据时的一个问题
如下代码所示,某些情况下更改data中的属性值,页面不会发生变化
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="update">更新马冬梅信息</button>
<ul>
<li v-for="p in persons" :key="p.id">
姓名:{{p.name}},年龄:{{p.age}},性别:{{p.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
persons: [
{id:'001',name:'马冬梅',age:18,sex:'女'},
{id:'002',name:'周杰伦',age:19,sex:'男'},
{id:'003',name:'周冬雨',age:20,sex:'女'},
{id:'004',name:'温兆伦',age:21,sex:'男'}
]
},
methods: {
update() {
// 下面这3行会使页面发生变化
// this.persons[0].name = '马老师';
// this.persons[0].age = 50;
// this.persons[0].sex = '男';
// 下面这一行不会使页面发生变化
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'};
}
}
})
</script>
2、Vue.set添加属性
<body>
<div id="root">
<button @click.once = "addSex">添加性别</button>
<button @click.once = "addNumber">添加数量</button>
<h2>学校名:{{name}}</h2>
<h2>地址:{{name}}</h2>
<h2>学生姓名:{{school.name}}</h2>
<h2>学生年纪:{{school.age}}</h2>
<h2>学生性别:{{school.sex}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
name:'尚硅谷',
address: '北京',
school: {
name: '张三',
age: 20
}
},
methods: {
addSex() {
Vue.set(this.school, 'sex', '男');
},
// 下面这个会报错,不能直接往data中加属性,只能给data中的对象加属性
addNumber() {
Vue.set(this, 'number', 20);
}
}
})
</script>
3、vm监测数据总结
vm中的属性有响应式的get和set方法,当发生修改时会调用set方法,并在set方法中生成新的虚拟vm对象,并和原来的进行比较,有区别的地方会进行更改,所以我们可以看到页面上的变化。但上面章节1中,直接将数组中的某个对象替换,而这一操作是没有set方法的,所以不会有效果
如下所示,persons是数组,persons自身有get、set方法。但数组中的每个对象自身没有get、set方法。而对象中的每个属性又有get、set方法,所以会出现上面章节1的结果
而如果要对数组进行操作,必须使用push、splice、pop、shift、unshift等方法才行,如下所示就会有效果,因为vm对这些方法进行了重构
methods: {
update() {
// 下面这3行会使页面发生变化
// this.persons[0].name = '马老师';
// this.persons[0].age = 50;
// this.persons[0].sex = '男';
// 下面这一行不会使页面发生变化
// this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'};
// 下面这一行会使页面发生变化
this.persons.splice(0, 1, {id:'001',name:'马老师',age:50,sex:'男'});
}
}
二、收集表单数据
<body>
<div id="root">
<form @submit.prevent="upload">
<label for="account">账号:</label><input type="text" id="account" v-model.trim="userInfo.account"><br/><br/>
<label for="pwd">密码:</label><input type="password" id="pwd" v-model="userInfo.pwd"><br/><br/>
<label for="age">年龄:</label><input type="number" id="age" v-model.number="userInfo.age"><br/><br/>
性别:
<input type="radio" name="sex" v-model="userInfo.sex" value="male">男
<input type="radio" name="sex" v-model="userInfo.sex" value="female">女
<br/><br/>
爱好:
<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="study">学习
<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="game">打游戏
<input type="checkbox" name="hobby" v-model="userInfo.hobby" value="eat">吃饭
<br/><br/>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="biejing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
<br/><br/>
其他信息:
<textarea v-model="userInfo.other"></textarea><br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="https://www.baidu.com" target="_blank">《用户协议》</a><br/><br/>
<button>提交</button><br/>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
userInfo: {
account: '',
pwd: '',
age: 18,
sex: 'female',
hobby: [],
city: 'shanghai',
other: '',
agree: true
}
},
methods: {
upload() {
console.log(JSON.stringify(this.userInfo));
}
}
})
</script>
三、内置指令
1、v-text
- v-text会直接替换节点中所有内容,{{XXX}}不会
- v-text无法识别html结构
2、v-html
- v-html可以识别html结构
- 有安全问题,动态渲染html容易导致XXS攻击
3、v-cloak
- 是一个特殊属性,vue实例创建完毕并接管实例后,会删掉v-cloak属性
- 使用css的属性display:none配合v-cloak可以解决网速慢时页面展示出{{XXX}}的问题
<head>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
</body>
4、v-once指令
v-once所在节点在初次渲染后,视为静态内容,以后数据的改变不会引起v-once结构的更新,可以用于优化性能
<body>
<div id="root">
<h2 v-once>初始化n值是:{{n}}</h2>
<h2>当前n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
n: 1
}
})
</script>
5、v-pre指令
跳过其所在节点的编译过程,可利用它跳过没有vue语法的节点,加快编译
<body>
<div id="root">
<h2 v-pre>演示pre指令</h2> <!-- 这一行vue就会直接跳过,不会判断是否需要编译 -->
<h2 v-pre>当前n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
n: 1
}
})
</script>
四、自定义指令
1、自定义2个指令
- v-big:n改变时,自动乘10
- v-fbind:展示n的值,并自动获取光标
<body>
<div id="root">
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大10倍的n值是:<span v-big="n"></span></h2>
<button @click="n++">点我n+1</button>
<hr/>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
n: 1
},
directives: {
// 1、指令与元素成功绑定时,被调用
// 2、指令所在的模板被重新解析时,被调用
// 3、binding.value值就是v-big="n"中的n的值
big(element, binding) {
element.innerText = binding.value * 10;
},
fbind: {
// 当指令与元素成功绑定时
bind(element, binding) {
element.value = binding.value;
},
// 指令所在元素被插入页面时
inserted(element, binding) {
element.focus();
},
// 指令所在模板被重新解析时
update(element, binding) {
element.value = binding.value;
element.focus();
}
}
}
})
</script>
五、生命周期
如下图所示,其中mounted和beforeDestroy最为重要
mounted和beforeDestroy样例,vm.$destroy会销毁vm实例,从而触发beforeDestroy
<div id="root">
<h2 :style="{opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
stop() {
this.$destroy()
}
},
// Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted() {
console.log('mounted', this)
this.timer = setInterval(() => {
console.log('setInterval')
this.opacity -= 0.01
if (this.opacity <= 0) this.opacity = 1
}, 16)
},
beforeDestroy() {
clearInterval(this.timer)
console.log('vm即将驾鹤西游了')
},
})
</script>