Vue-5-nextTick、bus通信、插槽


Vue的nextTick、bus通信、插槽

nextTick

uodate => 只要视图更新都触发

为了单独针对某个数据变化导致的视图更新写单独的逻辑,应该用nextTick

vue的视图更新是异步的,数据变化不会马上导致视图更新

nextTick(回调函数),回调函数在视图更新后触发

触底

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 />'
})

render

代替template选项

// template => 字符串视图 => 优点是直观.缺点是需要编译.

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

直接render函数虚拟节点

createElement(标签名,属性列表,子节点列表)

createElement(组件配置项)

render(createElement) {
  return createElement('div', { attrs: { id: 'app' } }, [
    createElement('h3', null, 'App组件')
  ]);
}

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

bus组件通信

我们在比较复杂布局情况下,可以通过一个空的vue实例来充当中间组件

给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件

这个中间组件就充当 了任意两个组件的数据运输工具

vm.$on => 添加一个自定义事件

vm.$emit => 触发一个自定义事件

vm.off => 解绑自定义事件

在created里给bus实例添加自定义事件

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');

父子组件生命周期

// 父beforeCreate

// 父created

// 父beforeMount

// 子beforeCreate

// 子created

// 子beforeMount

// 子mounted

// 父mounted

面试题:

父子组件的生命周期钩子顺序.

为什么顺序是这样的. => 函数执行顺序回答.父组件的视图编译就是一个函数从上往下执行.

能不能修改这个顺序.(4父4子) => 通过v-if实现.父组件挂载后再编译子组件.即在mouted里面让flag变为true

插槽

默认插槽

  1. 组件挂载处要写双标签
  2. 双标签里面的写要插入的标签
  3. 组件里面需要插入标签的地方写上 <slot />
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 }
}

具名插槽

  1. 组件挂载处要写双标签
  2. 双标签里面写要插入的标签,并在里面添加slot属性,值为组件内需要插入位置的slot里name的值
  3. 组件里slot 须写上name='里面写名字'
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 }
}

作用域插槽

当插槽内容包含子组件数据时,就得用作用域插槽

因为当,作用域内有子组件数据,但是默认却在父组件上找数据

可以通过v-slot指令,这样组件内部就构成了一个作用域

而插槽内部的数据,会优先在作用域内查找

v-slot的值,默认是一个对象,这个对象的名字可以任意书写,这个对象能获取到slot组件上所有绑定的数据

  1. 组件里的slot 需要绑定数据,如<slot :name='name' a='100' />
  2. 组件挂载处双标签
  3. 并有属性v-slot='slotProps',slotProps任意名字,是一个对象,对象里面有绑定的所有数据(此时获得的是默认插槽绑定的数据
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 }
}
作用域插槽+具名插槽

因为直接使用v-slot,则默认只能获取到默认插槽绑定的数据

如果是具名插槽,则需要在template包裹需要插入的标签,并添加属性 v-slot:插槽名

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 }
}