Vue 学习Day4 (组件)

组件

组件化的开发其实就是把页面拆分成一块一块的,每一块就是我们的一个组件

组件是可复用的 Vue 实例 - 组件中的选型基本上和vue实例保持一致

组件初体验

<body>
  <div id="app">
    <my-button ></my-button>
  </div>
</body>
<script>
  // 全局注册组件
  // Vue.component('my-button', {
  //   template: `<h3>这里就是vue的组件 - {{ msg }}</h3>`,
  //   data () {
  //     return {
  //       msg: '112233'
  //     }
  //   }
  // })
  const app = new Vue({
    el: '#app',
    data: {

    },
    components: { // 局部注册组件
      'my-button': {
        template: `<h3>这里就是vue的组件 - {{ msg }}</h3>`,
        data () {
          return {
            msg: '112233'
          }
        }
      }
    }
  })
</script>

注册组件

全局注册组件

template

  • 类型:string

  • 详细:

  • 一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。

  • 如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板。常用的技巧是用<script type="x-template"> 包含模板。

在new Vue实例之前 使用 Vue.component() 去注册组件

<script>
  // 全局注册组件 
  // 一定是在new Vue实例之前
  /**
    Vue.component('component-name', { // 组件的配置
      template: '', // 组件的结构
      data () { // 组件的初始化数据,必须是函数
        return {}
      },
      methods: {}, // 组件自定义事件
      watch: {}, // 侦听属性
      computed: {}, // 计算属性
      mounted () {} // 生命周期钩子函数 - 都可以用
    })
  */
  // 组件名称在定义的时候可以写成
  // Vue.component('component-name', {}) 连接符
  // Vue.component('componentName', {}) 驼峰式命名
  // 使用 <component-name></component-name>
  const app = new Vue({
    el: '#app'
  })
</script>

实例代码

<body>
  <div id="app">
    <my-button ></my-button>
  </div>
</body>
<script>
  // 全局注册组件 
  // 一定是在new Vue实例之前
  /**
    Vue.component('component-name', { // 组件的配置
      template: '', // 组件的结构
      data () { // 组件的初始化数据,必须是函数
        return {}
      },
      methods: {}, // 组件自定义事件
      watch: {}, // 侦听属性
      computed: {}, // 计算属性
      mounted () {} // 生命周期钩子函数 - 都可以用
    })
  */
  // 组件名称在定义的时候可以写成
  // Vue.component('component-name', {}) 连接符
  // Vue.component('componentName', {}) 驼峰式命名
  // 使用 <component-name></component-name>

  // 1.定义组件
  const MyButton = {
    template: `<div>
                  <h3>vue的全局注册组件</h3>
                  {{ msg }}
               </div>`,
    data () {
      return {
        msg: '组件的初始化数据展示'
      }
    }
  }
  // 2.全局注册组件
  // Vue.component('my-button', MyButton);
  Vue.component('myButton', MyButton);
  const app = new Vue({
    el: '#app'
  })
</script>

局部注册组件

<body>
  <div id="app">
    <my-button ></my-button>
  </div>
</body>
<script>
  // 局部注册组件 
  // 在vue实例或者组件中添加 components 选项,在其内部进行组件的注册
  /**
    components: {
      'component-name': {
        template: '',
        data () { return {}}
        ...
      }
    }
   * */

  // 1.定义组件
  const MyButton = {
    template: `<div>
                  <h3>vue的局部注册组件</h3>
                  {{ msg }}
               </div>`,
    data () {
      return {
        msg: '组件的初始化数据展示'
      }
    }
  }

  const app = new Vue({
    el: '#app',
    components: {
      'my-button': MyButton
    }
  })
</script>

异步请求数据

<body>
  <div id="app">
    <my-button ></my-button>
  </div>
