Vue-组件开发-2

1.认识组件化

1.1什么是组件化

人面对复杂问题的处理方式:

  • 任何一个人处理信息的逻辑能力都是有限的
  • 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容。
  • 但是,我们人有一种天生的能力,就是将问题进行拆解。
  • 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解。

在这里插入图片描述

组件化也是类似的思想:

  • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
  • 但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

在这里插入图片描述

1.2Vue组件化思想

组件化是Vue.js中的重要思想

  • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
  • 任何的应用都会被抽象成一颗组件树。

在这里插入图片描述

组件化思想的应用:

  • 有了组件化的思想,我们在之后的开发中就要充分的利用它。
  • 尽可能的将页面拆分成一个个小的、可复用的组件。
  • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

所以,组件是Vue开发中,非常重要的一个篇章。

2.组件化基础

2.1注册组件

组件的使用分成三个步骤:

1.创建组件构造器
2. 注册组件
3. 使用组件

在这里插入图片描述

让我们看看通过代码如何注册组件:
ES6补充:模板字符串的使用(反引号 tab键上面的 ~ 键) https://www.jianshu.com/p/8b263a6bde4d

<body>
    <div id="app">
        <!-- 3.使用组件 -->
        <my-cpn></my-cpn>
    </div>
    <script>
        //1. 创建组件构造器
        const myComponent = Vue.extend({
            template: ` // 反引号
                    <div>
                        <h2>组件标题</h2>
                        <p>组件中的一个段落内容</p>
                    </div>                    
                    `
        });

        //2.  注册组件,并且定义组件标签名称
        Vue.component('my-cpn', myComponent)
   //  Vue.component('注册组件标签名称',组件构造器的名称)

        let app = new Vue({
            el: "#app"
        })
    </script>
</body>
  • 实际效果
    在这里插入图片描述

查看运行结果:

  • 和直接使用一个div看起来并没有什么区别
  • 但是我们可以设想,如果很多地方都要显示这样的信息,我们是不是就可以直接使用<my-cpn></my-cpn>来完成呢?

2.1.1注册的基本步骤解析

上面的步骤都代表什么含义呢?

1.Vue.extend():

  • 调用Vue.extend() 创建的是一个组件构造器
  • 通常在创建组件构造器时,传入template代表我们自定义组件的模板
  • 该模板就是在使用到组件的地方,要显示的HTML代码
  • 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

2.Vue.component():

  • 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
  • 所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

3.组件必须挂载在某个Vue实例下,否则它不会生效。(看下图)

  • 我们来看下面 我使用了三次<my-cpn></my-cpn>
  • 而第三次没有生效:
    在这里插入图片描述

2.1.2 全局和局部组件

当我们通过调用Vue.component()注册组件时,组件的注册是全局的

这意味着该组件可以在任意Vue实例下使用

如果我们注册的组件是挂载在某个实例中,那么就是一个局部组件

全局组件

语法:

Vue.component(组件名称,{
	data:组件数据,
	template:组件模板内容
})
  • 实际代码
<body>
    <div id="app1">
        <my-cpn>1</my-cpn>
    </div>
    <div id="app2">
        <my-cpn>2</my-cpn>
    </div>
    <script>
        // 1.创建组件构造器
        const myComponent = Vue.extend({
            template: `
        <div>
            <h2>组件标题</h2>
            <p>组件正文内容,我太难了!!!</p>
        </div>
        `
        });
        // 2. 注册组件
        Vue.component('my-cpn', myComponent);

        let app1 = new Vue({
            el: '#app1'
        })
        let app2 = new Vue({
            el: '#app2'
        })
    </script>
</body>

局部组件

