Vue系列入门教程(5)——组件

Web前端 潘老师 4年前 (2020-09-03) 2534 ℃ (0) 扫码查看

1、非模块化的缺点:

  • 代码杂乱无章,没有条理性,不便于维护,不便于复用
  • 很多代码重复、逻辑重复
  • 全局变量污染
  • 不方便保护私有数据(闭包)

2、常见模块化标准(了解):
CommonJS:Node中使用的模块化解决方案
ES6 Module:ES6模块化
AMD:异步模块定义,其实就是requireJS实现的模块化解决方案
CMD:seajs中提出来的模块化解决方案

3、ES6新增了模块化功能,我们来简要学习下
ES6的模块化分为导出(export)与导入(import)两个模块。

在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出),想要使用模块,就要使用import进行导入后使用。

案例代码:
1)新建test.js

//导出变量  其实是导出对象:{username:"潘子夜"}
export var username="潘子夜";
//默认导出  其实是导出对象:{default:fn}
//一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致。
export default function(a,b){
    return a+b;
}
//具名导出(普通导出)其实是导出对象:{multiply:fn}
export function multiply(a,b){
    return a*b;
}
//如果要输出多个变量可以将这些变量包装成对象进行模块化输出
var age = 20;
var grade = "大三";
export {
    age,
    grade
}
//如果你不想暴露模块当中的变量名字,可以通过as来进行操作:
var mySex = "男";
var myHobby = "打球";
var myFun = function(a,b){
    return a-b;
}
export {
    mySex as sex,
    myHobby as hobby,
    myFun as fun
}

2)新建test1.js

export var password="我来自test1.js";

3)新建test2.js

export var password="我来自test2.js";

4)新建test3.js

export var fileName="演示整体导入test3.js";
export function double(a){
    return a*2;
}

5)新建index.js进行模块引入

//导入变量
import {username} from './test.js'
//导入默认  导入名称随意,注意:没有{}
import sum from './test.js'
//导入普通
import {multiply} from './test.js'
//导入多个变量
import {age,grade} from './test.js'
//导入别名
import {sex,hobby,fun} from './test.js'
//重命名export和import
//如果导入的多个文件中,变量名字相同,即会产生命名冲突的问题,为了解决该问题,ES6为提供了重命名的方法,当你在导入名称时可以这样做:
import {password as pwd1} from "./test1.js";
import {password as pwd2} from "./test2.js";
//也可以直接导入整个模块
import * as test3 from "./test3.js"
//依次输出
console.log(username);
console.log(sum(6,3));
console.log(multiply(6,3));
console.log(age);
console.log(grade);
console.log(sex);
console.log(hobby);
console.log(fun(6,3));
console.log(pwd1);
console.log(pwd2);
console.log(test3.fileName);
console.log(test3.double(5));
注意:具名导入的都加{},不加{}都是默认导入

6)新建index.html引入index.js测试

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <!-- type="module"表示以模块方式引入 -->
        <script src="./js/index.js" type="module"></script>
    </head>
    <body>
        
    </body>
</html>

7)运行到浏览器,F12查看打印,发现正常使用:
Vue系列入门教程(5)——组件

1、一个完整的网页是复杂的,如果将其作为一个整体来进行开发,将会遇到下面的困难:

  • 代码凌乱臃肿
  • 不易协作
  • 难以复用

2、vue推荐使用-种更加精细的控制方案——组件化开发
所谓组件化,即把一个页面中区域功能细分, 每一个区域成为一个组件,每个组件包含:

  • 功能(JS代码)
  • 内容(模板代码)
  • 样式(CSS代码)
提示:我们目前由于没有使用构建工具,CSS代码暂时无法放到组件中,后面我们使用脚手架搭建就可以实现

1、组件是根据一个普通的配置对象创建的,所以要开发一个组件, 只需要写一个配置对象即可,该配置对象和vue实例的配置是几乎一样的。
2、Vue创建组件语法:

//自定义组件配置对象
var myComp = {
    data(){
        return {
            //.....
        }
    },
    computed: {
        //...
    },
    methods: {
        //....
    },
    template: {
        //.....
    }
}

3、值得注意的是,每一个组件其实都是一个Vue实例,不过组件配置对象和vue实例有以下几点差异:

  • el
  • data必须是一个函数,该函数返回的对象作为数据
  • 由于没有el配置,组件的模板必须定义在template

