首页 > 其他分享 >JS基础 模块设计

JS基础 模块设计

时间:2023-03-24 13:01:11浏览次数:45  
标签:console 导出 js 导入 模块 设计 JS hd


阅读目录

  • 模块设计
  • 使用分析
  • 实现原理
  • 基础知识
  • 标签使用
  • 模块路径
  • 延迟解析
  • 模块默认运行在严格模式
  • 模块都有独立的顶级作用域
  • 预解析
  • 导入导出
  • 导出模块
  • 具名导入
  • 批量导入
  • 导入建议
  • 别名使用
  • 导入别名
  • 导出别名
  • 默认导出
  • 单一导出
  • 混合导出
  • 使用建议
  • 导出合并
  • 解决问题
  • 实际使用
  • 动态加载
  • 静态导入
  • 动态使用
  • 指令总结
  • 编译打包
  • 安装配置
  • 目录结构
  • 执行打包

模块设计

使用分析

项目变大时需要把不同的业务分割成多个文件,这就是模块的思想。模块是比对象与函数更大的单元,使用模块组织程序便于维护与扩展。

生产环境中一般使用打包工具如 webpack 构建,他提供更多的功能。但学习完本章节后会再学习打包工具会变得简单。

  • 模块就是一个独立的文件,里面是函数或者类库
  • 虽然JS没有命名空间的概念,使用模块可以解决全局变量冲突
  • 模块需要隐藏内部实现,只对外开发接口
  • 模块可以避免滥用全局变量,造成代码不可控
  • 模块可以被不同的应用使用,提高编码效率

实现原理

在过去JS不支持模块时我们使用 AMD/CMD(浏览器端使用)、CommonJS(Node.js使用)、UMD(两者都支持)等形式定义模块。

AMD代表性的是 require.js,CMD 代表是淘宝的 seaJS 框架。

下面通过定义一个类似 require.js 的 AMD 模块管理引擎,来体验模块的工作原理。

let module = (function() {
  //模块列表集合
  const moduleLists = {};
  function define(name, modules, action) {
    modules.map((m, i) => {
      modules[i] = moduleLists[m];
    });
    //执行并保存模块
    moduleLists[name] = action.apply(null, modules);
  }

  return { define };
})(); // hd module show

//声明模块不依赖其它模块
module.define("hd", [], function() {
  return {
    show() {
      console.log("hd module show");
    }
  };
});

//声明模块时依赖其它模块
module.define("xj", ["hd"], function(hd) {
  hd.show();
});

基础知识

标签使用

在浏览器中使用以下语法靠之脚本做为模块使用,这样就可以在里面使用模块的代码了。

在 html 文件中导入模块,需要定义属性type="module"

<script type="module"></script>

模块路径

在浏览器中引用模块必须添加路径如 ./,但在打包工具如 webpack 中则不需要,因为他们有自己的存放方式。

测试的 hd.js 的模块内容如下

export let hd = {
    name: "hs.js_file_name:wgchen"
};

下面没有指定路径将发生错误

<script type="module">
  import { hd } from "hd.js";
</script>

正确使用需要添加上路径

<script type="module">
  import { hd } from "./hd.js";
</script>

JS基础 模块设计_javascript


解决办法

在本地搭建一个server,通过url地址访问测试文件。

JS基础 模块设计_User_02

JS基础 模块设计_html_03

延迟解析

模块总是会在所有 html 解析后才执行,下面的模块代码可以看到后加载的 button 按钮元素。

  • 建议为用户提供加载动画提示,当模块运行时再去掉动画
<body>

  <script type="module">
    console.log(document.querySelector("button")); //Button
  </script>
  <script>
    console.log(document.querySelector("button")); //null
  </script>

  <button>wgchen_button</button>

</body>

JS基础 模块设计_前端_04

模块默认运行在严格模式

模块默认运行在严格模式,以下代码没有使用声明语句将报错

<script type="module">
  hd = "wgchen"; // hd is not defined
</script>

下面的 this 也会是 undefined

<script>
  console.log(this); //Window
</script>

<script type="module">
  console.log(this); //undefiend
</script>

模块都有独立的顶级作用域

模块都有独立的顶级作用域,下面的模块不能互相访问

<script type="module">
  let hd = "wgchen.blog";
