Vue组件基础

通过Vue在项目中的应用,我发现它的双向绑定机制对于表单异步请求十分频繁的项目十分受用,因为相当于输入绑定在了变量域中,可以直接对它的data域进行提取。而且如果将Ajax请求设为同步的,之后可以直接使用请求完毕的数据设为Vue实例的data域,还是十分方便的。但是过程中也发现了,在Vue实例中,有时想嵌入其他的Vue实例,却是做不到,就想趁着不忙了把组件这块补上。

组件的定义
如下

    Vue.component('button-counter', {
        data: function () {
            return {
                count: 0
            }
        },
        template: '<button v-on:click="count++">You clicked me {{ count }} times. </button>'
    });

可以看到组件的基本结构分为data和template两种,类似于一个完整的Vue实例(只不过没有el这个属性),但是其中的data返回的是一个函数,如果不写成函数的话,则JS引擎每次初始化一个组件,他们的data域就会指向同一个引用,造成事实上的耦合。

而template则类似于React的JSX代码,使用HTML文本片来书写组件真实的模板HTML代码,配合合适的IDE,在其中也可以做到自动补全。

我们来实例化一个Vue对象。

    <div id="components-demo">
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
<!--        组件可以复用多次,每次复用代表创建一个新的Vue实例-->
    </div>
    var vm1 = new Vue({
        el: '#components-demo'
    });
最终的效果图,点击按钮之间的数据互不影响

颇有一丝XML的味道,目前小程序中稀奇古怪的标记语言也是基于XML构建的,在此不再赘述,在Vue实例装载后,会在渲染“button-counter”这个标记之前在Vue全局对象绑定的组件中找到对应的组件,如果没有,则会抛出一个异常,否则则会按照实例对象和组件中的data进行virtual dom的渲染。

组件的属性
说白了类似于对函数的调用,传入的参数。这里的“参数”可以不止一个(方括号),其中的字符串代表“参数名”。

    // 为组件声明属性列表
    Vue.component('blog-post', {
        props: ['title'],
        template: '<h3>{{ title }}</h3>'
    });

不同的是,标记语言元素的参数是以属性给出的,可以猜想Vue其中封装了一个高性能的XML解析器?

    <div id="property-demo">
        <blog-post title="hello"></blog-post>
        <blog-post title="thank you"></blog-post>
        <blog-post title="thank you very much"></blog-post>
    </div>

实例化Vue对象。

    var vm2 = new Vue({
        el: '#property-demo'
    });

效果也还不错。


效果

深入探讨组件属性

可能经历过HTML4时代的前端开发者,对于一些h4中的标记,比如<font></font>之类的是深恶痛绝,虽然极大的降低了学习成本,但是一旦接手对于该项目的重构,改完可能手都要抽筋了

HTML元素可以使用v-bind绑定数据和属性,自然组件也可以。

    var vm3 = new Vue({
        el: '#property-demo2',
        data: {
            posts: [
                { id: 1, title: 'My journey with Vue' },
                { id: 2, title: 'Blogging with Vue' },
                { id: 3, title: 'Why Vue is so fun' }
            ]
        }
    });

复用刚才的组件

    <div id="property-demo2">
        <blog-post v-for="post in posts"
                   v-bind:key="post.id"
                   v-bind:title="post.title"
        ></blog-post>
    </div>

结果是类似的,但内容不同。


结果

再联想到刚才我提到的可以使用Ajax初始化Vue实例的data域,是不是又有了新的想法呢#手动滑稽。

与此同时,搭配v-html属性,可以使模板的用法更加丰富。

    Vue.component('blog-posture', {
        props: ['post'],
        template:
        "<div class='blog-post'>" +
            "<h3>{{ post.title }}</h3>" +
            "<div v-html='post.content'></div>" +
            "</div>"
    })

    var vm4 = new Vue({
        el: '#property-demo3',
        data: {
            posts: [
                { id: 1, title: 'My journey with Vue' , content: '<div>123</div><p>4949494</p>'},
                { id: 2, title: 'Blogging with Vue' , content: '<img src=x onerror="javaScript:alert(1)>'},
                { id: 3, title: 'Why Vue is so fun' , content: '<textarea>1231231223123</textarea>'}
            ]
        }
    });

我们在posts数组中的数据里,定义了一个content用来盛放html代码(注意其中id为2的代码,它并不会执行),这样content的内容最终会渲染到template的div中。
HTML代码:

    <div id="property-demo3">
        <blog-posture v-for="post in posts"
                   v-bind:key="post.id"
                   v-bind:title="post.title"
                      v-bind:post="post"
        ></blog-posture>
    </div>

最终结果是这样的。


v-html渲染结果

但是其中的xss代码并没有被执行,或者说那个img元素,压根没被渲染。


XSS攻击失败

控制台中的结果证明了这个结论。

组件的双向绑定
首先这个让我想起了v-model这个指令。

<input v-model="searchText">

它实质上是一个语法糖,包含了v-bind和v-on:两条指令的搭配,在处理输入的双向绑定的时候,它等效于这两个属性的组合:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

基于这个原理,我们也可以使用组件自定义输入框(毕竟原生的确实是很丑)

    Vue.component('custom-input', {
        props: ['value'],
        template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
    });

注意

1.组件必须向外提供value属性,且HTML代码必须提供该属性
2.在组件的template内部,使用v-bind和v-on组合的方式,将组件的value属性绑定到template中输入框的value属性中,并且处理好v-on中的input事件。

于是Vue实例就可以使用v-model来绑定实例中的数据。

    var vm6 = new Vue({
        el: '#component-input',
        data: {
            searchText: ""
        }
    })

    <div id="component-input">
        <custom-input
                v-model="searchText"
        ></custom-input>
    </div>
组件双向绑定

组件-实例通信

组件可以共享实例的全局变量。

    Vue.component('blog-position', {
        props: ['post'],
        template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button v-on:click="$emit('enlarge-text')">
        Enlarge text
      </button>
      <div v-html="post.content"></div>
    </div>
  `
    });


    var vm5 = new Vue({
        el: '#blog-posts-events-demo',
        data: {
            posts: [
                { id: 1, title: 'My journey with Vue' , content: '<div>123</div><p>4949494</p>'},
                { id: 2, title: 'Blogging with Vue' , content: '<img src=x onerror="javaScript:alert(1)>'},
                { id: 3, title: 'Why Vue is so fun' , content: '<textarea>1231231223123</textarea>'}
            ],
            postFontSize: 1
        }
    })
    vlist.push(vm5);

这里我们还用到了$emit方法,是处理自定义事件的。

    <div id="blog-posts-events-demo">
        <div :style="{ fontSize: postFontSize + 'em' }">
            <blog-position
                    v-for="post in posts"
                    v-bind:key="post.id"
                    v-bind:post="post"
                    v-on:enlarge-text="postFontSize += 0.1"
            ></blog-position>
        </div>
    </div>

最终的结果如图:


点击中间的按钮

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