Vue学习笔记 ①

开始使用 Vue

  1. 引入 vue.js

    官网:vuejs.org

    开发版本:包含完整的警告和调试模式

    生产版本:删除了警告,体积更小

  2. 引入 vue.js 后,给我们提供了一个构造函数 Vue

  3. 在 js 中,new Vue()

  4. new Vue() 后会返回一个 vue 实例对象,我们用变量接着它

  5. const vm = new Vue()

  6. 传递一个配置对象{} – > const vm = new Vue({})

el

类型: 字符串

全称:element(元素)

作用:配置控制的元素,表示 Vue 要控制的区域,值为 css 选择器

<!-- 被Vue控制的区域,我们称之为模板 -->
<div id="app"></div>
const vm = new Vue({
  el: "#app", // 控制id为app的元素
});

$mount

  • 作用和 el 一致,都是配置控制的元素,使用哪个都可以,二选一
    <div id="app"></div>
    
    const vm = new Vue({});
    vm.$mount("#app");
    
  • 问:和 el 有什么不同?

    答:本质上没什么不同,$mount 为手动挂载,在项目中有时要进行延迟挂载,比如有时要在挂载之前进行一些其他的操作,比如判断等等(但是,这样做的时候很少,比邓哥回家的次数还少,emmmmm)

data

  • 类型:对象
  • 作用:存放要用到的数据,数据为响应式的
    const vm = new Vue({
      el: "#app",
      data: {
        mrDeng: "风姿绰约、花枝招展",
      },
    });
    

插值表达式

  • 使用方法: {{ }}

  • 可以将 vue 中的数据填在插值表达式中,如:

    <div id="app">{{ mrDeng }}</div>
    
    const vm = new Vue({
      el: "#app",
      data: {
        mrDeng: "邓哥:风姿绰约、花枝招展",
      },
    });
    
  • 除了填写 data 之外,还可以直接填写数据值(数字、字符串、布尔值、undefined、null、数组、对象),如:

    <div id="app">
      {{ 5201314 }} {{ '婀娜多姿、亭亭玉立' }} {{ true }} {{ ['邓旭明', '小刘',
      '王小宝'] }} {{ {name: '邓旭明', age: 80, height: '140cm', weight: '100kg'}
      }}
    </div>
    
  • 注意:在插值表达式中直接书写对象类型值时,不要将三个{}连在一起,这样会报错,如:

    <div id="app">
      <!-- 这样可是不行滴 -->
      {{{name: '邓旭明', age: 80, height: '140cm', weight: '100kg'}}}
    </div>
    
  • 还可在插值表达式中写表达式,如:

    <div id="app">
      <!-- 运算表达式 -->
      {{ 'you' + 'me' }} {{ 10 - 5 }} {{ 100 * 7 }} {{ 1000 / 12 }}
      <!-- 逻辑表达式 -->
      {{ liu || li }} {{ deng && liu }} {{ !wang }}
      <!-- 三元表达式 -->
      {{ 1 + 1 === 3 ? '邓旭明' : '正常人' }}
      <!-- 函数调用也是表达式,也可以使用,这个以后再学哈... -->
    </div>
    
  • 还可以填写其他的吗?不可以,No,以下这些都是不行滴:

    <div id="app">
      <!-- 这是语句,不可以写在插值表达式中 -->
      {{ var Deng = 'shuaige'; console.log(deng) }}
      <!-- 流程控制也不可以 -->
      {{ if(Deng.looks === 'shuai'){ console.log('不可能')} }}
    </div>
    
  • 记住:插值表达式中,可以写:data、js 数据、表达式,其他的想都不要想。

  • 注意,只要插值表达式中使用了数据,必须在 data 中声明过,否则会报错

    <!-- 此时就报错啦,因为mrCheng,未在data中声明过 -->
    <div id="app">{{ mrCheng }}</div>
    
    const vm = new Vue({
      el: "#app",
      data: {
        mrDeng: "邓哥:风姿绰约、花枝招展",
      },
    });
    
  • 还有另外一种可能,使用了未被声明过的数据,不报错:

    <!-- 此时不报错啦,why? -->
    <!-- 在作用域上找不到,报错 -->
    <!-- 在原型链上找不到,值为undefined -->
    <!-- undefined为js基本类型值,所以就不报错啦 -->
    <div id="app">{{ mrDeng.wife }}</div>
    
    const vm = new Vue({
      el: "#app",
      data: {
        mrDeng: {
          name: "邓旭明",
          age: 80,
          height: "140cm",
          weight: "100kg",
        },
      },
    });
    

vue 的响应式-1

  • 数据变化,页面就会重新渲染

  • 怎么更改数据?so easy