<body>
    <div id="app1">
        <my-cpn>展示了</my-cpn>
    </div>
    <div id="app2">
        <!-- 3.这个没有被渲染出来 -->
        <my-cpn>没展示</my-cpn>
    </div>
    <script>
        // 1.创建组件构造器
        const myComponent = Vue.extend({
            template: `
        <div>
            <h2>组件标题</h2>
            <p>组件正文内容,我真的太难了!!!!</p>
        </div>
        `
        })

        let app1 = new Vue({
            el: '#app1',
            // 2.把组件构造器放在 实例中
            components: { //这里要加 s
                'my-cpn': myComponent
            }
        })
        let app2 = new Vue({
            el: '#app2'
        })
    </script>
</body>

2.1.3 父组件和子组件

在之前我们看到了组件树

  • 组件和组件之间存在层级关系
  • 而其中一种非常重要的关系就是父子组件的关系

我们通过代码如何组成的这种层级关系:

<body>
    <div id="app">
        <paren-cpn></paren-cpn>
    </div>
    <script>
        // 1.创建一个子组件构造器
        const childComponent = Vue.extend({
                template: `
            <div>
                <p>我是子组件的内容,我是child</p>
            </div>
        `
            })
            // 2.创建一个父组件构造器
        const parentComponent = Vue.extend({
            template: `
            <div>
                我是父组件的内容,我是paren
                <h2>我是父组件中的一个标题</h2>
                <child-cpn></child-cpn>         
            </div>
        `
                // 3.注册子组件
                ,
            components: {
                'child-cpn': childComponent
            }
        })

        let app = new Vue({
            el: '#app',
            // 4.注册父组件
            components: {
                'paren-cpn': parentComponent
            }
        })
    </script>
</body>

在这里插入图片描述

父子组件错误用法:以子标签的形式在Vue实例中使用。

-因为当子组件注册到父组件的compontnts:{} 时,Vue会 编译好父组件的模块。

  • 该模板的内容已经决定好了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)。
  • <child-cpn></chlid-cpn>只能在父组件中被识别
  • 类似这种用法, <child-cpn></chlid-cpn> 是会被浏览器忽略的。

2.1.4 注册组件语法糖

在上面注册组件的方式,可能有些繁琐。

  • Vue 为了简化这个过程 ,提供了注册的语法糖
  • 主要是省去了调用Vue.extend()的步骤 ,而是可以直接使用一个对象来代替

语法糖注册全局组件和局部组件:

全局

<body>
    <div id="app">
        <my-cpn></my-cpn>
    </div>
    <script>
        // 全局注册
        Vue.component('my-cpn', { //组件构造器 写在 注册组件里 并用{} 来表示
            template: `
                <div>
                    <h2>组件标签</h2>
                    <p>组件正文的内容,全局语法糖</p>
                </div>
            `
        })
        new Vue({
            el: "#app",
        })
    </script>
</body>

在这里插入图片描述
局部

<body>
    <div id="app">
        <one-cpn></one-cpn>
        <two-cpn>
            </-cpn>
    </div>
    <script>
        new Vue({
            el: "#app",
            components: {
                'one-cpn': {
                    template: `
                    <div>这是one-cpn组件</div>
                    `
                },
                'two-cpn': {
                    template: `
                    <h2>这是two-cpn组件</h2>
                    `
                },
            }
        })
    </script>
</body>

在这里插入图片描述

2.1.5 模板的分类写法

刚才,我们通过语法糖简化了组件的注册过程,另外还有一个地方的写法比较麻烦,就是template模板语法。

如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰

Vue提供了两种方案来定义HTML模块内容:

注意创建自定义组件一定要写在vue实例上面

使用 script 标签

<body>
    <div id="app">
        <my_cpn></my_cpn>
    </div>

    <script type="text/x-template" id="mycpn">
        <!-- 创建的组件要写在vue实例上面 -->
        <div>
            <h2>组件名称</h2>
            <p>使用了script标签</p>
        </div>

    </script>
    <script>
        let app = new Vue({
            el: '#app',
            components: {
                'my_cpn': {
                    template: '#mycpn'
                }
            }
        })
    </script>

</body>

使用 template 标签

