二、第二部分


7、样式的一些写法

<!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>
  <style>
    .basic {
      height: 100px;
      width: 100px;
    }
    .selfColor {
      background-color: pink;
    }
    .selfSize {
      font-size: large;
    }
    .selfWeight {
      font-weight: 400;
    }
  </style>
</head>
<body>
  <!-- 1、动态绑定class -->
  <!-- 字符串写法,用于样式类名不确定,需要动态绑定 -->
  <div class="basic" :class="color">你好</div>
  <!-- 要绑定的样式个数和名称都不确定 -->
  <div class="basic" :class="classArr">你好</div>
  <!-- 名称确定、个数也确定,但是动态决定要不要添加 -->
  <div class="basic" :class="classObj">你好</div>
  <script>
    const color = 'selfColor';
    const classArr = ['selfColor', 'selfSize', 'selfWeight'];
    const classObj = {
      selfColor: true,
      selfSize: true,
      selfWeight: false
    }
    // 考虑一下,如果classObj里的样式判定是动态的,那么可以把它写成get()、set()形式
  </script>

  <!-- 2、动态绑定style -->
  <!-- 对象形式绑定,注意对象里是驼峰命名法 -->
  <div :style="styleObj">你好</div>
  <!-- 数组形式绑定,注意对象里是驼峰命名法 -->
  <div :style="styleObjArr">你好</div>

  <script>
    const num = 5;
    const styleObj = {
      fontSize: num + 'px',
      color: 'red',
      backgroundColor: 'pink'
    }

    const styleObjArr = [
      {
        fontSize: num + 'px',
        color: 'red',
        backgroundColor: 'pink'
      },
      {
        fontWeight: 400,
        border: '1px solid pink'
      }
    ]
  </script>

</body>
</html>

8、v-if的注意点

<body>
  <div v-if="true">
    <h2>你好</h2>
    <h2>你好</h2>
    <h2>你好</h2>  
  </div>

  <template v-if="true">
    <h2>你好</h2>
    <h2>你好</h2>
    <h2>你好</h2> 
  </template>
  <script>
    // 注意:v-if和v-else以及v-else-if,三者在一起使用时,
    // 他们必须紧紧的挨在一起,不能被打断,否则会失效

    
    // 这里还要说一个东西比如有三个h标签,我想他们同时被一个条件控制是否显示
    // 这里有两种写法,一种是div包着,一种是template包着,
    // 哪个好呢,当然是template了,应为它不会改变html结构,而div破坏了结构
    // 你看渲染的结果,template不会被渲染出来,这就是template的好处
    // 但是一定要注意,template只能和v-if搭配使用,不能和v-show搭配使用
  </script>
</body>

9、v-for中的key

<body>
  
  <div v-for="(num, index) in arr" :key="index">
    {{num}}
  </div>

  <script>
    const arr = [2,5,4,8,4];

    // 首先这个key不是给真实DOM用的,而是给虚拟DOM用的,是放在虚拟DOM标签里的,真实DOM不会携带key
    // 1、虚拟DOM中key的作用:
    // key就是虚拟DOM的标识,当状态数据发生变化时,Vue会根据新数据生成新的虚拟DOM
    // 随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下

    // 2、对比规则:
    // 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    // 若虚拟DOM中内容没变,直接使用之前的真实DOM
    // 如果虚拟DOM内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    // 旧虚拟DOM中未找到与新虚拟DOM相同的key
    // 创建新的真实DOM,随后渲染到页面

    // 3、用index作为key可能会引发的问题
    // 若对数据进行逆序添加、逆序删除等破坏顺序操作(比如往数组第一位添加元素),会产生没有必要的真实DOM更新,
    // 虽然界面没问题,但是效率低
    // 如果结构中还包含输入类的DOM,比如li里面包着input,会产生错误DOM更新,此时界面有问题


    // 注意:
    // 内存里对比的是虚拟DOM,虚拟DOM就是内存中的一堆数据,对比用的是diff算法
    // 当对比虚拟DOM时,发现一样的地方,那么新的虚拟DOM就不会再转成真实的DOM
    // 而是直接把旧虚拟DOM的转换成的真实DOM直接拿过来用

    // 具体看尚硅谷的Vue教程第30集

  </script>
</body>

10、Vue的数据监视的原理:数据劫持