组件在使用之前必须要先注册组件,注册组件有两种方式:全局注册局部注册

1、全局注册

1)一旦全局注册了一个组件,整个应用中任何地方都可以使用该组件
Vue系列入门教程(5)——组件
2)全局注册方式

//参数1:组件名称,将来在模板中使用组件时,会使用该名称
//参数2:组件配置对象
//该代码运行后,即可在模板中使用组件
Vue.component(' my-comp' ,myComp )

3)注册好的组件使用
上面的组件注册好后,在模板中,就可以使用组件了,使用格式:

<my-comp />
<!--或-->
<my-comp></my-comp>
提示:
在一些工程化的大型项目中, 很多组件都不需要全局使用。
比如一个登录组件,只有在登录的相关页面中使用,如果全局注册,将导致构建工具无法优化打包。
因此,除非组件特别通用,否则不建议使用全局注册。

4)代码案例:

<div id="app">
    {{title}}
    <ul>
        <!-- 应用组件-两种写法 -->
        <my-comp></my-comp>
        <my-comp/>
    </ul>
</div>
<script>
    //自定义组件
    var myComp = {
        data(){
            return {
                name:"潘子夜",
                age:20
            }
        },
        template:`<li>姓名:{{name}},年龄:{{age}}</li>`
    }
    //全局注册组件
    Vue.component("my-comp",myComp);
    //创建Vue实例
    var app = new Vue({
        el:"#app",
        data:{
            title:"用户列表"
        }
    })
</script>

效果:
Vue系列入门教程(5)——组件

2、局部注册

1)局部注册就是哪里要用到组件,就在哪里注册
Vue系列入门教程(5)——组件
2)局部注册的方式是,在要使用组件的组件或实例中加入一个配置:

//这是另一个要使用my-comp的组件
var otherComp = {
    components:{
        //属性名为组件名称,模板中将使用该名称
        //属性值为组件配置对象
        "my-comp": myComp
    },
    template :
    `<div>
        <!--该组件的其他内容-->
        <my-comp></my- comp>
    </div>`
}

1、在模板中使用组件特别简单,把组件名当作HTML元素名使用即可。
但要注意以下几点:
1)组件必须有结束。组件可以自结束,也可以用结束标记结束,但必须要有结束,以下两种方式都可以:

<my-comp/>
<!-- 或 -->
<my-comp></my-comp>

2)组件的命名规范:
无论你使用哪种方式注册组件,组件的命名需要遵循规范。组件可以使用kebab-case短橫线命名法,也可以使用PascalCase大驼峰命名法。下面两种命名均是可以的:

var otherComp = {
    components:{
        "my-comp": myComp,//方式1
        "MyComp": myComp//方式2
    }
}

实际上,使用小驼峰命名法camelCase 也是可以识别的,只不过不符合官方要求的规范。
3)使用PascalCase方式命名还有一个额外的好处,即可以在模板中使用两种组件名:

var otherComp = {
    components:{
        "MyComp": myComp
    }
}

在模板中以下两种方式都可以使用:

<!--可用-->
<my-comp />
<MyComp />
注意:PascalCase命名不可以直接在htmI中使用,但可以在template配置中使用,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

4)局部注册代码案例:

<div id="app"></div>
<script>
    //自定义组件
    var myComp = {
        data(){
            return {
                name:"潘子夜",
                age:20
            }
        },
        template:`<li>姓名:{{name}},年龄:{{age}}</li>`
    }
    //创建Vue实例
    var app = new Vue({
        el:"#app",
        data:{
            title:"用户列表"
        },
        components:{
            "MyComp":myComp
        },
        template:
        `<div id="app">
            {{title}}
            <ul>
                <my-comp></my-comp>
                <my-comp/>
                <MyComp></MyComp>
                <MyComp/>
            </ul>
        </div>`
    })
</script>

效果:
Vue系列入门教程(5)——组件

注意:
vue针对tr单独封装的组件不能直接使用组件标签,会将tr放到table外,需要借助is属性指定组件标签。

比如我们定义tr组件如下:

var myComp = {
    props:["item"],
    template:`<tr>
        <td>aaa</td>
        <td>bbb</td>
    </tr>`
};

引用如下:

<div id="app">
    <table>
        <tr>
            <td>产品编号</td>
            <td>产品名称</td>
        </tr>
        <my-comp></my-comp>
    </table>
</div>

