Home  >  Article  >  Web Front-end  >  A deep dive into Vue.js components and component communication

A deep dive into Vue.js components and component communication

高洛峰
高洛峰Original
2017-01-16 12:39:241623browse

Basically sorted out everything according to the Guide on the official website: http://vuejs.org/guide/index.html Here we take a Todo List application as an example to string together all the related things. All in this article The code is all on github https://github.com/lihongxun945/vue-todolist

Vue instance

A Vue application is booted and started by a root vue instance, and Vue Instance is created like this:

var vm = new Vue({
 // options
})

An instance is actually a VM in MVVM. All properties in the data in the configuration object passed in will be mounted to the instance. In order to avoid naming conflicts, Vue's built-in methods will mount properties starting with $ to the instance.

Instance will go through the following life cycle from creation to destruction:

It roughly goes through three steps during initialization:
•Bind data monitoring, that is, monitoring of data
• Compile template
•Insert document or replace the corresponding dom
# Vue basic syntax

Data binding

Vue uses a mastache-like syntax. Commonly used binding syntax is divided into the following categories:
•mastache syntax, such as {{ data }} {{ data | filter}}
•v-bind binding attributes, such as v-bind: href, v-bind :class
•v-on binding event, such as v-on:click, v-on:submit

where v-* are all directives

Example:
19de9f32bc61168916c704eb219bf5b9

Property calculation

Vue supports a very interesting property calculation Syntax, you can specify an attribute to be calculated from other attributes, so you don’t have to use $watch:

var vm = new Vue({
 el: '#example',
 data: {
 a: 1
 },
 computed: {
 // a computed getter
 b: function () {
 // `this` points to the vm instance
 return this.a + 1
 }
 }
})

## Syntax related to flow control and lists includes `v-if` , `v-show`, `v-else`, `v-for`

Form

Two-way data binding:

04815cf4782a373a364d283dc13af234

40a936b0856181660f10ed3a7a9f9b4d

## Animation animation is implemented in the same way as Angular and React, by adding and deleting classes. # Component

Basic usage of components

The definition of Component includes two parts:

1 Create component class:

var Profile = Vue.extend({
 template: "<div> Lily </div>"
});

2 Register a tagname:

Vue.component("me-profile", Profile);

So we can use such a component through the tagname:

<div id="todo">
 <my-profile></my-profile>
 <form v-on:submit="add" v-on:submit.prevent>
 <input type="text" v-model="input"/>
 <input type="submit" value=&#39;add&#39; />
 </form>
 ...
</div>

Vue.component("me-profile", Profile); is a global registration. If it is only used in a certain page, you can register it locally:

