VUE学习-组件(三)

一、组件

1、快速起步(局部组件)

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <div-b></div-b>
  </div>

  <script>
    
    Vue.config.productionTip = false;
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data : {
        name : 'wxb'
      },
      //局部组件
      components:{
        'div-b':{
          //模板
          template:`
            <div>111</div>
          `
        }
      }
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3sbWjaR-1625568018342)(image/61.png)]

2、组件的复用(全局组件)

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    // 定义一个名为 button-counter 的新组件
    //全局组件
    Vue.component('button-counter', {
      data: function () {
        return {
          count: 0
        }
      },
      template: '<button v-on:click="count++">点击 {{ count }} 次.</button>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data : {
        name : 'wxb'
      }
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EdUIwBZU-1625568018343)(image/63.png)]

3、全局组件的使用

A)错误代码:

注意:模板里面必须包含一个根标签,而且该根标签里面不能写v-for指令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHSMowno-1625568018343)(image/68.png)]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <b-button></b-button>
  </div>
  <script>
    
    Vue.component('b-button', {
      //模板,注意:模板里面必须包含一个根标签,而且该根标签里面不能写v-for指令
      template: `
        <button @click="sub">减</button>
        {{ count }}
        <button @click="add">加</button>
      `,
      props:{
        count : 0
      },
      methods: {
        sub:function(){
          this.count--;
        },
        add:function(){
          this.count++;
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        
      }
    })
</script>
</body>
</html>
B)正确代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <b-button count="0"></b-button>
  </div>
  <script>
    
    Vue.component('b-button', {
      //模板,注意:模板里面必须包含一个根标签,而且该根标签里面不能写v-for指令
      template: `
        <div>
          <button @click="sub">减</button>
            {{ count }}
          <button @click="add">加</button>  
        </div>
      `,
      //参数,注意:这里是简写,不需要明确参数的类型,默认值等等
      props:['count'],
      methods: {
        sub:function(){
          this.count--;
        },
        add:function(){
          this.count++;
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        
      }
    })
</script>
</body>
</html>

但是此时会提示一个错误:

大致意思为不要直接去修改prop中的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3SrGSoad-1625568018344)(image/69.png)]

解决办法:使用data接值,并操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fsmaNteY-1625568018345)(image/70.png)]

C)单向数据流

https://cn.vuejs.org/v2/guide/components-props.html#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81

D)使用

新建一个文件夹components和文件button.js

将刚定义的全局组件剪切到mybutton.js

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GE2zh6xe-1625568018345)(image/72.png)]

引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <!--注意:一定是先引入vue.js文件,再引入外部组件js文件-->
  <script src="./components/mybutton.js"></script>

  <div id="app">
    <b-button count="0"></b-button>
  </div>
  <script>
  //创建vue对象
  let vm = new Vue({
      el : "#app",
      data: {
        
      }
  })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0HqnO01-1625568018346)(image/73.png)]

E)补充
<div id="app">
    <!--
      count='abc' 表示将字符串‘abc’传给参数count
      :count='abc' 表示将变量abc的值传给参数count
    -->
    <b-button :count="0"></b-button>
  </div>

4、data必须是一个函数(vue规则)

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

如果 Vue 没有这条规则,点击一个按钮就可能影响到其它所有实例

5、通过Prop向子组件传递数据

官方文档:

https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87-Prop-%E5%90%91%E5%AD%90%E7%BB%84%E4%BB%B6%E4%BC%A0%E9%80%92%E6%95%B0%E6%8D%AE

A)基础用法

Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post title="11"></blog-post>
    <blog-post title="22"></blog-post>
    <blog-post title="33"></blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      props: ['title'],
      template: '<h3>{{ title }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data : {
        
      }
    })
</script>
</body>
</html>

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

<blog-post title="11"></blog-post>
<blog-post title="22"></blog-post>
<blog-post title="33"></blog-post>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jca62Ytk-1625568018346)(image/64.png)]

B)从data中取值

