Vue框架(五)

本文深入探讨了Vue.js中的虚拟节点技术,解释了为何现代视图框架如Vue、React采用虚拟节点以提高DOM操作性能。Vue的生命周期包括创建、挂载、更新和销毁四个阶段,每个阶段都有相应的钩子函数,如created、mounted等,用于在正确的时间执行正确的任务。文章还介绍了Vue中v-if和v-show、v-for与key的使用区别及其最佳实践,强调了在使用v-for时添加key的重要性。
摘要由CSDN通过智能技术生成

一、虚拟节点

<body>

  <!-- 真实的节点 -->
  <div id='app'>
    <h3>Vue你好</h3>
  </div>
</body>

虚拟节点 => 对于理解视图框架(Vue,React,小程序)

    目前流行的视图框架都是基于虚拟节点技术开发的.

    为什么jq落伍了 => jq的DOM操作性能不好.

    视图框架 => 更新视图的最后异步是DOM操作. => 尽量以最低的代价进行DOM操作.

    视图框架如何进行最低代价的DOM操作 => 虚拟节点 => 虚拟节点的更新 => diff算法.

    虚拟节点 => 一个纯对象 => 描述真实节点.

    #app的虚拟节点.

    虚拟节点在描述一个标签时,都有3个最基本的属性 => 标签名,属性列表,子节点列表。

    虚拟节点就是真实节点的js描述.(AST语法树)

 

<script src="js/vue.js"></script>
  <script>
    const Vnode = {
      // 描述标签名
      tag: 'div',
      // 描述属性节点列表
      attrs: [{id: 'app'}],
      children: [{
        tag: 'h3',
        attrs: [],
        children: ['Vue你好']
      }]
    }
</script>

更新后的虚拟节点 

    const Vnode = {
      tag: 'div',
      attrs: [{id: 'app'}],
      children: [{
        tag: 'h3',
        attrs: [],
        children: ['Vue你好1111111']
      }]
    }

 真实节点更新 => 一定是DOM操作完成.

    虚拟节点更新 => 修改纯对象.

    如何根据更新后的虚拟节点,决定如何进行DOM操作去更新真实节点. => diff算法.

    

    diff算法 => 比较更新前和更新后的虚拟节点一桶,最终决定如何进行DOM操作.

    如何比较更新前后的虚拟节点异同?

    比较出异同后,应不应该更新视图,更新那部分视图,如何更新视图?

二、Vue的虚拟节点

<body>
  
  <div id='app'>
    <h3>{{msg}}</h3>
  </div>
</body>
<script src="js/vue.js"></script>

  <script>

    const vm = new Vue({
      el: '#app',
      data: {
        msg: 'Vue你好'
      }
    });

    // vm实例的虚拟节点
    // console.log(vm._vnode);

  </script>

三、生命周期

<body>
  
  <div id='app'>
    <h3 ref='wq'>{{msg}}</h3>
  </div>
</body>

虚拟节点什么时候产生的?

  •     虚拟节点什么时候转换成真实节点的?
  •     Vue的生命周期 => Vue实例从创建达到销毁过程中的特定阶段.
  •     人 => 儿童期,青年期,中年期,老年期. => 正确的阶段做正确的事情.
  •     Vue实例 => 创建期,挂载期,更新期,销毁期. => 正确的函数内写正确的代码.
<script src="js/vue.js"></script>
  <script>

    new Vue({
      el: '#app',
      data: {
        msg: ''
      },
      // 生命周期.
      created() {
        this.msg = '吴签';
        // 报错.
        // this.$refs.wq.style.backgroundColor = 'red';
        // console.log(this.$refs.wq); // undefined
      },
      // 生命周期钩子函数
      mounted() {
        // $refs属性只能在mounted中获取.
        this.$refs.wq.style.backgroundColor = 'red';
        console.log(this.$refs.wq); // undefined
      }
    })

  </script>

四、生命周期创建期

<body>
  
  <div id='app'>
    <h3 ref='wq'>{{msg}}</h3>
  </div>
</body>
<script src="js/vue.js"></script>
  <script>

    new Vue({
      el: '#app',
      data: {
        msg: ''
      },
      // 创建前 => 没什么用.
      beforeCreate() {
        console.log('创建前');
        console.log('beforeCreate', this.$data); // undefined
      },
      // 创建后 => 一帮用于数据初始化。
      // 在这个构造函数内只能操作数据,不能操作视图.
      created() {
        console.log('创建后');
        console.log('created', this.$data);
        this.msg = '吴签';
      },
    })

  </script>

五、挂载期

<body>
  
  <div id='app'>
    <h3 ref='wq'>{{msg}}</h3>
  </div>
</body>

 挂载 => 新视图替换老视图.(组件template替换组件标签).

    挂载前后 => 新视图出现前后.

    这两个钩子也只触发一次.

    mounted => 视图初始化.

    创建前,创建后,挂载前,挂载后 => 都只触发一次. => 在Vue实例的实例化过程中触发的.

    挂载都发生了什么事情?

     1:编译template.(把字符串模板编译成虚拟节点).

     2:编译遇到指令和插值表达式,都会进行求值.(收集依赖).

     3:把虚拟节点转换为新视图,替换老视图.

    created => 处理数据(默认处理一次)

    mounted => 处理视图(默认处理一次)

<script src="js/vue.js"></script>
  <script>

    new Vue({
      el: '#app',
      data: {
        msg: '按钮'
      },
      template: `<button>{{msg}}</button>`,
      created() {
        console.log('created');
      },
      // 新视图挂载前 (老视图最终会被删除,数据不会显示在老视图上);
      beforeMount() {
        console.log('挂载前');
      },
      // 新视图挂载后.(数据显示到了视图上);
      mounted() {
        console.log('挂载后');
      }
    })

  </script>

六、生命周期图示

<body>

  <div id='app'>
    <button>按钮--{{msg}}</button>
  </div>
</body>

 

你描述下Vue的生命周期.(Vue的钩子函数).

    1:基本的回答 => 8个钩子函数的名字.(不懂英文说中午).

    2:回答生命周期图示.(更详细).

    new Vue实例化(组件实例化)

    触发beforeCreate

    通过Object.defineProperty给data数据设置数据劫持

    触发created

    判断有没有el选项.

    如果有,继续判断有没有template选项

    如果有template选项,则编译template

    如果没有template选项,则编译el所在的标签

    触发beforeMount

    根据编译的虚拟节点生成真实节点vm.$el.并且用真实的新视图替换掉老视图

    触发mounted.

    

    如果没有el选项,则等待$mount方法触发,如果这个方法也没触发,则实例化失败.

    $mount(选择器) === el: 选择器

<script src="js/vue.js"></script>
  <script>

    new Vue({
      template: `<h3>新视图</h3>`,
      data: {
        msg: '你好'
      }
    }).$mount('#app');

  </script>

七、更新阶段 

<body>
  
  <div id='app'>
    <h3 ref='wq'>{{msg}}</h3>
  </div>
</body>

 

视图更新前后 => 分别触发beforeUpdate和updated

    默认不触发(Vue实例化阶段不触发).

    Vue实例化完成之后,视图再更新,才会触发.

    有可能触发很多次.

    updated和watch的区别

    updated => 视图更新就触发.不能知道本次更新是由于哪个数据变化导致的.具有响应式效果的数据变化,都触发updated.

    watch => 数据变化就触发.监听哪个数据,就触发哪个方法.

    nextTick => 单次触发的updated

<script src="js/vue.js"></script>
  <script>

    new Vue({
      el: '#app',
      data: {
        msg: '按钮',
      },
      template: `<button @click='$forceUpdate()'>{{msg}}</button>`,
      // 更新前
      beforeUpdate() {
        console.log('视图更新前')
      },
      // 更新后
      updated() {
        console.log('视图更新后')
      }
    });

  </script>

八、视图更新过程 

<body>
  
  <div>
    <h3>1111</h3>
    <button key='a'>33333</button>
  </div>

  <div>
    <h3>2222</h3>
    <button key='b'>4444</button>
  </div>
</body>

 

数据变化 => 导致视图更新 (响应式)

    数据变化 => 先更新虚拟节点 => diff算法判断 => 最后更新真实节点。

    更新真实节点

    1:原地更新.(标签不增删,不移动,直接修改内容或者属性)

    2:替换更新.(会增删,移动元素)

    什么时候原地更新?什么时候替换更新?

     1:比较更新前后的虚拟节点.(同级比较)

     2:如果同级的tag一致,并且key一样,则表示更新前后是同一个标签,使用原地更新策略.

     3:如果同级的tag或者key有一个不一样,则表示更新前后不是同一个标签,使用替换更新策略.

 九、key

<body>
  
  <div id='app'></div>
</body>
  • 更新前后同级比较
  • 如果tag和key一致,就原地更新.(更新前后是同一个标签.)
  • 只要给上不一样的key,则使用替换更新策略.
<script src="js/vue.js"></script>
  <script>
    const App = {
      template: `
        <div>
          <button @click='flag = !flag'>切换</button>
          <div v-if='flag'>
            <h3>普通用户</h3>
            <input type='text' key='a' />
          </div>
          <div v-else>
            <h3>Vip用户</h3>
            <input type='text' key='b' />
          </div>
        </div>
      `,
      data() {
        return { flag: true }
      }
    }

    new Vue({
      components: { App },
      template: `<App />`
    }).$mount('#app');

  </script>

十、v-for的key 

<body>
  
  <div id='app'></div>
</body>
  • 以后记得v-for都要加key.
  • 数据的更新和视图的更新保持一致.(例如位置).需要加key
  • 还得保证更新前后的key是不一样的。
<script src="js/vue.js"></script>
  <script>
    const App = {
      template: `
        <div>
          <ul>
            <li v-for='(d,i) in arr' :key='d.id'>
              {{d.content}}
              <input type='text' />
              <button @click='up(i)'>上移</button>
            </li>
          </ul>
        </div>
      `,
      methods: {
        up(i) {
          [this.arr[i], this.arr[i-1]] = [this.arr[i-1], this.arr[i]];
          this.$forceUpdate();
        }
      },
      data() {
        return { 
          arr: [
            {
              content: 1111,
              id: 1
            },{
              content: 2222,
              id: 2
            },{
              content: 3333,
              id: 3
            }
          ],
        }
      }
    }

    new Vue({
      components: { App },
      template: `<App />`
    }).$mount('#app');

  </script>

十一、v-if和v-show的区别

<body>
  
  <div id='app'>
    <button v-show='flag'>按钮</button>
    <h3 v-if='flag'>你好</h3>
  </div>
</body>

v-if => 根据初始条件决定是否把对应的标签编译到虚拟节点中.
v-show => 不管初始条件是什么,都编译标签到虚拟节点中,渲染时,根据初始条件决定样式display的值.

对于多次显示隐藏的标签,应该用v-show.
对于只有一次显示隐藏的情况,就应该用v-if.

<script src="js/vue.js"></script>
  <script>    

    const vm = new Vue({
      el: '#app',
      data: {
        flag: false
      }
    })

  </script>

十二、v-if和v-for的优先级 

<body>
  
  <div id='app'>
   <ul>
      <li v-for='d in arr' v-if='d%2 === 0'>{{d}}</li>
   </ul>
   <ul>
     <li v-for='d in arr.filter(item => item%2 === 0)'>{{d}}</li>
   </ul>
   <ul>
     <li v-for='d in list'>{{d}}</li>
   </ul>
  </div>
</body>

v-for和v-if的优先级 => v-for优先级更高 => 不管有没有虚拟节点,都会循环 => 性能不好.不推荐v-for和v-if一起使用.
可以通过计算属性来过滤原始数组,只留下应该渲染的元素.

<script src="js/vue.js"></script>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        arr: [111, 222, 333, 444]
      },
      computed: {
        list() {
          return this.arr.filter(item => item%2 === 0)
        }
      }
    })

  </script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值