このセクションでは、Vue のコンポーネントについて学び、コンポーネントがどのように機能するかを理解し、一連の例を使用して、コンポーネントベースの考え方を使用してプロジェクトを開発すると、異なる感覚が得られることを証明します。 Vue のコンポーネント化のアイデアを理解していれば、このアイデアを使用して、ユーザーがコメントを投稿し、他のユーザーが任意のコメントに対して「はい」または「いいえ」を投票できるシステムを構築できます。
Vue に初めて触れる場合は、私の以前の記事「Vue をゼロから学ぶ」を読んで、Vue の基本的な構文を理解してください。
コンポーネントを使用すると、構築している複雑なインターフェイスを持つアプリケーションを非常に適切に分離できます。同時に、コンポーネントは、パッケージ化して再利用することもできますが、再利用性も高くなります。開発中のさまざまなプロジェクトで。
まず単純な HTML ページを作成し、Vue をインスタンス化して DOM 要素にマウントします。
<!DOCTYPE html> <html> <head> <title>揭开Vue组件的神秘面纱</title> </head> <body> //这中间就是实例挂载点的实例边界 <div id="vueInstance"></div> //Vuw的CDN,之后会省略不写 <script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script> <script> // 创建一个新的Vue实例,并设置挂载点 var V = new Vue({ el : '#vueInstance' }); </script> </body></html>
「ゼロから Vue を学ぶ」の基本が準備できたので、最初のシンプルで再利用可能なコンポーネントを作成します。 Vue では、Vue.component() を使用してコンポーネントを作成および登録できます。このコンストラクターには 2 つのパラメーターがあります:
コンポーネントの名前
コンポーネント パラメーターを含むオブジェクト
このオブジェクトは、Vue() コンストラクターのオブジェクトに少し似ており、Vue() のオブジェクトにも似ています。 ) el 属性と data 属性ですが、少し異なります。
Vue() コンストラクターの el と data はオブジェクトにすることができます。 Vue.component() コンストラクターの el と data は関数のみにすることができます。
次に、最初のコンポーネントがどのように機能するかを見てみましょう。コンポーネントを登録し、pタグを使って自己紹介文を一行出力したいと考えています。そこでコンポーネントを作成し、2 つのパラメーターを入力しました:
コンポーネントの名前: 'mine'
含まれるコンポーネント パラメーター オブジェクト: このオブジェクトにはプロパティ 'template' が含まれています。
Vue.component('mine',{ template : '<p>My name is Appian.</p>'})
これで、アプリケーションのどこでも使用できる独自のコンポーネントが完成しました。その一意の識別子 (コンポーネント名) を呼び出し、それを
<div id="vueInstance"> <mine></mine> //标识注册的内容会在这里插入 <mine></mine> <mine></mine> //重复插入注册内容 </div> <script> Vue.component('mine',{ //这里就是注册的内容 template : '<p>My name is Appian.</p>' }); var V = new Vue({ el : '#vueInstance' }); </script>
Vue はテンプレートを使用してコンポーネントを置き換え、HTML タグを使用してカスタムの一意の識別子を DOM 構造に挿入して、HTML をより簡潔、すっきり、直感的にします。
ここで、私が作成したコンポーネントに p タグが 1 行しかないのはなぜだろうと考えているかもしれません。 p タグの行に必要なコンポーネントを用意するのはそんなに面倒ですか?はい、コンポーネントはより複雑なカプセル化と再利用のために作成されます。したがって、Vue.component() コンストラクターの template 属性で HTML コードのみを定義し、文字列連結を使用してすべてのコードを記述する場合は、jq を使用するよりもさらに疲れ果てるだけです。
上記の状況を回避するために、テンプレート タグ (属性とタグは異なることに注意してください) を使用して目的を達成できます。ページ上の任意の場所にテンプレート タグを定義し、テンプレート タグ内にテンプレートを記述することができます。ページの読み込み時にはテンプレート タグは表示されないため、このタグのコンテンツは必要な場合にのみ表示されます。したがって、テンプレート タグを任意の場所に配置して ID を指定すると、コンポーネントの登録時にテンプレートを見つけることができます。
<div id="vueInstance"> <mine></mine> //标识注册的内容会在这里插入 </div> <template id="mineTpl"> <p>My name is Appian.</p> <button>点击没有任何事件</button> </template> <script> Vue.component('mine',{ //这里就是注册的内容 template : '#mineTpl' }); var V = new Vue({ el : '#vueInstance' }); </script>
このメソッドを使用して複雑なコンポーネントを作成できるようになりました。このようにして、複雑なコードを機能分割後にコンポーネントに分割し、コンポーネントの考え方を使用してコードの塊を回避できます。コンポーネント化により、モジュールをより明確に編成し、コンポーネントをより Vue らしくすることができます。
コンポーネント インスタンスが作成されるたびに、このインスタンスは独自のコンポーネント スコープを分割します。このスコープにより、このコンポーネント領域でその親を取得できなくなります。コンポーネントのデータ。では、Vue は親コンポーネントから子コンポーネントへのデータの受け渡しをどのように処理するのでしょうか?答えは、小道具によるものです。
親コンポーネントから子コンポーネントにデータを渡す最も単純な例を見てみましょう。登録された Mine コンポーネントは子コンポーネントであり、親コンポーネントから「city」データ情報を取得することを期待しているため、親コンポーネントによって渡された city 値を受け取るためにパラメーター props が Mine のコンストラクターに追加されます。
Vue.component('mine',{ template : '<p>Appian is from {{ city }}.</p>', props : ['city']});
上記の例では、props を配列として定義したため、props を使用して複数のフィールドを受け取ることができ、これらのフィールドは子コンポーネントが親コンポーネントから期待するものです。
Props は配列である必要はなく、オブジェクトにすることもできます。多くの props 制限はオブジェクト内で詳細に定義できます。
Vue.component('mine',{ template : '<p>Appian is from {{ city }}.</p>', props : { city : { type : String,//定义字符串类型 required : true,//该字段是必须的 default : 'China'//设置默认值 } }});
我们不需要每次都将props的限制条件都写出来,因为在这个例子中的数据是一个很简单的字段,上例中只是为了完整展示props的对象表示方法,所以才展开来写的。
那父组件那里又是怎么指派字段给子组件的呢?
<mine city="FuJian-YongAn"></mine>
这样直接在mine标签里面定义‘city’字段,就是父组件指派字段的方式之一。但是这样直接指派是很瞎的,我们需要的是动态变化的city,这个一会我们会说到的。
先简单介绍一下我们接下来要做的事。我们要假装我们正在搭一个博客,博客需要展示作者的基本信息,此时,我们可能会需要一些数据对象,可能是从数据库获得的,或者ajax请求到的,总之就是请求到了之后,将这个数据对象定义在父级的data中。并在html的template标签中准备渲染。
<div id="vueInstance"> <mine></mine> //标识注册的内容会在这里插入,之后也要展开来说!!!</div><template id="mineTpl"> <h1>{{ name }}</h1> <h2>{{ title }}</h2> <h3>{{ city }}</h3> <p>{{ content }}</p></template><script> //Vue.component()的构造在下文中展开来说!!! var V = new Vue({ el : '#vueInstance', data : { name : 'Appian', title : 'This is a title', city : 'FuJian-XiaMen', content : 'There are some desc about Appians Blog' } });</script>
准备好模板渲染之后,当然也要注册mine的构造器Vue.component()。除了基本的绑定模板id(#mineTpl)之外,还需要指定这个子组件想要的数据的字段名,并把期望得到的4个字段写在props中。
Vue.component('mine',{ template : '#mineTpl', props : ['name','title','city','content']});
这样我们就能告诉父级,子组件需要的字段是哪些。接下来父级就可以指派字段的值给子组件了。前面的city字段是写死的,现在这里绑定的字段就是动态绑定的。
<mine :name='name' :title='title' :city='city' :content='content' ></mine>
等号左右两边的字段名称可以不一样。
等号左边的字段名,是指在子组件的props中声明的名字。在html写成肉串式,但是在props中写成驼峰式。
等号右边的字段名,是指在父级里定义的字段的名字。
‘:’是‘v-bind’的缩写
这样就能把父组件的4个字段绑定到子组件上。现在,只要父组件指派的字段的值一发生改变,子组件的值也会发生相应的改变。
现在就可以利用前面学到的内容,搭建一个简易的评论社区系统,样式什么的先不管,只讲究js的具体实现。
创建一个新的Vue实例
给实例挂载一个div(#vueInstance)
定义数据,然后渲染。
<div id="vueInstance"> <div class="container"> <ul> //这里即将渲染出评论的投票列表 </ul> </div></div><template id="postTpl"> <li> <i class="up">我支持</i> <span>票数: {{ post.votes }}</span> <i class="down">我反对</i> <a>话题: {{ post.title }}</a> </li></template><script> //Vue.component()的构造在下文中展开来说!!! var V = new Vue({ el : '#vueInstance', data : { posts: [{ title: '请大大多多为我投票,我给大家发红包', votes: 15 },{ title: '投我准没错', votes: 53 },{ title: '不要投给我楼上的', votes: 10 }] } });</script>
现在先构造好数据,有投票的话题和投票人数。然后再构造好模板(template标签),这个模板值用来渲染单个话题。模板里有除了渲染话题和投票人数,还有两个按钮,用来投赞成票或者反对票。之后我们只需要在html循环模板,然后就能多次插入模板,渲染成列表。当然也可以在模板里渲染好列表再一次插入。我们先用前一种方法。
不管css的样式问题,现在就可以开始注册Vue.component()构造器了,以便我们渲染页面。我们在注册的时候要明确,我注册的子组件需要的props是父级的posts数组中的一个元素对象post。所以我们应该这样注册:
Vue.component('post',{ template : '#postTpl', props : ['post']});
然后我们使用自定义的
<div id="vueInstance"> <div class="container"> <ul> <post v-for="post in posts" :post="post"></post> </ul> </div></div>
这样我们就已经完成了循环输出posts数组了。因为我们把数组的元素指派给了子组件,所以子组件就可以渲染title和vote。接下来要做的就是增加“赞成”和“反对”按钮的逻辑代码,并我们需要对投票状态进行锁定,就是如果我们投了某个话题的赞成票就不能再投该话题的反对票,反之亦然。所以让我们开始在模板里面定义点击事件吧,投赞成票的事件叫做“upvote”,投反对票的事件叫做“downvote”。
<template id="postTpl"> <li> <i class="up" @click="upvote">我支持</i> <span>票数: {{ post.votes }}</span> <i class="down" @click="downvote">我反对</i> <a>话题: {{ post.title }}</a> </li></template>
Vue.component('post',{ template : '#postTpl', props : ['post'], data : function (){ return { //data必须为function,定义投票状态 upvoted: false, downvoted: false } }, methods : { upvote : function() { //点击赞成的事件 this.upvoted = !this.upvoted; this.downvoted = false; }, downvote: function() { //点击反对的事件 this.downvoted = !this.downvoted; this.upvoted = false; } }});
注意,data里面的两个布尔值的定义是作为一个函数的返回值定义的。那是因为我们想要给每一个组件都设置一个是否投票的状态。
(还记得之前我说过,这次的列表渲染是循环渲染多个模板,每个模板都只是一个话题的相关信息,而现在在组件中定义的data:upvoted和downvoted,也是专属于某个模板的。)
这样我们如果投了某个话题的赞成票,并不会影响剩下话题的投票情况。
接下来已经完成了点击事件的状态控制,那么点击“赞成”和“反对”会导致话题的票数发生变化。这个时候我们可以利用之前的教程中学过的computed属性进行计算。
组件也有computed属性哦~
Vue.component('post',{ template : '#postTpl', props : ['post'], data : function (){ //同上,略 }, methods : { //同上,略 }, computed: { //重点部分 votes: function () { if (this.upvoted) { return this.post.votes + 1; } else if (this.downvoted) { return this.post.votes - 1; } else { return this.post.votes; } } }});
到此为止,应该组件中的逻辑处理差不多完成了,现在要做的就是让我们在组件中修改的投票人数,能够在模板中渲染出来。因为我们现在的votes的值是在子组件中修改的,而一开始渲染的时候,我们的votes只是一味的接受父级传过来的值,所以,现在要把修改的值显示在模板上。所以模板变成了这样:
<template id="postTpl"> <li> <i class="up" @click="upvote">我支持</i> <span>票数: {{ votes }}</span> <i class="down" @click="downvote">我反对</i> <a>话题: {{ post.title }}</a> </li></template>
现在我们的投票系统已经基本完成了。我们可能会希望我们的投票系统好看一点,直观一点。所以我们可以在按钮上绑定一些样式。比如当用户已经投了某个话题的赞成票或者反对票,就让这个按钮变成橙色。
.disabled { color : orange;}
Vue是利用v-bind:class来进行样式绑定的,可以简写成一个‘:’。其绑定的内容是一个对象,对象里面是class的名字和class对应的状态。
了解更多样式绑定的信息点这里<template id="postTpl"> <li> <i class="up" @click="upvote" :class="{disabled: upvoted}">我支持</i> <span>票数: {{ votes }}</span> <i class="down" @click="downvote" :class="{disabled: downvoted}">我反对</i> <a>话题: {{ post.title }}</a> </li></template>
如果我们点击了某个话题的赞成按钮,则它的upvoted就会变成true,则disabled的样式就绑定到赞成按钮上去。如果点了该话题的反对按钮,则它的upvoted就会变成false,则disabled就不会绑定到赞成按钮上。以上就是模板最终的样子。
我们已经利用了我们对Vue的基本语法还有一些组件的基本操作,建立起了一个基本的话题投票系统。在教程的最后一部分,我们将看看,我们应该如何将这个投票系统组件进行复用。
复用的关键在于组件的命名,让你的命名能够复用。至少你在构建你的组件的时候,你要问自己,“是否其他地方也能用到这个组件?”
比如,在上面的例子中,posts的意思是,这个数据是从ajax的post请求过来的数据,为了便于理解,才取名为posts,为了让自己的组件名字一看就知道关联,所以把模板的id定义为#postTpl,tpl是template的简写,自定义标签的命名也是post。总之,就是这样的细节,不仅方便自己阅读,也方便其他人阅读你的代码。
现在我们的评论系统需要增加一个功能,就是增加一个发布评论的功能。就是增加了一个发布评论的区域(#commentBox)。
<div id="vueInstance"> <div class="container"> <ul> <post v-for="post in posts" :post="post"></post> </ul> <div id="commentBox"> 请输入评论内容并提交: <input type="text" v-model="comment" @keyup.enter="postComment"> <button @click="postComment">提交评论</button> </div> </div></div>//模板渲染不变<script>//Vue.component()的注册不变。var V = new Vue({ el : '#vueInstance', data : { posts: [{ title: '请大大多多为我投票,我给大家发红包', votes: 15 },{ title: '投我准没错', votes: 53 },{ title: '不要投给我楼上的', votes: 10 }], comment: '' }, methods: { postComment: function() { this.posts.push({ title: this.comment, votes: 0 }) this.comment = ''; } }});</script>
输入框用v-model绑定了comment字段,为了无论用户输入什么,在提交的时候都能获得他输入的值。当用户按回车或者点击提交按钮的时候都会触发postComment方法。这里的事件绑定一个是用的回车事件 @keyup.enter,还有一个点击事件@click。postComment方法就是把话题和投票为0的对象push进posts数组中去,Vue会将新的模板自动渲染出来。
这样一个可复用的组件就构造完成了,组件化会节省开发者的很多时间。组件是否复用,也要结合开发的实际需求而定。
到此为止,你就已经能够掌握了Vue的组件的基本使用,组件通过props向下传递数据,通过使用标签来构造模板。我们在上文中利用构建了一个评论发布投票系统来说明了组件的用法,并且简单介绍了如何复用。
利用 props,子组件可以定义需要父级传递的字段
利用 'v-bind:' 或 ':' 在自定义标签的地方绑定父级指派的字段
组件的 data 和 el 必须定义为function
不要忘记,还是需要将Vue实例化,才能使用组件。