测试如下:
Vue系列入门教程(5)——组件
修改引用如下即可解决:

<div id="app">
    <table>
        <tr>
            <td>产品编号</td>
            <td>产品名称</td>
        </tr>
        <tr is="my-comp"></tr>
    </table>
</div>

如图:
Vue系列入门教程(5)——组件

一个组件创建好后,往往会在各种地方使用它。它可能多次出现在vue实例中,也可能出现在其他组件中。于是就形成了一个组件树。
Vue系列入门教程(5)——组件

1、大部分组件要完成自身的功能,都需要一些额外的信息,比如一个头像组件,需要告诉它头像的地址,这就需要在使用组件时向组件传递数据,传递数据的方式有很多种,最常见的一种是使用组件属性props

2、首先在组件中申明可以接收哪些属性,例如:

var myComp = {
    props:["p1", "p2","p3"],
    //和vue实例一样,使用组件时也会创建组件的实例
    //而组件的属性会被提取到组件实例中,因此可以在模板中使用
    template:
        `<div>
        {{p1}},{{p2}}, {{p3}}
        </div>`
}

3、在使用组件时,向其传递属性:

var otherComp = {
    components: {
        "my-comp":myComp
    }
    data(){
        return {
            a:1
        }
    },
    //可以使用:绑定属性传值,也可以使用属性直接赋值传值
    template:
    `<my-comp :p1="a" :p2="2" p3="3"/>`
}

4)注意:在组件中,props属性中数据是别人传过来的,并且是只读的,绝不可以更改,这叫做单向数据流,从上面的组件流向下面的组件。组件中data中的数据是可以修改的,因为这里组件自身的数据。
Vue系列入门教程(5)——组件
5)案例代码:

<div id="app">
    {{title}}
    <ul>
        <my-comp v-for="(user,i) in users" :key="i" :name="user.name" :age="user.age"/>
    </ul>
</div>
<script>
    //自定义组件
    var myComp = {
        props:["name","age"],
        template:`<li>姓名:{{name}},年龄:{{age}}</li>`
    }
    //创建Vue实例
    var app = new Vue({
        el:"#app",
        data:{
            title:"用户列表",
            users:[
                {name:"张三",age:25},
                {name:"王五",age:45},
                {name:"李四",age:65}
            ]
        },
        components:{
            "my-comp":myComp
        }
    })
</script>

效果:
Vue系列入门教程(5)——组件

如果需要通过点击事件删除上述案例的用户纪录,点击删除是子组件触发的,而数据是在父组件中的,存在单向数据流问题,那么该怎么做?
需要子组件与父组件通信,子组件发布点击事件,父组件订阅子组件的事件,一旦监听到该事件,父组件就删除对应的用户纪录数据,也就是我们的发布订阅模式

在子组件开发过程中,我们只需要关心子组件需要做的事情,如果子组件有事件,其实子组件并不知道该事件的具体实现,因为子组件可能会用在多个不同的地方,而针对不同的地方可能该事件有不同的具体实现,因此我们有必要将该事件的具体实现交给调用子组件的父组件去实现,因此子组件只需向外触发一个事件,回传一些参数即可,子父组件交互如下图。

Vue系列入门教程(5)——组件

<div id="app">
    {{title}}
    <ul>
        <my-comp 
        v-for="(user,i) in users" 
        :key="i" 
        :name="user.name" :age="user.age" 
        :index="i"
        @delete="handleDelete"/>
    </ul>
</div>
<script>
    //自定义组件
    var myComp = {
        //接收index属性值
        props:["name","age","index"],
        template:`<li>姓名:{{name}},年龄:{{age}}  
                    <button @click="handleClick">删除</button></li>`,
        methods:{
            handleClick(){
                //子组件向外触发delete事件,在父组件中应用时绑定该事件,参数为index,即纪录索引
                this.$emit("delete",this.index)
            }
        }
    }
    //创建Vue实例
    var app = new Vue({
        el:"#app",
        data:{
            title:"用户列表",
            users:[
                {name:"张三",age:25},
                {name:"王五",age:45},
                {name:"李四",age:65}
            ]
        },
        components:{
            "my-comp":myComp
        },
        methods:{
            handleDelete(index){
                this.users.splice(index,1);
            }
        }
    })
</script>

效果:
Vue系列入门教程(5)——组件


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/java/web/955.html
喜欢 (12)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】