</script>

<script type="module">
  alert(hd); // Error:hd is not defined
</script>

单独文件作用域也是独立的,下面的模块 1.2.js 不能访问模块 1.1.js 中的数据

<script type="module" src="1.1.js"></script>
<script type="module" src="1.2.js"></script>

文件内容如下
# 1.1.js
let hd = "wgchen";

# 1.2.js
console.log(hd)

JS基础 模块设计_html_05


JS基础 模块设计_html_06

预解析

模块在导入时只执行一次解析,之后的导入不会再执行模块代码,而使用第一次解析结果,并共享数据。

  • 可以在首次导入时完成一些初始化工作
  • 如果模块内有后台请求,也只执行一次即可

引入多入 hd.js 脚本时只执行一次

<script type="module" src="./hd.js"></script>
<script type="module" src="./hd.js"></script>

#hd.js内容如下
console.log("wgchen");

JS基础 模块设计_html_07

JS基础 模块设计_User_08

在开发服务器模式下这样也可以访问到

<script type="module" src="hd.js"></script>
<script type="module" src="hd.js"></script>

下面在导入多次 hd.js 时只解析一次

JS基础 模块设计_前端_09


JS基础 模块设计_webpack_10

导入导出

ES6使用基于文件的模块,即一个文件一个模块。

  • 使用 export 将开发的接口导出
  • 使用 import 导入模块接口
  • 使用 * 可以导入全部模块接口
  • 导出是以引用方式导出,无论是标量还是对象,即模块内部变量发生变化将影响已经导入的变量

导出模块

下面定义模块 modules/wgchen.js ,使用 export 导出模块接口,没有导出的变量都是模块私有的。

下面是对定义的 hd.js 模块,分别导出内容

export const site = "wgchen";

export const func = function() {
  return "is a module function";
};

export class User {
  show() {
    console.log("user.show");
  }
}

下面定义了 hd.js 模块,并使用指量导出

const site = "wgchen";

const func = function() {
  return "is a module function";
};

class User {
  show() {
    console.log("user.show");
  }
}
export { site, func, User };

具名导入

下面导入上面定义的 hd.js 模块,分别导入模块导出的内容

<script type="module">
  import { User, site, func } from "./hd.js";
  console.log(site);
  console.log(User);
</script>

JS基础 模块设计_前端_11


JS基础 模块设计_html_12


像下面这样在 {} 中导入是错误的,模块默认是在顶层静态导入,这是为了分析使用的模块方便打包

if (true) {
  import { site, func } from "./hd.js"; // Error
}

批量导入

如果要导入的内容比较多,可以使用 * 来批量导入。

<script type="module">
  import * as api from "./hd.js";
  console.log(api.site);
  console.log(api.User);
</script>

导入建议

因为以下几点,我们更建议使用明确导入方式

  • 使用 webpack 构建工具时,没有导入的功能会删除节省文件大小
  • 可以更清晰知道都使用了其他模块的哪些功能

别名使用

导入别名

可以为导入的模块重新命名,下面是为了测试定义的 hd.js 模块内容。

  • 有些导出的模块命名过长,起别名可以理简洁
  • 本模块与导入模块重名时,可以通过起别名防止错误
const site = "wgchen";

const func = function() {
  return "is a module function";
};

class User {
  show() {
    console.log("user.show");
  }
}

export { site, func, User };

模块导入使用 as 对接口重命名,本模块中已经存在 func 变量,需要对导入的模块重命名防止重名错误。

<script type="module">
  import { User as user, func as action, site as name } from "./hd.js";
  let func = "wgchen";
  console.log(name);
  console.log(user);
  console.log(action);
</script>

导出别名

模块可以对导出给外部的功能起别名,下面是 hd.js 模块对导出给外部的模块功能起了别名

const site = "wgchen";
const func = function() {
  console.log("is a module function");
};
class User {
  show() {
    console.log("user.show");
  }
}
export { site, func as action, User as user };

这时就要使用新的别名导入了

<script type="module">
  import { user, action } from "./hd.js";
  action();
</script>

默认导出

很多时候模块只是一个类,也就是说只需要导入一个内容,可以使用默认导入。