</body>
<script>
  // 局部注册组件 
  // 在vue实例或者组件中添加 components 选项,在其内部进行组件的注册
  /**
    components: {
      'component-name': {
        template: '',
        data () { return {}}
        ...
      }
    }
   * */

  // 1.定义组件
  const MyButton = {
    template: `<div>
                  <h3>vue的局部注册组件</h3>
                  {{ msg }}
                  <ul>
                    <li v-for="item of list" :key="item.proid">{{item.proname}}</li>  
                  </ul>
               </div>`,
    data () {
      return {
        msg: '组件的初始化数据展示',
        list: []
      }
    },
    mounted () {
      fetch('http://daxun.kuboy.top/api/pro').then(res => res.json()).then(data => {
        console.log(data)
        this.list = data.data
      })
    }
  }

  const app = new Vue({
    el: '#app',
    components: {
      'my-button': MyButton
    }
  })
</script>

抽离组件的模板

  • 1.使用script标签 — 老式写法
    *
<script type="text/x-template" id="button"></script>
  • 2.使用template标签 — 推荐
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
<template id="button"></template>

组件之间的传值

父子组件

在这里插入图片描述

<body>
    <div id="app">
        <!-- 不要直接使用content这种关键词<content></content> -->
        <my-content></my-content>
    </div>
</body>
<template id='content'>
    <div>
        <h3>content 父级</h3>
        <my-nav></my-nav>
    </div>
</template>
<template id='nav'>
    <div>
        <h3>nav 子级</h3>
    </div>
</template>
<script>
    const Nav = {
        template : '#nav'
    }
    // 注意 Content 下方 引用 Nav 时 需要先在上方定义,否则报错
    const Content = {
        template : '#content' ,
        components: {
            'my-nav' : Nav ,
        }
    }
    new Vue({
        el : '#app' ,
        components: {
            'my-content' : Content ,
        }
    })
</script>

父组件给子组件传值

  • 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
  • 这种方式就是组件的属性传值方式
  • 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性 (如果属性的值是boolean或者number类型,也需要使用绑定属性)
  • <my-nav :data="list" :flag="true" :num="10"></my-nav>
  • 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
  • props选项有三种写法
  • 1.props的形式为数组,数组的元素就是 自定义的属性名
  • props: ['data', 'flag', 'num']
  • 可以直接在子组件的模板中通过props数组元素访问数据
<body>
  <div id="app">
    <my-content></my-content>
  </div>
</body>
<template id="nav">
  <div>
    这里就是nav - {{ flag }} - {{ num }}
    <ul>
    	 <!-- 这里用的时data不是list -->
      <li v-for="(item, index) of data" :key="index">
        {{ item }}
      </li>
    </ul>
  </div>
</template>
<template id="content">
  <div>
    这里就是content区域
    <my-nav :data="list" :flag="true" :num="10"></my-nav>
  </div>
</template>
<script>
  // 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
  // 这种方式就是组件的属性传值方式

  // 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
  // <my-nav :data="list" :flag="true" :num="10"></my-nav>
  // 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
  // props选项有三种写法
  // 1.props的形式为数组,数组的元素就是 自定义的属性名
  //    props: ['data', 'flag', 'num']
  //   可以直接在子组件的模板中通过props数组元素访问数据
  const Nav = { // 子组件
    props: ['data', 'flag', 'num'],
    template: '#nav'
  }
  const Content = { // 父组件
    template: '#content',
    data () {
      return {
        list: ['a', 'b', 'c', 'd']
      }
    },
    components: {
      'my-nav': Nav
    }
  }
  new Vue({
    el: '#app',
    components: {
      'my-content': Content
    }
  })
</script>
  • 2 props的形式为对象,key值为自定义的属性名,value值为数据类型 — 严谨
  • props: { data: Array }
  • 可以直接在子组件的模板中通过props对象的key值访问数据
    在这里插入图片描述
<body>
  <div id="app">
    <my-content></my-content>
  </div>
</body>
<template id="nav">
  <div>
    这里就是nav区域 - {{ num }} - {{ flag }}
    <ul>
      <li v-for="item of data" :key='item.proid'>
        {{ item.proname }}
      </li>
    </ul>
  </div>
