组件

作者: 66pillow | 来源:发表于2017-09-10 17:19 被阅读0次

1.使用组件

//组件命名规则:全小写-分隔(kebab-case)
//注册一个全局组件
Vue.component('my-component', {});
//注册一个局部组件
new Vue({
    components:{
        'my-component':{}
    }
});
<table>
    <!-- ul,ol,table,select标签限制了被包裹元素的类型,如果使用自定义组件会有问题 -->
    <!-- my-row></my-row -->
    <!-- 变通方案使用is属性 -->
    <tr is="my-row"></tr>
</table>

为什么component的data被设计为一个函数?
嘻嘻...你猜...

2.父子组件通信

父组件通过props向下传递数据给子组件,子组件通过events给父组件发送消息

组件实例作用域是孤立的,不应该在子组件内部直接引用父组件数据,应该通过props选项访问

<!-- myMessage属性camelCase在html中要写成kebab-case -->
<!-- my-message绑定静态字符串 -->
<my-component my-message="hello"></my-component>
<!-- :my-message动态绑定父组件变量,当父组件数据变化,会传导给子组件 -->
<my-component :my-message="message"></my-component>
Vue.component('my-component', {
    props:['myMessage'],
    template:'<span>{{myMessage}}</span>'
});

var app = new Vue({
    el: "#app",
    data:{
        message:"from parent"
    }
});

props是单向绑定,父组件属性变化会传到给子组件,但不会反过来,不应该在子组件内部直接改变prop值

<my-component :init="init" :size="size"></my-component>
Vue.component('my-component', {
    props: ['init', 'size'],
    template: '<span>{{myInit}}{{mySize}}</span>',
    //组件内部改变prop的2种方案:
    //1.prop作为值初始值传入,赋值给组件局部变量
    data: function () {
        return {myInit: this.init}
    },
    //2.prop作为初始值传入,由组件处理为其他数据输出
    computed: {
        mySize: function () {
            return this.size.toLowerCase();
        }
    }
});

var app = new Vue({
    el: "#app",
    data: {
        init: 0,
        size: "six"
    }
});

props如果是引用类型,在子组件内部改变其属性会影响父组件

可为组件props指定验证规则

<my-component :pro-a="proA" :pro-b="proB" :pro-c="proC" :pro-d="proD"></my-component>
Vue.component('my-component', {
    props: {
        //String,Number,Boolean,Function,Object,Array,Symbol,null(任意类型)
        proA: Number,
        //多种类型
        proB: [String, Number],
        proC: {
            type: String,
            //设置默认值
            default: "66",
            //设置必填验证
            required: true
        },
        proD: {
            type: Number,
            //自定义默认值
            default: function () {
                return 6;
            },
            //自定义验证规则
            validator: function (value) {
                return value >= 6;
            }
        }
    },
    template: '<span></span>'
});

var app = new Vue({
    el: "#app",
    data: {
        //报错
        proA: "6",
        //报错
        proB: [],
        //报错
        proC: null,
        //报错
        proD: 5
    }
});

3.自定义事件

子组件通过自定义事件系统于父组件通信

  • $on(eventName)监听事件
  • $emit(eventName)触发事件
<!-- 父组件可直接使用v-on监听子组件触发的自定义事件 -->
<my-button @myevent="addTotal"></my-button>
<!-- 父组件监听子组件根元素上的原生事件,使用.native修饰符 -->
<my-button @click.native="addTotal"></my-button>
Vue.component('my-button', {
    template: '<button @click="addCount">{{count}}</button>',
    data: function () {
        return {
            count: 0
        }
    },
    methods: {
        addCount: function () {
            this.count++;
            //触发自定义事件
            this.$emit('myevent');
        }
    }
});

var app = new Vue({
    el: "#app",
    data: {
        total: 0
    },
    methods: {
        addTotal: function () {
            this.total++;
        }
    }
});

v-model语法糖实际会转化为::value="value" @input="value=$event.target.value",因此,自定义事件可以用来创建自定义表单输入组件,使用v-model实现数据双向绑定

{{currency}}
<!--my-currency :value="currency" @input="currency = $event.target.value"></my-currency-->
<my-currency v-model="currency"></my-currency>
Vue.component('my-currency', {
    props: ["value"],
    template: '<div><input ref="myInput" :value="value" @input="updateValue"/></div>',
    methods: {
        updateValue: function (event) {
            var currency = "$" + event.target.value.replace(/\$/g, "");
            this.$refs.myInput.value = currency;
            //触发input事件
            this.$emit('input', currency);
        }
    }
});

var app = new Vue({
    el: "#app",
    data: {
        currency: "0"
    }
});

可定制组件的v-model的prop和event,避免冲突

{{currency}}
<!--my-currency :myvalue="currency" @myinput="currency = $event.target.value"></my-currency-->
<my-currency v-model="currency" value="hello"></my-currency>
Vue.component('my-currency', {
    //可定制v-model
    model: {
        prop: 'myvalue',
        event: 'myinput'
    },
    //释放value属性
    props: ['myvalue', 'value'],
    template: '<div><input ref="myInput" :value="myvalue" @input="updateValue"/></div>',
    methods: {
        updateValue: function (event) {
            var currency = "$" + event.target.value.replace(/\$/g, "");
            this.$refs.myInput.value = currency;
            //触发myinput事件
            this.$emit('myinput', currency);
        }
    }
});