<div id="app">{{ mrDeng }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    mrDeng: "邓哥:风姿绰约、花枝招展",
  },
});
vm.mrDeng = "手如柔荑、肤如凝脂"; // 见证奇迹的时刻,页面变化啦
  • 问:为什么 data 会直接出现在 vm 实例对象中咧?

    答:当创建 vue 实例时,vue 会将 data 中的成员代理给 vue 实例,目的是为了实现响应式,监控数据变化,执行某个监听函数(怎么实现的?想一想,提示:Object.defineProperty,试着实现一下)

  • 问:实例中除了 data 数据外,其他东西是啥子?

    为了防止名称冲突。因为会将 data 中数据代理给 vue,假如说我们自己写的 data 名称和 vue 中自带的属性冲突了,那么就会覆盖 vue 内部的属性,所以 vue 会把自己内部的属性成员名称前加上$或_,如果加上的是$,代表是我们可以使用的,如果加上的是_,是 vue 自己内部使用的方法或属性,我们不需要调用

  • 更改的数据必须是存在的数据,否则不能重新渲染页面,因为他监听不到,如:

<!-- 即使更改了数据,也不会重新渲染页面 -->
<div id="app">{{ mrDeng.wife }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    mrDeng: {
      name: "邓旭明",
      age: 80,
      height: "140cm",
      weight: "100kg",
    },
  },
});

vm.mrDeng.wife = "liu";
  • 更改的数据必须已渲染过的数据,否则从性能角度考虑,不会重新渲染页面,如:
<!-- 即使更改了数据,也不会重新渲染页面 -->
<div id="app">{{ mrDeng.wife }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    msg: "邓哥:风姿绰约、花枝招展",
    mrDeng: {
      name: "邓旭明",
      age: 80,
      height: "140cm",
      weight: "100kg",
    },
  },
});

vm.mrDeng.wife = "liu";
vm.msg = "邓哥:手如柔荑、肤如凝脂";
  • 更改数据后,页面会立刻重新渲染吗?

    vue 更新 DOM 的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和 DOM 操作。

同步执行栈执行完毕后,会执行异步队列

<div id="app">{{ msg }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    msg: "杉杉",
  },
});
vm.msg = "杉杉超美的";
console.log(vm.msg); // 杉杉超美的,此时数据已更改
console.log(vm.$el.innerHTML); // 杉杉。此时页面还未重新渲染

vm.$el

  • 值为被 Vue 控制的元素(或者说,Vue 挂载的元素)

vm.$nextTick & Vue.nextTick

  • 如何在更改数据后,看到渲染后的页面上的值?

    答:利用 vm.$nextTick 或 Vue.nextTick,在页面重新渲染,DOM 更新后,会立刻执行 vm.$nextTick

<div id="app">{{ msg }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    msg: "杉杉",
  },
});
vm.msg = "杉杉超美的";
console.log(vm.msg); // 杉杉超美的,此时数据已更改
// 1. 使用vm.$nextTick
vm.$nextTick(() => {
  console.log(vm.$el.innerHTML); // 杉杉超美的
});
// 2. 使用Vue.nextTick
Vue.nextTick(() => {
  console.log(vm.$el.innerHTML); // 杉杉超美的
});
  • vm.nextTick 和 Vue.nextTick 还可以作为 Promise 使用
<div id="app">{{ msg }}</div>
const vm = new Vue({
  el: "#app",
  data: {
    msg: "杉杉",
  },
});
vm.msg = "杉杉超美的";
// 1. 使用vm.$nextTick
vm.$nextTick().then(() => {
  console.log(vm.$el.innerHTML); // 杉杉超美的
});
// 2. 使用Vue.nextTick
Vue.nextTick().then(() => {
  console.log(vm.$el.innerHTML); // 杉杉超美的
});
  • vm.$nextTick 和 Vue.nextTick 的区别?

    Vue.nextTick 内部函数的 this 指向 window

Vue.nextTick(function () {
  console.log(this); // window
});

vm.$nextTick 内部函数的 this 指向 Vue 实例对象

vm.$nextTick(function () {
  console.log(this); // vm实例
});
  • 好奇 nextTick 是怎么实现的吗?

  • 异步任务分为宏任务(macro)和微任务(micro)

  • 宏任务比较慢(如 setTimeout 等),微任务比较快(如 Promise.then()等)

  • 微任务在前,宏任务在后(eventloop,事件环)

    // 控制台打印顺序:promise > timeout
    setTimeout(() => {
      console.log("timeout");
    }, 0);
    Promise.resolve().then(() => {
      console.log("promise");
    });
    
  • 在 nextTick 的实现源码中,会先判断是否支持微任务,不支持后,才会执行宏任务

    if (typeof Promise !== "undefined") {
      // 微任务
      // 首先看一下浏览器中有没有promise
      // 因为IE浏览器中不能执行Promise
      const p = Promise.resolve();
    } else if (typeof MutationObserver !== "undefined") {
      // 微任务
      // 突变观察
      // 监听文档中文字的变化,如果文字有变化,就会执行回调
      // vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数
    } else if (typeof setImmediate !== "undefined") {
      // 宏任务
      // 只在IE下有
    } else {
      // 宏任务
      // 如果上面都不能执行,那么则会调用setTimeout
    }
    
  • 曾经 vue 用过的宏任务

    • MessageChannel 消息通道 宏任务

