Vue框架(六)

一、销毁前后

<body>
  
  <div id='app'></div>
</body>
  • destroyed => 组件销毁后的善后处理,例如停止定时器
  • 销毁发送了什么事情?组件实例变null了吗?
  • 组件销毁只是让数据不驱动视图更新了而已.组件实例还是存在的.
<script src="js/vue.js"></script>

  <script>

      const box = {
      template: `<h3>box组件--{{count}}</h3>`,
      data() {
        return { count: 0 }
      },
      // 销毁前
      beforeDestroy() {
        console.log('销毁前')
      },
      // 销毁后
      destroyed() {
        console.log('销毁后');
        // 销毁后关闭定时器
        clearInterval(this.id);
      },
      created() {
        console.log('创建后');
        // 记录当前的定时器id.
        this.id = setInterval(() => {
          // console.log(this.count);
          this.count++
        },1000);
      },
      mounted() {
        console.log('挂载后')
      }
    }

    const App = {
      template: `
        <div>
          <button @click='flag = !flag'>切换</button>
          <box v-if='flag' />
        </div>
      `,
      components: { box },
      data() {
        return {
          flag: true
        }
      }
    }

    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App />'
    })

  </script>

二、静态数据 

<body>
  
  <div id='app'></div>
</body>

哪些vue数据需要放在data内,哪些不方法?
不写在视图上的数据,是不应该劫持的,不应该劫持就不应该写在data中.

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

  <script>

      const box = {
      template: `<h3>box组件--{{count}}</h3>`,
      data() {
        return { count: 0 }
      },
      // 销毁后
      destroyed() {
        console.log('销毁后');
        // 销毁后关闭定时器
        clearInterval(this.id);
      },
      created() {
        // 这个id不应该放在data中.这是一个静态数据,不驱动视图更新,直接给当前组件实例添加即可.
        this.id = setInterval(() => {
          this.count++
        },1000);
      }
    }

    const App = {
      template: `
        <div>
          <button @click='flag = !flag'>切换</button>
          <box v-if='flag' />
        </div>
      `,
      components: { box },
      data() {
        return {
          flag: true
        }
      }
    }

    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App />'
    })

  </script>

三、nextTick 

<body>
  
  <div id='app'></div>
</body>

update => 只要视图更新都触发.
如何针对某个数据变化导致的视图更新写单独的逻辑?
vue的视图更新是异步的,数据变化不会马上导致视图更新.

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

  <script>

    const App = {
      template: `
        <div>
          <button @click='msg=Math.random()'>修改msg</button>
          <button @click='str=Math.random()'>修改str</button>
          <div>msg:{{msg}}</div>
          <div>str:{{str}}</div>
        </div>
      `,
      data() {
        return {
          msg: 0,
          str: 0
        }
      },
      watch: {
        msg() {
          // console.log('msg导致视图更新了');

          // nextTick的回调函数,会在视图更新后自动触发.只触发一次
          // 一定是用来处理跟视图相关的逻辑.
          this.$nextTick(() => {
            console.log('msg导致视图更新了');
          });
        },
        str() {
          // nextTick的回调函数,会在视图更新后自动触发.只触发一次
          this.$nextTick(() => {
            console.log('str导致视图更新了');
          });
        }
      }
      // updated没办法在视图变化时知道是因为哪个数据变化导致的.
      // updated() {
      //   console.log('视图更新了')
      // }
    }

    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App />'
    })

  </script>

四、触底 

<style>
    ul{
      height: 200px;
      overflow-y: auto;
      width: 300px;
    }
  </style>
</head>
<body>
  
  <div id='app'></div>
</body>
  • update => 只要视图更新都触发.
  • 如何针对某个数据变化导致的视图更新写单独的逻辑?
  • vue的视图更新是异步的,数据变化不会马上导致视图更新.
