打造灵活可复用的Web应用:Vue组件化开发指南!


一、组件的简介

1.1、官方概念

​ 组件(Component)是Vue最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性进行了扩展的原生HTML元素。

​ Vue让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ugvqRms-1690870417749)(/004.webp)]

所有的Vue组件同时也都是Vue的实例,所以可以接受相同的选项对象。

1.2、简单介绍下is特性

​ 通俗来说,比如说有些元素,比如ul 里面只能直接包含li元素、select里面只能包含optiontable表格里面只能包含trtdtbody等。比如像这样:

<ul><li></li><li></li>
</ul>

​ 这种写法是没有问题的,浏览器可以正确的解析,但是你要这么做,浏览器就看不懂了,比如下面的:

<ul><my-li></my-li><my-li></my-li>
</ul>

​ 这样就不能使用my-li这种标签了,如果要达到我们的目的,我们就要使用is特性。像这样,其中is属性的值是组件名:

<ul><li is="my-li"></li><li is="my-li"></li>
</ul>

1.3、组件化和模块化的区别

​ 组件和模块的定位不同。组件一般用于前端,模块化在后台运用的比较多。例如vue中的组件,主要是为了拆分vue实例的代码量,让我们可以以不同的组件来划分不同的功能模块,将来我们需要什么样的功能,就直接调用对应的组件即可。

​ 模块化中的模块一般指的是 Javascript 模块

​ 组件则包含了 template、style 和 script,而它的 Script 可以由各种模块组成。也可以理解为“框架”,意思是把功能进行划分,将同一类型的代码整合在一起。组件化就相当于做一个页面,把页面中的每一个独立的功能拆分出来,可以尽情的拆分,最后组装成一个完整的页面。

  • 组件化

    ​ 主要从ui界面上进行划分。例如前端的组件化,方便ui组件的调用。类似于以前的导航栏。

  • 模块化

    ​ 主要从代码逻辑的角度进行划分,方便代码分层开发,保证每个功能模块职责单一。

二、简单的HelloWorld案例

2.1、使用组件的步骤

  • 创建组件构造器
  • 注册组件
  • 使用组件

2.2、案例

<body><div id="myDiv"><my-component></my-component><my-component></my-component></div><script src="../js/vue.js"></script><script type="text/javascript">// 1、创建组件构造器const myComponent = Vue.extend({template: `<div><h1>爱学习的少年</h1><p>HelloWorld</p><p>HelloWorld</p></div>`});// 2、注册组件Vue.component("my-component",myComponent);const app = new Vue({el: "#myDiv"});</script>
</body>

三、创建全局组件

3.1、方式一

<body><div id="myDiv"><my-component></my-component><my-component></my-component></div><script src="../js/vue.js"></script><script type="text/javascript">// 1、创建组件构造器const myComponent = Vue.extend({template: `							// 模板只能有一个而且只能有一个根元素<div><h1>爱学习的少年</h1><p>HelloWorld</p><p>HelloWorld</p></div>`});// 2、注册组件【注意:如果使用的是驼峰命名,则在使用组件的时候需要使用'-'连接】Vue.component("myComponent",myComponent);const app = new Vue({el: "#myDiv"});</script>
</body>

3.2、方式二

<body><div id="myDiv"><my-component></my-component><my-component></my-component></div><script src="../js/vue.js"></script><script type="text/javascript">// 1、直接使用Vue.component创建组件Vue.component("myComponent",{template: `<div><h1>爱学习的少年</h1><p>HelloWorld</p></div>`});const app = new Vue({el: "#myDiv"});</script>
</body>

3.3、方式三

<body><div id="myDiv"><my-component></my-component><my-component></my-component></div><template id="myTemplate"><div><h1>爱学习的少年</h1><p>Spring</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 1、引入外部一个模板片段Vue.component("myComponent",{template: "#myTemplate"});const app = new Vue({el: "#myDiv"});</script>
</body>

3.4、方式四

<body><div id="myDiv"><my-component></my-component></div><script type="text/x-template" id="myTemplate"><h1>使用script实现注册组件</h1></script><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate"});const app = new Vue({el: "#myDiv"});</script>
</body>

四、创建私有组件

4.1、方式一

<body><div id="myDiv"><my-nav></my-nav><my-nav></my-nav></div><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",components: {myNav: {template: "<h1>这个是导航条,私有组件</h1>"}}});</script>
</body>

4.2、方式二

<body><div id="myDiv"><my-nav></my-nav><my-nav></my-nav></div><template id="myTemplate"><h1>这个是导航条,私有组件</h1></template><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",components: {myNav: {template: "#myTemplate"}}});</script>
</body>

五、组件填充数据

