组件

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>'
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容

  • 此文基于官方文档,里面部分例子有改动,加上了一些自己的理解 什么是组件? 组件(Component)是 Vue.j...
    陆志均阅读 3,818评论 5 14
  • Vue.js 是一套构建用户界面的渐进式框架。我们可以使用简单的 API 来实现响应式的数据绑定和组合的视图组件。...
    前端小透明阅读 8,384评论 10 54
  • 9.1 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,...
    白水螺丝阅读 769评论 0 2
  • 序 今年大前端的概念一而再再而三的被提及,那么大前端时代究竟是什么呢?大前端这个词最早是因为在阿里内部有很多前端开...
    一缕殇流化隐半边冰霜阅读 11,233评论 19 92
  • 什么是组件 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用...
    angelwgh阅读 781评论 0 0