十四天学会Vue——Vue核心中篇(理论+实战)(第二天)

声明:是接着上篇讲的哦,感兴趣可以去看一看~

  这里一些代码就不写了,为了缩减代码量,大家知道就可以了:  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">
        <!--1. 绑定class样式  字符串写法   适用于:样式的类名不确定 需要动态指定 -->
        <div class="basic" :class='mood' @click="changeMood">{{name}}</div><br><br>
        <!-- 2.绑定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>
        <!-- 3.绑定class样式  对象写法   要绑定的样式个数确定、名字也确定,但要动态决定用不用-->
        <!-- <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>

        <!-- 1.绑定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> -->
        <!-- 2.绑定style样式 数组写法 适用于:要绑定的样式个数确定、名字也确定 但要动态决定用不用 -->
        <div class="basic" :style="styleArr">{{name}}</div><br><br>
    </div>
</body>

<script type="text/javascript">
    Vue.config.productionTip = false
    const 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  非零值代表true
                    if (this.sortType) {
                        arr.sort((p1, p2) => {
                            //这一次提到sortType是为了判断是不是为1
                            return 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 = false
			
			const 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 = val
                console.log('name被修改')
            }
        })


    </script>

结果表示内存溢出:
在这里插入图片描述
原理:重复调用 不会停
在这里插入图片描述

使用Obverser方法检测数据属性的变化

<script>
        let data = {
            name: '哈哈哈',
            address: '河南'
        }
        const obs = new Observer(data)
        // 准备一个实例
        let vm = {}
        //将我们写出来的obs给data和vm._data
        vm._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的根数据对象 添加属性!!!
  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值