响应式数据、双向数据绑定、过滤器、计算属性-Vue2

发布于:2023-01-04 ⋅ 阅读:(505) ⋅ 点赞:(0)

1.响应式数据

1.1什么是响应式数据

只能由代码改变UI或者只能由UI改变代码
如果内存中的数据变化了 页面UI也会动态跟着刷新 这种数据就是响应式数据

1.2Vue中响应式数据原理

1.2.1Object.defineProperty()原理

  • set:在写入属性的时候调用的函数,默认值是undefined
  • get:在读取属性时调用的函数,默认值是undefined
var obj ={};
Object.defineProperty(obj,"b",{
    set:function(a){
        console.log("正在进行赋值"+a);
    },
    get:function(){
        return "aaa"
    }
})
obj.b="bbb"; // 正在进行赋值bbb
console.log(obj.b); // aaa

1.2.2Vue响应式数据原理

  • 采用数据劫持方式,即 Object.defineProperty() 劫持 data 中各属性,实现响应式数据
  • 若 data 中某属性多次发生变化,watcher 仅会进入更新队列一次
  <div id="myapp">
         <h1 id="title"></h1>
         <p id="msg"></p>
      </div>
      <script>
         function MyVue(option) {
            let _myvm = {};
            //劫持
            let arr = Object.keys(option.data); //["title","msg"]
            // _myvm[arr[0]]=option.data[arr[0]]
            // _myvm[arr[1]]=option.data[arr[1]]
            for (let i = 0; i < arr.length; i++) {
               Object.defineProperty(_myvm, arr[i], {
                  set(v) {
                     //写入的时候调用
                     //劫持
                     option.data[arr[i]] = v;
                     //响应-刷新页面
                     let title = document.querySelector(option.el + " #title");
                     let msg = document.querySelector(option.el + " #msg");
                     title.innerHTML = _myvm["title"];
                     msg.innerHTML = _myvm["msg"];
                  },
                  get() {
                     //读取的时候调用
                     return option.data[arr[i]];
                  },
               });
               //  写入
               _myvm[arr[i]] = option.data[arr[i]];
            }
            return _myvm;
         }
         var myvm = new MyVue({
            el: "#myapp",
            data: {
               title: "mytitle66666",
               msg: "mymsg666666",
               obj: { age: 10, name: "karen" },
               arr: [10, 230, 5],
            },
         });

2.双向数据绑定

2.1定义

双向数据绑定:代码改变UI,UI也能改变代码
双向数据绑定的实现: 2种方式

2.2双向数据绑定实现方式

  • 1.自己实现,vue可以自己实现(没必要) 微信开发可以自己实现(只能自己实现) 利用input事件,用户交互的时候,获取用户输入的值,然后把值绑定到data容器中
  • 2.系统指令:v-model
  • vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
1.如果数据容器中的数据变了也会让页面刷新(dom操作让页面改变)
2.如果用户操作dom,改变了页面,反之也会让数据容器中的数据的值改变
3. 对 input 使用 v-model,实际上是指定其 :value 和 @input

2.2.1:value 和 @input + v-model

<div id="app">
          <p>{{msg}}</p>
         <input type="text" :value="msg" @input="myinput" />
         <input type="text" v-model="msg" />
         //对 input 使用 v-model,实际上是指定其 :value 和 @input
      </div>
      <script>
         new Vue({
            el: "#app",
            data: {
               msg: "hello",
            },
            methods: {
               myinput(e) {
                  //   console.log(e.target.value);
                  this.msg = e.target.value;
               },
            },
         });
      </script>

3.循环渲染中key的原理
data中for循环的容器数据个数发生变化时,会跟for中的vm节点个数作比较
如果数据多了,会在vm节点后面增加对应数量的节点,并不会重新创建所有节点,然后vm去更新对应的DOM
然后就去刷新数据到界面: 按照for的数据容器中的数据顺序来渲染如果用户以前操作过旧节点,那么新数据顺序可能会出现跟旧节点顺序不匹配的效果(旧节点跟旧数据没有对应起来)
解决方案: for循环时把数据跟创建的节点利用给元素绑定唯一key值

简答:因为vue在刷新页面组件时,会把旧节点跟新vm节点做比较,如果要增加节点,并不会删除旧节点,而是复用
这样会导致节点跟数据没有绑定关系而重新渲染,用key可以将数据与节点绑定起来

  • key 意义
    让数据跟真实的dom一一关联 使之不发生渲染混乱的情况
    提高绘制渲染效率
 <div id="app">
         <h1>请选择你最喜欢的5件商品(免费赠送给你)</h1>
         <div v-for="(el,index) in arr" :key="el.id">
            <input type="checkbox" name="goods" :value="el.id" />
            <b>{{el.title}}</b>
         </div>
         <button @click="addmore">加载更多</button>
      </div>
      <script type="text/javascript">
         new Vue({
            el: "#app",
            data: {
               id: 5,
               arr: [
                  { id: 1, title: "包包1", price: 123 },
                  { id: 2, title: "鞋子1", price: 123 },
                  { id: 3, title: "衣服1", price: 123 },
                  { id: 4, title: "商品1", price: 123 },
               ],
            },
            methods: {
               addmore() {
                  let obj = {
                     id: this.id++,
                     title: "商品" + this.id,
                     price: 123,
                  };
                  // this.arr.push(obj)
                  this.arr.unshift(obj);
               },
            },
         });
      </script>

3.过滤器filters

  • filter主要用于数据展示之前的处理
  • 过滤器只能用在v-bind或者插值表达式中

3.1过滤器作用

  • 过滤器可以用来筛选出符合条件的,丢弃不符合条件的;加工车间既可以筛选,又可以对筛选出来的进行加工。
  • 过滤器中传入多个参数:

3.2过滤器中传入多个参数

{{ message | filterA('arg1', arg2) }}
  • 这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 ‘arg1’
    作为第二个参数,表达式 arg2 的值作为第三个参数。

3.3多个过滤器串联

{{ message | filterA | filterB }}
  • 在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到
    filterB 中。
<div id="app">
         <p>{{a}}</p>
         <p>{{timerformatter(birth)}}</p>
         <p>{{birth|tool|tool2}}</p>
         //多个过滤器串联,用于过滤
         <p>{{birth|tool3(100,200)}}</p>
         <a :href="url|tool4">baidu</a>
         <button @click="change1">change1</button>
      </div>
      <script>
         var vm = new Vue({
            el: "#app",
            data: {
               a: "测试",
               birth: "2007-02-03",
               url: "www.baidu.com",
            },
            methods: {
               change1() {
                  this.a = "66666";
               },
               timerformatter(str) {
                  console.log("调用了函数timerformatter--methods");
                  let age =
                     new Date().getFullYear() - new Date(str).getFullYear();
                  return age + "岁";
               },
            },
            filters: {
               tool(str) {
                  console.log("调用了函数tool--filters");
                  let age =
                     new Date().getFullYear() - new Date(str).getFullYear();
                  return age + "岁";
               },
               tool2(str) {
                  if (parseInt(str) > 18) {
                     return "可以喝酒:" + str;
                  } else {
                     return "禁止进入:" + str;
                  }
               },
               tool3(arg1, arg2, arg3) {
                  console.log(arg1, arg2, arg3);
                  return "hello";
               },
               tool4(url) {
                  // 后端传过来的url通常没带协议
                  return "http://" + url;
               },
            },
         });
      </script>

3.4ps:

过滤器中通过this是获取不到vue实例的!!!在其中发起http请求也会失
败,因此,为了获取后台数据,我们可以在mounted中先获取tableData,然
后,把这个tableData作为过滤器的第二个参数进行传递!!!!

在这里插入图片描述
在这里插入图片描述

4.计算属性computed

4.1把computed中的方法当做属性使用,会返回一个数据供使用:

new Vue({
	el:"",//关联界面元素
	data:{},//vm的数据源
	methods:{},//方法
	filters:{qq(){}},//过滤器
	computed:{xx(){}} //xx就是一个计算属性
})
<div id="app">
			<p>{{msg}}</p>
			<p>方法获取的年龄:{{getAge()}}</p>
			<p>计算属性获取的年龄:{{getAge_computed}}</p>
			<button @click="change">改变birth的值看看年龄变不变</button>
		</div>