使用 default 定义默认导出的接口,导入时不需要使用 {}

  • 可以为默认导出自定义别名
  • 只能有一个默认导出
  • 默认导出可以没有命名

单一导出

下面是 hd.js 模块内容,默认只导出一个类。并且没有对类命名,这是可以的

export default class {
  static show() {
    console.log("User.method");
  }
}

从程序来讲如果将一个导出命名为 default 也算默认导出

class User {
  static show() {
    console.log("User.method");
  }
}
export { User as default };

导入时就不需要使用 {} 来导入了

<script type="module">
  import User from "./hd.js";
  User.show();
</script>

默认导出的功能可以使用任意变量接收

<script type="module">
  import hd from "./hd.js";
  hd.show();
</script>

混合导出

模块可以存在默认导出与命名导出。

使用 export default 导出默认接口,使用 export {} 导入普通接口

const site = "wgchen";
const func = function() {
  console.log("is a module function");
};
export default class {
  static show() {
    console.log("user.show");
  }
}
export { site, func };

也可以使用以下方式导出模块

const site = "wgchen";
const func = function() {
  console.log("is a module function");
};
class User {
  static show() {
    console.log("user.show");
  }
}
export { site, func, User as default };

导入默认接口时不需要使用 {} ,普通接口还用 {} 导入

<script type="module">
	//可以将 hd 替换为任何变量
  import hd from "./hd.js";
  import { site } from "./hd.js";
  console.log(site);
  hd.show();
</script>

可以使用一条语句导入默认接口与常规接口

import show, { name } from "/modules/wgchen.js";

也可以使用别名导入默认导出

import { site, default as hd } from "./hd.js";
console.log(site);
hd.show();

如果是批量导入时,使用 default 获得默认导出

<script type="module">
  import * as api from "./hd.js";
  console.log(api.site);
  api.default.show();
</script>

使用建议

对于默认导出和命名导出有以下建议

  • 不建议使用默认导出,会让开发者导入时随意命名
import hd from "./hd.js";
import xj from "./hd.js";
  • 如果使用默认导入最好以模块的文件名有关联,会使用代码更易阅读
import hd from "./hd.js";

导出合并

解决问题

可以将导入的模块重新导出使用,比如项目模块比较多如下所示,这时可以将所有模块合并到一个入口文件中。

这样只需要使用一个模块入口文件,而不用关注多个模块文件

|--hd.js
|--wgchen.js
...

实际使用

下面是 hd.js 模块内容

const site = "wgchen";
const func = function() {
  console.log("is a module function");
};
export { site, func };

下面是 wgchen.js 模块内容

export default class {
  static get() {
    console.log("wgchen.js.get");
  }
}

下面是 index.js 模块内容,使用 * 会将默认模块以 default 导出

export * as hd from "./hd.js";

// 默认模块需要单独导出
export { default as wgchen } from "./wgchen.js";

// 以下方式导出默认模块是错误的
// export houdunren from "./wgchen.js";

使用方法如下

<script type="module">
  import * as api from "./index.js";
  console.log(api);
  api.wgchen.get();
  console.log(api.hd.site);
</script>

动态加载

使用 import 必须在顶层静态导入模块,而使用 import() 函数可以动态导入模块,它返回一个 promise 对象。

静态导入

使用 import 顶层静态导入,像下面这样在 {} 中导入是错误的,这是为了分析使用的模块方便打包,所以系统禁止这种行为

if (true) {
  import { site, func } from "./hd.js"; // Error
}

动态使用

测试用的 hd.js 模块内容如下

const site = "wgchen";
const func = function() {
  console.log("is a module function");
};
export { site, func };

使用 import() 函数可以动态导入,实现按需加载

<script>
  if (true) {
    let hd = import("./hd.js").then(module => {
      console.log(module.site);
    });
  }
</script>

下面是在点击事件发生后按需要加载模块

<button>wgchen</button>
<script>
  document.querySelector("button").addEventListener("click", () => {
    let hd = import("./hd.js").then(module => {
      console.log(module.site);
    });
  });
</script>

JS基础 模块设计_javascript_13


JS基础 模块设计_webpack_14


因为是返回的对象可以使用解构语法

<button>wgchen</button>
<script>
  document.querySelector("button").addEventListener("click", () => {
    let hd = import("./hd.js").then(({ site, func }) => {
      console.log(site);
    });
  });