<body>

  <button @click="updateMessage">更新信息</button>
  <div v-for="(p, index) in persons" :key="p.id">
    {{p.name}}-{{p.age}}
  </div>

  <script>
    // 先发现一个问题
    new Vue({
      el: '#root',
      data: {
        persons: [
          { id: '001', name: 'jjwu27', age: 18 },
          { id: '002', name: 'jjwu26', age: 19 },
          { id: '003', name: 'jjwu28', age: 17 }
        ]
      },
      methods: {
        updateMessage() {
          // this.persons[0].name = 'jjwu29'; //奏效,vue可以监听到并实时更新
          // this.persons[0].age = 20; //奏效,vue可以监听到并实时更新

          // 下面这个有意思,这个改变vue监听不到,数据虽然改了但是页面不会更新
          this.persons[0] = { id: '001', name: 'jjwu29', age: 20 };
        }
      }
    })

  </script>

  <script>
    // 模拟一个vue的数据响应式监听
    let data = {
      name: '尚硅谷',
      address: '北京'
    }

    // Vue底层写了一个构造函数
    // 在构造函数里创建了一个监视的实例对象,用于监视obj中属性的变化
    function Observer(obj) {
      // 汇总对象中所有的属性形成一个数组
      const keys = Object.keys(obj);
      keys.forEach(k => {
        // 构造函数里的this指向他实例化的对象
        Object.defineProperty(this, k, {
          get() {
            return obj[k];
          },
          set(val) {
            obj[k] = val;
          }
        })
      })
    }

    const obs = Observer(data);

    // 接下来
    let vm = {} // 模仿Vue实例对象
    vm._data = data = obs;

    // 以上就是数据劫持
    // 那我们说到的数据代理啊其实就是
    // 我们在用数据的时候需要vm._data.name
    // 但是在vue里面可以直接vm.name,它把name拿出来了,就是做了一层数据代理

    // 当然如果数据是多层级的对象,那么深度监听就用递归实现
  </script>

  <script>
    // Vue.set()的使用,也就是vm.$set(),好好想想为什么他两是一样的,这就是
    // 构造函数和实例对象之间的关系
    // 直接给对象添加属性,这个属性是没有getter和setter的,Vue是监听不到的
    // Vue.set()可以,它添加的属性是有


    // 注意:这一块不是为了和你说Vue.set()有多好用的,而是想给你说这玩意的局限性
    const vm = new Vue({
      el: '#root',
      data: {
        name: 'jjwu27',
        age: 18,
        friends: {
          name: 'nanguabing',
          age: 1
        }
      }
    })

    // Vue.set()只能给data里的对象添加属性,不能直接给data添加属性
    // 错误写法:
    Vue.set(vm.data, 'animal', 'cat');
    // 正确写法:
    Vue.set(vm.data.friends, 'animal', 'cat');

    // 总结:Vue.set()不能动data的根数据
  </script>


  <script>
    // Vue监视数组
    const vm = new Vue({
      el: '#root',
      data: {
        name: 'jjwu27',
        age: 18,
        hobby: ['抽烟', '喝酒', '烫头'],
        friends: [
          {
            name: 'cat'
          },
          {
            name: 'dog'
          }
        ]
      }
    })

    // Vue只会给对象自身以及对象的所有属性添加getter和setter属性,
    // 它会给数组本身添加监听属性,但是他不会给数组里的每一项添加呀,这就是关键呀
    // 所以直接通过索引去修改数组的值,Vue是监听不到的,也就不是响应式的
    // 那Vue是怎么监听数组的呢????

    // Vue就想了,那好吧,只有当你改变了原数组,我才监听
    // 所以Vue就对会改变原数组的方法做了监听(push、pop、shift、unshift、splice、sort、reverse)
    // Vue重新包装了这几个方法,这几个方法和Array.出来的不是同一个东西了,当数组被Vue所管理,那么这个数组的
    // 这7个方法就会被Vue做一层封装,当这些方法被调用的时候,Vue会重新生成虚拟DOM做Diff对比,重新解析模版,渲染页面
    // 像filter、reduce这种不改变原数组的就不会去监听

    // 除此之外,用Vue.set()就可以通过索引改数组值了
    Vue.set(vm.hobby, 1, '打台球');

  </script>

    
  <script>
    // 注意!!!!!!!!!!!!!!!!!!!!!!!
    let arr = [
      {
        name: 'jjwu27'
      },
      {
        name: 'nanguabing'
      },
      {
        name: 'dog'
      }
    ];
    // 1、先说一个点,当你通过this.$set()给数组添加一个值,这个值是一个对象
    // 那么请问Vue会给这个对象里的所有的属性都添加上getter和setter属性嘛

    // 当当当当!!!答案是!!! 会的!!!!!!!!是可以被监听的

    // 两种情况:直接操作索引和把索引作为中间站
    // Vue监听不到:直接利用索引修改值,因为Vue并没有对数组的索引值做代理
    this.arr[0] = { name: 'rong' };

    // Vue监听的到的:这种的可以监听到,因为Vue对数组里的索引值里的所有属性做了监听
    this.arr[1].name = 'cat';

    // 2、再说一个注意:下面这种写法能不能监听到呢
    const newArr = [3,5,7];
    // 我直接写一个新的数组给他替换掉
    arr = newArr;
    // 答案是!!!!!!可以监听到!!!!!!!!!!!
    // 因为Vue虽然没有对数组里的索引值做监听,但是Vue对这个数组字段做了监听啊!!!!!
    const obj = {
      name: 'jjwu27',
      age: 18
    }
    // 上面这个对象也是的,Vue会对里面的name和age属性做监听,它当然也会对obj做监听啦,宝贝~~
    
  </script>