vue 的响应式-2

  • 除了未被声明过和未被渲染的数据外,还有什么数据更改后不会渲染页面?

    1. 利用索引直接设置一个数组项时:

    <!-- 即使向数组中添加了第4项,数组仍然显示3项 -->
    <!-- 咳咳,一家三口,有第4个人也不能摆出来给大家看呀~ -->
    <div id="app">{{ list }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        dengFamily: ['邓哥', '小刘', '王小宝']
      }
    })
    vm.dengFamily[3] = '铁锤妹妹'; // 不是响应式的
    

    2. 修改数组的长度时:

    <!-- 更改了数组长度后,数组仍然显示1项 -->
    <div id="app">{{ list }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        dengWife: ['小刘']
      }
    })
    vm.dengWife.length = 0; // 不是响应式的
    

    3. 添加或删除对象:

    <!-- 身高还是那个身高,媳妇也只有一个,不要痴心妄想 -->
    <div id="app">{{ deng }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        deng: {
          wife: '小刘',
          son: '王小宝',
          weight: '100kg',
          height: '140cm',
          age: 60
        }
      }
    })
    vm.deng.secondWife = '铁锤妹妹'; // 不是响应式的
    delete vm.deng.height; // 不是响应式的
    
  • 问:要如何响应式的更新数组和对象?

    更改数组:
    1. 利用数组变异方法:push、pop、shift、unshift、splice、sort、reverse
    2. 利用 vm.$set/Vue.set 实例方法
    3. 利用 vm.$set 或 Vue.set 删除数组中的某一项

    vm.$set 是 Vue.set 的别名
    使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)

    vm.$delete 是 Vue.delete 的别名
    使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)

    <!-- 从此,一家三口过上了愉快生活 -->
    <div id="app">{{ list }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        dengFamily: ['邓哥', '小刘', '王小宝']
      }
    })
    // 使用数组变异方法
    vm.dengFamily.push('铁锤妹妹');
    // 使用vm.$set
    vm.$set(vm.dengFamily, 3, '铁锤妹妹');
    
    
    <!-- 邓哥的媳妇多了起来~ -->
    <div id="app">{{ list }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        dengWife: ['小刘']
      }
    })
    // 更改长度时,可以用数组的splice方法
    vm.dengWife.splice(100);
    

    更改对象:
    1. 添加利用 vm.$set/Vue.set 实例方法
    2. 删除利用 vm.$delete/Vue.delete 方法

    <div id="app">{{ deng }}</div>
    
    const vm = new Vue({
      el: '#app'
      data: {
        deng: {
          wife: '小刘',
          son: '王小宝',
          weight: '100kg',
          height: '140cm',
          age: 60
        }
      }
    })
    // 添加
    vm.$set(vm.deng, 'secondWife', '铁锤妹妹');
    // 删除
    vm.$delete(vm.deng, 'height')
    
  • 总结:

    更改数组用变异方法,就够了
    更改对象就用 vm.$set 和 vm.$delete

  • 问题解决了,但是为什么会这样呢?

    Object.defineProperty 的锅,咱们下节课说~

扩展_剖析 Vue 响应式原理

const data = {
  name: "shanshan",
  age: 18,
  shan: {
    name: "shanshan",
    age: 18,
    obj: {},
  },
  arr: [1, 2, 3],
};

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
["push", "pop", "shift", "unshift", "sort", "splice", "reverse"].forEach(
  (method) => {
    arrayMethods[method] = function () {
      arrayProto[method].call(this, ...arguments);
      render();
    };
  }
);

function defineReactive(data, key, value) {
  observer(value);
  Object.defineProperty(data, key, {
    get() {
      return value;
    },
    set(newVal) {
      if (value === newVal) {
        return;
      }
      value = newVal;
      render();
    },
  });
}

function observer(data) {
  if (Array.isArray(data)) {
    data.__proto__ = arrayMethods;
    return;
  }

  if (typeof data === "object") {
    for (let key in data) {
      defineReactive(data, key, data[key]);
    }
  }
}

function render() {
  console.log("页面渲染啦");
}

function $set(data, key, value) {
  if (Array.isArray(data)) {
    data.splice(key, 1, value);
    return value;
  }
  defineReactive(data, key, value);
  render();
  return value;
}

function $delete(data, key) {
  if (Array.isArray(data)) {
    data.splice(key, 1);
    return;
  }
  delete data[key];
  render();
}

observer(data);

利用 Object.defineProperty 实现响应式的劣势

  1. 天生就需要进行递归
  2. 监听不到数组不存在的索引的改变
  3. 监听不到数组长度的改变
  4. 监听不到对象的增删
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值