一、MVC模型的底层实现
1.1 相关代码
<!DOCTYPE html>
<html lang="zh-CN">
<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>MVC模型</title>
<style></style>
</head>
<body>
<div id="view"></div>
<div id="btn"></div>
<script type="text/javascript">
//MVC模型分为下面三部分:
//1.model(模型层)模拟后端提供来的数据
const data = {
name:'小明'
}
//2.view(视图层)将数据在页面渲染的操作
function showName(){
//方法定义获取dom中的id为view的node节点,并通过innerHTML来动态写入html标签。
document.getElementById('view').innerHTML = `<h3>${data.name}</h3>`
document.getElementById('btn').innerHTML = `<button οnclick="switchName()">切换名字</button>`
}
//在页面调用showName()方法
showName()
//3.controller(控制台层)
// 通过视图层点击按钮,对model模型层的数据进行修改并且调用showName(),方法刷新页面(需要手动刷新DOM页面才能看到修改的数据)
function switchName(){
if(data.name == '小红'){
data.name = '小明'
}else{
data.name = '小红'
}
showName()
}
</script>
</body>
</html>
1.2 运行截图
1.3 遇到的坑
问题1:$ { }与{{ }}的区别?
回答1:$ { }是原生JS中,模板字符串中,某变量对应的值,{{ }}是vue中,用来调用组件属性某变量对应的值。
- $ { }:在js中的模板字符串,而 $ {A}是代表变量A的值插入进此模板字符串中,通常模板字符串与${ }搭配使用。
- {{ }}:是vue的语法特性,通常是用来在html中插入组件属性,通常为data函数中的数据。
二、MVVM模型的底层实现
2.1 VUE2
2.1.1 Object.defineProperty()的简单应用
首先介绍vue2的Object.defineProperty()的应用,因为MVVM模型在视图层与模型层中有一个视图模型,而VUE2中视图模型层的响应式数据双向绑定,是通过Object.defineProperty()实现的,所以先看看Object.defineProperty()的相关代码。
const data ={
name:'小蓝'
}
//Object.defineProperty() 表示数据劫持
//它是Object类的defineProperty
// 第一个参数为劫持对象,第二个参数为劫持对象中的属性名称,第三个定义get和set的方法
Object.defineProperty(data,'name',{
// 访问劫持数据时触发
get(){
console.log('get====')
return '我是从劫持数据中get所返回的值'
},
//需要给挟持数据进行赋值的时候触发
set(getNewValue){
console.log('set====',getNewValue)
}
})
//给模型层的数据赋值时,触发数据劫持中的set方法,阻止直接赋值并把想赋的值传到set的getNewValue中
data.name = '想要传数据'
//视图层需要读取model的值时,触发数据劫持中的get方法,所打印出来的值是get方法中返回的值。
console.log('data.name',data.name)
2.1.2 基于Object.defineProperty()的MVVM模型实现(vue2.0)
2.1.2.1 VUE2的MVVM模型底层实现相关代码
<!DOCTYPE html>
<html>
<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>MVVM模型</title>
<style></style>
</head>
<body>
<div id="view"></div>
<script type="text/javascript">
//基于Object.defineProperty()的MVVM模型实现(vue2.0)
//1.model(模型层)后端传过来的数据
const data = {
name:'小红'
}
//2.view(视图层)数据渲染的操作
function showName(){
//下方定义一个input框,表单元素,修改视图层的数据通知视图模型层进行数据劫持进行数据的双向响应式绑定
document.getElementById('view').innerHTML = `<h3>${data.name}</h3>
<input type="text" value="${data.name}" οnchange='changeValue(this)'>
`
}
showName()
//3.viewModel(视图模型层)介于视图与模型之间的桥梁,实现数据的双向绑定
let insideValue = data.name
Object.defineProperty(data,'name',{
//访问data.name中的值执行get
get(){
console.log('get')
return insideValue
},
//修改data.name中的值执行set
set(newValue){
console.log('set')
insideValue = newValue
//调用以下showName()方法中,由于在其中innerHTML中获取了两次模型层中的data.name,所以会进行两次数据劫持的get操作
showName()
}
})
//定义input修改它的值时,对model模型进行赋值并触发视图模型层的数据劫持
function changeValue(setValue){
console.log('输入框中的值为',setValue.value)
data.name = setValue.value
}
</script>
</body>
</html>
2.1.2.2 VUE2的MVVM模型底层实现运行截图
2.1.3 Object.defineProperty()的缺点
vue 2中使用上述数据劫持时会有个问题,就是当对象的属性新增与数组的长度,或是下标添加的元素的时候,会监听不到此对象。
因此vue2也有相应的解决方案,在vue2中会当修改对象或者数组的时候,会对底层进行封装,对原数据原型对象进行重写实现响应式。方法如下:
- 对象:
- 对象新增:this.set(对象,‘属性’,值)
- 对象合并:Object.assign({},对象,新对象)
- 对象的浅拷贝:…对象,{‘属性’:值}
- 数组:
- 数组新增:this.set(数组,下标,值)
- 数组的增删:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
2.2 VUE3
2.2.1 基于proxy的MVVM模型实现(vue3.0)
proxy是个构造函数,从ES6中提出来的,为了解决vue2的Object.defineProperty()的缺陷。通过proxy确定第一个参数确定劫持对象,在里面get与set中再确定进行监听数据的对象以及它的属性名。
let vThree = new Proxy(data,{
insideItem是监听数据对象,indsideIndex是监听数据对象的属性名
get(insideItem,insideIndex){
console.log('get===')
return insideItem[insideIndex]
},
set(insideItem,insideIndex,newValue){
console.log('set===')
insideItem[insideIndex] = newValue
showName()
}
})