Vue-3-组件和组件通信


组件和组件通信

过滤器和自定义指令 (不重要)

某些情况下,可以通过过滤器来代替watch。

例如:数据的原有格式和渲染到视图上的格式不一致时,可以使用过滤器。

toUpperCase的参数就是管道符号前面表达式msg

如果一个数据要显示的格式需要更改,应该使用过滤器

<input type='text' v-model='msg' />

// 原始数据是msg,通过过滤规则toUpperCase进行转换后显示到视图上是全大写的msg.
<div>{{msg | toUpperCase}}</div>

new Vue({
    el:'#app',
    data:{
        msg:''
    },
    filters:{
        toUpperCase(val){
            return val.toUpperCase()
        }
    }
})

自定义指令

封装自己的DOM操作(封装一些ui框架)

配置项directives

函数(标签,binding),binding.value是自定义指令的值

<div id='app'>
  <div v-color='color'>111</div>
</div>

new Vue({
  el: '#app',
  data: {
    color: 'green'
  },
  directives: {
    // 第一个参数是绑定了自定义指令的标签。
    // 第二个参数是当前指令的一些信息.
    // config.value就是自定义指令的值.
    color(el, config) {
      el.style.backgroundColor = config.value
    }
  }
})

组件

组件是为了复用布局和逻辑

每个组件都是一个Vue实例。

这就意味着,组件可以写所有的Vue构造选项。(除了el选项)

每个组件都有自己的 视图数据

=> 组件的视图用template来书写。组件的 template 必须有一个唯一的父元素

=> 组件的数据用data来书写,组件的data必须写成一个函数,然后返回一个对象,这个对象内写组件数据。

data(){ return {数据}}

组件methods内的方法内的 this,默认指向当前组件。

组件注册

通过配置项components注册

分为局部注册(当前组件视图可用)

和全局组件(哪都能用)

// 函数要声明
// 组件要注册

const news = {
  template: `
    <div>
      <h3>今天的新闻</h3>
      <p>今天的新闻内容</p>
    </div>
  `
}
// 全局注册 => 哪里都能用news组件
Vue.component('news', news);

new Vue({
  el: '#app',
  // 注册一个叫news的组件
  // 局部注册 => 只能在当前组件视图中使用.
  components: { news }
})

组件命名

如果组件的名字是驼峰命名,挂载时需要把驼峰命名转换为带 – 的自定义标签名。

components:{ newsView }注册用驼峰命名

在组件挂载的时候使用需要转为<news-view />

组件挂载

如果需要在组件A中使用组件B,除了要通过components注册以外,还需要在A的视图内挂载B的视图。

视图挂载需要通过一个跟组件名相同的自定义标签来进行挂载。

挂载时,可以使用单标签也可以使用双标签,如果连续挂载两次,不能使用单标签。

如果组件的这个父元素需要v-for渲染.则应该把v-for写在组件挂载的地方.<news-view v-for='a in 3'></news-view>

  <div id='app'>
    <!-- 一个组件挂载了3次.挂载时可以用单标签,也可以用双标签. -->
    <!-- 挂载时,如果组件名是带驼峰的,你需要挂载的时候加- -->
    <!-- 挂载的最终结果就是挂载的标签被组件的template指定的布局替换 -->
    <news-view></news-view>
    <news-view></news-view>
    <news-view></news-view>
  </div>
<script>
    const newsView = {
      template: `
        <div>
          <h3>今天的新闻</h3>
          <p>今天的新闻内容</p>
        </div>
      `
    }
    new Vue({
      el: '#app',
      components: { newsView }
    })
</script>

根组件

new Vue创建的实例叫根实例,它用选项 el 指定的视图就是整个Vue程序的主视图。

这个 el 的 视图可以写在html的body内,其实也可以写在 new Vue的 template 选项里。

根实例有自己的视图,自己的数据,这时,可以把这个根实例封装成一个根组件。

const App = {
    template:`
        <div id='app'>
            <p>{{msg}}</p>
        </div>
    `,
    data(){
        return {msg:'hello Vue'}
    }
}

然后把这个组件注册到new Vue上

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

记住:

数据写在哪个组件的视图里,它就是哪个组件的数据。

子组件挂载在哪个组件的视图内,它就应该在哪个组件中注册。

组件通信

想要每次挂载都渲染不同的内容,可以在这个自定义标签上通过自定义属性来创建不同的渲染内容

然后子组件内通过props选项来声明对应的数据,这个数据名就是自定义标签上的自定义属性,

props声明的数据都是由父组件传递给子组件的数据

且props声明的数据不能再子组件中修改(这数据如果是基本类型,修改了,父组件的不变,子组件的变,但会报错。如果是引用类型,被修改了,则都变(没有修改引用))

props: ['msg', 'obj'],
methods: {
  fn() {

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

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

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

    //报错.不能修改obj本身
    this.obj = 10000;
  }

props父传子

Vue => 单向数据流 => 从上往下的.(不能自下而上)

父组件数据变化 => 父组件视图更新 => :msg=’msg’重新执行 => 新的msg的值传递给了子组件 => 子组件msg变化 => 子组件视图更新

如果props的数据在赋值时,希望通过一个变量来赋值,则需要加:(v-bind)

直接赋值 day='今天'

变量赋值:day='day2'

const newsView = {
  template: `
    <div>
      <h3>{{day}}的新闻标题</h3>
      <p>{{day}}的新闻内容</p>
    </div>
  `,
  // 声明一个props数据
  props: ['day']
}

// 如果props的数据在赋值时,希望通过一个变量来赋值,则需要加:(v-bind)

const App = {
  template: `
    <div id='app'>
      <news-view v-bind:day='day1'></news-view>
      <news-view :day='day2'></news-view>
      <news-view day='后天'></news-view>
    </div>
  `,
  data() {
    return {
      day1: '今天',
      day2: '明天',
      day3: '后天',
    }
  },
  components: { newsView },
}

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

子传父

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

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

​ 一、把父组件的方法传递给子组件触发(引用赋值传递)

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

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

​ 二、自定义事件实现(给子组件绑定一个自定义事件,这个自定义事件触发时调用父组件的方法)

父组件把接收msg方法传给自定义事件myevent <myComponent @myevent='getMsg' />

子组件 <button @click='fn'>按钮</button>,

  methods: {
    fn() {
      // $emit => 主动触发事件.
      // 第一个参数是事件名, 第二个参数是传递给事件句柄的参数
      this.$emit('myevent', this.msg);
    }
  }
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 },
}

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

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

  1. 把修改数据的逻辑方法写在父组件数据上
  2. 在子组件中触发父组件方法(子传父)
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 },
}

new Vue({
  el: '#app',
  template: `<App />`,
  components: { App }
})
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 },
}

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