声明:是接着上篇讲的哦,感兴趣可以去看一看~
这里一些代码就不写了,为了缩减代码量,大家知道就可以了: Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
热身小tips,可以安装这个插件,这样写代码有提示哦~
一、Vue核心(中篇)
1.9 监视属性(侦听属性)
使用计算属性确定天气案例
<body><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 --><!-- <button @click="isHot = !isHot">切换天气</button> --><button @click="changeWeather">切换天气</button></div></body><script type="text/javascript"> const vm = new Vue({el:'#root',data:{isHot:true,},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},})</script>
所有vue写成的函数都要写成普通函数,不要使用箭头函数,因为箭头函数牵涉到没有this的问题
监视属性watch:
<!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><button @click="changeWeather">切换天气</button></div></body><script type="text/javascript"> const vm = new Vue({el:'#root',data:{
2.监视的属性必须存在,才能进行监视!!isHot:true,},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作this.isHot = !this.isHot}},
3.监视的两种写法:(1).new Vue时传入watch配置(2).通过vm.$watch监视/* watch:{isHot:{immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}}} */})vm.$watch('isHot',{immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}})</script>
深度监视
<!-- 深度监视:(1).Vue中的watch默认不监测对象内部值的改变(一层)。(2).配置deep:true可以监测对象内部值改变(多层)。备注:(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!(2).使用watch时根据数据的具体结构,决定是否采用深度监视。--><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><button @click="changeWeather">切换天气</button><hr/><h3>a的值是:{{numbers.a}}</h3><button @click="numbers.a++">点我让a+1</button><h3>b的值是:{{numbers.b}}</h3><button @click="numbers.b++">点我让b+1</button><button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>{{numbers.c.d.e}}</div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{isHot:true,numbers:{a:1,b:1,c:{d:{e:100}}}},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},watch:{isHot:{// immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}},//监视多级结构中某个属性的变化/* 'numbers.a':{handler(){console.log('a被改变了')}} *///监视多级结构中所有属性的变化numbers:{deep:true,handler(){console.log('numbers改变了')}}}})</script>
监视属性的简写
<div id="root"><h2>今天天气很{{info}}</h2><button @click="changeweather">切换天气</button></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {//布尔值 可以直接被识别出来?isHot: true},computed: {info () {return this.isHot ? '炎热' : '凉爽'}}, methods: {changeweather () {this.isHot = !this.isHot}},watch: {// isHot: { //对象// // immediate: true,// // deep: true, //深度监视// handler (newValue, oldValue) {// console.log('info被修改了', newValue, oldValue)// }// }// 只有handler配置项可以简写 ()就代表handler函数isHot (newValue, oldValue) {console.log('info被修改了', newValue, oldValue)}}})// vm.$watch('isHot', {// handler (newValue, oldValue) {// console.log('info被修改了', newValue, oldValue)// }// })vm.$watch('isHot', function (newValue, oldValue) {console.log('isHot被修改了', newValue, oldValue)})//简写/* vm.$watch('isHot',(newValue,oldValue)=>{console.log('isHot被修改了',newValue,oldValue,this)}) */</script>
天气案例中计算属性和侦听属性区分:
<!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{firstName:'张',lastName:'三',fullName:'张-三'},watch:{firstName(val){setTimeout(()=>{console.log(this)this.fullName = val + '-' + this.lastName},1000);},lastName(val){this.fullName = this.firstName + '-' + val}}})</script>
1.10 绑定样式
<style>.basic {width: 400px;height: 100px;border: 1px solid black;}.happy {border: 4px solid red;;background-color: rgba(255, 255, 0, 0.644);background: linear-gradient(30deg, yellow, pink, orange, yellow);}.sad {border: 4px dashed rgb(2, 197, 2);background-color: gray;}.normal {background-color: skyblue;}.atguigu1 {background-color: yellowgreen;}.atguigu2 {font-size: 30px;text-shadow: 2px 2px 10px red;}.atguigu3 {border-radius: 20px;}</style><script type="text/javascript" src="../../js/vue.js"></script>
</head><body><!-- 绑定样式:1. class样式写法:class="xxx" xxx可以是字符串、对象、数组。字符串写法适用于:类名不确定,要动态获取。对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。2. style样式:style="{fontSize: xxx}"其中xxx是动态值。:style="[a,b]"其中a、b是样式对象。--><!-- 准备好一个容器--><div id="root"><!-- 绑定class样式 字符串写法 适用于:样式的类名不确定 需要动态指定 --><div class="basic" :class='mood' @click="changeMood">{{name}}</div><br><br><!-- 绑定class样式 数组写法 适用于 帮绑定的样式个数不确定 名字也不确定 --><!-- 相当于拿到的是值 --><div class="basic" :class="['atguigu1', 'atguigu2' , 'atguigu3' ]" @click="changeMood">{{name}}</div><br><br><!-- 相当于拿到的是变量 还要从vm中找 --><!-- <div class="basic" :class="[a, b , c]" @click="changeMood">{{name}}</div><br><br> --><div class="basic" :class="classArr" @click="changeMood">{{name}}</div><br><br><!-- 对象写法 要绑定的样式个数确定、名字也确定,但要动态决定用不用--><!-- <div class="basic" :class="classObj" @click="changeMood">{{name}}</div><br><br> --><!-- 没在vm上定义 在vue开发工具添加样式不可能实现 --><div class="basic" :class="{ atguigu1: false,atguigu2: false}" @click="changeMood">{{name}}</div><br><br><!-- 绑定style样式 对象写法 适用于:要绑定的样式个数确定、名字也确定 但要动态决定用不用 --><!-- <div class="basic" :style="{fontSize:fsize + 'px'}">{{name}}</div><br><br> --><!-- <div class="basic" :style="styleObj">{{name}}</div><br><br> --><!-- <div class="basic" :style="[styleObj1,styleObj2]">{{name}}</div><br><br> --><!-- 绑定style样式 数组写法 适用于:要绑定的样式个数确定、名字也确定 但要动态决定用不用 --><div class="basic" :style="styleArr">{{name}}</div><br><br></div>
</body><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el: '#root',data: {name: '尚硅谷',mood: 'normal',classArr: ['atguigu1', 'atguigu2', 'atguigu3'],// a: 'atguigu1',// b: 'atguigu2',// c: 'atguigu3',classObj: {atguigu1: false,atguigu2: false},// fsize: 40// styleObj1: {// fontSize: '60px',// color: 'red',// }, styleObj2: {// backgroundColor: 'orange'// }, styleArr: [{fontSize: '60px',color: 'red',}, {backgroundColor: 'orange'}]}, methods: {changeMood () {// // 如果这样写 直接js了 不用vue// // document.querySelector('.basic').className = 'basic happy'// this.mood = 'happy'const arr = ['happy', 'sad', 'normal']const index = Math.floor(Math.random() * 3)this.mood = arr[index]}},})
</script>
1.11 条件渲染
条件渲染:
1.v-if
写法:
(1).v-if=“表达式”
(2).v-else-if=“表达式”
(3).v-else=“表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
2.v-show写法:v-show="表达式"适用于:切换频率较高的场景。特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
<div id="root"><h2>当前的值是{{n}}</h2><button @click="n++">点我n+1</button><!-- <h2 v-show='a'>欢迎来到{{name}}</h2><h2 v-show="1===3">欢迎来到{{name}}</h2> --><!-- 用v_if做条件渲染 结构也不显示 --><!-- <h2 v-if='false'>欢迎来到{{name}}</h2> --><!-- 切换频率 如果成立 其他不成立--><!-- 快 高效 --><!-- <div v-show="n===1">a</div><div v-show="n===2">b</div><div v-show="n===3">c</div><div v-show="n===4">d</div> --><!-- 慢 低效 --><!-- <div v-show="n===1">a</div><div v-show="n===2">b</div><div v-show="n===3">c</div><div v-show="n===4">d</div> --><!-- 第一句找到了 后面就都不执行了 佐证 --><div v-if="n===1">a</div><div v-else-if="n===1">aa</div><div>@</div>//中间不能断<div v-else-if="n===3">c</div><div v-else>哈哈哈</div><!-- 就是不会破坏结果 可以直接拿到css样式 --><template v-if="n===1"><h2>1</h2><h2>3</h2><h2>4</h2></template></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el: '#root',data: {name: '哈哈哈',a: false,n: 0},methods: {}});</script>
</body>
1.12 列表的渲染
1.基本列表
1.遍历数组数据形成页面上的列表
思路:使用v-for遍历数组对象,然后展示在列表上
具体说来就是:js中有顺序 要用数组对象
先写一个 想生成多个li 就在谁身上写个v-for 遍历
v-for能遍历,加在谁身上 谁就能遍历,persons能决定遍历多少次
<div id="root"><!-- 第一种写法:只有一个参数 遍历出来的是数组对象中的每一项 --><!-- 每一个li都有一个标识 (通过遍历) 所以有key -->//这里in也可以用of<li v-for="p in persons" :key="p.id">//使用插值语法中的p可能来自三个地方:data中的属性 计算属性 还有参数,这里是参数{{p.name}}-{{p.age}}</li><!-- 第二种写法:两个参数 分别是数组对象中的每一项 索引号 --><!-- <li v-for="(p,index) in persons" :key="p.id"> --><!-- key的取值 只要保证每一项对应的key值不一样即可 --><li v-for="(p,index) in persons" :key="index"><!-- {{p.name}}-{{p.age}} -->{{p}}---{{index}}</li></div><script type="text/javascript">new Vue({el: '#root',data: {persons: [{ id: '001', name: '张三', age: 18 },{ id: '002', name: '李四', age: 19 },{ id: '003', name: '王五', age: 20 }]}});</script>
2.遍历对象数据形成页面上的列表
<!-- 遍历对象 --><li v-for="(value,key) of car" :key="key"><!-- {{p.name}}-{{p.age}} -->{{key}}:{{value}}</li>car: {name: '奥利',price: '70万',color: '黑色'}
具体来说一共五种
<body><div id="root"><!-- 1.遍历数组 --><!-- 第一种写法:只有一个参数 遍历出来的是数组对象中的每一项 --><!-- 每一个li都有一个标识 (通过遍历) 所以有key --><li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li><!-- 第二种写法:两个参数 分别是数组对象中的每一项 索引号 --><!-- <li v-for="(p,index) in persons" :key="p.id"> --><!-- key的取值 只要保证每一项对应的key值不一样即可 --><li v-for="(p,index) in persons" :key="index"><!-- {{p.name}}-{{p.age}} -->{{p}}---{{index}}</li><!-- 2.遍历对象 --><li v-for="(value,key) of car" :key="key">{{key}}:{{value}}</li><!-- 3.遍历字符串 --><li v-for="(index,char) of str" :key="index">{{index}}:{{char}}</li><!-- 4.遍历指定次数 --><li v-for="(number,index) of 6" :key="index">{{index}}:{{number}}</li></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el: '#root',data: {persons: [{ id: '001', name: '张三', age: 18 },{ id: '002', name: '李四', age: 19 },{ id: '003', name: '王五', age: 20 }],car: {name: '奥利',price: '70万',color: '黑色'},str: 'hello'}});</script>
2.key的原理
1.按照数组 key作用:给节点进行标识
2.效率 每一个人后面加input框 p.id唯一数据标识
死记硬背:遍历列表的时候就是会有个key来标识每个节点,使用key来标识;这里如果牵涉到列表的增加或者删除,使用id来标识,如果不牵涉则使用index,如果没有写index,vue默认使用index来标识
上述图片的详细理解:
1.拿到刚开始的数据,也就是还没有添加老刘
2.vue会将初始数据生成虚拟DOM,(加了key) 此时页面中没有数据,内存中有
3.vue将虚拟DOM转化为真实DOM,真实DOM才是用户能看到的,用户才可以在input框输入
4.添加老刘,更新数据,生成了新的数据
5.vue根据新数据生成虚拟DOM
6.因为是第一条的添加位置,所以老刘的key对应为0
7.vue会开启虚拟DOM对比算法 也就是新旧虚拟DOM对比
8.按照顺序,从key为0开始对比,这里的老刘-30属于文本节点,input框属于标签节点。key=0时,文本节点不同,则老刘-30生成新的数据,从虚拟DOM转化为真是DOM;标签节点相同(,这里只看虚拟DOM,单纯诸葛词语对比,因为值对比虚拟DOM,不要看真实DOM),实现复用。也就是旧的虚拟DOMinput框一定转化为真实的DOM,所以这里会拿之前变好的input真实DOM框,实现复用。
9.挨个对比,相同的直接用之前的,不同的直接下来(直接生成真实DOM)
10.这里到了王2-50 key=3,找不到与他相同的 所以新的虚拟DOM直接转化为真实的DOM,那么真实DOM的input框下来的时候,用户还没有填数据,就为空
从这里就可以看出:效率低(因为错乱的数据都不能使用,统统都需要vue工作由虚拟DOM转化为真实DOM,都是需要重新生成,错误DOM更新) 并且数据错乱
另外:如果将王五追加在后面没有问题,但是对数据破坏顺序的操作,就不能使用index。而需要使用数据唯一标识id
另外 不写key的值 index在遍历的时候vue会自动补充,index会取遍历时的索引值 往后加push 好用
手机、邮箱、用户,vue不可能采集到这些信息,所以只能通过数据库录入的方式 id:001
有上述可知,再把列表项追加到第一位时要用标识: p.id 如下图 想改成index 直接在下面改即可
<body><div id="root"><!-- 1.在persons的上方追加老刘 绑定click事件并且只添加一次--><h2>人员列表</h2><button @click.once="add">添加一个老刘</button><!-- //发现html结构中没有key 因为他是在vue内部使用 转化为真实的dom之后就丢弃了 --><li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}<input type="text"><!-- //2. 这里加上input框 是为了实现在每一个对象后都有一个对应的input框 // 会发现当我们填写之后添加老刘,就会出现错乱的情况 不仅仅是效率低下的问题 // 此时使用p.id(数据的唯一标识)不会有问题 这里就开始引入key工作原理和虚拟dom对比算法--></li></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el: '#root',data: {persons: [{ id: '001', name: '张三', age: 18 },{ id: '002', name: '李四', age: 19 },{ id: '003', name: '王五', age: 20 }]}, methods: {add () {const p = { id: '004', name: '老刘', age: 40 }this.persons.unshift(p)}}});</script>
3.列表过滤
<body><div id="root"><input type="text" placeholder="请输入名字" v-model="keyword"><ul><li v-for="p in filterPersons">{{p.name}}-{{p.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示new Vue({el: '#root',data: {persons: [{ id: '001', name: '张三', age: 18 },{ id: '002', name: '李四', age: 19 },{ id: '003', name: '王五', age: 20 }],keyword: '',// filterPersons: []},// watch: {// keyword: {// immediate: true,//一上来就调用 用户什么也没输入的情况下 这样可以获得完整的数据// // 死记硬背:通过watch监听 能够知道keyword被修改了// // console.log('keyword被修改了', val)// // 这里的p与上文li中的p不是一个变量 只不过名字相同 根据filter语法 p标识数组对象中的每一个对象// // 但是这里有一个问题:就是filter返回的新数组给了persons 这导致persons数据缺失 所以需要新的空数组接收// // this.persons = this.persons.filter((p) => {// // return p.name.indexOf(val) !== -1// // })// // 每次都从persons过滤数据可以避免 但是这里有一个问题 在没有过滤的情况下不展示所有数据 解决办法: immediate: true// handler (val) {// this.filterPersons = this.persons.filter((p) => {// return p.name.indexOf(val) !== -1// })// }// }// }// computed返回值就是返回真正的过滤结果 filterPersons 依赖keyword发生变化 computed的返回值就是结果computed: {filterPersons () {return this.persons.filter((p) => {return p.name.indexOf(this.keyword) !== -1})}}});</script>
4.列表排序
思路如下图:
思路
<div id="root"><input type="text" placeholder="请输入名字" v-model="keyword"><!-- //1.不同按钮 用不同序号标识 然后根据不同的sortType值知道选的哪个button --><button @click="sortType = 2">年龄升序</button><button @click="sortType = 1">年龄降序</button><button @click="sortType = 0">原顺序</button><ul><li v-for="p in filterPersons">{{p.name}}-{{p.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示new Vue({el: '#root',data: {sortType: 0, // 0,1,2分别是原顺序 年龄降序 年龄升序persons: [{ id: '001', name: '张三', age: 18 },{ id: '002', name: '李四', age: 44 },{ id: '003', name: '王五', age: 20 },{ id: '004', name: '赵四', age: 22 },],keyword: '',},// computed返回值就是返回真正的过滤结果 filterPersons 依赖keyword发生变化 computed的返回值就是结果computed: {filterPersons () {// 2.需要明确:过滤+排序都是在filterPersons基础之上进行的const arr = this.persons.filter((p) => {return p.name.indexOf(this.keyword) !== -1})//if 语句中使用一个数值时,该数值会被隐式地转换为布尔值//这一次提到sortType是为了判断是不是为0 布尔值中零代表false 非零值代表trueif (this.sortType) {arr.sort((p1, p2) => {//这一次提到sortType是为了判断是不是为1return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age})}return arr}}});</script>
这里分不清是升序还是降序可以直接试一下 不是升序就是降序
let arr = [1, 6, 8, 43, 44];
arr.sort(function (a, b) { return a - b; // 升序排序
});
console.log(arr)
5.更新时的一个问题
会出现修改数据不奏效的问题,这是因为直接拿到数组的索引值来改变数据是不能奏效的,Vue内部不答应,我们可以用splice语句一集下问题道德Vue.set语句 ,下面6,7,8,9都会一直在探讨这个问题
<!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><button @click="updateMei">更新马冬梅的信息</button><ul><li v-for="(p,index) of persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{persons:[{id:'001',name:'马冬梅',age:30,sex:'女'},{id:'002',name:'周冬雨',age:31,sex:'女'},{id:'003',name:'周杰伦',age:18,sex:'男'},{id:'004',name:'温兆伦',age:19,sex:'男'}]},methods: {updateMei(){// 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:'男'})}}}) </script>
6.Vue监测数据改变的原理_对象
先探讨Vue检测对象的数据改变,再检测数组数据的改变
首先我们知道data数据中的属性最终会放在vm中,但是data数据到vm数据需要经历两步:
Vue中实现数据监视:
1.加工data
2.vm._data=data
reactive代表响应式 是数据改变页面也会改变
7.模拟一个数据监测
步骤一:大家可能会想既然只要检测到data中数据的改变页面也会改变,并且控制条也要输出,那我只要自己写不就可以了吗?
<script>let data = {name: '哈哈哈',address: '河南'}// 如果不写定时器 没办法实时检测数据的改变 所以采用定时器进行数据的实时监测// 并且需要引入变量tmp 这样做是为了让name值最后恢复与temp相同的值 省的代码一直被检测到发生变化,也就是让他们每次都保证相同 只进行数值发生改变的时候才会有变化let tmp = '哈哈哈'setInterval(() => {if (data.name !== '哈哈哈') {console.log('name被修改了', name)}}, 100)</script>
缺点:我们总不能每次数据改变就去开定时器吧,所以监测数据还是采用getter和setter
步骤二:我们试试使用Object.defineProperty匹配getter和setter
<script>let data = {name: '哈哈哈'}Object.defineProperty(data, 'name', {get () {return data.name},set (val) {data.name = valconsole.log('name被修改')}})</script>
结果表示内存溢出:
原理:重复调用 不会停
使用Obverser方法检测数据属性的变化
<script>let data = {name: '哈哈哈',address: '河南'}const obs = new Observer(data)// 准备一个实例let vm = {}//将我们写出来的obs给data和vm._datavm._data = data = obs//创建一个监视的实例对象 用于监视data中的属性的变化function Observer (obj) {//汇总对象中所有的属性形成一个数组const keys = Object.keys(obj)//遍历keys.forEach((k) => {Object.defineProperty(this, k, {get () {//obj[k] 则是使用方括号语法来访问或设置 obj 对象上对应名称的属性。这是正确的做法,//因为它允许使用变量来动态地引用对象的属性return obj[k]}, set (val) {console.log(`${k}被改变了,我要去解析模版,生成虚拟dom,。。。我要开始忙了`)obj[k] = val}})})}</script>
缺点1:改变属性的时候必须全称 并且我们改变属性值之后会立刻显示在页面上
缺点2:对象中还有对象 多层 使用Observer只能监测一层对象属性,对象中如果嵌套对象,则不能检测;而Vue监测则是多层监测,直到找到不是对象的才罢休
模拟的数据监测:
Vue监测的数据:
所以引出了大牛还得数Vue监测数据的改变 ;只要改变属性 就能调用setter接着解析模版
8.Vue.set()方法
步骤一:要求学生添加性别,属于动态添加,譬如:用户点击了才会添加性别,所以我们不能直接添加:这里有一些问题需要理解:如果我们定义在student下的性别,在模版中写出可以在页面展示出来;如果没有在data中定义,写student.sex不会报错,因为只是未定义,undefined不会报错;但如果只是写sex就会报错
<div id="root"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><hr><h2>姓名:{{student.name}}</h2><!-- <h2>性别:{{student.sex}}</h2> --><!-- <h2>性别:{{sex}}</h2> --><!-- //不会报错 --><h2>性别:{{undefined}}</h2><h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2><ul><!-- 参数 如果有两个参数 那么用小括号 并且因为只是将数组元素展示在页面上 不涉及元素的添加和删除 直接index --><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {name: '哈哈哈',address: '南京',student: {age: {rAge: 19,sAge: 29},friends: [{ name: 'jerry', age: 35 },{ name: 'tony', age: 36 }],// sex: '男'}}});</script>
步骤二:如果在控制台动态添加性别,会发现不会展示在页面上 后添加的不会有对应的getterhesetter 也就是不存在响应式
<div id="root"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><hr><h2>姓名:{{student.name}}</h2><h2>性别:{{student.sex}}</h2><h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2><ul><!-- 参数 如果有两个参数 那么用小括号 并且因为只是将数组元素展示在页面上 不涉及元素的添加和删除 直接index --><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {name: '哈哈哈',address: '南京',student: {name: 'tom',age: {rAge: 19,sAge: 29},friends: [{ name: 'jerry', age: 35 },{ name: 'tony', age: 36 }],// sex: '男'}}});</script>
所以根据Vue内部特性 也就是需要想用什么事先添加好 想要做到动态添加特性 需要是Vue.set()
步骤三:首先控制台打印:
两种添加响应式属性的方法:
第一种:Vue调用的API:Vue.set()
第一个参数表示往谁身上追加属性;第二个是追加的属性 第三是追加的属性值
第二种:vm.$set()
使用数据代理的方法知道:通过Object.defineProperty()原本是修改data上的属性转化为修改vm上的属性
使用button按钮添加
<div id="root"><h2>学校名称:{{school.name}}</h2><h2>学校地址:{{school.address}}</h2><h2>学校校长:{{school.leader}}</h2><hr><h1>学生信息</h1><button @click="addSex">添加一个性别属性,默认值是男</button><h2>姓名:{{student.name}}</h2><!-- //有性别则展示 没有性别不展示 --><h2 v-if="student.sex">性别:{{student.sex}}</h2><h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2><ul><!-- 参数 如果有两个参数 那么用小括号 并且因为只是将数组元素展示在页面上 不涉及元素的添加和删除 直接index --><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {school: {name: '哈哈哈',address: '南京',},student: {name: 'tom',age: {rAge: 19,sAge: 29},friends: [{ name: 'jerry', age: 35 },{ name: 'tony', age: 36 }],// sex: '男'}}, methods: {addSex () {Vue.set(this.student, 'sex', '女')}}});</script>
有局限:
必须在data中某一个对象中添加属性 不能直接在data下追加属性
9.Vue监测数据的改变_数组
Vue内部没有为数组匹配对应的getter和setter
Vue中使用push通过包装的思想,也就是Vue中使用的push,不是数组上原型对象上的push,而是Vue的push会经过两步:
1.调用原型对象上的push,2.重新模版解析,然后数组更新检测,对数据进行增删改查
并且返回的是真实能够影响到数组的,例如:filter 不能影响到数组 没有返回值
<div id="root"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><h1>爱好</h1><ul><li v-for="(h,index) in student.hobby" :key="index">{{h}}</li></ul><h1>朋友们</h1><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {name: '哈哈哈',address: '南京',student: {name: 'tom',age: {rAge: 19,sAge: 29},friends: [{ name: 'jerry', age: 35 },{ name: 'tony', age: 36 }],hobby: ['抽烟', '喝酒', '烫头']}}});</script>
在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()2.Vue.set() 或 vm.$set(),
理论来源
所以更新时的问题,修改数据的解决办法是:不能使用数组元素的修改方法 而应该通过包装了的vue语句
使用Vue.set() vm.$set()来实现替换 也是响应式 但是用得不多
10 总结Vue数据检测
<div id="root"><h1>学生信息</h1><h3>姓名:{{student.name}}</h3><h3>年龄:{{student.age}}</h3><!-- 2.当时没有的 添加一个性别 没有性别不要再出现 --><h3 v-if="student.sex">性别:{{student.sex}}</h3><!-- 1.逻辑简单 直接加加 --><button @click="student.age++">年龄一点一加</button><br><br><!-- 2.当时没有的 添加一个性别 没有性别不要再出现 --><button @click="addSex">添加性别属性,默认值:男</button><br><br><!-- 3.修改性别 里面是正常的js表达式 由于逻辑简单 直接写 --><button @click="student.sex='未知'">修改性别</button><br><br><!-- 4.使用unshift列表数组元素首位添加属性 对象里的属性是响应式的 --><button @click="addFriend">在列表首位添加元素</button><br><br><!-- 5.修改第一个朋友的名字为张三--><button @click="updateFirends">修改第一个朋友的名字为张三</button><br><br><!-- 6.添加爱好 --><button @click="addHobby">添加爱好</button><br><br><!-- 7.修改爱好 --><button @click="updateHobby">修改第一个爱好为:开车</button><br><br><!-- 8.Vue检测不到filter的新数组变化 因为不会返回新数组,我们自己把原来的数组替换 过滤掉爱好中的抽烟 --><button @click="removeSmoke">过滤掉爱好中的抽烟</button><h3>爱好</h3><ul><li v-for="(h,index) in student.hobby" :key="index">{{h}}</li></ul><h1>朋友们</h1><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el: '#root',data: {student: {name: 'tom',age: 29,friends: [{ name: 'jerry', age: 35 },{ name: 'tony', age: 36 }],hobby: ['抽烟', '喝酒', '烫头']}}, methods: {addSex () {// this.student.sex = '男'// 一开始没有 后来有的vm.$set(this.student, 'sex', '男')}, addFriend () {this.student.friends.unshift({ name: 'amy', age: 23 })}, updateFirends () {//可是直接按照数组的形式来写 因为是对象数组索引值赋值修改,Vue不承认,没有对应的egetter和setter;//但是对象中有属性就会有getter和setter this.student.friends[0].name = '张三'}, addHobby () {this.student.hobby.push('学习')}, updateHobby () {//不能直接通过数组索引值修改// this.student.hobby.splice(0, 1, '开车')Vue.set(this.student.hobby, 0, '开车')}, removeSmoke () {this.student.hobby = this.student.hobby.filter((h) => {return h !== '抽烟'})}}});</script>
数据劫持:有人修改了student,对应的setterr会被调用(感知),然后模版解析
总结:
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。2. 如何监测对象中的数据?通过setter实现监视,且要在new Vue时就传入要监测的数据。(1).对象中后追加的属性,Vue默认不做响应式处理(2).如需给后添加的属性做响应式,请使用如下API:Vue.set(target,propertyName/index,value) 或 vm.$set(target,propertyName/index,value)3. 如何监测数组中的数据?通过包裹数组更新元素的方法实现,本质就是做了两件事:(1).调用原生对应的方法对数组进行更新。(2).重新解析模板,进而更新页面。4.在Vue修改数组中的某个元素一定要用如下方法:1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()2.Vue.set() 或 vm.$set()特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!