<script src="js/vue.js"></script>

  <script>

    const App = {
      template: `
        <div>
          <button @click='fn'>修改str</button>
          <ul ref='ul'>
            <li v-for='d in count'>{{d}}</li>
          </ul>
        </div>
      `,
      data() {
        return {
          count: 0
        }
      },
      methods: {
        fn() {
          // 数据变化会导致视图变化.视图变化完成之后,就会触发nextTick
          this.count++;
          // 这里只会在count变化导致的视图更新后触发.
          this.$nextTick(() => {
            let oUl = this.$refs.ul;
            oUl.scrollTop = oUl.scrollHeight - oUl.clientHeight;
          });
        }
      }
      // 每次视图更新后,都设置触底.
      // updated() {
      //   let oUl = this.$refs.ul;
      //   oUl.scrollTop = oUl.scrollHeight - oUl.clientHeight;
      // }
    }

    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App />'
    })

  </script>

五、render选项 

<body>

  <div id='app'></div>
</body>

render选项 => 代替template选项
render选项也是为了确定组件视图.

template => 字符串视图 => 优点是直观.缺点是需要编译.
render => 跳过编译字符串template,直接生成虚拟节点 => 优点是性能好,不用编译,确定是模板不直观

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

    // tag,属性列表,子子节点

    const App = {
      // template: `
      //   <div id='app'>
      //     <h3>App组件</h3>
      //   </div>
      // `,
      // 直接通过render函数生成虚拟节点.
      // createElement(标签名, 属性列表, 子节点列表)
      // createElement(组件配置项)
      render(createElement) {
        return createElement('div', { attrs: { id: 'app' } }, [
          createElement('h3', null, 'App组件')
        ]);
      }
    }

    new Vue({
      // el: '#app',
      // template: '<App />',

      // render(h) {
      //   return h(App)
      // },
      render: h => h(App),
    }).$mount('#app');

  </script>

六、bus组件通信 

<body>

  <div id='app'></div>
</body>

任意组件的组件通信.
     A组件传B组件
    1:B函数使用了一个方法接收A组件数据.
    2:A组件利用自定义事件调用B组件的这个方法,并传入A组件数据.

     自定义事件一定需要给A组件添加吗?
     不是,给任何组件添加自定义事件都可以.(只要方便获取这个组件)

     因此我们在比较复杂的布局情况下,可以通过一个空的vue实例来充当一个中间组件.
     给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件.
     这个中间组件就充当了任意两个组件的数据运输工具.bus.

     vm.$on => 添加一个自定义事件
     vm.$emit => 触发一个自定义事件
     vm.$off => 解绑自定义事件.

<script src="js/vue.js"></script>
  <script>
  
    // 空的vue实例。
    const bus = new Vue();

    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
          <button @click='fn'>传递count给item</button>
        </div>
      `,
      data() {
        return { count: 666 }
      },
      methods: {
        fn() {
          bus.$emit('getcount', this.count);
        }
      }
    }

    const item = {
      template: `
        <div>
          <h3>item组件---{{num}}</h3>
        </div>
      `,
      data() {
        return { num: 0 }
      },
      methods: {
        getCount(count) {
          this.num = count
        }
      },
      created() {
        bus.$on('getcount', this.getCount);
      }
    }

    const App = {
      template: `
        <div id='app'>
          <h3>App组件</h3>
          <box />
          <item />
        </div>
      `,
      components: { box, item }
    }

    new Vue({
      render: h => h(App),
    }).$mount('#app');

  </script>

七、子传父 

<body>

  <div id='app'></div>
</body>

 

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

    // 空的vue实例。
    const bus = new Vue();

    // <box @getcount='getcount' /> => 给box组件绑定自定义事件的一个简写

    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
          <button @click='fn'>传递count给item</button>
        </div>
      `,
      data() {
        return { count: 666 }
      },
      methods: {
        fn() {
          this.$emit('getcount', this.count);
        }
      },
      created() {
        // 给当前的子组件绑定一个getcount事件,这个事件触发父组件的getcount方法
        // this.$on('getcount', this.$parent.getcount);
      }
    }

    const App = {
      template: `
        <div id='app'>
          <h3>App组件---{{num}}</h3>
          <box @getcount='getcount' />
        </div>
      `,
      data() {
        return {
          num: 0
        }
      },
      methods: {
        getcount(count) {
          this.num = count
        }
      },
      components: { box }
    }

    new Vue({
      render: h => h(App),
    }).$mount('#app');

  </script>