<body>
    <div id="app">
        <my_cpn></my_cpn>
    </div>

    <template id="mycpn">
        <!-- 创建组件要写在vue实例上面 -->
        <div>
            <h2>组件名称</h2>
            <p>使用了template标签</p>
        </div>

    </template>
    <script>
        let app = new Vue({
            el: '#app',
            components: {
                'my_cpn': {
                    template: '#mycpn'
                }
            }
        })
    </script>

</body>

2.1.6 组件的中数据的存放问题

组件可以访问Vue实例的数据吗?

组件是一个单独功能模块的封装:

  • 这个模块有属于自己的HTML模板,也应该有属于自己的数据data

组件的数据是保存在哪里呢?顶层的Vue实例中吗?

我们先来测试一下组件中能不能直接访问Vue实例中的data
在这里插入图片描述

我们发现不能访问Vue实例的数据(父组件),而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变得非常臃肿

结论:Vue组件应该有自己保存数据的地方

组件数据存放

那么组件自己的的数据存放在哪里呢?

组件对象也有一个data属性(也可以有`methods`等属性,下面会提到)
  • 只是这个data属性必须一个函数
  • 而且这个函数返回一个对象,对象内部保存着数据
    >

为什么date 是一个函数?

首先,如果不是一个函数,Vue直接就会报错

其次,原因是:在于Vue让每个组件对象都返回新一个的对象,因为如果是同一个对象的,组件存在多次使用后会相互影响

所以 data必须是一个函数,不然当多个组件使用同一个对象的时候,一个组件里对象的数据发生了变化,别的组件也会发生变化。

在这里插入图片描述

2.2 父子组件的通讯/数据传递

> 在上面,我们提到了子组件不能引用父组件或者Vue 实例的数据

但是,在开发中 ,往往一些数据确实需要从上层传递到下层:

  • 比如在一个页面中,我们从服务器请求到了很多的数据。
  • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件) 将数据传递给小组件(子组件)

那么,如何进行父子组件之间的通信呢?Vue官方提到

  1. 通过props 向组件传递数据
  2. 通过事件向父组件发送消息

在这里插入图片描述

  • 在下面的代码中,我们将Vue实例当做父组件,并且其中包含子组件来简化代码。
  • 真实的开发中,Vue实例和子组件的通信父组件和子组件的通信 过程是一样的。

2.2.1父级向子级传递 props

props 基本用法

在组件中,使用选项prors来声明需要从父级接收到的数据

换个角度说就是 子组件接收父组件的数据,子组件使用 props 获取了 父组件的数据

props的值有两种方式:

  • 方式一:字符串数组数组中的字符串就是传递时的名称
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

我们先来看一个最简单的props传递:

在这里插入图片描述

props的大小写

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

<script>
	Vue.component('blog-post', {
	  // 在 JavaScript 中是 camelCase 的
	  props: ['postTitle'],
	  template: '<h3>{{ postTitle }}</h3>'
	})
</script>

props 数据验证

在上面,我们的props选项是使用一个数组

我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。

验证都支持哪些数据类型呢?

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
props:{
	type:数据类型,
	default:'默认值'
}
Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

当我们有自定义构造函数时,验证也支持自定义的类型
在这里插入图片描述

2.2.2 子级向父级传递-自定义事件

props 用于 父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。

这个时候,我们需要用到自定义事件来完成

  • 之前我们学习的 v-on 不仅仅可以用于监听DOM事件,也可以用于组件之间的自定义事件。

自定义事件的流程:

  • 在子组件中,通过$emit()来触发。
  • 在父组件中,通过v-on@ 来监听子组件事件。
<body>
    <div id="app">
        <child-cpn @increment="changeTotal" @decrement="changeTotal"></child-cpn>
        <h2>{{total}}</h2>
    </div>
    <!-- 1.创建子组件模板 -->
    <template id="childCpn">
        <div>   
            <button @click="increment">+1</button>
            <button @click="decrement">-1</button>
        </div>
    </template>

    <script>
        let app = new Vue({
            el: "#app",
            data: {
                total: 0
            },
            methods: {
                changeTotal(counter) {
                    this.total = counter
                }
            },
            components: {
                // 注册子组件
                'child-cpn': {
                    template: '#childCpn',
                    data() {
                        return {
                            counter: 0
                        }
                    },
                    methods: { //在渲染期间引用的方法
                        increment() {
                            this.counter++;
                            this.$emit('increment', this.counter)
                        },
                        decrement() {
                            this.counter--;
                            this.$emit('decrement', this.counter)
                        }
                    }
                }
            }
        })
    </script>