5.1、说明

  • 组件其实也是Vue的一个实例对象,也可以有自己的data,只不过此处的data的值是一个函数,并且函数必须返回一个Object对象
  • data的使用方式和之前的完全一样。

5.2、案例

<body><div id="myDiv"><my-nav></my-nav><my-nav></my-nav></div><template id="myTemplate"><h1>用户名是: {{username}}</h1></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("my-nav",{template: "#myTemplate",data: function(){return {username: 'HelloWorld'}}});const app = new Vue({el: "#myDiv"});</script>
</body>

5.3、探讨一下为什么data值是一个函数而不是一个对象

<body><div id="myDiv"><my-component></my-component><my-component></my-component><my-component></my-component></div><template id="myTemplate"><div>计数器: {{count}} <br/><button @click="count++">+</button></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",data: function () {console.log("--"); // 打印了3次return {count: 0}}});const app = new Vue({el: "#myDiv"});</script>
</body>

六、组件切换

6.1、方式一:使用v-if判断

<body><div id="myDiv"><a href="" @click.prevent="login">登录</a><a href="" @click.prevent="register">注册</a><login v-if="isLogin"></login><register v-else></register></div><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("login",{template: `<div>用户名登录: <input type="text" placeholder="用户名登录"/></div>`});Vue.component("register",{template: `<div>用户名注册: <input type="text" placeholder="用户名注册"/></div>`});const app = new Vue({el: "#myDiv",data: {isLogin: true},methods: {login(){this.isLogin = true;},register(){this.isLogin = false;}}});</script>
</body>

6.2、方式二:is实现

<body><div id="myDiv"><a href="" @click.prevent="componentName = 'login'">登录</a>		// 记得加单引号<a href="" @click.prevent="componentName = 'register'">注册</a>	// 记得加单引号<component :is="componentName"></component></div><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("login",{template: `<div>用户名登录: <input type="text" placeholder="用户名登录"/></div>`});Vue.component("register",{template: `<div>用户名注册: <input type="text" placeholder="用户名注册"/></div>`});const app = new Vue({el: "#myDiv",data: {componentName: "login"}});</script>
</body>

七、父子组件

7.1、初步认识父子组件的定义

<body><div id="myDiv"><father></father></div><script src="../js/vue.js"></script><script type="text/javascript">// 1、定义子组件const children =  Vue.extend({template: `<h1>这个子组件</h1>`	});// 2、定义父组件const father = Vue.extend({template: `<div><h2>这个是父组件</h2><children></children></div>`,components: {children}});const app = new Vue({el: "#myDiv",components: {father}});</script>
</body>

7.2、父组件向子组件传递数据

7.2.0、问题案例

<body><div id="myDiv"><my-component></my-component></div><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",data: {username: "HelloWorld"},components: {myComponent: {template: '<h1>姓名是: {{username}}</h1>' // 会报错}}});</script>
</body>
// 按道理来说,子作用域应该可以访问父级别作用域的,但是发现并不行。

解决思路可以这样:在父组件中,可以在使用子组件的时候,通过属性绑定的方式,把需要传递给子组件的数据以属性绑定的形式传递到子组件内部,这样的话,子组件内部就可以接收到了。

方式:通过props向子组件传递数据

7.2.1、形式一

字符串数组,数组中的字符串就是传递时的名称。

​ 说明:让Vue的实例作为父组件,之后再定义一个子组件

<body><div id="myDiv"><my-component v-bind:subusername="username"></my-component></div><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",data: {username: "HelloWorld"},components: {myComponent: {props: ['subusername'],template: '<h1>姓名是: {{subusername}}</h1>'}}});</script>
</body>
<body><div id="myDiv"><my-component v-bind:subaddresses="fatherAddresses"></my-component></div><template id="myTemplate"><div><p v-for="item in subaddresses">{{item}}</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: ["subaddresses"]});const app = new Vue({el: "#myDiv",data: {fatherAddresses: ['河南林州',"陕西西安","浙江杭州"]}});</script>
</body>

父组件要向子组件传递数据,总结如下:

  • 第一步:

    ​ 在父组件去应用子组件的时候,去动态的绑定一个属性。

    <my-component v-bind:subusername="username"></my-component>
    

    subusername这个属性就是自定义的一个属性名称,目的是向子组件中传递数据的这么一个名称。而username就是父组件的data中的数据。

  • 第二步:

    ​ 自定义属性做好之后,子组件还不能直接使用,还需要接收。即:把父组件传递过来的subusername属性,需要在子组件的props数组中定义,注意是一个字符串的形式,这样的话,子组件才能使用这个数据。

    ​ 也可以这么说:组件中所有的props中定义的数据,都是通过父组件传递给子组件的。

  • 第三步:

    ​ 子组件在template中使用子组件props定义的名称,就可以使用数据了。

说明几个细节问题:

  • 对于子组件来说,也可以有自己的data属性,也就是说子组件可以有自己的数据,而data中的数据并不是通过父组件传递过来的,而是子组件自身所独有的,常用的方式是:子组件可以通过调用ajax请求数据,之后把查询出来的数据填充到data属性中。而子组件的props属性一定是从父组件传递过来的。
  • data属性中的数据都是可读可更改的,而props属性中的数据是只读的。

7.2.2、形式二

对象,对象可以设置传递时的类型,也可以设置默认值

<body><div id="myDiv"><my-component v-bind:subaddresses="fatherAddresses"></my-component></div><template id="myTemplate"><div><p v-for="item in subaddresses">{{item}}</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: {subaddresses: Array  // 对象,可以指定类型}});const app = new Vue({el: "#myDiv",data: {fatherAddresses: ['河南林州',"陕西西安","浙江杭州"]}});</script>
</body>
  • 指定默认值:基本类型

    <body><div id="myDiv"><my-component v-bind:subusername="fatherUsername"></my-component> // HelloWorld<my-component></my-component>					//使用组件没有传,就用默认的值</div><template id="myTemplate"><div>{{subusername}}</div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: {subusername: {type: String,default: "Spring"}}});const app = new Vue({el: "#myDiv",data: {fatherUsername: "HelloWorld"}});</script>
    </body>
    
  • 指定默认值:对象或者数组

    <body><div id="myDiv"><my-component v-bind:subaddresses="fatherAddresses"></my-component><my-component></my-component></div><template id="myTemplate"><div><p v-for="item in subaddresses">{{item}}</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: {subaddresses: {type: Array,default: function(){return ['HelloWorld' , "Spring"];	// 对象或数组默认值的话要用函数}}}});const app = new Vue({el: "#myDiv",data: {fatherAddresses: ["河南林州","浙江杭州"]}});</script>
    </body>
    
  • 指定是否必传

    <body><div id="myDiv"><my-component v-bind:subusername="fatherUsername"></my-component><my-component></my-component>       // 会报错,要必传才行</div><template id="myTemplate"><div>{{subusername}}</div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: {subusername: {type: String,required: true  // 表示必传}}});const app = new Vue({el: "#myDiv",data: {fatherUsername: "HelloWorld"}});</script>
    </body>
    

7.2.3、写法上的改进:驼峰

<body><div id="myDiv"><my-component v-bind:sub-addresses="fatherAddresses"></my-component>// 标签需要"-"连接<my-component></my-component></div><template id="myTemplate"><div><p v-for="item in subAddresses">{{item}}</p>		// 拿数据依然是驼峰法方式</div></template><script src="../js/vue.js"></script><script type="text/javascript">// 注册组件Vue.component("myComponent",{template: "#myTemplate",props: {subAddresses: {				// 驼峰命名type: Array,default: function(){return ['HelloWorld' , "Spring"];}}}});const app = new Vue({el: "#myDiv",data: {fatherAddresses: ["河南林州","浙江杭州","云南大理"]}});</script>
</body>

7.3、子组件向父组件传递数据

方式:通过自定义事件向父组件发送消息

<body><div id="myDiv"><my-component @fn="fatherFn"></my-component></div><template id="myTemplate"><div><button @click="subCompClick">子组件向父组件传递参数使用的事件的方式</button></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 定义子组件Vue.component("myComponent",{template: "#myTemplate",data: function(){return {person: {username: '我是子组件HelloWorld'}}},methods: {subCompClick(){// 点击之后,子组件发射一个自定义的事件this.$emit("fn",this.person);}}});const app = new Vue({el: "#myDiv",data: {username: "HelloWorld"},methods: {fatherFn(obj){console.log(obj);}}});</script>
</body>

解决的思路是这样的:既然我们能做到父组件的data数据传递到子组件中,我们就可以实现将父组件的方法传递到子组件中。【实际上,虽然我们现在的确是在讲子组件向父组件传递数据,如果要按照这种思维方式去实现代码的话,不太好理解,我个人更推荐这种理解方式,即:现在不考虑子组件向父组件传递数据,我们就一律看做是:父组件向子组件传递数据,如果按照这种方式来去理解的话,写代码就会顺畅很多,通过此种方式去理解代码,写着写着就自然而然的就成了子组件向父组件传递数据了。】==

子组件向父组件传递数据/父组件向子组件传递方法,步骤总结如下:

  • 第一步

    ​ 在父组件中定义一个方法。本案例中Vue实例作为父组件,自定义的方法是fatherFn

  • 第二步

    ​ 在应用子组件的标签上,去动态绑定一个事件。本案例中的事件是fn

    <my-component @fn="fatherFn"></my-component>
    

    ​ 代码的含义:就相当于是将父组件中的fatherFn方法的引用传递给子组件的fn事件函数。此时该fn函数肯定是需要在某个时刻要用到的。

  • 第三步

    ​ 既然我们是子组件向父组件传递数据,那么肯定是在子组件中做了一些操作,然后将数据传递给父组件。本案例中,在子组件template中有一个button按钮,该按钮的作用就是当点击的时候,向父组件传递数据。并且为该按钮绑定了一个事件,属于该按钮的事件,是subCompClick

  • 第四步

    ​ 在子组件中的methods属性中去定义subCompClick事件,当点击按钮的时候就会触发该事件,那么在

    subCompClick事件中,操作是:调用$emit方法去发射我们的fn事件,并且通过该方法传递数据,而我们知道,我们的fn其实就是我们第一步骤中的fatherFn 。 然后在fatherFn函数中可以通过参数的形式去接收子组件传递的数据。

<body><div id="myDiv"><my-component v-on:get-list-click="fatherHandle"></my-component></div><template id="myTemplate"><div><a href=""v-for="item in categories"@click.prevent="aClick(item)">{{item.name}} <br></a></div></template><script src="../js/vue.js"></script><script type="text/javascript">// 子组件Vue.component("myComponent",{template: "#myTemplate",data: function(){return {categories: [{id: 1, name: "动作片"},{id: 2, name: "爱情片"},{id: 3, name: "悬疑片"},{id: 4, name: "悬疑片"}]};},methods: {aClick(item){// 点击之后,子组件发射一个自定义的事件this.$emit("get-list-click",item);}}});const app = new Vue({el: "#myDiv",methods: {fatherHandle(item){console.info("接收的信息是:" ,item);}}});</script>
</body>

7.4、案例

该案例:子组件向父组件传数据综合运用。该案例是我们二期jQuery的一个案例:发表评论的案例。

7.4.1、第一步:先把数据给模拟出来

<body><div id="myDiv"><p v-for="item in list" :key="item.id">评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}</p></div><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",data: {list: [{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}]}});</script>
</body>

7.4.2、第二步:定义子组件,发表评论

<body><div id="myDiv"><my-component></my-component><p v-for="item in list" :key="item.id">评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}</p></div><template id="myTemplate"><div>评论id: <input type="text"> <br/>评论人: <input type="text"> <br/>评论内容:<textarea></textarea><br/><input type="button" value="提交"></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv",data: {list: [{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}]}});</script>
</body>

7.4.3、第三步:点击发表评论

  • 为表单控件绑定v-model,目的是获取数据
  • 绑定的数据需要定义在子组件template的data中
  • 为发表按钮提供单击事件
<body><div id="myDiv"><my-component @fn="fatherFn"></my-component><p v-for="item in list" :key="item.id">评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}</p></div><template id="myTemplate"><div>评论id: <input type="text" v-model="id"> <br/>评论人: <input type="text" v-model="user"> <br/>评论内容:<textarea v-model="content"></textarea><br/><input type="button" value="发表" @click="publish"></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data:function(){return {id: '' , user: '' , content: ''}},methods: {publish: function(){// 发表评论业务处理// 1、构造一个评论数据对象let comment = {id: this.id, user: this.user , content: this.content, pubDate: new Date()};// 2、需要将创建好的评论数据保存到父组件中的data中,涉及到了子组件向父组件传值// >> 子组件发射一个自定义的事件this.$emit("fn",comment);}}});const app = new Vue({el: "#myDiv",data: {list: [{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}]},methods: {fatherFn(comment){// 刚刚新增评论就插入到数组的头部this.list.unshift(comment);}}});</script>
</body>

八、综合案例

说明:该案例体现了父子组件相互传递数据。

8.1、第一步:实现父组件数据传递给子组件

<body><div id="myDiv">父组件的数据:{{username}} {{age}}<my-component :subusername="username" :subage="age"></my-component></div><template id="myTemplate"><div>通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}}</div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",props: {subusername: String,subage: Number}});const app = new Vue({el: "#myDiv",data: {username: 'HelloWorld',age: 12}});</script>
</body>

说明:通过案例可以发现,子组件的确可以使用到了父组件中的数据。子组件可以使用props中的数据并显示到页面上,不过props中的数据是从父组件中获取的,是只读数据。

8.2、第二步:看一个细节问题

​ 在子组件中,定义两个input,实现双向绑定,绑定到了props属性的subusernamesubage这两个属性,看下实验效果。

<body><div id="myDiv">父组件的数据:{{username}} {{age}}<my-component :subusername="username" :subage="age"></my-component></div><template id="myTemplate"><div>通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>双向绑定props属性中的subusername: <input type="text" v-model="subusername"><br/>双向绑定props属性中的subage: <input type="text" v-model="subage"><br/></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",props: {subusername: String,subage: Number}});const app = new Vue({el: "#myDiv",data: {username: 'HelloWorld',age: 12}});</script>
</body>

通过实验发现,该案例是有问题的,控制台会报错。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UlBrOQmL-1690870417750)(/005.png)]

结论:根据提示,避免去直接修改props属性中的数据,因为props属性中的数据都是通过父组件传递过来的,是只读的,避免去覆盖。取而代之的是,可以使用data和computed计算属性。

8.3、第三步:使用data属性

<body><div id="myDiv">父组件的数据:{{username}} {{age}}<my-component :subusername="username" :subage="age"></my-component></div><template id="myTemplate"><div>通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>子组件data属性中的数据:{{data_subusername}}---{{data_subage}} <br/>双向绑定data属性中的subusername: <input type="text" v-model="data_subusername"><br/>双向绑定data属性中的subage: <input type="text" v-model="data_subage"><br/></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",props: {subusername: String,subage: Number},data: function(){return {data_subusername: this.subusername,data_subage: this.subage}}});const app = new Vue({el: "#myDiv",data: {username: 'HelloWorld',age: 12}});</script>
</body>

使用data问题解决。但是还有一个需求就是:如果我想改变子组件中的文本框的数据,也想同步修改到父组件,让父组件也实现同步更新。这样的话,就涉及到了子组件向父组件传递数据,需要使用到自定义事件。

8.4、第四步、给子组件的数据设置侦听器

​ 需求:要想实现子组件的数据可以传递到父组件,也就是说子组件中的data数据如果发生了改变,那么父组件也可以感知到,则需要发射自定义事件来解决。此时,可以在子组件中为data中的属性设置侦听器来实现,当属性发生了修改,立马侦听到之后,再发送事件。

<body><div id="myDiv">父组件的数据:{{username}} {{age}}<my-component :subusername="username" :subage="age"@subchangeusername="fatherChangeUsername"@subchangeage="fatherChangeAge"></my-component></div><template id="myTemplate"><div>子组件通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>子组件通过data属性获中的数据:{{data_subusername}}---{{data_subage}} <br/>双向绑定props属性中的subusername: <input type="text" v-model="data_subusername"><br/>双向绑定props属性中的subage: <input type="text" v-model="data_subage"><br/></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",props: {subusername: String,subage: Number},data: function(){return {data_subusername: this.subusername,data_subage: this.subage}},watch: {data_subusername(newValue){// 发生了修改,发送事件this.$emit("subchangeusername",newValue);},data_subage(newValue){// 发生了修改,发送事件this.$emit("subchangeage",newValue);}}});const app = new Vue({el: "#myDiv",data: {username: 'HelloWorld',age: 12},methods: {fatherChangeUsername(value){this.username = value;},fatherChangeAge(value){this.age = parseInt(value);}}});</script>
</body>

九、父组件直接访问子组件

使用方式:$children或者是$refs

9.1、方式一:$children

<body><div id="myDiv"><my-component></my-component><my-component></my-component><input type="button" value="单击" @click="handleClick"></div><template id="myTemplate"><div>这个是子组件</div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function(){return {username: "HelloWorld"}},methods: {info(){console.info("子组件的用户名:" + this.username);}}});const app = new Vue({el: "#myDiv",methods: {handleClick(){// 单击父组件的按钮,执行该函数,目的是访问子组件中的数据和调用子组件中的方法console.info(this.$children); // 返回的结果是一个数组for (let comp of this.$children){console.info(comp.username);comp.info();}}}});</script>
</body>

总结:此种方式使用不多,要获取具体的子组件还需要通过下标的方式来去获取,非常不方便。

9.2、方式二:$refs

<body><div id="myDiv"><my-component ref="comp1"></my-component><my-component ref="comp2"></my-component><input type="button" value="单击" @click="handleClick"></div><template id="myTemplate"><div>这个是子组件</div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function(){return {username: "HelloWorld"}},methods: {info(){console.info("子组件的用户名:" + this.username);}}});const app = new Vue({el: "#myDiv",methods: {handleClick(){// 单击父组件的按钮,执行该函数,目的是访问子组件中的数据和调用子组件中的方法console.log(this.$refs.comp1.username);this.$refs.comp1.info();console.log(this.$refs.comp2.username);this.$refs.comp2.info();}}});</script>
</body>

总结:这种方式类似于给组件起了一个id值,通过id的方式直接获取到了某个组件。通常,通过$refs来去获取Dom元素。

<body><div id="myDiv"><p ref="p1">HelloWorld</p><p ref="p2">Spring</p><input type="button" value="操作Dom元素" @click="handleClick"></div><script src="../js/vue.js"></script><script type="text/javascript">const app = new Vue({el: "#myDiv",methods: {handleClick(){console.log(this.$refs.p1.innerText);console.log(this.$refs.p2.innerText);}}});</script>
</body>

十、子组件直接访问父组件和根组件

访问父组件使用方式:$parent

访问根组件使用方式:$root,也就是访问的Vue实例这个根组件

<body><div id="myDiv"><my-component></my-component></div><template id="myTemplate"><div>这个是子组件<input type="button" value="访问父组件" @click="subHandleClick"></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",methods: {subHandleClick(){// 访问父组件console.log(this.$parent.message);this.$parent.fatherInfo();// 访问根组件console.log(this.$root.message);this.$root.fatherInfo();}}});const app = new Vue({el: "#myDiv",data: {message: 'Spring'},methods: {fatherInfo(){console.info("父组件的信息:" + this.message);}}});</script>
</body>

十一、插槽

11.1、说明

​ 插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。

插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。

适用场景:是那些可以将多个组件看做一个整体,这个整体会被复用。但其中的一些部分内容不固定。

11.2、HelloWorld案例

需求:我现在有一个div,是一个子组件,里面有公共的代码,就是p标签,但是这个子组件在不同页面上所展示的效果还是有细微区别的,可能A页面是一个button按钮,B页面可能是一个p标签。

11.2.1、简单案例

<body><div id="myDiv"><my-component><button>这个是按钮</button></my-component>-------------------------------------- <br><my-component><p>这个是p标签</p></my-component></div><template id="myTemplate"><div><p>我是子组件</p><slot></slot>		// 相当于是占位符</div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv"});</script>
</body>

11.2.2、改进:为插槽指定默认内容

<body><div id="myDiv"><my-component>			</my-component>-------------------------------------- <br><my-component><p>这个是p标签</p></my-component></div><template id="myTemplate"><div><p>我是子组件</p><slot><button>这个是按钮,为插槽指定默认内容</button></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv"});</script>
</body>

总结:该案例是在slot插槽中设置了一个button按钮,相当于是一个默认值,那么此时,在使用该组件的时候,可以不传入了,那么就用默认值button,如果传入了,则就用指定的传入Dom模板。

11.3、具名插槽

​ 说明:如果在一个组件内有多个插槽,如何为指定的插槽填充内容呢,此时就需要为每个插槽提供一个名字,这种插槽就叫做具名插槽。

11.3.1、问题案例

<body><div id="myDiv"><my-component><button>此处替换为按钮</button><p>此处是P标签</p></my-component></div><template id="myTemplate"><div><slot><p>Tomcat</p></slot><p>我是子组件</p><slot><p>HelloWorld</p></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv"});</script>
</body>

总结:会发现,组件中的slot插槽中的内容都被buttonp所替代了。

11.3.2、解决方案一

<body><div id="myDiv"><my-component><button slot="slot1">此处替换为按钮</button><p>此处是P标签</p></my-component></div><template id="myTemplate"><div><slot name="slot1"><p>Tomcat</p></slot><p>我是子组件</p><slot name="slot2"><p>HelloWorld</p></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv"});</script>
</body>

11.3.3、 解决方案二

<body><div id="myDiv"><my-component><template v-slot:slot1><button>此处替换为按钮</button></template><p>此处是P标签</p></my-component></div><template id="myTemplate"><div><slot name="slot1"><p>Tomcat</p></slot><p>我是子组件</p><slot name="slot2"><p>HelloWorld</p></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",});const app = new Vue({el: "#myDiv"});</script>
</body>

注意:在使用slot的时候,单独的使用slot已经在Vue2.6版本已经废弃,取而代之的是v-slot。

11.4、编译作用域

11.4.1、案例1

<body><div id="myDiv"><my-component v-show="isShow"></my-component></div><template id="myTemplate"><div><p>我是HelloWorld</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function () {return {isShow: true}}});const app = new Vue({el: "#myDiv",data:{isShow: false}});</script>
</body>

看代码:在Vue实例中的data属性中有一个isShow,值是false,在子组件中也有一个isShow属性,值是true,在使用子组件的时候,my-component v-show="isShow"中的isShow实际上使用的是Vue实例中的isShow,所以页面中是不显示子组件的。

注意:通过该案例发现,在使用isShow这个变量的时候,操作是这样的:是看这个isShow这个变量是在哪个模板/组件中的,而不是看这个变量被哪个子组件使用的。以当前案例为例,isShow这个变量是在一个叫做myDiv这个模板中的,所以isShow这个变量的作用域就是Vue实例,那么当然使用的是Vue实例中的data。

11.4.2、案例2

<body><div id="myDiv"><my-component v-show="isShow"></my-component></div><template id="myTemplate"><div><p v-show="isShow">我是HelloWorld</p></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function () {return {isShow: false}}});const app = new Vue({el: "#myDiv",data:{isShow: true}});</script>
</body>

案例:将Vue实例的data中的isShow的值改为true,子组件中的isShow的值改为false,并且在template模板/子组件中也使用了isShow,会发现,页面依然是不显示子组件内容,此时,模板中的isShow使用的就是子组件中的isShow。

11.5、作用域插槽

11.5.1、说明

含义:父组件替换插槽的标签,但是内容由子组件提供。

11.5.2、案例

​ 需求:子组件有一组数据,比如说是一个数组,那么这些数据需要在多个界面进行展示,可能有的界面需要横向排列,有的界面需要纵向排列,有些界面可能就是直接显示一个数组,问题是:数据在子组件中,希望父组件

告诉我们如何显示,如何做?此时,就需要用到==slot作用域插槽==。

11.5.2.1、方式一

<body><div id="myDiv"><my-component></my-component><my-component><div slot-scope="subData">{{ subData.data.join(' - ') }}</div></my-component><my-component><template slot-scope="subData">{{ subData.data.join(' * ') }}</template></my-component></div><template id="myTemplate"><div><slot :data="addresses"><ul><li v-for="item in addresses">{{ item }}</li></ul></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function () {return {addresses: ['河南林州',"浙江杭州","陕西西安"]}}});const app = new Vue({el: "#myDiv"});</script>
</body>

11.5.2.2、方式二

​ 自 2.6.0 起有所更新。已废弃的使用 slot-scope,使用v-slot代替,直写在组件标签上。

<body><div id="myDiv"><my-component></my-component><my-component v-slot="subData">{{ subData.data.join(' - ') }}</my-component><my-component  v-slot="subData">{{ subData.data.join(' * ') }}</my-component></div><template id="myTemplate"><div><slot :data="addresses"><ul><li v-for="item in addresses">{{ item }}</li></ul></slot></div></template><script src="../js/vue.js"></script><script type="text/javascript">Vue.component("myComponent",{template: "#myTemplate",data: function () {return {addresses: ['河南林州',"浙江杭州","陕西西安", "云南大理"]}}});const app = new Vue({el: "#myDiv"});</script>
</body>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 终端操作高效率快捷键!

今天给大家分享一下 Linux 下终端中命令操作常用的快捷键。 作为一名 Linux 下的开发人员&#xff0c;和 Linux 系统打交道是每天必做的事情&#xff0c;通过 Linux 终端下命令行与 Linux 进行交互。 熟练掌握 Linux 终端下命令行的操作可以让我们的工作达到事半功倍的效果&a…

STM32-风速传感器(ADC)

目录 0 说明 1 传感器介绍 2 代码说明 2.1 ADC.c 2.2 adc.h 2.3 main.c 0 说明 本篇文章主要是说明怎么使用STM32单片机读取风速传感器采集到的数据&#xff0c;读取方式是ADC&#xff0c;并且附带着STM32所需要的全部代码&#xff0c;所使用的风速传感器如下图所示。 附&am…

IDEA的基础使用——【初识IDEA】

IDEA的基础使用——【初识IDEA】 文章目录 IDEA简介前言官网 IDEA的下载与安装选择下载路径勾选自己需要的其余按默认选项进行即可 目录简介安装目录简介 运行Hello WorldIDEA快捷键常用模板模板一&#xff1a;psvm&#xff08;main&#xff09;模板二&#xff1a;模板三&#…

PHP-Mysql好运图书管理系统--【白嫖项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 首页必要的项目知识ThinkPHP的MVCThinkTemplateThinkPHP 6和ThinkPHP 5 phpStudy 设置导数据库前台展示页面后台的管理界面数据库表结构项目目录如图&#xff1a;代码部分&a…

【c语言初级】c++基础

文章目录 1. C关键字2. 命名空间2.1 命名空间定义2.2 命名空间使用 3. C输入&输出4. 缺省参数4.1 缺省参数概念4.2 缺省参数分类 5. 函数重载5.2 C函数重载的原理--名字修饰采用C语言编译器编译后结果 1. C关键字 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想…

adb shell使用总结

文章目录 日志记录系统概览adb 使用方式 adb命令日志过滤按照告警等级进行过滤按照tag进行过滤根据告警等级和tag进行联合过滤屏蔽系统和其他App干扰&#xff0c;仅仅关注App自身日志 查看“当前页面”Activity文件传输截屏和录屏安装、卸载App启动activity其他 日志记录系统概…

android studio 找不到符号类 Canvas 或者 错误: 程序包java.awt不存在

android studio开发提示 解决办法是&#xff1a; import android.graphics.Canvas; import android.graphics.Color; 而不是 //import java.awt.Canvas; //import java.awt.Color;

JNPF-一个真正可拓展的低代码全栈框架

一、前言 尽管现在越来越多的人开始对低代码开发感兴趣&#xff0c;但已有低代码方案的一些局限性仍然让大家有所保留。其中最常见的担忧莫过于低代码缺乏灵活性以及容易被厂商锁定。 显然这样的担忧是合理的&#xff0c;因为大家都不希望在实现特定功能的时候才发现低代码平台…

【SVO】位姿优化及其误差模型

位姿优化及其误差模型 1. calculateFeatureResidualUnitPlane 函数功能2. calculateFeatureResidualUnitPlane 函数功能实现步骤&#xff1a;3. 位姿优化误差模型&#xff1a; 1. calculateFeatureResidualUnitPlane 函数功能 计算特征点在单位平面上的残差&#xff08;residu…

iOS--frame和bounds

坐标系 首先&#xff0c;我们来看一下iOS特有的坐标系&#xff0c;在iOS坐标系中以左上角为坐标原点&#xff0c;往右为X正方向&#xff0c;往下是Y正方向如下图&#xff1a; bounds和frame都是属于CGRect类型的结构体&#xff0c;系统的定义如下&#xff0c;包含一个CGPoint…

递归竖栏菜单简单思路

自己的项目要写一个竖栏菜单&#xff0c;所以记录一下思路吧&#xff0c;先粗糙的实现一把&#xff0c;有机会再把细节修饰一下 功能上就是无论这个菜单有多少层级&#xff0c;都能显示出来&#xff0c;另外&#xff0c;需要带图标&#xff0c;基于element-plus写成&#xff0…

ConcurrentHashMap底层具体实现以及实现原理

问题描述 ConcurrentHashMap 底层具体实现以及实现原理 分析维度&#xff1a; 1. ConcurrentHashMap的整体架构 2. ConcurrentHashMap的基本功能 3. ConcurrentHashMap在性能方面的优化 解决方案&#xff1a; ConcurrentHashMap 的整体架构 如图所示&#xff0c;这个是 Concu…

清风数学建模——层次分析法

层次分析法 文章目录 层次分析法评价类问题可以用打分来解决1.通过查阅资料选定指标2.画出权重表格并填写2.1.判断矩阵一致矩阵2.3一致性检验的步骤先算一致性指标CI根据表格查找n对应的RI&#xff08;平均随机一致性指标&#xff09;&#xff0c;表格一般会在题目中给出计算一…

3 PostGIS基础查询

PostGIS 基础查询 数据库维护 ps aux | grep postgrespsql 使用命令登录数据库psql -U postgres -d testdb -h localhost -p 5432postgres用户名&#xff0c;testdb数据库名称&#xff0c;localhost ip地址&#xff0c;可以省略&#xff0c;5432端口&#xff0c;可以省略。 …

【iOS】—— UIKit相关问题

文章目录 UIKit常用的UIKit组件懒加载的优势 CALayer和UIView区别关系 UITableViewUITableView遵循的两个delegate以及必须实现的方法上述四个必须实现方法执行顺序其他方法的执行顺序&#xff1a; UICollectionView和UITableView的区别UICollectionViewFlowLayout和UICollecti…

uniapp scroll-view显示滚动条

在style中添加样式&#xff1a; ::v-deep ::-webkit-scrollbar {/* 滚动条整体样式 */display: block;width: 10rpx !important;height: 10rpx !important;-webkit-appearance: auto !important;background: transparent;overflow: auto !important;}::v-deep ::-webkit-scroll…

oracle rman不能自动删除归档日志备份解决

发现在日常备份中&#xff0c;rman无法将过期的归档日志备份删除&#xff0c;查相关资料&#xff0c; delete noprompt backup completed before sysdate-2; 可通过该语句将所有备份记录删除&#xff0c;包括归档日志备份。 整理的脚本如下&#xff1a; 10 20 * * * su - or…

【Java】详解volatile和synchronized关键字

volatile和synchronized都是Java中用于控制并发的关键字&#xff0c;但是它们的使用场景和原理是不同的。 volatile关键字&#xff1a; 特点&#xff1a;volatile关键字主要有两个特性&#xff1a;保证变量的可见性和防止指令重排。当一个共享变量被volatile修饰时&#xff0c…

大数据Flink(五十一):Flink的引入和Flink的简介

文章目录 Flink的引入和Flink的简介 一、Flink的引入 1、第1代——Hadoop MapReduce

CentOS 8 服务器安装 MySQL 报错:no match mysql-community-server

参考 MySQL 官方文档&#xff1a; Re: No match for argument: mysql-community-serverMySQL yum 安装文档 报错如下&#xff1a; 1.No match for argument: mysql-community-server 2.Error: Unable to find a match: mysql-community-server上面的错误都提示找不到 mysql-…