new Vue({
				el: "#app",
				data: {
					msg: "hello",
					birth: "1995-02-03"
				},
				methods: {
					getAge() {
						var age = new Date().getFullYear() - new Date(this.birth).getFullYear()
						return age + "岁"
					},
					change() {
						this.birth = "1996-02-03"
                        this.xx=200   //方法中的函数可以使用下面的computed中的属性
					}
				},
				computed:{
                   //计算属性第一种用法,会先去判断使用的数据源,变了没有,没有变就不会重新运行
					getAge_computed(){  //函数方法
						var age = new Date().getFullYear() - new Date(this.birth).getFullYear()
						return age + "岁"
					}
					//计算属性第二种用法
					xx:{   //属性
						set(oldvalue){},   //设置
						get(){}  //获取   只有当里面的数据改变了,才会执行get
					}
				}
			})

4.2计算属性和方法的区别

  • 计算属性会把使用到的data中的属性缓存起来,如果计算属性中使用到的data中那部分数据变了才会重新调用计算属性,防止页面发生大量重复计算,提升js运行效率.
  • methods方法没有计算结果缓存起来,data任何数据发生改变,方法都会被重新调用一遍方法常常是作用的事件使用,计算属性常常是动态计算结果时使用.

4.3关于计算属性 函数什么情况下调用

计算属性使用时当做属性使用
计算属性设计时当做函数设计(就像es6中的属性)
当计算属性的函数中使用到的data中的数据发生变化时,计算属性就会重新执行并刷新UI
1.如果是修改了data中监听的某个的属性值 计算属性就会运行
2.如果是修改了data中监听的某个属性值内部的数据,计算属性就不会重新运行
比如:计算属性使用的是data中的一个数组,某个交互把数组内部的某个下标的值改了,但是这个数组没有改变,就不会触发计算属性
3.1解决2的办法1:把修改后的数组重新赋值给data,让引用发生变化,来触发计算属性
3.2解决2的办法2:赋值 JSON.parse(JSON.stringfy(data))
(计算属性就是处理数据源中的数据,然后用于渲染,而且会监听计算属性中使用到的数据源,然后把计算的结果缓存,如果监听的数据源发生了变化,才会重新计算,否则直接使用缓存的数据)

缺点:如果简单的运算也用计算属性,反而会增加资源消耗(计算属性会监听计算的值,而且会缓存计算的结果),比如:生日转年龄的时候,可以用过滤器

change(arg,index1){
	 this.arr[index1]=arg
	 this.arr=[...this.arr]
}
computed:{
	total(){
		//eval(this.arr.join("+"))
		//  eval("100+200+88")==>把字符串当做代码运行 产生运算结果
		return  eval(this.arr.join("+"))
		}
	}

4.4计算属性案例-计算总价

  <div id="app">
         <h1>{{birth}}</h1>
         <p>methods---{{age1(birth)}}</p>
         <p>computed---{{age}}</p>
         <p>{{x}}</p>
         <button @click="change1">change1--{{msg}}</button>
      </div>
      <script>
         //刷新页面==>模板重新渲染 重新取值
         var vm = new Vue({
            //关联模板的选择器
            el: "#app",
            //数据源
            data: {
               birth: "1996-02-03",
               msg: "hello",
            },
            //方法
            methods: {
               change1() {
                  this.msg = "6666";
                  this.birth = this.birth;
                  this.x = 30;
               },
               age1(str) {
                  console.log("方法");
                  return new Date().getFullYear() - new Date(str).getFullYear();
               },
            },
            //过滤器
            filters: {},
            // 计算属性
            computed: {
               age() {
                  console.log("计算属性");
                  return (
                     new Date().getFullYear() -
                     new Date(this.birth).getFullYear()
                  );
               },
               x: {
                  get() {
                     console.log(this, 11111);
                     return (
                        new Date().getFullYear() -
                        new Date(this.birth).getFullYear() +
                        "岁"
                     );
                  },
                  set(v) {
                     console.log(v);
                     this.birth = `${2022 - v}-02-03`;
                  },
               },
            },
         });
      </script>

网站公告

今日签到

点亮在社区的每一天
去签到