</body>

在这里插入图片描述

2.2.3 父子组件的访问

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件

父组件访问子组件 $children 数组类型

this.$children是一个数据类型,它包含了所有子组件对象

所以,我们可以通过一个遍历,取出所有子组件的message状态

<body>
    <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
        <button @click="btn">点击循环</button>
    </div>
    <!-- 子组件模板 -->
    <template id="chilid-cpn"> 
        <h2>我是子组件</h2>

    </template>

    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            methods: {
                btn() {
                    console.log(this.$children);
                    this.$children[0].cliclbtn(); //父组件访问子组件
                    for (let i of this.$children) {
                        console.log(i.name);
                        i.cliclbtn();
                    }

                }
            },
            components: { //子组件
                cpn: {
                    template: '#chilid-cpn',
                    data() {
                        return {
                            name: "子组件name"
                        }
                    },
                    methods: {
                        cliclbtn() {
                            console.log(' cliclbtn - 子组件方法被触发');
                        }
                    }
                },
            }
        })
    </script>
</body>

在这里插入图片描述

父组件访问子组件 $refs 对象类型

$children的 缺陷

  • 通过$children访问 子组件的时候,是一个数据类型,访问其中的子组件必须通过索引值。
  • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
  • 有时候我们想明确获取其中一个特定的组件,这时候就可以使用$refs

$refs的使用:

  • $refsref指令通常是一起使用的$refs是一个对象类型
  • 首先,我们通过ref给某一个子元素绑定一个特定的ID
  • 其次,通过this.$refs.ID就可以访问到该组件了

在这里插入图片描述

<body>
    <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn ref="three"></cpn>
        <cpn></cpn>
        <button @click="btn">点击循环</button>
    </div>
    <!-- 子组件模板 -->
    <template id="chilid-cpn"> 
        <h2>我是子组件</h2>

    </template>

    <script>
        const app = new Vue({
            el: '#app',
            data: {
                message: '你好啊'
            },
            methods: {
                btn() {
                    console.log(this.$refs.three);

                }
            },
            components: { //子组件
                cpn: {
                    template: '#chilid-cpn',
                    data() {
                        return {
                            name: "子组件name"
                        }
                    },
                    methods: {
                        cliclbtn() {
                            console.log(' cliclbtn - 子组件方法被触发');
                        }
                    }
                },
            }
        })
    </script>
</body>

子组件访问父组件 $parent

如果我们想在子组件中直接访问父组件,可以通过$parent

注意事项:

  • 尽管在Vue中开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
  • 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
  • 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

在这里插入图片描述

子组件访问根组件 $root

通过 $root 属性进行访问
对于 demo 或非常小型的有少量组件的应用来说这是很方便的。
在绝大多数情况下,推荐使用 Vuex 来管理应用的状态。

<div id="temp">
	<my-con></my-con>
</div>
<script>
Vue.component('my-son',{
	template:'<div>根组件:{{this.$root.msg}}</div>'
})
new Vue({
  	el:"#temp",
    data:{
		msg:'根组件文本'
	},
    methods:{}
})
</script>

2.2.4 非父子组件通信

刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?

  • 非父子组件关系包括多个层级的组件,也包括兄弟组件的关系。

在Vue1.x的时候,可以通过 d i s p a t c h 和 dispatch和 dispatchbroadcast完成

  • $dispatch用于向上级派发事件
  • $broadcast用于向下级广播事件
  • 但是在Vue2.x都被取消了

在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。

  • 但是这种方案和直接使用Vuex的状态管理方案还是逊色很多。
  • 并且Vuex提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习Vuex的状态管理。

