Vue
mvvm框架是什么?
mvvm框架(model-view-viewMode),本质是mvc框架的改进版,mvc框架一旦项目复杂度越来越高,代码量大,维护起来很难,尤其管理层,controller
就有了viewMOdel,有前端人员生成和维护的视图数据层,前端通过从后端获取到的model数据进行转换,封装成view层预期的数据,用来生成view层的视图数据模型
Vue设计模式
vue使用发布订阅者模式,在vue中使用observer和definereactive两个方法结合,对数据递归劫持,用watch这个类来进行监听订阅,dep用于解耦合,当数据变更后触发set方法,之后调用dep.notiify通知视图更新
发布订阅者模式
<!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>
<script>
/* 观察者 */
class Watcher{
constructor(){
//用来存储事件类型和事件函数的一个对象
// 进入超市首先推一个小推车
this._message = {}
}
//消息订阅
on(type,fn){
// 想买方便面,我去方便面区域看看有没有想要买的牌子
//往消息盒子当中存储事件类型与事件函数
// 找到方便面区域了
if(this._message[type]){
//第二次往后存储事件类型和事件函数
// 把康师傅放进小推车里
this._message[type].push(fn)
}else{
//第一次存储事件类型与事件函数
this._message[type] = [fn]
}
}
//发布消息
// 结账
emit(type,...rest){
/*
1. 先看消息盒子里面有没有事件类型,没有就弹出一句话,请先订阅
*/
// 如果不买东西请你出去
if(!this._message[type]) return alert("请先订阅消息")
//遍历消息盒子,看事件类型对应的函数有没有相同的,有相同的,那么就触发
// 收营员先把小推车里的东西一个一个倒出来
this._message[type].forEach(item=>{
//如果消息盒子里面的函数与我们传入进来的函数有一样的,那么触发这个函数
// 倒出来的同时我们可以看看有没有不想买的,有没有想买的
for(let i = 0 ; i < rest.length ; i++){
// 我们传入进来的函数有一样的,那么触发这个函数
// 如果是想买的就付款这个东西
if(rest[i] === item){
//触发这个函数
item(i);
}
}
})
}
//取消订阅
// 从收银台装入自己的购物袋中
off(type,fn){
/*
1. 先看消息盒子里面有没有事件类型,没有就弹出一句话,请先订阅
*/
// 如果不买东西请出去
if(!this._message[type]) return alert("请先订阅消息")
//遍历所有的消息盒子得到的是消息盒子里面的对象的value就是每一个函数,如果其中有函数与我们传入进行的函数是一样的,那么删除这个函数
// 还是一件一件看我们挑选好了的东西
for(let i= 0 ; i< this._message[type].length ; i++){
// item函数与我们传入进行的函数是一样的,那么删除这个函数
// 如果这个东西是我已经付钱了,我就拿走
if(this._message[type][i] === fn){
this._message[type].splice(i,1)
//解决数组塌陷
i--
}
}
}
//清除订阅
// 归还购物车
clear(type){
/*
1. 先看消息盒子里面有没有事件类型,没有就弹出一句话,请先订阅
*/
// 保安对你说 要是不买东西就出去
if(!this._message[type]) return alert("请先订阅消息")
//清除消息盒子里面的事件类型
// 扔掉购物车
delete this._message[type]
}
}
//先建立两个函数
function fn1(){
console.log("我是函数1");
}
function fn2(){
console.log("我是函数2");
}
let w = new Watcher();
//消息订阅
w.on("buy",fn1)
w.on("buy",fn2)
//发布消息
w.emit("buy",fn1,fn2)
//删除订阅
w.off("buy",fn1)
w.off("buy",fn2)
//清除订阅
w.clear("buy");
console.log(w);
</script>
</body>
</html>
Vue响应式原理:
数据劫持 Object.defineProperty(): 1. 只能监听对象(Object)的属性,不能监听数组的变化,无法触发push, pop, shift, unshift,splice, sort, reverse。 2. 必须遍历对象的每个属性。3. 只能劫持当前对象属性,如果想深度劫持,必须深层遍历嵌套的对象。
数据劫持 Proxy: 1. Proxy 可以直接监听对象而非属性。2. Proxy 可以直接监听数组的变化。3. Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是Object.defineProperty 不具备的。4. Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改。5. Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利。
<!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">
<input id="ipt" type="text">
<h1 id="h1"></h1>
</div>
</body>
<script>
// app理解成vue组件实例this
var app = {}
// 理解成你手动放在data选项上的数据
var c = 0
var name = '杨'
// var data = {
// c : 0,
// name : '杨'
// } 下面的逻辑就用for of 遍历
Object.defineProperty(app, 'count', {
get() {
console.log('有人访问了count');
return c
},
set(newVal) {
c = newVal
console.log('有人修改了count');
}
})
// 实现响应式底层原理,劫持name普通属性,将其变成一个响应式变量
Object.defineProperty(app, 'name', {
get() {
console.log('有人访问了name');
return name
},
set(newVal) {
name = newVal
console.log('有人修改了name');
// 当有人修改响应式变量时,我们通知Watcher去操作真实DOM更新视图
Watcher('name')
}
})
// 页面初始化操作
(function init() {
Watcher('name')
// 初始化事件绑定
document.getElementById('ipt').addEventListener('input', function(e) {
// 给input表单绑定事件
// 当表单的值发生变化
app.name = e.target.value
})
})() // 初始化自执行
// 依赖收集(用于记录DOM结构和响应式变量之间的一对一的关系):vue源码map结构
var deepObj = {
// h1: 'name',
name: ['h1','ipt'],// name: []可以是数组,在多个dom节点
}
// 用于更新视图(背后是DOM操作) 根据依赖收集的记录,进行更新视图
function Watcher(key) { // key 可以换成reactive
// 把name这个响应式变量渲染到视图结构中去
var idArr = deepObj[key]
document.getElementById(idArr[0]).innerText = app[key]
document.getElementById(idArr[1]).value = app[key]
}
init()
</script>
</html>