Vue初探

145 阅读5分钟

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.png

image.png

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中数据更改了 视图一定会更新 前提是数组得调用那七个变异方法 更新数组想让视图更新 只能使用一下七个方法 push pop shift unshift splice sort reverse 这七个方法为变异数组方法 不是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来更新视图