一、监测数据改变原理
1. 监测对象数据改变原理
当数据发生改变之后:直接会显示数据改变(一种强硬写法)
let data = { name: "北京大学", address: "北京" }; // 以下通过temp进行监视:还得还原temp值(硬写) let temp = "北京大学"; setInterval(() => { if (data.name !== temp) { console.log("name被改了"); temp = data.name; } }, 100);
当对象数据发生变化之后就会调用观察构造函数
- 通过构造观察构造函数
- 将传入数据转为数组并进行遍历
- 遍历加入回调函数(箭头函数),通过defineProperty添加或者获取数据
- 然后进行实例化并吧实例化得数据给vm实例和data
let data = { name: "北京大学", address: "北京" }; const obs = new Observer(data); // 准备vm实例 let vm = {}; //三连等就可以通过data获取实例对象 vm._data = data = obs; // 创建观察构造函数 function Observer(obj) { // 汇总对象的多所有属性形成数组 const keys = Object.keys(obj); //遍历当前数组 keys.forEach((k) => { // 当前this指的是实例对象(相当于给实例对象中添加属性)当前this是实例对象 Object.defineProperty(this, k, { get() { // 返回传入对象的值 return obj[k]; }, set(val) { // 只要对象内容发生修改,就会调用set进行修改然后解析模板 console.log(`此处修改${k},需要重解析`); obj[k] = vla; }, }); }); }
2. 监测对象数据改变中的v-set使用
添加一个初始没有在data中定义一个性别属性值(没有响应式)
在初始定义得值模板解析之后都会存在getter和setter,但是通过直接添加是没有响应式
<div id="root"> <h2>学校名称:{{school.name}}</h2> <h2>学校地址:{{school.address}}</h2> <h2>校长名字:{{school.leader}}</h2> <hr /> <!-- ---------------------------------- --> <h2>学生信息</h2> <button @click="addSex">添加一个性别属性,默认值是男</button> <h2>学生姓名:{{students.name}}</h2> <!-- 添加 --> <h2 v-if="students.sex">学生性别:{{students.sex}}</h2> <h2> 学生年龄:真实年龄{{students.age.rAge}},对外年龄{{students.age.rAge}} </h2> <ul> <li v-for="(f,index) in students.friends" :key="index"> {{f.name}}--{{f.age}} </li> </ul> </div> </body> <script> Vue.config.production = false; const vm = new Vue({ el: "#root", data: { school: { name: "北京大学", address: "北京" }, students: { name: "tom", age: { rAge: 40, sAge: 20 }, friends: [ { name: "toy", age: 15, }, { name: "boom", age: 20, }, ], }, }, methods: { //添加一个属性得api addSex() { // Vue.set(this.students, "sex", "男"); this.$set(this.students, "sex", "男"); }, }, }); </script>
解决:
通过Vue.set(target,name,value) 进行响应式添加
通过vm.$set(target,name,value)进行响应式添加
3. 监测数组数据改变
监测对象数据时候是通过getter和setter进行监测和响应式获取数据
但是在监测数组数据变化时候就不奏效:数组在vue中没有getter和setter方法
说明数组下标修改数据不会被vue监视
解决: 数组对于元素得增删改查通过数组方法(非arr数组方法)
push : 末尾添加(返回index)
pop :末尾删除
shift :第一个删除(返回删除数组元素)
unshift :第一个添加
splice(a,b,item) :指定位置删除:标识从a开始删除b个插入item
这里补充上一章改变马老师响应式的解决方法:
this.persons.splice(0, 1, { id: "001", name: "马老师", age: 50, sex: "男", });
sort :排列
reverse : 反转
<div id="root"> <h2>爱好</h2> <ul> <!-- 遍历对象或者数组都是v-for指令 --> <li v-for="(h,index) in students.hobby" :key="index">{{h}}</li> </ul> </div>
data: { school: { name: "北京大学", address: "北京", }, students: { name: "tom", age: { rAge: 40, sAge: 20, }, hobby: ["抽烟", "喝酒", "烫头"], friends: [ { name: "toy", age: 15, }, { name: "boom", age: 20, }, ], }, },
判断数组方法得区别:
数组方法等于数组原型对象上的方法
- vue上的数组方法对模板进行解析
当然也可以直接通过$set-api进行添加响应式:
4. Vue监视数据原理总结
vue会监视data中所有层次的数据
如何监视对象中的数据?
通过setter实现监视,并且new Vue时候就要传入检测数据
(1)对象中后追加的属性,Vue默认不做响应处理
(2)如果需要给后追加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)或者
vm.$set(target,propertyName/index,value)如何检测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新
(2)重新解析模板,进而更新页面在Vue修改数组中的某个元素要用如下方法
使用这些API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
Vue.set(),vm.$set()
>>> 特别注意:Vue.set()和vm.$set()不能给vm或vm的根数据对象添加属性
二、收集表单数据 通过v-model
1. 表单数据通过vue管理
账号、密码直接使用v-model进行双向绑定
单选框通过双向绑定之后还需要给每个单选添加一个value值:需要默认值直接在vue的data中添加value值
多选框
通过双向绑定之后,给每个选项添加一个value值
没有配置input的value属性,那么收集的就是checked(勾选or 未勾选:都是布尔值)
select选项框
也是双向绑定和数据的value值,然后通过设置选项初始值进行设置默认选项
最后通过json数据处理再通过form进行数据传输或者btn进行传输上传
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据(而不是实时收集)
number:输入字符串转为有效的数字
trim:输入首位空格过滤<div id="root"> <!-- 原本使用action通过ajax页面不刷新发送数据 --> <!--这里通过表单绑定一个提交事件,并阻止默认事件--> <form @submit.prevent="demo"> 账号:<input type="text" v-model.trim="useInfo.account" /> <br /> <br /> 密码:<input type="text" v-model="useInfo.passward" /> <br /> 年龄:<input type="number" v-model.number="useInfo.age" /> <br /> <br /> 性别: <!-- 区分性别单选使用name:并且没有value值 --> 男<input type="radio" name="sex" v-model="useInfo.sex" value="male" /> 女<input type="radio" name="sex" v-model="useInfo.sex" value="fmale" /> <br /> <br /> 爱好: <!-- 多选框 :数组--> 学习<input type="checkbox" v-model="useInfo.hobby" value="learn" /> <!-- --> 打游戏<input type="checkbox" v-model="useInfo.hobby" value="game" /> <!-- --> 吃饭<input type="checkbox" v-model="useInfo.hobby" value="eat" /> <br /> <br /> 所选地区:<select v-model="useInfo.city"> <option value="">请选择校区</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> <option value="shenzhen">深圳</option> </select> <br /> <br /> 其他信息 <textarea v-model.lazy="useInfo.other"></textarea> <br /> <br /> //这里的checkboc的只接受布尔值 <input type="checkbox" v-model="useInfo.aggree" />阅读并接收信息:<a href="www.baidu.com" >请详细阅读</a > <button>注册</button> </form> </div> <script> Vue.config.production = false; const vm = new Vue({ el: "#root", data: { useInfo: { account: "", passward: "", sex: "male", hobby: [], city: "beijing", other: "", aggree: "", age: 18, }, }, methods: { demo() { console.log(JSON.stringify(this.useInfo)); }, }, }); </script>
三、过滤器 (非必要)
需求:页面中显示格式化后的时间:时间戳
Date.now()生成的数据转为年月日-时分秒
解决:第三方库moment(重量级)/day(轻量级)是第三方的库两个库api同步引入到文件中
https://github.com/iamkun/dayjs/blob/dev/docs/zh-cn/README.zh-CN.mdhttps://github.com/iamkun/dayjs/blob/dev/docs/zh-cn/README.zh-CN.mdapi
1. 计算属性:
computed: { fameTime() { return dayjs(this.time).format("YYYY-MM-DD HH-mm-ss"); }, },
2. 方法:
methods: { getFumTime() { return dayjs(this.time).format("YYYY-MM-DD HH-mm-ss"); }, },
3. 过滤器实现:
filters: { timeFormater(value) { return dayjs(value).format("YYYY-MM-DD HH-mm-ss"); }, } filters: { //这里可以给过滤函数添加第二个参数作为默认参数进行转换格式 timeFormater(value,str='YYYY-MM-DD HH-mm-ss') { return dayjs(value).format(str); }, }
过滤器:数据 | 过滤
过滤过程:将数据传给过滤器函数然后使用通用api-format返回一个格式化值
过滤器还可以使用串联过滤方式进行过滤:查看下方的串联过滤器使用方式
还有过滤器可以声名全局过滤器
Vue.filter("mySilice", function (value) { return value.slice(0, 4); });
<div id="root"> <h2>显示格式化后的时间</h2> <!-- 计算属性实现:时间戳变为可以看懂的时间 --> <h3>现在的时间是:{{fameTime}}</h3> <!-- 方法实现:时间戳变为可以看懂的时间:方法再插值语法中需要使用() --> <h3>现在的时间是:{{getFumTime()}}</h3> <!-- 过滤器属性实现:时间戳变为可以看懂的时间(过滤器本质就是函数) --> <h3>现在的时间是:{{time | timeFormater}}</h3> <!-- 多个过滤器可以串联 --> <h3> 现在的时间是:{{time | timeFormater("YYYY-MM-DD HH-mm-ss") | mySilice}} </h3> <!-- 除了插值语法可以使用过滤器:数据绑定也可以:查看控制台结构,但是不允许使用双向绑定 --> <h2 :bind="msg | mySilice">这里是数据绑定过滤器用法</h2> </div> <!-- 使用全局过滤器:每一个过滤器都是单一包裹的 --> <div id="root2"> <h2>{{msg | mySilice}}</h2> </div> </body> <script> Vue.config.production = false; // 全局过滤器 Vue.filter("mySilice", function (value) { return value.slice(0, 4); }); const vm = new Vue({ el: "#root", data: { time: 1681109185734, msg: "nihaoya", }, // 通过方法直接使用库api进行数据转换 computed: { fameTime() { return dayjs(this.time).format("YYYY-MM-DD HH-mm-ss"); }, }, methods: { getFumTime() { return dayjs(this.time).format("YYYY-MM-DD HH-mm-ss"); }, }, // 局部过滤器 filters: { //使用es6中形参默认值:将传入的参数值转为以下格式 timeFormater(value, str = "YYYY-MM-DD") { // console.log("@", value); // 这里的模板字符可以使用字符串格式(作为第二个参数) // return dayjs(value).format("YYYY-MM-DD HH-mm-ss"); return dayjs(value).format(str); }, mySilice(value) { return value.slice(0, 4); }, }, }); // 第二个实例 const vm2 = new Vue({ el: "#root2", data: { msg: "xlf-nihao", }, }); </script>