</template>
<template id="content">
  <div>
    这里就是content区域
    <my-nav :data="list" :flag="true" :num="1"></my-nav>
  </div>
</template>
<script>
  // 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
  // 这种方式就是组件的属性传值方式

  // 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
  // <my-nav :data="list" :flag="true" :num="10"></my-nav>
  // 在子组件Nav定义的地方,添加一个选项props,props就是用来描述接收父组件的地方
  // props选项有三种写法
  // 1.props的形式为数组,数组的元素就是 自定义的属性名
  //    props: ['data', 'flag', 'num']
  
  // 2. props的形式为对象,key值为自定义的属性名,value值为数据类型 --- 严谨
  // props: { data: Array }

  //   可以直接在子组件的模板中通过props数组元素访问数据
  const Nav = {
    // props: ['data'],
    props: {
      data: Array,
      num: Number,
      flag: Boolean
    },
    template: '#nav'
  }
  const Content = {
    template: '#content',
    data () {
      return {
        list: []
      }
    },
    mounted () {
      fetch('http://localhost:3000/api/pro').then(res => res.json()).then(data => {
        console.log(data)
        this.list = data.data
      })
    },
    components: {
      'my-nav': Nav
    }
  }
  new Vue({
    el: '#app',
    components: {
      'my-content': Content
    }
  })