八、bus实现子传父 

<body>

  <div id='app'></div>
</body>

空的vue实例。
     const bus = new Vue();

     bus通信方式可以实现任意两个组件间的通信问题,包裹父子组件.
     bus实现的通信,无法实现状态管理.

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

    // 把控的vue实例bus对象,放在Vue的原型上。
    // 这样所有组件实例都可以通过原型链访问到这个bus对象.
    Vue.prototype.$bus = new Vue();

    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
          <button @click='$bus.$emit("getcount", count)'>传递count给item</button>
        </div>
      `,
      data() {
        return { count: 666 }
      },
      // methods: {
      //   fn() {
      //     this.$bus.$emit('getcount', this.count);
      //   }
      // }
    }

    const App = {
      template: `
        <div id='app'>
          <h3>App组件---{{num}}</h3>
          <box />
        </div>
      `,
      data() {
        return {
          num: 0
        }
      },
      components: { box },
      created() {
        // 在父组件created中给bus实例添加自定义事件
        this.$bus.$on('getcount', (count) => {
          this.num = count
        });
      }
    }

    new Vue({
      render: h => h(App),
    }).$mount('#app');

  </script>

九、父子组件生命周期顺序 

<body>

  <div id='app'></div>
</body

父子钩子顺序

  1.      父beforeCreate
  2.      父created
  3.      父beforeMount
  4.      子beforeCreate
  5.      子created
  6.      子beforeMount
  7.      子mounted
  8.      父mounted
<script src="js/vue.js"></script>
  <script>  

    const item = {
      template: `
        <div>
          <h3>box组件</h3>
          <button>传递count给item</button>
        </div>
      `,
      created() {
        console.log('item子组件created')
      },
      beforeCreate() {
        console.log('item子组件beforeCreate')
      },
      beforeMount() {
        console.log('item子组件beforeMount')
      },
      mounted() {
        console.log('item子组件mounted')
      }
    }

    const box = {
      template: `
        <div>
          <h3>box组件</h3>
          <button>传递count给item</button>
        </div>
      `,
      created() {
        console.log('box子组件created')
      },
      beforeCreate() {
        console.log('box子组件beforeCreate')
      },
      beforeMount() {
        console.log('box子组件beforeMount')
      },
      mounted() {
        console.log('box子组件mounted')
      }
    }

    // <div id='app'>
    //   <h3>App组件</h3>
    //   <box />
    // </div>

    // function App () {
    //   编译div
    //   编译h3
    //   创建box,挂载box
    //   完成编译,挂载App组件
    // }

    const App = {
      template: `
        <div id='app'>
          <h3>App组件</h3>
          <box v-if='flag' />
        </div>
      `,
      components: { box, item },
      created() {
        console.log('父组件created')
      },
      beforeCreate() {
        console.log('父组件beforeCreate')
      },
      beforeMount() {
        console.log('父组件beforeMount')
      },
      mounted() {
        console.log('父组件mounted');
        // 父组件挂载完成之后再编译子组件.
        this.flag = true;
      },
      data() {
        return {
          flag: false
        }
      }
    }

    new Vue({
      render: h => h(App),
    }).$mount('#app');

  </script>

十、bus通信 

<body>

  <div id='app'></div>
</body>

任意组件的组件通信.
     A组件传B组件
     1:B函数使用了一个方法接收A组件数据.
     2:A组件利用自定义事件调用B组件的这个方法,并传入A组件数据.

     自定义事件一定需要给A组件添加吗?
     不是,给任何组件添加自定义事件都可以.(只要方便获取这个组件)

     因此我们在比较复杂的布局情况下,可以通过一个空的vue实例来充当一个中间组件.
     给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件.
     这个中间组件就充当了任意两个组件的数据运输工具.bus.

     vm.$on => 添加一个自定义事件
     vm.$emit => 触发一个自定义事件
     vm.$off => 解绑自定义事件.

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

    // 空的vue实例。
    const bus = new Vue();

    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
        </div>
      `,
      data() {
        return { count: 666 }
      },
      // box组件的mounted在item组件的creatd之后触发
      // 因此bus的事件触发应该写在这里.
      mounted() {
        bus.$emit('getcount', this.count);
      }
    }

    const item = {
      template: `
        <div>
          <h3>item组件---{{num}}</h3>
        </div>
      `,
      data() {
        return { num: 0 }
      },
      methods: {
        getCount(count) {
          this.num = count
        }
      },
      created() {
        bus.$on('getcount', this.getCount);
      }
    }

    const App = {
      template: `
        <div id='app'>
          <h3>App组件</h3>
          <box />
          <item />
        </div>
      `,
      components: { box, item }
    }

    new Vue({
      render: h => h(App),
    }).$mount('#app');

  </script>