3. 组件化高级

3.1插槽slot

3.1.1 编译作用域

在真正学习插槽之前,我们需要先理解一个概念: 编译作用域

官方对于编译的作用域 比较简单,我们通过一个例子来理解这个概念:

<body>
    <div id="app">
        <my-cpn v-show="isShow"></my-cpn>
    </div>
    <!-- 组件模板 -->
    <template id="myCpn">
        <h2>我显示了吗 </h2>
    </template>


    <script>
        Vue.component('myCpn', { //注册组件模板
            template: "#myCpn",
            data() {
                return {
                    isShow: false //子组件的属性
                }
            }
        })

        let app = new Vue({
            el: "#app",
            data: {
                isShow: true //父组件的属性/Vue实例属性
            }
        })
    </script>
</body>

我们来考虑一下下面的代码是否可以渲染出来:

  • <my-cpn v-show = "isShow"></mt-cpn> 中,我们使用了isShow 属性。
  • isShow属性包含组件中,也包含在Vue实例中。

答案: 最终可以渲染出来,也就是使用的是Vue实例的属性

为什么呢?

  • 官方给出了一条准则:**父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译 **

3.1.2 为什么使用slot

slot翻译为插槽,组件的插槽:

  • 组件的插槽也是为了让我们封装的组件更加具有扩展性。

例子:移动网站中的导航栏

-移动开发中,每个页面中都有导航栏。

  • 导航栏我们必然会封装成一个插件,比如nav-bar
  • 一旦有了这个组件,我们就可以在多个页面中复用了。

在这里插入图片描述

3.1.3 slot的基本使用

如何使用slot

  • 在子组件中,使用特殊的元素<slot>就可以为子组件开启一个插槽。
  • 该插槽插入什么内容取决于父组件如何使用

我们通过一个简单的例子,来给子组件定义一个插槽:

  • <slot>中的内容表示,如果没有在该组件中插入任何其他值,就默认显示该内容
<body>
    <div id="app">
        <my-cpn></my-cpn>
        <my-cpn>
            <h2>替换插槽内容1</h2>
            <p>替换插槽内容2</p>
        </my-cpn>
    </div>
    <template id="myCpn">
    <div>
        <p>组件内容:----------------------------------</p>
        <slot>我是一个插槽中的默认内容</slot>
    </div>
</template>


    <script>
        Vue.component('myCpn', {
            template: '#myCpn'
        })

        let app = new Vue({
            el: '#app'
        })
    </script>
</body>

在这里插入图片描述

3.1.4 slot的具名插槽

当子组件的功能复杂是,子组件的插槽可能并非是一个。

  • 比如我封装一个导航栏的子组件,可能就需要三个插槽,分别代表左,中,右
  • 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
    这时候,我们就需要 给插槽起一个名字

如何使用具名插槽呢?

非常简单,只要给slot元素一个name属性即可
<slot name="left"></slot>

  • 案例

在这里插入图片描述

3.1.5 slot作用域插槽

作用域插槽是solt一个比较难理解的点,而且官方文档说的有点不清楚。

这里,我们一句话对其一个总结

  • 父组件替换插槽的标签,但是内容由子组件来提供

在这里插入图片描述
我们来看看子组件的定义:
在这里插入图片描述

在父组件使用我们的子组件时,从子组件中拿到数据:

我们通过<template slot-scope = "slotProps"> 获取到slotProps属性

  • 再通过slotProps.data就可以获取到刚才我们传入的data了

在这里插入图片描述

4. ES6模块化开发

4.1.1 javascript 原始功能

在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。

那个时候的代码是怎么写的呢?直接将代码写在<script>标签中即可
在这里插入图片描述

随着ajax异步请求的出现,慢慢形成了前后端的分离

  • 客户端需要完成的事情越来越多,代码量也是与日俱增。
  • 为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。
  • 但是这种维护方式,依然不能避免一些灾难性的问题

比如全局变量同名问题:看下面的例子
在这里插入图片描述