可以使用 v-bind 来动态传递 prop

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post v-for="post in posts" :title="post.title" :key="post.id">
    </blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      props: ['title'],
      template: '<h3>{{ title }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        posts: [
          { id: 1, title: '11' },
          { id: 2, title: '22' },
          { id: 3, title: '33' }
        ]
      }
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgFJJDDm-1625568018346)(image/65.png)]

C)Prop的大小写

深入了解prop:https://cn.vuejs.org/v2/guide/components-props.html

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post v-for="post in posts" :title-aa="post.title" :key="post.id">
    </blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      props: ['titleAa'],
      template: '<h3>{{ titleAa }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        posts: [
          { id: 1, title: '11' },
          { id: 2, title: '22' },
          { id: 3, title: '33' }
        ]
      }
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gd3nem2u-1625568018347)(image/66.png)]

D)prop类型

以上,默认使用以字符串数组形式列出的prop,在实际业务中,通常会希望每个prop都有自己的类型,此时,可以以对象形式列出prop,这些property的名称和值分别是prop各自的名称和类型

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

第一种写法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post v-for="post in posts" :title="post.title" :key="post.id">
    </blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      //第一种写法
      props: {
        title:String
      },
      template: '<h3>{{ title }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        posts: [
          { id: 1, title: '11' },
          { id: 2, title: '22' },
          { id: 3, title: '33' }
        ]
      }
    })
</script>
</body>
</html>

第二种写法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post v-for="post in posts" :title="post.title" :key="post.id">
    </blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      //第一种写法
      // props: {
      //   title:String
      // },
      //第二种写法
      props : {
        title : {
          //类型
          type : String,
          //默认值
          default : 'aa',
          //是否必须
          required : true
        }
      },
      template: '<h3>{{ title }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        posts: [
          { id: 1 },
          { id: 2, title: '22' },
          { id: 3, title: '33' }
        ]
      }
    })
</script>
</body>
</html>
E)传入一个对象的所有property

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post

post: {
  id: 1,
  title: 'My Journey with Vue'
}

下面的模板:

<blog-post v-bind="post"></blog-post>

等价于:

<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  
  <div id="app">
    <blog-post v-bind="post">
    </blog-post>
  </div>
  <script>
    
    Vue.config.productionTip = false;
    
    Vue.component('blog-post', {
      //第一种写法
      props: {
        title:String
      },
      template: '<h3>{{ title }}</h3>'
    })
    //创建vue对象
    let vm = new Vue({
      el : "#app",
      data: {
        post:{ id: 1, title: '11' }
      }
    })
</script>
</body>
</html>

6、动态组件

在不同组件之间进行动态切换

通过 Vue 的 <component> 元素加一个特殊的 is attribute 来实现:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
<!DOCTYPE html>
<html>
  <head>
    <title>Dynamic Components Example</title>
    <script src="https://unpkg.com/vue"></script>
    <style>
      .tab-button {
        padding: 6px 10px;
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
        border: 1px solid #ccc;
        cursor: pointer;
        background: #f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {
        background: #e0e0e0;
      }
      .tab-button.active {
        background: #e0e0e0;
      }
      .tab {
        border: 1px solid #ccc;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-on:click="currentTab = tab"
      >
        {{ tab }}
      </button>

      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>

    <script>
      Vue.component("tab-home", {
        template: "<div>Home component</div>"
      });
      Vue.component("tab-posts", {
        template: "<div>Posts component</div>"
      });
      Vue.component("tab-archive", {
        template: "<div>Archive component</div>"
      });

      new Vue({
        el: "#dynamic-component-demo",
        data: {
          currentTab: "Home",
          tabs: ["Home", "Posts", "Archive"]
        },
        computed: {
          currentTabComponent: function() {
            return "tab-" + this.currentTab.toLowerCase();
          }
        }
      });
    </script>
  </body>
</html>


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U0kjJbnH-1625568018347)(image/67.png)]

二、父子组件通信

1、子组件使用父组件数据($parent)

