其他分享
首页 > 其他分享> > 个人自学前端26-Vue3-组件components,父传子props,子传父,组件混合,bus通信

个人自学前端26-Vue3-组件components,父传子props,子传父,组件混合,bus通信

作者:互联网

目录

一.组件components

组件都是vm实例.它可以有自己的视图,自己的数据,自己的逻辑.
组件是为了复用布局以及逻辑.

  1. 组件
    • 组件要注册
    • 组件要挂载(组件的引用)
  <div id='app'>
    <!-- 一个组件挂载了3次.挂载时可以用单标签,也可以用双标签. -->
    <!-- 挂载时,如果组件名是带驼峰的,你需要挂载的时候加- -->
    <!-- 挂载的最终结果就是挂载的标签被组件的template指定的布局替换 -->
    <!-- 组件挂载时,大写都会变小写 -->
    <news></news>
    <news-view></news-view>
    <news-view></news-view>
    <news-view></news-view>
  </div>
  <script>
    const news = {
      template: `
        <div>
          <h3>今天的新闻</h3>
          <p>今天的新闻内容</p>
        </div>
      `
    }
    const newsView = {
      template: `
        <div>
          <h3>今天的新闻</h3>
          <p>今天的新闻内容</p>
        </div>
      `
    }
    // 全局注册 => 哪里都能用news组件
    Vue.component('news', news);
    new Vue({
      el: '#app',
      // 注册一个叫news的组件
      // 局部注册 => 只能在当前组件视图中使用.
      // 组件名如果是驼峰命名,需要在引用组件时,添加-
      components: { news, "news-view": newsView }
    })
  </script>
  1. 组件的选项

    newsView是组件的配置项.
    template是组件的布局选项.(写组件的视图用的)
    组件都是vue实例.可以写任意的Vue选项.除了el选项(例如data,methods,computed,filter,components...)

    组件的选项和new Vue的选项区别:
    1:不用el选项.(写了没意义).
    2:data必须写成函数.把数据对象return出来.

    const newsView = {
      template: `
        <div>
          <h3 @click='fn'>{{title}}</h3>
          <p>今天的新闻内容</p>
        </div>
      `,
      data() {
        return {
          title: '今天的新闻'
        }
      },
      methods: {
        fn() {
          alert(this.title)
        }
      }
    }
    
  2. 组件的组件

    组件都是vue实例,隐藏它也可以写components选项
    哪个组件的视图使用了数据,就在哪个组件内声明这个数据
    哪个组件的视图挂载了其他组件A,就在哪个组件内声明这个A组件

  3. v-for和组件

    组件的template内必须有一个唯一的父元素包裹所有的布局.
    如果组件的这个父元素需要v-for渲染.则应该把v-for写在组件挂载的地方.
    <news v-for='d in 3'></news>

  4. 根组件

    根组件. => 所有组件的唯一祖先组件。
    new Vue时,如果没有写template选项,则vue会使用el所在的标签作为视图。

二.父传子props

父传子 => 让子组件复用时渲染了不同的内容(数据).
父传子 => 前子后父.
Vue => 单向数据流 => 从上往下的.(不能自下而上)
父组件数据变化 => 父组件视图更新 => :msg='msg'重新执行 => 新的msg的值传递给了子组件 => 子组件msg变化 => 子组件视图更新

    const newsView = {
      template: `
        <div>
          <h3>{{day}}的新闻标题</h3>
          <p>{{day}}的新闻内容</p>
        </div>
      `,
      // 声明一个props数据
      props: ['day']
    }
    // 如果props的数据在赋值时,希望通过一个变量来赋值,则需要加:(v-bind)
    // 子组件数据 => day
    // 父组件数据 => day1,day2,day3
    // 子组件在挂载时,子组件数据被父组件数据赋值.(父传子)
    const App = {
      template: `
        <div id='app'>
          <news-view v-bind:day='day1'></news-view>
          <news-view :day='day2'></news-view>
          <news-view :day='day3'></news-view>
        </div>
      `,
      data() {
        return {
          day1: '今天',
          day2: '明天',
          day3: '后天',
        }
      },
      components: { newsView },
    }

在子组件中,是不能修改父组件传递的props数据.
但是可以操作应用类型的数据.
如果传递的是一个引用类型,则传递的是这个引用类型的引用.
我们不能在子组件中修改这个引用,但是可以修改这个引用背后的对象.
这样做修改的对象其实是父组件的对象,会导致父组件和子组件同时更新.

    const myComponent = {
      template: `
        <div>
          <h3>子组件msg---{{msg}}</h3>
          <h3>子组件obj---{{obj.name}}</h3>
          <button @click='fn'>按钮</button>
        </div>
      `,
      props: ['msg', 'obj'],
      methods: {
        fn() {

          // 可以通过this访问props数据.
          // console.log(this.msg)

          // 报错
          // this.msg = Math.random();

          // 不报错.
          // this.obj.name = '超越';

          // 报错.不能修改obj本身
          // this.obj = 10000;
        }
      }
    }
    const App = {
      template: `
        <div id='app'>
          <h3>App根组件msg--{{msg}}</h3>
          <h3>App根组件obj--{{obj.name}}</h3>
          <myComponent :msg='msg' :obj='obj' />
          <button @click='fn'>父按钮</button>
        </div>
      `,
      data() {
        return {
          msg: '父组件的数据msg',
          obj: { name: '幂幂' },
        }
      },
      methods: {
        fn() {
          this.msg = Math.random();
        }
      },
      components: { myComponent },
    }

