Vue初探
Vue是一种渐进式的框架,渐进式的意思就是可以自底向上逐层应用,不管是大型项目或是小项目,都可以用Vue来做。 Vue的核心就是可以使用非常简洁的语法将数据渲染DOM,Vue本身实际上就是一个类,我们在使用的时候通过创建Vue的实例来进行使用。vue中每个组件都是Vue的一个实例,都可以调用Vue原型上的方法。
MVVM 与 MVC
MVVM(Model-View-ViewMode),是MVC(Model-View-Controller)的加强版,MVVM将View的状态和行为抽象化,让我们将view和业务逻辑分开,这些事情ViewModel帮我们实现了。在Vue中是数据驱动视图渲染,所以我们只需要关注数据层即可。
MVVM & MVC的区别
1.MVC 中 Controller演变成 MVVM 中的 ViewModel
2.MVVM 的各个部分之间的通信都是双向的,而MVC则是单向通信
3.MVVM 做到了将页面与数据逻辑分离到js中实现,而MVC没有实现分离
4.MVVM 通过数据来驱动视图层的显示而不是节点操作
5.MVVM 主要解决了: MVC 中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验
开始使用Vue
我们在new Vue的时候,会把data中的数据全部挂载到实例上,之后我们就可以基于this.xxx去访问数据,挂载到实例上的数据我们可以直接使用
{{}}小胡子语法,在页面中使用。对于data中的数据Vue基于Object.defineProperty进行了数据劫持,但是只有在data中的数据会被劫持,如果我们直接使用this.xxx = xxx手动添加的数据不会被劫持
数据劫持的规则
只有在最初new Vue的时候data中的数据才会做“深层次”的数据劫持
1 对data中的每一项数据进行数据劫持
2 对于对象 会对其每一个属性分别做数据劫持
3 对于数组,并不会对其索引做数据劫持 所以按照索引司改数组某一项的值 不会触发视图的更新 但是它会重构数组的
原型指向 让其__proto__指向自己构建的原型对象[arr.__proto__ === 自己构建原型对象:自己构建的原型对象]
__proto__ === Array.prototype 在自己构建的原型对象中 对数组的七个方法进行了重写
push/pop/shift/unshift/reverse/splice/sort
基于这七个方法修改数组中某一项的内容 不仅可以修改值 而且还可以通知视图渲染
手动挂载数据让其响应式
手动添加的数据,以及后期加入到data中的数据,并没有做数据劫持。所以我们一般使用的时候,会把所有需要的数据放在data中进行初始化
vm.arr[0] = 100;不会更新视图
// 解决方法
//1.整体修改arr的值
this.arr = [1,2,3];
//2.基于$set修改某一项的值
this.$set(this.arr,0,100);
3.标准解决方案,使用Vue重写的七个方法进行修改
Vue2响应式数据的原理
在Vue2中的响应式数据是依靠Object.defineProperty实现的
// 检测是否为纯粹对象
const toString = Object.prototype.toString;
const isPlainObject = function isPlainObject(obj) {
let proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") return false;
proto = Object.getPrototypeOf(obj);
if (!proto) return true;
Ctor = proto.hasOwnProperty('constructor') && proto.constructor;
return typeof Ctor === "function" && Ctor === Object;
};
// 视图编译的方法
const compile = function compile() {
console.log('开始编译视图');
};
// 重构原型对象
const proto = {};
Object.setPrototypeOf(proto, Array.prototype);
['push', 'shift', 'pop', 'unshift', 'splice', 'sort', 'reverse'].forEach(name => {
Object.defineProperty(proto, name, {
enumerable: false,
writable: true,
configurable: true,
value: function mutator(...params) {
// 按照数组内置的相关方法,去修改对应内容
Array.prototype[name].call(this, ...params);
// 通知视图重新渲染
compile();
}
});
});
// Vue2数据监听劫持的原理
const observe = function observe(obj) {
let isObject = isPlainObject(obj),
isArray = Array.isArray(obj);
// 不是数组和对象,则不需要进行任何的处理
if (!isObject && !isArray) return;
// 如果是数组:重构原型指向
if (isArray) {
Object.setPrototypeOf(obj, proto);
// 递归进行深层次处理
obj.forEach(item => observe(item));
return;
}
// 冻结和密封的对象也不能做劫持
if (Object.isFrozen(obj) || Object.isSealed(obj)) return;
// 正常的对象,则需要做劫持
let keys = Reflect.ownKeys(obj),
proxy = { ...obj };
keys.forEach(key => {
Object.defineProperty(obj, key, {
get() {
return proxy[key];
},
set(val) {
if (proxy[key] === val) return;
proxy[key] = val;
// 通知视图渲染
compile();
}
});
// 递归进行深层次处理
observe(obj[key]);
});
};
let data = {
msg: 'Hello World',
obj: {
x: 10,
y: 20
},
arr: [10, 20, 30, [40, 50], { n: 10, m: 20 }]
};
observe(data);
可以让视图更新的七个方法
vue中数据更改了 视图一定会更新 前提是数组得调用那七个变异方法 更新数组想让视图更新 只能使用一下七个方法
pushpopshiftunshiftsplicesortreverse这七个方法为变异数组方法 不是JS原生的方法 是vue中的方法 这其中无论那个数组方法执行 都会更新视图
数组的七个变异方法的原理
原理就是:原型重写
把Vue劫持的数组的__proto__指向Vue自己创建的空间(Object.create(Array.prototype)), 这个空间的__proto__指向 Array的原型;空间里边有这7个变异方法 那么为什么这七个方法的执行能够更新视图 是因为这七个方法执行完成之后 都去调用一个更新视图的api ->notify()
如果我们想要使后续加入到data中的数据同样是响应式的,那么我们可以使用Vue.$set方法,这个方法可以向data中添加一条数据并确保数据是响应式的。
Vue.$set原理
function set (target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val
}
var ob = (target).__ob__;
defineReactive$$1(target, key, val);// 保证当前增加的属性被劫持了 在 defineReactive$$1方法中进行了defineProperty 数据劫持
ob.dep.notify();// 让视图更新了
return val
}
Vue.$forceUpdate:强制让视图更新
Vue.$forceUpdate原理 就是触发watcher的update来更新视图