十一、插槽 

1.0

<body>

  <div id='app'></div>
</body>

如何在同一个组件复用多次时,渲染不同的标签?
     可以使用插槽来实现.

     要使用插槽,组件挂载必须使用双标签.
     子组件中,通过内置组件slot来引入插槽.
     组件插槽最终会替换slot组件.

     以下的插槽是默认插槽.
     默认插槽可以是多个标签.

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

    const box = {
      template: `
        <div>
          <h3>{{title}}</h3>
          <p>大家都好</p>
          <slot />
        </div>
      `,
      props: ['title']
    }

    const App = {
      template: `
        <div>
          <box title='你好'>
            <button>按钮111</button>
            <button>按钮222</button>
          </box>
          <box title='他好'>
            <span>span</span>
          </box>
          <box title='我好'>
            <a href='#'>百度一下</a>
          </box>
        </div>
      `,
      components: { box }
    }

    new Vue({
      el: '#app',
      render: h => h(App)
    })

  </script>

1.1

插槽:
     1:默认插槽.
     2:具名插槽.
     3:作用域插槽.

     具名插槽 => 具有name属性命名的插槽。插槽内容需要通过slot来对应name属性.
     具名插槽可以一对多.
     如果要一对多,可以用template抽象组件包裹所有的插槽内容.然后给template组件设置slot.

     template内置的抽象组件 => 最终不会渲染到页面上. => 只能在插槽中使用.

<script src="js/vue.js"></script>
  <script>
  const box = {
      template: `
        <div>
          <slot name='a' />
          <h3>{{title}}</h3>
          <slot name='span' />
          <slot name='btn2' />
          <p>大家都好</p>
          <slot name='btn1' />
        </div>
      `,
      props: ['title']
    }

    const App = {
      template: `
        <div>
          <box title='你好'>
            <button slot='btn1'>按钮1111</button>
            <button slot='btn2'>按钮2222</button>
          </box>
          <box title='他好'>
            <template slot='span'>
              <span>span</span>
              <span>span</span>
            </template>
          </box>
          <box title='我好'>
            <a href='#' slot='a'>百度一下</a>
          </box>
        </div>
      `,
      components: { box }
    }

    new Vue({
      el: '#app',
      render: h => h(App)
    })

  </script>

 1.2

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

    // 如果多个组件的模板多数类似,少数不一致,就可以使用插槽来复用组件.

    const box1 = {
      template: `
        <div>
          <table border='1' cellspacing='10' cellpadding='10'>
            <tr v-for='({name,age,sex}) in arr'>
              <td>{{name}}</td>
              <td>{{age}}</td>
              <td>{{sex}}</td>
              <slot />
            </tr>
          </table>
        </div>
      `,
      data() {
        return {
          arr: [
            {
              name: '小崔',
              age: 32,
              sex: '女'
            }, {
              name: '小陈',
              age: 23,
              sex: '女'
            }, {
              name: '小邓',
              age: 65,
              sex: '男'
            }
          ]
        }
      }
    }

    const App = {
      template: `
        <div>
          <box1></box1>
          <box1>
            <td>汉</td>
          </box1>
        </div>
      `,
      components: { box1 }
    }

    new Vue({
      el: '#app',
      render: h => h(App)
    })

  </script>

十二、作用域插槽 

<body>

  <div id='app'>

  </div>
</body>