三.子传父

  1. 父组件需要一个方法来接收子组件数据.
  2. 子组件触发父组件的这个方法.并传入子组件数据.

子组件如何触发父组件的方法?

  1. 把父组件的方法传递给子组件触发.(引用赋值传递)
    const myComponent = {
      template: `
        <div>
          <h3>子组件msg---{{msg}}</h3>
          <button @click='fn'>按钮</button>
        </div>
      `,
      props: ['getMsg'],
      data() {
        return {
          msg: '子组件的msg'
        }
      },
      methods: {
        fn() {
          // 调用父组件方法,并传递子组件的数据.
          this.getMsg(this.msg);
        }
      }
    }
    const App = {
      template: `
        <div id='app'>
          <h3>App根组件msg--{{msg}}</h3>
          <!--父组件方法传递给子组件-->
          <myComponent :getMsg='getMsg' />
        </div>
      `,
      data() {
        return {
          msg: '',
        }
      },
      methods: {
        // 父组件用于接收子组件的方法。
        getMsg(msg) {
          this.msg = msg;
        }
      },
      components: { myComponent },
    }
  1. 自定义事件实现.(给子组件绑定一个自定义事件,这个自定义事件触发时调用父组件的方法)
    const myComponent = {
      template: `
        <div>
          <h3>子组件msg---{{msg}}</h3>
          <button @click='fn'>按钮</button>
        </div>
      `,
      data() {
        return {
          msg: '子组件的msg'
        }
      },
      methods: {
        fn() {
          // $emit => 主动触发事件.
          // 第一个参数是事件名, 第二个参数是传递给事件句柄的参数
          this.$emit('myevent', this.msg);
        }
      }
    }
    const App = {
      template: `
        <div id='app'>
          <h3>App根组件msg--{{msg}}</h3>
          <!--给子组件绑定一个myevent自定义事件,这个自定义事件的事件句柄是getMsg-->
          <myComponent @myevent='getMsg' />
        </div>
      `,
      data() {
        return {
          msg: '',
        }
      },
      methods: {
        // 父组件用于接收子组件的方法。
        getMsg(msg) {
          this.msg = msg;
        }
      },
      components: { myComponent },
    }

四.如何在子组件修改父组件数据

  1. 把修改数据的逻辑方法写在父组件上.
    const myComponent = {
      template: `
        <div>
          <h3>子组件msg---{{msg}}</h3>
          <button @click='show'>按钮</button>
        </div>
      `,
      props: ['fn', 'msg'],
      methods: {
        show() {
          this.fn(Math.random());
        }
      }
    }
    const App = {
      template: `
        <div id='app'>
          <h3>App根组件msg--{{msg}}</h3>
          <myComponent :fn='fn' :msg='msg' />
        </div>
      `,
      data() {
        return {
          msg: '父组件的数据msg',
        }
      },
      methods: {
        // 用fn修改父组件的msg数据 => 这样父子组件可以同时更新。
        // 只要让子组件能够触发父组件的这个fn.就可以实现子组件修改父组件的数据.
        fn(msg) {
          this.msg = msg
        }
      },
      components: { myComponent },
    }
  1. 在子组件中触发父组件的方法.(子传父)(Vue和React推荐的做法)

    const myComponent = {
      template: `
        <div>
          <h3>子组件msg---{{msg}}</h3>
          <button @click='show'>按钮</button>
        </div>
      `,
      props: ['msg'],
      methods: {
        show() {
          this.$emit('myevent', Math.random())
        }
      }
    }
    const App = {
      template: `
        <div id='app'>
          <h3>App根组件msg--{{msg}}</h3>
          <myComponent @myevent='fn' :msg='msg' />
        </div>
      `,
      data() {
        return {
          msg: '父组件的数据msg',
        }
      },
      methods: {
        // 用fn修改父组件的msg数据 => 这样父子组件可以同时更新。
        // 只要让子组件能够触发父组件的这个fn.就可以实现子组件修改父组件的数据.
        fn(msg) {
          this.msg = msg
        }
      },
      components: { myComponent },
    }

五.组件混合

组件的公共选项部分
这部分需要通过组件的mixins选项引入.
如果组件本身就包含mx内的选项,则以组件的的选项为准.

    const mx = {
      computed: {
        ...mapState(['count'])
      },
      methods: {
        ...mapActions(['asyncPlus']),
        ...mapMutations(['reduce'])
      }
    }
    const box = {
      template: `
        <div>
          <h3>box组件---{{count}}</h3>
          <button @click='asyncPlus(1)'>box--count++</button>
          <button @click='reduce(1)'>box--count--</button>
        </div>
      `,
      mixins: [mx],
    }

六. bus通信

1. bus组件通信

任意组件的组件通信.
A组件传B组件

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

自定义事件一定需要给A组件添加吗?
不是,给任何组件添加自定义事件都可以.(只要方便获取这个组件)
因此我们在比较复杂的布局情况下,可以通过一个空的vue实例来充当一个中间组件.
给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件.
这个中间组件就充当了任意两个组件的数据运输工具.bus.

bus实现的通信,无法实现状态管理.

  <script src="js/vue.js"></script>
  <script>
    // vm.$on => 添加一个自定义事件
    // vm.$emit => 触发一个自定义事件
    // vm.$off => 解绑自定义事件.

    // 空的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>

2.子传父1

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

3. 子传父2 通过原型链

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

标签:count,26,const,子传父,App,msg,template,组件
来源: https://www.cnblogs.com/DarkCrow/p/15317055.html