另外,这种代码的编写方式对js文件的依赖顺序几乎是强制性

  • 但是当js文件过多,比如有几十个的时候,弄清楚它们的顺序是一件比较同时的事情。
  • 而且即使你弄清楚顺序了,也不能避免上面出现的这种尴尬问题的发生。
    

4.1.2 匿名函数的解决方案

我们可以使用匿名函数来解决方面的重名问题

在aaa.js文件中,我们使用匿名函数

(function(){
	var flag = true;
}())

但是如果我们希望在main.js文件中,用到lfag,应该如何处理?

  • 显然,另外一个文件中不容易使用,因为flag是一个局部变量。

4.1.3 使用模块作为出口

我们可以使用将需要暴露到外面的变量,使用一个模块作为出口,什么意思呢?

  • 来看下对应的代码:在这里插入图片描述

我们做了什么事情呢?

  • 非常简单,在匿名函数内部,定义一个对象。
  • 对象添加各种需要暴露到外面的属性和方法(不需要暴露的直接定义即可)。
  • 最后将这个对象返回,并且在外面使用了一个MoudleA接受。

接下来,我们在man.js中怎么使用呢?

  • 我们只需要使用属于自己模块的属性和方法即可

这就是模块最基础的封装,事实上模块的封装还有很多高级的话题

  • 但是我们这里就是要认识一下为什么需要模块,以及模块的原始雏形。
  • 幸运的是,前端模块化开发已经有了很多规范,以及对应的实现方案。

常见的模块化规范:

  • CommonJS、AMD、CMD,也有ES6的Modules

4.2 CommonJS(了解)

  • 模块化有两个核心: 导入和导出

4.2.1 CommonJS 的导出

module.exports = {
		flag = true,
		test(a,b){
			return a + b
		},
		demo(a,b){
		return a * b
		}
}

4.2.2 CommonJS 的导入

let{ test,demo,flag} = reuquire('moduleA');
//等同于
let _mA = reuquire('moduleA');
let test = _mA.test;
let demo = _mA.demo;
let flag = _mA.flag;

4.3 export 基本使用–导出

export指令用于导出变量,比如下面的代码:

//info.js
export len name = 'gkz'
export let age = 20
export let height = 1.78

上面的代码还有另一种写法:

let name  = 'gkz'
let age = 20
let height = 1.78

export {name,age,height}

export不仅仅只输出变量,也可以输出函数或者输出类

export function test(content){
	console.log(content);
	}
export class Person{
	constructor(name,age){
	this.name = name;
	this.age = age;
	}
	run(){
	console.log(this.name+'在奔跑')
	}
}

上面的代码也可以写成这种形式:

function test(content){
	console.log(content);
	}
class Person{
	constructor(name,age){
	this.name = name;
	this.age = age;
	}
	run(){
	console.log(this.name+'在奔跑')
	}
}

export {test,Person}

在某些情况下,一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名

这时候可以使用export default

//info.js
export default function (){
	consle.log('default function')
	}

我们来到main.js 中,这样使用就可以了

这里myFunc 是我们自己命名的,你也可以根据需要命名它对应的名字

import myFunc from './info.js'

myFunc()

另外,需要注意:

export default 在同一个模块中,不允许同时存在多个。

4.4 import 基本使用–引入

在上面 我们使用export指令导出了模块对外提供的接口,下面我们就可以通过import命令来加载对应的这个模块了

首先,我们需要在HTML代码中引入两个js文件,并且类型需要设置为module

<script src="info.js" type="module"></script>
<script src="main.js" type="module"></script>

import 指令用于导入模块中的内容,比如main.js

import {name,age,height} for "./info.js"
console,log(name,age,height);

如果我们希望某个模块中所有的信息都导入,一个个导入显然有些麻烦:

通过*可以导入模块中所有的export变量
但是通常情况下我们需要给*起一个别名,方便后续的使用

import * as info from './info.js'
consloe.log(info.name,info.age,info.height,info.friends); 
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页