vue之组件

vue 郑士旭
文章标签: 组件

VUE组件

一.组件基础

1.概念

组件类似于Vue实例,但是没有el挂载,而且data里面必须是函数(为了保证组件的复用不相互影响)

2.组件的定义和注册使用

定义:组件中定义模板(注意:模板中必须要包含一个根节点,最外层需要包住)

    var component1=({
        data(){
            return {
                count:0,
            }
        },
        template:`<button @click='count++'>{{count}}</button>`
    })

注册:全局注册和局部注册

Vue.component("buttoncount",component1);//全局
    var vm=new Vue({
        el:"#app",
        data:{

        },
        components:{
            "buttoncount":component1,
        }
    })
//局部注册,组件名全部使用小写不易犯错

3.关于组件与父级之间的数据传递

父传子:props

image.png

组件里面的props取一个自定义名字,那么template里面也要使用这个定义的名字,然后把vue实例的需要绑定的在组件上绑定

props绑定一整个对象,且props可以近行语法的校验

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> 校验:         props:{ number:Number, age:{ type:Number, default:18, required:true,//是否是必须的 validator(value){ return value>18 &&value<60; } } },

props是一个单向数据流,所以不要再子组件中试图改变他,如果要改变需要先定义好

①这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

但是在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

非prop的特性

①替换合并已有的特性

<input type="date" class="form-control">
<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
></bootstrap-date-input>

class和style的特性会合起来,而其他的一些比如type的就会被替换掉

②禁用特性继承

    <div id="app">
        <custom-input
        v-bind:value="customInputText"
        v-on:input="customInputText = $event" 
        ></custom-input>
        <!-- object{} -->
        <custom-input
        v-bind:value="customInputText"
        v-on:input="customInputText = $event" 
        :a="a"
        ></custom-input>   
        <!-- {a:1} -->
    </div>
    Vue.component('custom-input', {
        props: ['value'],
        template: `
            <input
            v-bind:value="value"
            v-on:input="$emit('input', $event.target.value)"
            >
        `,
        mounted:function(){
            console.log(this.$attrs);
        }
    })
    const app = new Vue({
        el:'#app',
        data:{
            customInputText:"customInputText",
            a:'1'
        }
    });

image.png

image.png

当在子组件中加入inheritAttrs:false时a属性就不会自动加到根元素上了。

总之一句话:$attrs存储非prop特性,inheritAttrs控制vue对非prop特性默认行为


也可以使用插槽(具名插槽)来传递数据

    var component1=({
        template:`
        <div>
            <p>插槽代码</p>
            <slot name="default"></slot>
            <slot name="top"></slot>
            <slot name="late"></slot>
        </div>
        `,
    })
        <ppt>
            <p slot="top">hello</p>
            <span slot="late">word</span><span slot="default">e</span>        
        </ppt>

子传父:$emit

image.png

后面跟着的参数$event是当前的对象

$emit后面跟着的自定义方法如果写成了input,那么久相当于是input事件,就是双向绑定了

            <textx @input="leftcon=$event"></textx>
            <textx v-model="leftcon"></textx>
            input输入框的绑定就是双向绑定的所以可以用v-model来绑定

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})
//绑定了value值即input的值,绑定了input事件,传递的数据是当前对象的value值,就形成了v-model


4.is的使用和keep-alive

is可以在组件之间切换,但是切换的时候vue会重新创建实例,会反复重渲染导致性能问题,所以使用keep-alive可以在它们第一次被创建的时候缓存下来,使用keep-alive 要求被切换到的组件都有自己的名字

    <div id="app">
        <button v-for="tab in tabs" :key="tab.name" @click="zjs=tab">{{tab.name}}</button>
        <component :is="zjs.component"></component>
        //使用keep-alive
        <keep-alive>
          <component v-bind:is="zjs.component"></component>
        </keep-alive>
    </div>

<script>
    var tabs = [
        {
            name: 'Home', 
            component: { 
            template: '<div>Home component</div>' 
            }
        },
        {
            name: 'Posts',
            component: {
            template: '<div>Posts component</div>'
            }
        },
        {
            name: 'Archive',
            component: {
            template: '<div>Archive component</div>',
            }
        }
    ]
    var vm=new Vue({
        el:"#app",
        data:{
            tabs,
            zjs:tabs[0],
        }
    })
</script>

解析html的时候,有些元素,诸如 <li><tr> 和 <option>,只能出现在其它某些特定的元素内部。

当你要在table里面插入组件的时候就需要用到is

<table>
  <tr is="blog-post-row"></tr>//组件名
</table>

5.  .sync的使用(类似model的双向绑定)

功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。

this.$emit('update:title', newTitle)
<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>
//等同于下面的这段
<text-document v-bind:title.sync="doc.title"></text-document>

<template>
    <div class="details">
        <myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
        <button @click="changeValue">toggle</button>
    </div>
</template>
<script>
import Vue from 'vue'
Vue.component('myComponent', {
      template: `<div v-if="show">
                    <p>默认初始值是{{show}},所以是显示的</p>
                    <button @click.stop="closeDiv">关闭</button>
                 </div>`,
      props:['show'],
      methods: {
        closeDiv() {
          this.$emit('update:show', false); //触发 input 事件,并传入新值
        }
      }
})
export default{
    data(){
        return{
            valueChild:true,
        }
    },
    methods:{
        changeValue(){
            this.valueChild = !this.valueChild
        }
    }
}
</script>

6.异步组件:会异步解析你的组件定义缓存起来,在这个组件需要被渲染的时候使用(通过异步的promise)

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

Vue.component('async-webpack-example', function (resolve) {
  // 这个特殊的 `require` 语法将会告诉 webpack
  // 自动将你的构建代码切割成多个包,这些包
  // 会通过 Ajax 请求加载
  require(['./my-async-component'], resolve)
})

Vue.component(
  'async-webpack-example',
  // 这个 `import` 函数会返回一个 `Promise` 对象。
  () => import('./my-async-component')
)

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

关于异步,说一下vue+webpack实现异步加载的三种方法

1.
const Home = resolve => {
  import("@/components/home/home.vue").then( module => {
      resolve(module)
  }
}
2.
const router = new Router({
  routes: [
    {
       path: '/home',
       component: (resolve)=> {
         require(['../components/home/home'], resolve) // 省去了在上面去import引入
       }
     }
  ]
})
3.
const Home = r => require.ensure([], () => r(require('../components/home/home')), 'home');
const router = new Router({
  routes: [
    {
     path: '/home/home',
     component: Home,
     name: 'home' ,
    }
  ]
})


处理加载状态:

const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})

7.处理边界情况

还能输出{{restrictNumber}}个字符  
  • {{reply.author}}

    {{CommonUtil.formateDate(reply.ac_CommentDate).shortTime}}
  • 回复了{{Comments.author}} :