</script>

指令总结

表达式

说明

export function show(){}

导出函数

export const name='wgchen'

导出变量

export class User{}

导出类

export default show

默认导出

const name = 'wgchen'

export {name}

导出已经存在变量

export {name as hd_name}

别名导出

import defaultVar from 'wgchen.js'

导入默认导出

import {name,show} from 'a.j'

导入命名导出

Import {name as hdName,show} from 'wgchen.js'

别名导入

Import * as api from 'wgchen.js'

导入全部接口

编译打包

编译指将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

首先登录 https://nodejs.org/en/ 官网下载安装 Node.js,我们将使用其他的 npm 命令,npm 用来安装第三方类库。

在命令行输入node -v 显示版本信息表示安装成功。

安装配置

使用以下命令生成配置文件 package.json

npm init -y

修改 package.json 添加打包命令

...
"main": "index.js",
"scripts": {
	"dev": "webpack --mode development --watch"
},
...

安装 webpack 工具包,如果安装慢可以使用淘宝 cnpm (opens new window)命令

npm i webpack webpack-cli --save-dev

目录结构

index.html
--dist #压缩打包后的文件
--src
----index.js  #入口
----style.js //模块

index.html 内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script src="dist/main.js"></script>
  </body>
</html>

index.js 内容如下

import style from "./style";
new style().init();

style.js

export default class User {
  constructor() {}
  init() {
    document.body.style.backgroundColor = "green";
  }
}

执行打包

运行以下命令将生成打包文件到 dist 目录,因为在命令中添加了 --watch 参数,所以源文件编辑后自动生成打包文件。

npm run dev


标签:console,导出,js,导入,模块,设计,JS,hd
From: https://blog.51cto.com/u_13571520/6147117

相关文章

  • JS基础 原型与继承
    阅读目录原型基础原型对象使用数组原型对象的concat方法完成连接操作默认情况下创建的对象都有原型。以下x、y的原型都为元对象Object,即JS中的根对象创建一个极简对象(......
  • R3枚举进程模块的方法
    R3层枚举进程模块的方法有以下三种:1.ToolHelp库2.PsApi库3.遍历Peb中的Ldr链表1.ToolHelp库:点击查看代码HANDLEhSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAP......
  • 【设计模式】行为型之访问者模式
    前言最近在看Solidity编译器代码(C++实现),其中使用到了设计模式中的访问者模式,这里正好学习一下。GoF的设计模式书虽然讲的很详细,但是这里还是结合实际项目中的应用来说一......
  • requireJS 源码(二) data-main 的加载实现
    requireJS源码(二)data-main的加载实现(一)requireJs的整体结构:requireJS源码前192行,是一些变量的声明,工具函数的实现以及对三个全局变量(requirejs,require,def......
  • requireJS 源码(一) require() 为何可以全局使用
    requireJS源码(一)require()为何可以全局使用requireJS源码加注释总共不到2100行。我看的requireJs版本是2.19。 总体结构如下。......
  • requireJS 源码(三) data-main 的加载实现
    requireJS源码(三)data-main的加载实现(一)入口通过data-main去加载JS模块,是通过  req(cfg) 入口去进行处理的。为了跟踪,你可以在此加断点进行调试跟......
  • Vue.js 路由简介
    路由理解:一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router)进行管理。前端路由:key是路径,value是组件。......
  • js保存文件到本地
    使用原生方法保存文件到本地基本流程确定要保存的文本、保存格式及保存文件类型;根据保存格式生成url链接,设置文件的下载地址;创建一个a标签(即a标签指向的就是我们要......
  • 前端js RSA jsrsasign加密、解密、加签、验签
     jsrsasign(RSA-SignJavaScript库)是一个免费的开源加密库,支持RSA/RSAPSS/ECDSA/DSA签名/验证,ASN.1,PKCS#1/5/8私钥/公钥,X.509证书,纯JavaScript中的CRL,OCSP,CMSSigned......
  • 重学Java设计模式-结构型模式-组合模式
    重学Java设计模式-结构型模式-组合模式内容摘自:https://bugstack.cn/md/develop/design-pattern/2020-06-08-重学Java设计模式《实战组合模式》.html#重学-java-设计模......