A)搭基础框架
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-div title="香蕉"></b-div>
   <b-div title="菠萝"></b-div>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}</div>
      `
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        }
    })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6nQ8wd7E-1625568018347)(image/74.png)]

B)引出问题

想要在子组件中使用父组件的值

先查看父子组件的渲染过程:

​ 页面加载时,是先渲染里面的组件(子),再渲染外面的组件(父)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
    <!--页面加载时,是先渲染里面的组件(子),再渲染外面的组件(父)-->
   <b-div title="苹果"></b-div>
   <b-div title="香蕉"></b-div>
   <b-div title="菠萝"></b-div>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
        },
    })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DSOuB0ya-1625568018347)(image/75.png)]

问题现象:

因为先渲染子组件,所以此时子组件是拿不到父组件的值的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X0M4S7fs-1625568018347)(image/76.png)]

C)解决办法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-div title="香蕉"></b-div>
   <b-div title="菠萝"></b-div>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
        //$parent获取父组件对象
        console.log(this.$parent);
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
        },
    })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FySbtWi-1625568018348)(image/77.png)]

D)使用
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-div title="香蕉"></b-div>
   <b-div title="菠萝"></b-div>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}---{{ name }}---{{ $parent.age }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
        //$parent获取父组件对象
        console.log(this.$parent);
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
        },
    })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9mw5SEW-1625568018348)(image/78.png)]

2、父组件使用子组件数据($children)

A)搭基础框架

创建了三个不同类型的子组件

并且每个子组件都有自己的数据address

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-p title="香蕉"></b-p>
   <b-li title="菠萝"></b-li>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}---{{ name }}---{{ $parent.age }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '北京'
        }
      },
    })
    Vue.component('b-p', {
      props : ['title'],
      template : `
        <p style="border:1px solid red">{{ title }}---{{ name }}---{{ $parent.age }}</p>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '上海'
        }
      },
    })
    Vue.component('b-li', {
      props : ['title'],
      template : `
        <li style="background:green">{{ title }}---{{ name }}---{{ $parent.age }}</li>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '西安'
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
        },
    })  
</script>
</body>
</html>
B)引出问题

父组件中想要使用子组件的值

C)解决办法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5POH0Nxl-1625568018348)(image/79.png)]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-p title="香蕉"></b-p>
   <b-li title="菠萝"></b-li>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}---{{ name }}---{{ $parent.age }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '北京'
        }
      },
    })
    Vue.component('b-p', {
      props : ['title'],
      template : `
        <p style="border:1px solid red">{{ title }}---{{ name }}---{{ $parent.age }}</p>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '上海'
        }
      },
    })
    Vue.component('b-li', {
      props : ['title'],
      template : `
        <li style="background:green">{{ title }}---{{ name }}---{{ $parent.age }}</li>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '西安'
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
          console.log(this.$children);
        },
    })  
</script>
</body>
</html>
D)问题

也就是说,上面的$children所得到的数组,数组的顺序未必是我们使用组件的顺序

可以使用$refs来精确得到子组件的值(查看E内容)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JiIiQGWG-1625568018348)(image/80.png)]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div title="苹果"></b-div>
   <b-p title="香蕉"></b-p>
   <b-li title="菠萝"></b-li>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}---{{ name }}---{{ $parent.age }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '北京'
        }
      },
    })
    Vue.component('b-p', {
      props : ['title'],
      template : `
        <p style="border:1px solid red">{{ title }}---{{ name }}---{{ $parent.age }}</p>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '上海'
        }
      },
    })
    Vue.component('b-li', {
      props : ['title'],
      template : `
        <li style="background:green">{{ title }}---{{ name }}---{{ $parent.age }}</li>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '西安'
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
          console.log(this.$children[0].address);
        },
    })  
</script>
</body>
</html>

E)$refs

在子组件上添加ref属性,在父组件中通过$refs属性获取所有带ref属性的子组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
  <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <div id="app">
   <b-div ref="pg" title="苹果"></b-div>
   <b-p ref="xj" title="香蕉"></b-p>
   <b-li ref="bl" title="菠萝"></b-li>
  </div>

  <script>
    Vue.component('b-div', {
      props : ['title'],
      template : `
        <div>{{ title }}---{{ name }}---{{ $parent.age }}</div>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '北京'
        }
      },
    })
    Vue.component('b-p', {
      props : ['title'],
      template : `
        <p style="border:1px solid red">{{ title }}---{{ name }}---{{ $parent.age }}</p>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '上海'
        }
      },
    })
    Vue.component('b-li', {
      props : ['title'],
      template : `
        <li style="background:green">{{ title }}---{{ name }}---{{ $parent.age }}</li>
      `,
      mounted() {
        console.log(this.title+'渲染完毕。。。');
      },
      //定义data中的值
      data() {
        return {
          name : this.$parent.name,
          address : '西安'
        }
      },
    })
    //创建vue对象
    let vm = new Vue({
        el : "#app",
        data: {
          name : 'wxb',
          age : 18
        },
        mounted() {
          console.log('app父组件渲染完毕。。。');
          //注意:这里的【0】,可能获取的不是第一个组件
          console.log(this.$children[0].address);
          //$children返回的是数组
          console.log(this.$children);
          //$refs返回的是对象,通过对象的属性获取对应的组件
          console.log(this.$refs);
          
          console.log(this.$refs.bl.address);
        },
    })  
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6GaywTXo-1625568018348)(image/81.png)]

三、组件内部向组件外部通信($emit)

1、搭基础

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<!--开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<div id="app">
    <b-search></b-search>
    <div>b-search组件内部搜索的内容是:{{ text }}</div>
</div>

<script>
     //开发阶段,取消生成提示信息
    Vue.config.productionTip = false;
    
    Vue.component('b-search', {
        template:`
        <div>
            <input v-model="searchText" type="search" placeholder="请输入。。">
            <button @click="search">搜索</button>
        </div>
        `,
        data() {
            return {
                searchText:''
            }
        },
        methods: {
            search:function(){
                console.log(this.searchText);
            }
        },
    })
    
    new Vue({
        el:"#app",
        data() {
            return {
                text:''
            }
        },
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AaG5zfdo-1625568018348)(image/83.png)]

2、引出问题

输入框内的输入内容,在组件内部是可以得到并且使用的,但是在组件外部,如何获取使用?

3、解决办法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<!--开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<div id="app">
    <b-search @abc="getText"></b-search>
    <b-search @abc="text=$event"></b-search>
    <div>b-search组件内部搜索的内容是:{{ text }}</div>
</div>
<script>
     //开发阶段,取消生成提示信息
    Vue.config.productionTip = false;
    Vue.component('b-search', {
        template:`
        <div>
            <input @keyup.enter="search" v-model="searchText" type="text" placeholder="请输入。。">
            <button @click="search">搜索</button>
        </div>
        `,
        data() {
            return {
                searchText:''
            }
        },
        methods: {
            search:function(){
                console.log(this.searchText);
                //在search方法里需要将值传出去
                //自定义一个事件,‘abc’为事件名,searchText为要传的参数
                this.$emit('abc', this.searchText);
            }
        },
    }) 
    new Vue({
        el:"#app",
        data() {
            return {
                text:''
            }
        },
        methods: {
            getText(e){
                console.log(e);
                this.text = e;
            }
        },
    })
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lv72su92-1625568018349)(image/84.png)]

四、常用vue开源库

https://element.eleme.cn/#/zh-CN/component/installation

http://v1.iviewui.com/docs/introduce

1、简单使用

注意要在vue引入之后引入资源库

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!--开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

  <!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
  <div id="app">
    <el-radio v-model="radio" label="1">备选项</el-radio>
    <el-radio v-model="radio" label="2">备选项</el-radio>
  </div>

  <script>
      //开发阶段,取消生成提示信息
      Vue.config.productionTip = false;

      new Vue({
          el:"#app",
          data () {
            return {
                radio: '1'
            };
          }
      })
  </script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lxf0l109-1625568018349)(image/82.png)]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值