</body>

11、自定义指令

<body>

  <div id="root">
    <span v-text="n"></span>
    <span v-big="n"></span>
    <button @click="n++">点我加1</button>
    <input type="text" v-fbind:value="n">
  </div>
  <script>
    // 需求:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
    // 需求:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
    new Vue({
      el: '#root',
      data: {
        n: 1
      },
      // 指令
      directives: {
        // 方法名称就是指令名称(去掉v-)
        big(ele, binding) {
          // 这里的ele就是指令(v-big)关联的标签(span)
          // 这里的binding就是v-big绑定的信息,其中binding.value就是n
          ele.innerText = binding.value * 10;
        },
        // 这种写法满足不了focus()功能
        /* fbind(ele, binding) {
          ele.value = binding.value;
          ele.focus();
        } */
        fbind: {
          // 指令与元素成功绑定时
          bind(ele, binding) {
            ele.value = binding.value;
          },
          // 指令所在元素被插入页面后
          inserted(ele, binding) {
            ele.focus();
          },
          // 指令所在的模板被重新解析时
          update(ele, binding) {
            ele.value = binding.value;
          }
        }
      }
    })

    // 注意:如何保证v-big绑定的值会实时变化呢
    // 也就是说big这个函数要实时调用返回最新的值,那么big函数什么时候会被调用呢???

    // 回答!!!!
    // 1、指令与元素成功绑定时(初始化)
    // 2、指令所在的模板被重新解析时
    // 所以可以推断出来:指令写成函数形式就等于对象形式的bind()加update()

    // input案例的注意点:
    // 1、ele.focus()是否起作用了,我们发现并没有自动聚焦,所以写函数不好使,这时需要把指令写成对象
    // 2、为什么focus会失效呢,因为,focus必须在DOM创建完成后写入到body中,这时候调用focus函数才能聚焦
    // 而fbind函数的首次调用是指令与元素成功绑定时,这个时候是Vue在解析模板的过程,input标签还没创建
    // 放入到body中,所以这个时候调用focus是没有用的

    // 还有一个非常重要的地方,指令函数里打印一下this看下
    // 他们都是window
  </script>
</body>

12、生命周期

<script>
  // 注意点:常用的生命周期钩子:
  // 1、mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息【一系列初始化工作】
  // 2、beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】

  // 关于销毁Vue实例:
  // 1、销毁后借助Vue开发者工具看不到任何信息
  // 2、销毁后自定义事件会失效,但是原生DOM事件依然有效,比如@click!!!!!!!!!!!!!!
  // 其实你可以理解是为什么,因为vue销毁只是那个$vm实例销毁了,他不会代理DOM了,DOM又回到自己
  // 初始的样子了,但是你别忘了,没有Vue前,我们自己也是啥都能做的哦
  // 3、一般不会在beforDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

</script>


文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
  目录