模块化概念

  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起。
  • 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信。

为什么使用模块化

  • 在实际的开发过程中,经常会遇到变量、函数、对象等名字的冲突,这样就容易造成冲突,还会造成全局变量被污染,特别是合作开发时,大家的命名规范可能类似,很有可能出现重名,所以冲突时还要排查。
  • 程序或者网站复杂时需要写很多代码,而且还要引入很多类库,这样稍微不注意就容易造成文件依赖混乱。

    • 在没有模块化之前,一个文件中可能需要引入很多的js文件和包,比如jQuery、Bootstrap、swiper等等,所以上来需要先使用link导入很多包。

补充:引用js文件时的变量覆盖

现在有两个js文件aaa.js和bbb.js文件
aaa.js:

var name="张三"

bbb.js:

var name="李四"

新建html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/aaa.js"></script>
    <script src="js/bbb.js"></script>
    <script>
        alert(name);
    </script>
</head>
<body>
    
</body>
</html>
  • 第8、9两行分别引用aaa.js和bbb.js
  • 第11行打印name变量

问题:弹窗的内容是什么?

测试页面:
image.png

可以发现是李四被打印出来了。
原因是因为引用js文件时,当有同一个变量名时,最后引用的会覆盖之前的,这就是变量覆盖,也造成了变量污染,因此需要模块化。

模块化的发展历程

  • 在之前的javascript中是没有模块化概念的。如果要进行模块化操作,需要引入第三方的类库。
  • AMD/CMD(用于浏览器环境)、CommonJs(用于Node环境)
  • 随着技术的发展,前后端分离,前端的业务变的越来越复杂化。直至ES6带来了模块化,才让javascript第一次支持了module(用于浏览器环境)

    ES6的模块化的使用

  • ES6的模块化分为导出(export)与导入(import)两个模块。
  • ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。

    • 如果希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出)。
    • 如果在其他文件中想要获取暴露的内容,以import的形式将这个变量进行引入。

例如vue-cli创建的初始项目中,在views目录下的页面文件Home.vue:

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'Home',
  components: {
    HelloWorld
  }
}
</script>
  • 第10行使用imprt导入了components组件库的HelloWorld.vue组件

打开components组件库中对应的HelloWorld.vue文件:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
  • 35-40行使用export导出自己,否则别人无法引用。

所以总结以上思路,当A模块调用B模块时,B模块首先要使用export导出,这样A模块才能使用import导入。

导入与导出(import/export)

首先在public文件夹中新建js目录,然后在js目录中新建aaa.js文件:

aaa.js:

var name="冒险岛"
var job="战士"
  • 创建name和job变量

在public文件夹中新建demo.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        var job="魔法师"
    </script>
    <script src="js/aaa.js"></script>
    <script>
        console.log(name,job)
    </script>
</head>
<body>
    
</body>
</html>
  • 第9行在html中创建job变量为魔法师
  • 第11行使用src引入aaa.js文件
  • 第13行打印name和job变量

测试页面:
image.png
可以发现第9行的job变量魔法师被覆盖了。

如果现在需要实现打印job为魔法师,同时打印name为冒险岛,怎么解决变量覆盖问题?

答案就是使用模块化来解决变量覆盖问题。

导出

  • 任何需要被其他组件或者文件调用的内容都需要使用export导出
  • 可以导出变量,函数,对象等等多种类型
  • 在一个js文件中可以将通过计算后的结果使用export导出,供其他文件进行使用。

使用export将aaa.js中的name变量导出:

export var name="冒险岛"
export var job="战士"
export var fun=function(){
    return "maple story"
}

导入

html页面导入引用js文件

在demo.html中使用import导入aaa.js中的变量和方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
<script type="module">
    import {name, job, fun} from "./js/aaa.js"
    console.log(name);
    console.log(job);
    console.log(fun());  // 使用导入的方法需要添加括号
</script>
</head>
<body>
    123
</body>
</html>
  • 第9行使用import...from...({} 中添加需要导入的变量名和函数名)来导入文件中的变量

测试页面:

在aaa.js同级目录中新建bbb.js文件:
bbb.js:

var a=123;
var b=456;
var c=789;
export {c}

aaa.js:

import {c} from "./bbb.js"
export var name="冒险岛"
export var job="战士"
export var fun=function(){
    return "maple story"
}
export var computed = c+123;
  • 导入bbb.js中的c变量,然后新创建computed变量导出计算之后的值。

demo.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
<script type="module">
    import {name, job, fun, computed} from "./js/aaa.js"
    console.log(name);
    console.log(job);
    console.log(fun());  // 使用导入的方法需要添加括号
    console.log(computed); 
</script>
</head>
<body>
    123
</body>
</html>
  • 第13行打印computed变量

测试页面:
image.png
成功打印computed值,总结一下,aaa.js调用了bbb.js中的c变量,然后对c变量计算之后赋值给computed变量,然后export导出,再在demo.html中import导入并调用aaa.js中的computed变量。

注意

  • 没有使用export导出的变量或者方法,是不能被其他组件import导入的
  • 按需导入,只导入使用到的内容
  • 当导入时from后面的模块没有路径,如下图所示,代表是从mode_modules目录下去寻找模块。

    • image.png
    • 所以一般引用插件,直接在index.js中import插件名称即可自动去node_modules目录下寻找

image.png

最后修改:2024 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