var vm = new Vue({
 el: "#todo",
 components: {
 "my-profile": Profile
 },
 ...
}

Because our Vue instance is bound to the todo element, it is invalid if my-profile is placed outside this element. Only if it is placed inside will it be initialized by this instance of Vue. .

Notes:

The parameters that can be passed to the Vue constructor can basically be used in Vue.extend, but you need to pay attention to the two parameters el and data, in order to avoid different instances When sharing the same object, it is more reliable to always return a new object through function:

var MyComponent = Vue.extend({
 data: function () {
 return { a: 1 }
 }
})

Because the parameters are the same, they are actually the same thing, but one is a component , one is used to guide Vue to start.

Template Notes

Because Vue is the native DOM, some custom tags may not comply with DOM standards. For example, if you want to customize a tr in the table, if you directly insert my-component, no It complies with the specification, so it should be written like this:

<table>
 <tr is="my-component"></tr>
</table>

Props transfer data

In Vue, each component is independent and cannot and should not directly access the parent class. data. So we pass data to sub-components through props, is it similar to React's method?

Unlike React, sub-components in Vue need to declare their own props first:

var Profile = Vue.extend({
 props: ["name"],
 template: `
 <h2>{{name}}&#39;s Todo List</h2>
 <h4>{{name}} is a good girl</h4>
 `
});

Then we can pass parameters like this when using Profile:
5262f07c1c15b67e64242fa6ad7262432ce8caae7ced446f67526b3e9d39dfa1

This is to pass parameters through literals, so the value passed must be a string. Another way is to dynamically pass parameters through v-bind, which can bind data in two directions or pass non-string parameters:
1bb17a1f1b55d117ee6295fdbd89361300e5c9026e9f7060769b3176bf9b59a1

If v-bind is a string, it is bound to the corresponding field in the data of the parent component. For example, the above is the value of the input that is bidirectionally bound. If it is a number, it is bound to a number.

Vue can also explicitly specify one-way or two-way data binding:

<!-- default, one-way-down binding -->
<child :msg="parentMsg"></child>
 
<!-- explicit two-way binding -->
<child :msg.sync="parentMsg"></child>
 
<!-- explicit one-time binding -->
<child :msg.once="parentMsg"></child>

Props verification

A good component should always First verify whether the parameters are correct. In addition, you may also need to set the default values ​​​​of some parameters:

var Profile = Vue.extend({
 input: {
 type: String
 }
});

Parent-child component communication

上面讲到的 props 其实就是父组件向子组件传递消息的一种方式。 
在子组件中有一个 this.$parent 和 this.$root 可以用来方法父组件和根实例。不过,现在我们应该避免这么做。因为组件本身就是为了封装独立的逻辑,如果又去直接访问父组件的数据就破坏了组件的封装性。 
所以我们应该还是应该通过父组件向子组件传递 props 的方式来通信。

当然 props 其实只能做回调。在 React 中就探讨过这个问题,React 的做法就是通过 props 来做,传一个回调函数给子组件。其实我不是很喜欢这种把回调函数传来传去的方式,我更喜欢的是事件的方式。Vue 中子组件可以通过通过事件和父组件进行通信的。向父组件发消息是通过 this.$dispatch,而向子组件发送消息是通过 this.$boardcast,这里都是向所有的父亲和孩子发送消息,但是一旦执行一个回调之后就会停止,除非这个回调函数显式返回了 true。

我们把之前的Todo List拆成不同的组件来实现,这样可以体验下如何进行组件的双向通信,我们拆分出两个组件,分别是 List 和 Form 。

Form 负责处理用户输入,并在提交表单的时候向父组件发送一个 add 消息,代码如下:

var Form = Vue.extend({
 props: {
 username: {
 type: String,
 default: "Unnamed"
 }
 },
 data: function() {
 return {
 input: "",
 };
 },
 template: `
 <h1>{{username}}&#39;s Todo List</h1>
 <form v-on:submit="add" v-on:submit.prevent>
 <input type="text" v-model="input"/>
 <input type="submit" value=&#39;add&#39; />
 </form>
 `,
 methods: {
 add: function() {
 this.$dispatch("add", this.input); //这里就是向父组件发送消息
 this.input = "";
 }
 }
});

 List 只负责展示列表和处理用户勾选操作,它接收到 add 消息之后会在自己上添加一个条目:

var List = Vue.extend({
 template: `
 <ul>
 <li v-for=&#39;todo in list&#39;>
 <label v-bind:class="{ done : todo.done }" >
  <input type="checkbox" v-model="todo.done"/>
  {{todo.title}}
 </label>
 </li>
 </ul>`,
 props: {
 initList: {
 type: Array
 }
 },
 data: function() {
 return {
 list: []
 }
 },
 events: {
 add: function(input) {
 if(!input) return false;
 this.list.unshift({
 title: input,
 done: false
 });
 }
 }
});

 然后,因为这是两个组件,当然需要一个 Vue 实例来引导启动,我们的实例如下:

var vm = new Vue({
 el: "#todo",
 components: {
 "todo-form": Form,
 "todo-list": List
 },
 events: {
 add: function(input) {
 this.$broadcast("add", input);
 }
 }
});

 注意,其实 Form 和 List 在逻辑上是平级的组件,所以他们没有父子关系,他们共同都是 vm 的孩子。这里 vm 接到 Form 的消息之后会转发给 List。

html 代码就更简单了:

<div id="todo">
<todo-form username=&#39;Lily&#39;></todo-form>
<todo-list></todo-list>
</div>

 Slot

通过 Slot 可以实现把父组件渲染出来的HTML插入到子组件中,目前还不清楚什么时候会需要这样做,而且这么做对子组件的侵入性太大。

动态切换组件

这个功能感觉有点多余,感觉很多情况下我们应该是通过逻辑代码来实现切换,而不是通过Vue内置的动态组件来切换。不过用来实现一个类似 tab 切换的功能还是很方便的。

我们这里给 Todo List 增加一个 about 页面。那么首先我们需要把 vm 改成一个组件,这个组件叫 Todo,它就是整个 Todo 页面:

var Todo = Vue.extend({
 template: `
 <div id="todo">
 <todo-form username=&#39;Lily&#39;></todo-form>
 <todo-list></todo-list>
 <slot>not show</slot>
 </div>
 `,
 components: {
 "todo-form": Form,
 "todo-list": List
 },
 events: {
 add: function(input) {
 this.$broadcast("add", input);
 }
 }
});

 

 其实改动就第一行。

然后我们需要创建一个 About 组件:

var About = Vue.extend({
 template: `
 <div id="about">
 <p>About Todo List V0.1.0</p>
 <p>Content here</p>
 </div>`
});

 接下来是重点了,我们要创建一个实例 vm,这vm要负责切换这两个页面:

var vm = new Vue({
 el: "body",
 data: {
 currentView: "todo"
 },
 components: {
 "todo": Todo,
 "about": About
 }
});

 这里我们定义了一个 currentView 字段,当然可以是任意名称,然后通过特殊的 component 标签来进行组件切换:

<component :is="currentView"></component>
 <ul>
 <li><label><input type="radio" name=&#39;page&#39; value=&#39;todo&#39; v-model=&#39;currentView&#39;> Home</label></li>
 <li><label><input type="radio" name=&#39;page&#39; value=&#39;about&#39; v-model=&#39;currentView&#39;> About</label></li>
 </ul>

 上面的代码有两处需要注意:
 •通过 component这个特殊标签,然后用 :is 属性来进行组件的切换。
 •radio 通过双向绑定来修改 currentView 字段,从而实现点击之后就可以进行切换。

数据绑定的实现原理

Vue 把双向绑定称作 reactive,可以翻译为响应式数据绑定。内部是通过 ES5 定义的 getter 和 setter 方法实现的,所以不支持 IE8 及以下浏览器,这种实现方式有两个容易犯错的地方:
 •如果在 data 上直接添加和删除属性是无法被检测到的,一般删除是不会的,但是可能会动态添加,这个时候应该通过 vm.$set(“name”, value) 的方式来添加。
 •无法检测到对象内部的变化,也就是只能检测 data 的属性变化,如果 data.a 是一个对象,那么 data.a.b = 1 这种变化是无法被检测到的。这种情况下应该创建一个新的对象并赋值给 data.a 就行了。 

异步更新机制

Vue 对DOM的更新是异步的! 这个异步是在一个异步队列中进行的,不过这个异步队列会在当前的 Event Loop 中执行完,所以如果修改了 Data 立刻去DOM中做查询操作是不对的,这个时候DOM还没有更新,正确的做法是这样做:

vm.msg = &#39;new message&#39; // change data
vm.$el.textContent === &#39;new message&#39; // false
Vue.nextTick(function () {
 vm.$el.textContent === &#39;new message&#39; // true
})

 或者这样:

vm.$nextTick(function () {
 this.$el.textContent === &#39;new message&#39; // true
})

 花了半天时间才看完组件,下面应该去看一下另一个重点: Directive

本文已被整理到了《Vue.js前端组件学习教程》,欢迎大家学习阅读。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。

更多X深入探讨Vue.js组件和组件通信相关文章请关注PHP中文网!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn