从零开始学 Web 之 Vue.js(六)Vue的组件

发布日期:2019-07-27

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......

github:https://github.com/Daotin/Web微信公众号:Web前端之巅博客园:http://www.cnblogs.com/lvonve/CSDN:https://blog.csdn.net/lvonve/

在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。现在就让我们一起进入 Web 前端学习的冒险之旅吧!

一、Vue组件

什么是组件: 组件的出现,就是为了拆分 Vue 实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;

组件化和模块化的不同:

模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

二、定义组件

1、定义全局组件

定义全局组件有三种方式:

1、使用 Vue.extend 配合 Vue.component 方法:

// 1.使用 Vue.extend 来创建全局的Vue组件var login = Vue.extend({ // 通过 template 属性,指定了组件要展示的HTML结构 template: "<h1>登录</h1>"});// 2.使用 Vue.component("组件的名称", 创建出来的组件模板对象) Vue.component("login", login);// 3.使用组件<div id="app"> <!-- 如果要使用组件,直接,把组件的名称,以 HTML 标签的形式,引入到页面中即可 --> <login></login></div>

注意:

使用 Vue.component 定义全局组件的时候,组件名称使用了 驼峰命名(如myLogin),则在引用组件的时候,需要把 大写的驼峰改为小写的字母,同时在两个单词之前,使用 - 链接(<my-login></my-login>);如果不使用驼峰,则直接拿名称来使用即可;

当然,上面两步可以合成一个步骤完成:

Vue.component("login", Vue.extend({ template: "<h1>登录</h1>"}));

2、直接使用 Vue.component 方法:

Vue.component("login", { template: "<div><h3>注册</h3><span>123</span></div>"});

注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素,否则会报错。

3、将模板字符串,定义到 template 标签中:

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <!-- 3. 使用组件 --> <mycom></mycom> </div> <!-- 2.在 被控制的 #box 外面,使用 template 元素,定义组件的HTML模板结构 --> <template id="tmp1"> <!-- 还是需要遵从template 模板内容,必须有且只能有唯一的一个根元素 --> <div> <h3>登录</h3> <p>p标签</p> </div> </template> <script> // 1.定义组件 Vue.component("mycom", { template: "#tmp1" }); var vm = new Vue({ el: "#box", data: {}, methods: {} }); </script></body></html>

注意:

1、template: "#tmp1" 是定义模板标签的 id ,# 别忘写了。

2、被控制的 #box 外面,使用 template 标签;

3、 template 标签里面,还是遵从只能有唯一的一个根元素的原则。

2、定义私有组件

定义私有组件,就是再VM实例中定义组件。

如下,box中可以使用,box2不可以使用。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <mycom></mycom> </div> <div id="box2"> <mycom></mycom> </div> <template id="temp"> <h3>自定义私有属性</h3> </template> <script> var vm = new Vue({ el: "#box", data: {}, methods: {}, // 定义私有组件 components: { mycom: { template: "#temp" } } }); var vm2 = new Vue({ el: "#box2", data: {}, methods: {} }); </script></body></html>

3、组件的data和methods属性

组件中也可以有自己的data和methods属性,可以传入template中使用。

特点:

data属性为一个匿名函数,其返回值为一个对象。data 函数返回值为一个对象(最好是新开辟的对象,否则如果多次引用组件,不是新开辟的对象给的话,对象是同一份,而我们需要每一个组件有自己的对象),对象中可以放入数据。组件中 的data和methods,使用方式,和实例中的 data 和methods使用方式完全一样

<div id="box2"> <login></login></div><template id="temp2"> <div> <input type="button" value="按钮" @click="myclick"> <h3>自定义私有属性</h3> <p> {{msg}} </p> </div></template><script> Vue.component("login", { template: "#temp2", data: function () { return { msg: "这是组件中的data" } }, methods: { myclick() { console.log("点击按钮"); } } });</script>

三、组件切换

我们在登录注册一个网站的时候,经常看到两个按钮,一个登录,一个注册,如果你没有账号的话,需要先注册才能登录。我们在点击登录和注册的时候,网页会相应的切换,登录页面就是登陆组件,注册页面就是注册组件,那么点击登录和注册,如何实现组件的切换呢?

1、方式一

使用flag标识符结合v-ifv-else切换组件

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <!-- 给a注册点击事件,切换flag状态 --> <a href="javascript:;" @click.prevent="flag=true">登录</a> <a href="javascript:;" @click.prevent="flag=false">注册</a> <!-- 使用v-if v-else切换组件 --> <login v-if="flag"> </login> <register v-else="flag"> </register> </div> <script> Vue.component("login", { template: "<h3>登录组件</h3>" }); Vue.component("register", { template: "<h3>注册组件</h3>" }); var vm = new Vue({ el: "#box", data: { flag: true }, methods: {} }); </script></body></html>

缺陷:由于flag的值只有true和false,所以只能用于两个组件间 的切换,当大于两个组件的切换就不行了。

2、方式二

使用 component元素的:is属性来切换不同的子组件

使用 <component :is="componentId"></component> 来指定要切换的组件。

componentId:为需要显示的组件名称,为一个字符串,可以使用变量指定。

componentId: "login" // 默认显示登录组件。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <!-- 给a注册点击事件,切换flag状态 --> <a href="javascript:;" @click.prevent="componentId="login"">登录</a> <a href="javascript:;" @click.prevent="componentId="register"">注册</a> <component :is="componentId"></component> </div> <script> Vue.component("login", { template: "<h3>登录组件</h3>" }); Vue.component("register", { template: "<h3>注册组件</h3>" }); var vm = new Vue({ el: "#box", data: { componentId: "login" // 默认显示登录 }, methods: {} }); </script></body></html>

为组件切换添加过渡:

很简单,只需要用 transition 将 component 包裹起来即可。

<transition> <component :is="componentId"></component></transition>

示例:

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script> <link rel="stylesheet" href="./lib/animate.css"> <style> .loginDiv { width: 200px; height: 200px; background-color: red; } .registerDiv { width: 200px; height: 200px; background-color: blue; } </style></head><body> <div id="box"> <!-- 给a注册点击事件,切换flag状态 --> <a href="javascript:;" @click.prevent="componentId="login"">登录</a> <a href="javascript:;" @click.prevent="componentId="register"">注册</a> <transition mode="out-in" enter-active- leave-active-> <component :is="componentId"></component> </transition> </div> <template id="login"> <div > </div> </template> <template id="register"> <div > </div> </template> <script> Vue.component("login", { template: "#login" }); Vue.component("register", { template: "#register" }); var vm = new Vue({ el: "#box", data: { componentId: "login" }, methods: {} }); </script></body></html>

mode="out-in":可以设置切换组件的模式为先退出再进入。

四、组件传值

1、父组件向子组件传值

我们先通过一个例子看看子组件可不可以直接访问父组件的数据:

<body> <div id="box"> <mycom></mycom> </div> <template id="temp"> <h3>子组件 --- {{msg}}</h3> </template> <script> var vm = new Vue({ el: "#box", data: { msg: "父组件的msg" }, methods: {}, components: { mycom: { template: "#temp" } } }); </script></body>

由于 components 定义的是私有组件,我们直接在子组件中调用父组件的msg会报错。

那么,怎么让子组件使用父组件的数据呢?

父组件可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 。

<body> <div id="box"> <mycom v-bind:parentmsg="msg"></mycom> </div> <template id="temp"> <h3>子组件 --- 父组件:{{parentmsg}}</h3> </template> <script> var vm = new Vue({ el: "#box", data: { msg: "父组件的msg" }, methods: {}, components: { mycom: { template: "#temp", // 对传递给子组件的数据进行声明,子组件才能使用 props: ["parentmsg"] } } }); </script></body>

注意:父组件绑定的属性名称不能有大写字母,否则不会显示,并且在命令行会有提示:

组件data数据和props数据的区别:

data数据是子组件私有的,可读可写;props数据是父组件传递给子组件的,只能读,不能写。

案例:发表评论功能

父组件为评论列表,子组件为ID,评论者,内容和按钮的集合,在输入ID,评论者等内容,然后点击添加的时候,需要首先获取子组件的list列表,然后再添加新的列表项到列表中。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <mycom :plist="list"></mycom> <ul> <li v-for="item in list" :key="item.id"> ID:{{item.id}} --- 内容:{{item.content}} --- 评论人:{{item.user}} </li> </ul> </div> <template id="tmp1"> <div> <label> ID: <input type="text" v-model="id"> </label> <br> <label> 评论者: <input type="text" v-model="user"> </label> <br> <label> 内容: <textarea v-model="content"></textarea> </label> <br> <!-- 把父组件的数据作为子组件的函数参数传入 --> <input type="button" value="添加评论" @click="addContent(plist)"> </div> </template> <script> var vm = new Vue({ el: "#box", data: { list: [{ id: Date.now(), user: "user1", content: "what" }, { id: Date.now(), user: "user2", content: "are" }] }, methods: {}, components: { mycom: { template: "#tmp1", data: function () { return { id: "", user: "", content: "", } }, methods: { addContent(plist) { plist.unshift({ id: this.id, user: this.user, content: this.content }); } }, props: ["plist"] } } }); </script></body></html>

把添加ID,评论人,内容作为子组件,把列表作为父组件,然后把添加的数据放到父组件列表上,由于要获取到父组件列表的数据,所以必然涉及到父组件向子组件传值的过程。这里还通过子组件方法参数来保存父组件的数据到子组件的数据中。

2、父组件向子组件传方法

既然父组件可以向子组件传递数据,那么也可以向子组件传递方法。

<body> <div id="box"> <mycom v-bind:parentmsg="msg" @parentfunc="show"></mycom> </div> <template id="temp"> <div> <input type="button" value="调用父组件方法" @click="sonClick"> <h3>子组件 --- 父组件:{{parentmsg}}</h3> </div> </template> <script> var vm = new Vue({ el: "#box", data: { msg: "父组件的msg" }, methods: { show(data1, data2) { console.log("这是父组件的show方法" + data1 + data2); } }, components: { mycom: { template: "#temp", // 对传递给子组件的数据进行声明,子组件才能使用 props: ["parentmsg"], methods: { sonClick() { // 调用父组件的show方法 this.$emit("parentfunc", 111, 222); } } } } }); </script></body>

1、@parentfunc="show" 绑定父组件的show方法。

2、<input type="button" value="调用父组件方法" @click="sonClick"> 点击按钮调用父组件的show方法

3、在 子组件的 sonClick 方法中使用 this.$emit("parentfunc"); 来调用父组件的show方法

4、父组件的show方法也可以传参,在调用的时候,实参从 this.$emit 的第二个参数开始传入。

5、如果 this.$emit 的第二个参数传的是子组件的data数据,那么父组件的方法就可以获得子组件的数据,这也是把子组件的数据传递给父组件的方式。

3、使用 ref 获取DOM和组件的引用

我们知道Vue不推荐直接获取DOM元素,那么在Vue里面怎么获取DOM及组件元素呢?

我们呢可以在元素上使用 ref 属性来获取元素。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script></head><body> <div id="box"> <input type="button" value="获取元素" @click="getrefs" ref="mybtn"> <h3 ref="myh3">这是H3</h3> <mycom ref="mycom"></mycom> </div> <template id="tmp1"> </template> <script> // 定义组件 Vue.component("mycom", { template: "#tmp1", data: function () { return { msg: "子组件的msg", pmsg: "" } }, methods: { show(data) { console.log("调用子组件的show"); this.pmsg = data; console.log(this.pmsg); }, } }); var vm = new Vue({ el: "#box", data: { parentmsg: "父组件的msg" }, methods: { getrefs() { console.log(this.$refs.myh3); console.log(this.$refs.mycom.msg); this.$refs.mycom.show(this.parentmsg); } } }); </script></body></html>

总结:

1、ref 属性不仅可以获取DOM元素,也可以获取组件(无论全局还是私有组件)元素。

2、获取到组件元素后,就可以获取组件元素的data数据和methods方法。

3、获取到组件中的方法后,可以传入VM的data数据,就可以把VM的data数据传入组件中。

, 1, 0, 9);