什么情况下用作用域插槽?
     插槽内容中包含子组件数据时,就得用作用域插槽.

     作用域插槽内有子组件数据,但是默认却在父组件上找数据,找不到会报错.

     可以通过给组件添加一个v-slot指令,这样组件内部就构成了一个作用域。
     插槽内部的数据,会优先在作用域内查找.
     v-slot的值,默认是一个对象,这个对象的名字可以任意书写
     这个对象就能获取到slot组件上的所有绑定数据.

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

    // v-slot支持解构赋值的写法.

    const box1 = {
      template: `
        <div>
          <table border='1' cellspacing='10' cellpadding='10'>
            <tr v-for='({name,age,sex,nation}) in arr'>
              <td>{{name}}</td>
              <td>{{age}}</td>
              <td>{{sex}}</td>
              <slot :nation='nation' a='100' c='200' />
            </tr>
          </table>
        </div>
      `,
      data() {
        return {
          arr: [
            {
              name: '小陈',
              age: 32,
              sex: '女',
              nation: '汉'
            }, {
              name: '小崔',
              age: 23,
              sex: '女',
              nation: '苗'
            }, {
              name: '小邓',
              age: 65,
              sex: '男',
              nation: '蒙古'
            }
          ]
        }
      }
    }

    const App = {
      template: `
        <div>
          <box1></box1>
          <!--
          <box1 v-slot='slotProps'>
            <td>{{slotProps.nation}}--{{slotProps}}</td>
          </box1>
          -->
          <box1 v-slot='{nation}'>
            <td>{{nation}}</td>
          </box1>
        </div>
      `,
      components: { box1 }
    }

    new Vue({
      el: '#app',
      render: h => h(App)
    })

  </script>

十三、作用域插槽 

<body>

  <div id='app'></div>
</body>

如果在组件上使用v-slot,则默认只能获取到默认插槽的绑定数据。
     如果是具名插槽,就不能再组件上使用v-slot指令.

     需要在template组件上使用 => v-slot:插槽名

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

    const box1 = {
      template: `
        <div>
          <table border='1' cellspacing='10' cellpadding='10'>
            <thead>
              <tr>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <slot name='c'></slot>
              </tr>
            </thead>
            <tr v-for='({name,age,sex,nation,marryed}) in arr'>
              <td>{{name}}</td>
              <td>{{age}}</td>
              <td>{{sex}}</td>
              <slot name='a' :marryed='marryed' />
              <slot name='b' :nation='nation'/>
            </tr>
          </table>
        </div>
      `,
      data() {
        return {
          arr: [
            {
              name: '小陈',
              age: 32,
              sex: '女',
              nation: '汉',
              marryed: '已离异'
            }, {
              name: '小崔',
              age: 23,
              sex: '女',
              nation: '苗',
              marryed: '未婚'
            }, {
              name: '小邓',
              age: 65,
              sex: '男',
              nation: '蒙古',
              marryed: '已婚'
            }
          ]
        }
      }
    }

    const App = {
      template: `
        <div>
          <box1>
            <th slot='c'>婚姻状况</th>
            <template v-slot:a='{marryed}' slot='a'>
              <td>{{marryed}}</td>
            </template>
          </box1>
          <box1>
            <th slot='c'>民族</th>
            <template v-slot:b='{nation}' slot='b'>
              <td>{{nation}}</td>
            </template>
          </box1>
        </div>
      `,
      components: { box1 }
    }

    new Vue({
      el: '#app',
      render: h => h(App)
    })

  </script>

十四、视图更新为什么是异步的 

<body>
  
  <div id='app'></div>
</body>

 视图更新是异步的,为了性能考虑。
 需要等待所有数据都修改完毕之后,再进行视图更新,只更新最新的数据状态.

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

  <script>   

    const App = {
      template: `
        <div>
          <button @click='fn'>修改msg</button>
          <div>msg:{{msg}}</div>
          <div>str:{{str}}</div>
        </div>
      `,
      data() {
        return {
          msg: 0,
          str: 0
        }
      },
      methods: {
        fn() {
          // this.msg = Math.random();
          // this.str = Math.random();

          for (let i = 0; i < 1000; i++) {
            this.msg = Math.random();
          }
        }
      },
      updated() {
        console.log('视图更新了')
      }
    }

    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App />'
    })

  </script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值