目录
一、数据绑定(面试)--响应式数据、双向数据绑定
先执行{{msg}},再执行v-html,所以{{msg}}会被替换
js对象的成员添加三种方式:
obj.age=10 obj["name"]="karen"
Object.defineProperty(obj,"birth",{
set(){},
get(){}
})
1)响应式数据:
1、响应式数据概念:
只能由代码改变UI或者只能由UI改变代码
( 如果内存中的数据变化了 页面UI也会动态跟着刷新 这种数据就是响应式数据)
什么vue的响应式数据:
采用数据劫持方式,即 Object.defineProperty() 劫持 data 中各属性,实现响应式数据
若 data 中某属性多次发生变化,watcher 仅会进入更新队列一次
2、响应式数据的原理——笔试题写代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js"></script>
</head>
<body>
<div id="app">
<!-- <h1 v-html="title">{{msg}}</h1> -->
<h1 v-html="title"></h1>
<p>{{msg}}</p>
<button @click="fn">change1</button>
</div>
<script>
var vm=new Vue({
el:"#app",
data:{
title:"hello",
msg:"666"
},
methods:{
fn(){
for(let i=0;i<10000;i++){
this.title=i
}
}
}
})
</script>
<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]
}
})
</script>
</body>
</html>
3、笔试题
1、对于 Vue 中响应式数据原理的说法,下列哪项是不正确的?
A. 采用数据劫持方式,即 Object.defineProperty() 劫持 data 中各属性,实现响应式数据
B. 视图中的变化会通过 watcher 更新 data 中的数据
C. 若 data 中某属性多次发生变化,watcher 仅会进入更新队列一次
D. 通过编译过程进行依赖收集选择B:视图中的变化会通过 watcher 更新 vm中或者this中 的数据
(一般程序员会说data是数据源,所以就更新data,但是正确的是更新vm)
选择D: 通过编译过程进行依赖收集
数组的函数可以劫持
数组的下标不能劫持
(解决方法:
1、this.$set
2、Vue.set
3、刷新页面中的无关紧要的变量,比如:this.msg="原本的内容" ,程序员常用,但是不是官方的解决方法)
Vue 中的数组变更通知,通过拦截数组操作方法而实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
</script>
<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.js"></script>
<div id="app">
<!-- <h1 v-html="title">{{msg}}</h1> -->
<h1 v-html="title"></h1>
<p>{{msg}}</p>
<p>{{person.age}}</p>
<p>{{arr[2]}}</p>
<button @click="fn">change1</button>
<button @click="look">look</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
title: "hello",
msg: "666",
person: { age: 20 },
arr: [102, 200, 300]
},
methods: {
fn() {
// this.person.age=30
// this.arr[2]="hello"
//没有劫持数组的下标(不能刷新页面),但是look里面会打印hello
// this.arr.splice(2,1,"hello")//劫持了数组的方法(可以刷新页面)
// Vue.set(this.arr,2,"hello")//vue帮我们刷新页面
this.$set(this.arr, 2, "hello")//vue帮我们刷新页面
},
look() {
console.log(this.arr[2])
}
}
})
</script>
</body>
</html>
2)双向数据绑定:
1、双向数据绑定概念:
代码改变UI,UI也能改变代码
双向数据绑定的概念要包括响应式数据绑定,所以要先说响应式数据绑定概念
(如果数据容器中的数据变了也会让页面刷新(DOM操作让页面改变)
如果用户操作DOM,改变了页面,反之也会让数据容器中的数据值改变)
2、双向数据绑定的实现: 2种方式
1.自己实现,vue可以自己实现(没必要) 微信开发可以自己实现(只能自己实现)
利用input事件,用户交互的时候,获取用户输入的值,然后把值绑定到data容器中2.系统指令:v-model
mode是语法糖
<input type="text" :value="msg" @input="myinput">
<input type="text" v-mode="msg">
v-mode="msg" 是 :value="msg" @input="myinput"的合成
3、响应式数据设计原理:
vue2.0:vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue3.0 --使用es6 proxy 代理了data对象
//利用 Object.defineProperty 自己实现双向数据绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj['txt']
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="change1">改变数据容器中的数据</button>
<button @click="look"></button>
<p>{{msg}}</p>
<input type="text" :value="msg" @input="myinput">
<!-- 下面一行代码是上面一行代码的合成 -->
<input type="text" v-model="msg">
</div>
<script>
new Vue({
el:"#app",
data:{
msg:"hello"
},
methods: {
change1(){
this.msg="666" //底层:在执行this对象(vm)的msg的set
},
look(){
console.log(this.msg)
},
myinput(e) {
this.msg=e.target.value
}
},
})
</script>
</body>
</html>
4、面试题——
下列关于 v-model 的说法,哪项是不正确的?
A. v-model 能实现双向绑定
B. v-model 本质上是语法糖,它负责监听用户的输入事件以更新数据
C. v-model 是内置指令,不能用在自定义组件上
D. 对 input 使用 v-model,实际上是指定其 :value 和 :input选择:C:能用在自定义组件上
选择:D.:对 input 使用 v-model,实际上是指定其 :value 和 @input (:oninput)
二、过滤器(面试)
filter主要用于数据展示之前的处理 过滤器只能用在v-bind或者插值表达式中 语法:
<div id="app">
<div v-for="(item) in sinaArr" class="box">
<p v-html="item.user.name"></p>
<img :src="item.user.img">
<p v-text="item.text"></p>
<!-- <p>发表时间:{{item.create_At|timerTool}}</p> -->
<!-- 下面这种写法是不行的:过滤器只能应在v-bind或者插值表达式中 -->
<p v-html="item.create_At|timerTool">发表时间:</p>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
sinaArr: [{ create_At: "Thu Apr 16 14:08:18 +0800 2020", text: "内容1", user: { name: "karen1", img: "1.jpg" } },
{ create_At: "Thu Apr 16 14:01:18 +0800 2020", text: "内容2", user: { name: "karen2", img: "2.jpg" } },
{ create_At: "Thu Apr 16 10:39:18 +0800 2020", text: "内容3", user: { name: "karen3", img: "3.jpg" } },
{ create_At: "4.jpg" }]
},
filters: {
timerFormat(arg) {
var dt1 = new Date(arg)
var dt2 = new Date()
var abstime = dt2 - dt1
if (0 <= abstime && abstime < 1000 * 60) {
return "刚刚"
} else if (1000 * 60 <= abstime && abstime < 1000 * 60 * 60) {
var m = new Date(abstime).getMinutes() + ""
return `${m}分钟前`
} else if (1000 * 60 * 60 <= abstime && dt1.getDate() == dt2.getDate()) {
var m = dt1.getMinutes() + ""
var h = dt1.getHours()
return `今天 ${h}:${m.padStart(2, "0")}`
} else if ((dt1.getDate() + 1) == dt2.getDate()) {
var m = dt1.getMinutes() + ""
var h = dt1.getHours()
return `昨天 ${h}:${m.padStart(2, "0")}`
} else if ((dt1.getDate() + 2) == dt2.getDate()) {
var m = dt1.getMinutes() + ""
var h = dt1.getHours()
return `前天 ${h}:${m.padStart(2, "0")}`
}
}
}
})
</script>
<div id="app">
<p>{{a}}</p>
<p>{{timerformatter(birth)}}</p>
<p>{{birth|tool|tool2}}</p> <!-- birth作为参数传入tool函数,然后结果再作为参数传给tool2-->
<p>{{birth|tool3(100,200)}}</p>
<!--birth是第一个参数,100,200是第二、三个参数-->
<a :href="url|tool4">baidu</a>
<!-- <p v-text="birth|tool3"></p> 不能用在这里 -->
<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) {
return "http://" + url
}
}
})
</script>