var app = new Vue({
    el: "#app",
    data: {
        currency: "0"
    }
});

非父子组件通信,简单场景使用一个空Vue实例做事件中转,复杂情况请考虑状态管理模式

var bus = new Vue();
bus.$emit('my-event', 6);
bus.$on('my-event', function(arg){});

4.使用Slots分发内容

slots标签让组件内容可以混合嵌套,类似Angular的transclusion

<!-- 最终生成html -->
<!--div>
    <h1>child</h1>
    <div>parent content</div>
</div-->
<my-component>
    <div>parent content</div>
</my-component>
Vue.component('my-component', {
    template: '<div><h1>child</h1><slot></slot></div>'
});

为slot指定name来配置如何分发内容

<!-- 最终生成html -->
<!--div>
    <h1>header</h1>
    <p>footer</p>
</div-->
<my-layout>
    <h1 slot="header">header</h1>
    <p slot="footer">footer</p>
</my-layout>
Vue.component('my-layout', {
    template: '<div><slot name="header"></slot><slot name="footer"></slot></div>'
});

作用域插槽,暴露数据,允许父组件自定义渲染

<my-list :items="items">
    <!-- template表示作用域插槽模板,scope为一临时变量,来至子组件 -->
    <template slot="listItem" scope="props">
        <!-- 自定义渲染 -->
        <li>{{props.text}}</li>
    </template>
</my-list>
Vue.component('my-list', {
    props: ['items'],
    template: '<ul><slot name="listItem" v-for="item in items" :text="item"></slot></ul>'
});

var app = new Vue({
    el: "#app",
    data: {
        items: ["1", "2"]
    }
});

5.动态组件

通过<component>元素,动态绑定is特性,可使用同一个挂载点,动态切换组件

<component :is="currentView"></component>
<!-- keep-alive保留切换出去的组件在内存,避免重新渲染 -->
<keep-alive>
    <component :is="currentView"></component>
</keep-alive>
<button @click="change">change</button>
var myHeader = {
    template: '<h1>Header</h1>'
};

var myFooter = {
    template: '<div>Footer</div>'
};

var myText = {
    template: '<input type="text"/>'
};

var app = new Vue({
    el: "#app",
    data: {
        //currentView: myHeader
        currentView: 'myFooter'
    },
    components: {
        myFooter: myFooter,
        myText: myText
    },
    methods: {
        change: function () {
            this.currentView = (this.currentView == 'myText' ? 'myFooter' : 'myText');
        }
    }
});

6.杂项

组件分3部分:

  • props允许外部传递数据给组件
  • events允许外部和组件内部通信
  • slots允许外部自定义内容渲染到组件中

子组件索引

<!-- ref为子组件指定索引,用来在javascript中直接访问子组件 -->
<my-component ref="my"></my-component>
Vue.component('my-component', {
    data: function () {
        return {name: "66"};
    },
    template: '<div>66</div>'
});

var app = new Vue({
    el: "#app"
});

//通过实例属性$refs访问子组件
app.$refs.my.name;

异步组件
将应用拆分多个小模块,按需从服务器下载,Vue允许将组件定义为一个工厂函数,动态解析,建议配合Webpack异步加载功能食用

<async-component></async-component>
Vue.component('async-component', function (resolve, reject) {
    //模拟异步网络请求
    setTimeout(function () {
        //调用porime模式resolve方法表示成功返回异步组件
        resolve({template: '<div>async</div>'});
    }, 1000);
});

var app = new Vue({
    el: "#app"
});

组件命名约定
注册组件(或props)使用kebab-case,camelCase,PascalCase,HTML模板中只能使用kebab-case

递归组件
设置了name选项,组件才可以递归调用自己

<my-component :tree="tree"></my-component>
Vue.component('my-component', {
    props: ['tree'],
    name: 'component',
    template: '<div>' +
    '<div v-if="tree instanceof Array">' +
    '<component :tree="item" v-for="item in tree"></component>' +
    '</div>' +
    '<div v-else>{{tree}}</div>' +
    '</div>'
});

var app = new Vue({
    el: "#app",
    data: {
        tree: [[1, 2], 3]
    }
});

要保证递归调用有终止条件(如v-if最终返回false),否则可能出现死循环

组件循环引用
A引用B,B中也引用A,使用Vue.component注册的全局组件,框架会自动解决依赖组件的矛盾

X-Templates

<my-component></my-component>

<script type="text/x-template" id="my-component">
    <div>hello</div>
</script>
Vue.component('my-component', {
    template: '#my-component'
});

低开销静态组件使用v-once,缓存渲染结果

Vue.component('my-component', {
    template: '<div v-once>message</div>'
});

相关文章

网友评论

      本文标题:组件

      本文链接:https://www.haomeiwen.com/subject/tlhnjxtx.html