</script>
  • 3.props的形式为对象,key值为自定义的属性名,value值为一个对象,在此对象下,第一个key为type,value值为数据类型,第二个key值为 default,value值为默认值,如果默认的数据是数组或者对象,需要使用函数返回默认值 — 更加严谨
  • props: { data: { type: Array, defualt: () => { return []}, num: { type: Number, default: 000}, flag: { type: Boolean, default: false}}
  • 可以直接在子组件的模板中通过props对象的key值访问数据
<body>
  <div id="app">
    <my-content></my-content>
  </div>
</body>
<template id="nav">
  <div>
    这里就是nav区域 - {{ num }} - {{ flag }}
    <ul>
      <li v-for="item of data" :key='item.proid'>
        {{ item.proname }}
      </li>
    </ul>
  </div>
</template>
<template id="content">
  <div>
    这里就是content区域
    <my-nav :data="list" :flag="true" :num="1"></my-nav>
    <my-nav></my-nav>
  </div>
</template>
<script>
  // 父组件中的数据自己并不渲染,通过某一种方式传递给子组件,子组件负责渲染
  // 这种方式就是组件的属性传值方式

  // 父组件(Content)在调用子组件(Nav)的地方,给他添加一个自定义的属性(data), 那么自定义属性的值就是父组件需要传递给子组件的值,如果属性的值是一个变量,就需要使用绑定属性(如果属性的值是boolean或者number类型,也需要使用绑定属性)
  // <my-nav :data="list" :flag="true" :num="10"></my-nav>
  // 在子组件(Nav)定义的地方,添加一个选项props,props就是用来描述接收父组件数据的地方
  // props选项有三种写法
  // 1.props的形式为数组,数组的元素就是 自定义的属性名
  //    props: ['data', 'flag', 'num']
  //   可以直接在子组件的模板中通过props数组元素访问数据
  // 2. props的形式为对象,key值为自定义的属性名,value值为数据类型 --- 严谨
  // props: { data: Array }
  //   可以直接在子组件的模板中通过props对象的key值访问数据
  // 3.props的形式为对象,key值为自定义的属性名,value值为一个对象,在此对象下,第一个key为type,value值为数据类型,第二个key值为 default,value值为默认值,如果默认的数据是数组或者对象,需要使用函数,返回默认值 --- 更加严谨
  // props: { data: { type: Array, defualt: () => { return []}, num: { type: Number, default: 000}, flag: { type: Boolean, default: false}}
  //   可以直接在子组件的模板中通过props对象的key值访问数据
  const Nav = {
    // props: ['data'],
    // props: {
    //   data: Array,
    //   num: Number,
    //   flag: Boolean
    // },
    props: {
      data: {
        type: Array,
        default: () => { // 如果是数组或者对象,使用函数返回
          return [
            {
              proid: '111',
              proname: '默认值'
            }
          ]
        }
      },
      num: {
        type: Number,
        default: 100
      },
      flag: {
        type: Boolean,
        default: false
      }
    },
    template: '#nav'
  }
  const Content = {
    template: '#content',
    data () {
      return {
        list: []
      }
    },
    mounted () {
      fetch('http://localhost:3000/api/pro').then(res => res.json()).then(data => {
        console.log(data)
        this.list = data.data
      })
    },
    components: {
      'my-nav': Nav
    }
  }
  new Vue({
    el: '#app',
    components: {
      'my-content': Content
    }
  })
</script>

子组件给父组件传值

  • 在子组件中,通过某一个事件触发,执行以下事件
  • this.$emit(自定义事件名, 传递的参数)
  • 在父组件调用子组件的地方,绑定 自定义事件名 的事件,此事件由 父组件实现,注意绑定事件不加(),方法实现有默认参数,此参数的值就是子组件传递过来的值
<body>
  <div id="app">
    <my-content></my-content>
  </div>
</body>
<template id="nav">
  <div>
    这里就是nav区域
    <button @click="senddata('aaa')">改变为aaa</button>
    <button @click="senddata('bbb')">改变为bbb</button>
    <button @click="senddata('ccc')">改变为ccc</button>
    <button @click="senddata('ddd')">改变为ddd</button>
  </div>
</template>
<template id="content">
  <div>
    这里就是content区域 - {{ tip }}
    <my-nav @my-event="getData"></my-nav>
  </div>
</template>
<script>
  // 在子组件中,通过某一个事件触发,执行以下事件
  // this.$emit(自定义事件名, 传递的参数)
  // 在父组件调用子组件的地方,绑定 自定义事件名 的事件,此事件由 父组件实现,注意绑定事件不加(),方法实现有默认参数,此参数的值就是子组件传递过来的值
  const Nav = {
    template: '#nav',
    methods: {
      senddata (val) {
        this.$emit('my-event', val)
      }
    }
  }
  const Content = {
    template: '#content',
    data () {
      return {
        tip: '1'
      }
    },
    methods: {
      getData (val) {
        this.tip = val
      }
    },
    components: {
      'my-nav': Nav
    }
  }
  new Vue({
    el: '#app',
    components: {
      'my-content': Content
    }
  })
</script>

非父子组件之间传值

// 非父子组件之间传值 — 中央事件总线
// new Vue实例作为中央事件总监 const bus = new Vue()
// 在需要传递数据的一方,通过以下代码传递数据
// bus. e m i t ( , ) / / / / b u s . emit(自定义事件名, 传递的数据) // 在需要接受数据的一方,通过以下代码接收数据 // bus. on(自定义事件名, function (val) {})

<body>
  <div id="app">
    <my-nav></my-nav>
    <my-content></my-content>
  </div>
</body>
<template id="nav">
  <div>
    <button @click="sendData('首页')">首页</button>
    <button @click="sendData('分类')">分类</button>
    <button @click="sendData('购物车')">购物车</button>
    <button @click="sendData('我的')">我的</button>
  </div>
</template>
<template id="content">
  <div>
    content - {{ tip }}
  </div>
</template>
<script>
  // 非父子组件之间传值 --- 中央事件总线
  // new Vue实例作为中央事件总监 const bus = new Vue()
  // 在需要传递数据的一方,通过以下代码传递数据
  // bus.$emit(自定义事件名, 传递的数据)
  // 在需要接受数据的一方,通过以下代码接收数据
  // bus.$on(自定义事件名, function (val) {})
  const bus = new Vue()
  const Nav = {
    template: '#nav',
    methods: {
      sendData (val) {
        bus.$emit('my-event', val)
      }
    }
  }
  const Content = {
    template: '#content',
    data () {
      return {
        tip: '首页'
      }
    },
    mounted () {
      // const that = this
      // bus.$on('my-event', function (val) {
      //   console.log(val)
      //   that.tip = val
      // })
      bus.$on('my-event', (val) => {
        console.log(val)
        this.tip = val
      })
    }
  }
  new Vue({
    el: '#app',
    components: {
      'my-nav': Nav,
      'my-content': Content
    }
  })
</script>

动态组件

<body>
  <div id="app">
    <button @click="com = 'home'">首页</button>
    <button @click="com = 'kind'">分类</button>
    <button @click="com = 'cart'">购物车</button>
    <button @click="com = 'user'">我的</button>
    <component :is="com"></component>
  </div>
</body>
<template id="home">
  <div>首页</div>
</template>
<template id="kind">
  <div>分类</div>
</template>
<template id="cart">
  <div>购物车</div>
</template>
<template id="user">
  <div>我的</div>
</template>
<script>
  const Home = {
    template: '#home'
  }
  const Kind = {
    template: '#kind'
  }
  const Cart = {
    template: '#cart'
  }
  const User = {
    template: '#user'
  }
  new Vue({
    el: '#app',
    data: {
      com: 'home'
    },
    components: {
      'home': Home,
      'kind': Kind,
      'cart': Cart,
      'user': User
    }
  })
</script>

如果每个中都含有一个表单,那么再来分析情况,输入之后切换组件,输入框中的值就全部清空 ---- 切换组件 实际上就是触发了组件的 销毁 与创建

<body>
  <div id="app">
    <button @click="com = 'home'">首页</button>
    <button @click="com = 'kind'">分类</button>
    <button @click="com = 'cart'">购物车</button>
    <button @click="com = 'user'">我的</button>
    <component :is="com"></component>
  </div>
</body>
<template id="home">
  <div>首页
    <input type="text">
  </div>
</template>
<template id="kind">
  <div>分类
    <input type="text">
  </div>
</template>
<template id="cart">
  <div>购物车
    <input type="text">
  </div>
</template>
<template id="user">
  <div>我的
    <input type="text">
  </div>
</template>
<script>
  const Home = {
    template: '#home',
    destroyed () {
      console.log('home destroyed')
    }
  }
  const Kind = {
    template: '#kind',
    destroyed () {
      console.log('kind destroyed')
    }
  }
  const Cart = {
    template: '#cart',
    destroyed () {
      console.log('cart destroyed')
    }
  }
  const User = {
    template: '#user',
    destroyed () {
      console.log('user destroyed')
    }
  }
  new Vue({
    el: '#app',
    data: {
      com: 'home'
    },
    components: {
      'home': Home,
      'kind': Kind,
      'cart': Cart,
      'user': User
    }
  })
</script>

看需求,有的不需要销毁与创建,有的正好

可以使用 keep-alive 缓存 当前的组件,避免组件的销毁与重建

<keep-alive><component :is="com"></component></keep-alive>
在这里插入图片描述

实质上触发了组件的 activated 和 deactivated 生命周期钩子 - 10个生命周期的钩子函数

<body>
  <div id="app">
    <button @click="com = 'home'">首页</button>
    <button @click="com = 'kind'">分类</button>
    <button @click="com = 'cart'">购物车</button>
    <button @click="com = 'user'">我的</button>
    <keep-alive>
      <component :is="com"></component>
    </keep-alive>
  </div>
</body>
<template id="home">
  <div>首页
    <input type="text">
  </div>
</template>
<template id="kind">
  <div>分类
    <input type="text">
  </div>
</template>
<template id="cart">
  <div>购物车
    <input type="text">
  </div>
</template>
<template id="user">
  <div>我的
    <input type="text">
  </div>
</template>
<script>
  const Home = {
    template: '#home',
    activated () {
      console.log('home activated')
    },
    deactivated () {
      console.log('home deactivated')
    },
    destroyed () {
      console.log('home destroyed')
    }
  }
  const Kind = {
    template: '#kind',
    activated () {
      console.log('kind activated')
    },
    deactivated () {
      console.log('kind deactivated')
    },
    destroyed () {
      console.log('kind destroyed')
    }
  }
  const Cart = {
    template: '#cart',
    activated () {
      console.log('cart activated')
    },
    deactivated () {
      console.log('cart deactivated')
    },
    destroyed () {
      console.log('cart destroyed')
    }
  }
  const User = {
    template: '#user',
    activated () {
      console.log('user activated')
    },
    deactivated () {
      console.log('user deactivated')
    },
    destroyed () {
      console.log('user destroyed')
    }
  }
  new Vue({
    el: '#app',
    data: {
      com: 'home'
    },
    components: {
      'home': Home,
      'kind': Kind,
      'cart': Cart,
      'user': User
    }
  })
</script>

思考: 要不就是创建和销毁,要不就是缓存与显示,那么需求如果是有些需要销毁和创建,有的需要缓存呢?

 <!-- 这里的include 一定不要有空格 -->
<keep-alive include="homecom,kindcom">
<component :is="com"></component>
</keep-alive>

homecom,kindcom 是定义组件的时候给每一个组件起的name,注意include中间不要加空格

<body>
  <div id="app">
    <button @click="com = 'home'">首页</button>
    <button @click="com = 'kind'">分类</button>
    <button @click="com = 'cart'">购物车</button>
    <button @click="com = 'user'">我的</button>
    <keep-alive include="homecom,kindcom">
      <component :is="com"></component>
    </keep-alive>
  </div>
</body>
<template id="home">
  <div>首页
    <input type="text">
  </div>
</template>
<template id="kind">
  <div>分类
    <input type="text">
  </div>
</template>
<template id="cart">
  <div>购物车
    <input type="text">
  </div>
</template>
<template id="user">
  <div>我的
    <input type="text">
  </div>
</template>
<script>
  const Home = {
    name: 'homecom',
    template: '#home',
    activated () {
      console.log('home activated')
    },
    deactivated () {
      console.log('home deactivated')
    },
    destroyed () {
      console.log('home destroyed')
    }
  }
  const Kind = {
    name: 'kindcom',
    template: '#kind',
    activated () {
      console.log('kind activated')
    },
    deactivated () {
      console.log('kind deactivated')
    },
    destroyed () {
      console.log('kind destroyed')
    }
  }
  const Cart = {
    name: 'cartname',
    template: '#cart',
    activated () {
      console.log('cart activated')
    },
    deactivated () {
      console.log('cart deactivated')
    },
    destroyed () {
      console.log('cart destroyed')
    }
  }
  const User = {
    name: 'usercom',
    template: '#user',
    activated () {
      console.log('user activated')
    },
    deactivated () {
      console.log('user deactivated')
    },
    destroyed () {
      console.log('user destroyed')
    }
  }
  new Vue({
    el: '#app',
    data: {
      com: 'home'
    },
    components: {
      'home': Home,
      'kind': Kind,
      'cart': Cart,
      'user': User
    }
  })
</script>

脚手架

创建项目

准备工作

node -v
npm -v

安装cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

安装脚手架 3版本以上 — 强烈推荐

cnpm i @vue/cli -g
vue -v

方式1 - 命令行操作

vue create myapp

方式2 - 可视化操作

vue ui

安装脚手架 2版本 ----- 不推荐

cnpm i vue-cli -g

创建项目

vue init webpack myapp

如果你安装的是3的脚手架,但是你又需要使用2的创建项目

cnpm i @vue/cli-init -g
vue init webpack myapp

发布了38 篇原创文章 · 获赞 0 · 访问量 904

猜你喜欢

转载自blog.csdn.net/ZywOo_/article/details/104905343