文章目录
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>