前言

ES6 语法中为 javascript 语言新增了模块化语法,天然支持模块化功能。在支持 ES6 语法的浏览器中,完全可以直接用 ES6 模块化去写。关于 ES6 模块语法的浏览器支持详见 JavaScript 模块 - JavaScript | MDN

由于 ES6 语法并不是所有版本的浏览器都支持,在一些不支持 ES6 语法的浏览器中,如何使用模块化呢?这里介绍3种基于不同的模块化规范的工具的使用。

使用 webpack / rollup 这样的打包工具打包成的 iife 格式的文件,也可以在浏览器里面直接运行。但是 iife 的方式其实失去了模块化的概念,将模块等价于了一个个函数,都封装在一个大的文件里面,函数间的依赖和执行顺序由打包工具在静态编译的时候进行了依赖分析。但是模块化的工程,应该是一个文件对应一个模块。加载不同的模块需要加载单独的文件。用打包工具打包成 iife 的格式运行,让编写的代码和运行的代码结构差异太大,可能编写了10个文件,打包后成了1个文件。但是模块化的工程里面,编写的代码和运行的代码结构是保持一致的,编写的时候是10个,运行的时候也是10个。

main.js 里分别加载 a.js 和 myquery.js 。在 a.js 里加载 b.js。
截屏2022-12-13 下午5.48.57.png

基于 es6 模块化的示例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>es6-modules</title>
  8. </head>
  9. <body>
  10. <script type="importmap">
  11. {
  12. "imports": {
  13. "myquery": "/deps/myquery.js"
  14. }
  15. }
  16. </script>
  17. <script type="module" src="./src/main.js"></script>
  18. </body>
  19. </html>
  1. import a from './a.js'
  2. import myquery from 'myquery'
  3. console.log('I am main')
  4. a()
  5. console.log(myquery)
  1. import b from "./b.js"
  2. export default function() {
  3. console.log('我是 a.js')
  4. b.sayHi()
  5. }
  1. export default {
  2. sayHi() {
  3. console.log('hi, i am b')
  4. }
  5. }
  1. export default {
  2. name() {
  3. console.log('my name is myqueryjs')
  4. }
  5. }

使用 http-server 在支持 es6 语法的浏览器运行的结果如下:
截屏2022-12-13 下午5.52.44.png
网络请求如下:
截屏2022-12-13 下午5.52.59.png
文档结构如下:
截屏2022-12-13 下午5.56.56.png

PS: 因为找不到使用 es6 语法直接发布的包,便在工程里建立了 deps/myquery.js 作为第三方包依赖,通过 importmap 的配置,可以直接import。

基于 AMD 规范的 requirejs

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Amd test</title>
  8. </head>
  9. <body>
  10. <script data-main="src/main.js" src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.min.js"></script>
  11. </body>
  12. </html>
  1. require.config({
  2. baseUrl: "/",
  3. paths: {
  4. "myquery": "deps/myquery"
  5. }
  6. })
  7. require(['src/a.js', 'myquery'], function(a, myquery) {
  8. console.log('I am main')
  9. a()
  10. console.log(myquery)
  11. })
  1. define(['./b.js'], function(b) {
  2. return function() {
  3. console.log('我是 a.js')
  4. b.sayHi()
  5. }
  6. })
  1. define(function() {
  2. return {
  3. sayHi: function() {
  4. console.log('hi, i am b')
  5. }
  6. }
  7. })
  1. define({
  2. name() {
  3. console.log('my name is myqueryjs')
  4. }
  5. })

使用 http-server 运行如下:
截屏2022-12-13 下午6.27.32.png
网络请求如下:
截屏2022-12-13 下午6.28.14.png
文档结构如下:
截屏2022-12-13 下午6.28.37.png

基于 System 规范的 systemjs

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>system test</title>
  8. </head>
  9. <body>
  10. <script src="https://cdn.bootcdn.net/ajax/libs/systemjs/6.13.0/system.min.js"></script>
  11. <script type="systemjs-importmap">
  12. {
  13. "imports": {
  14. "myquery": "/deps/myquery.js"
  15. }
  16. }
  17. </script>
  18. <script type="systemjs-module" src="/src/main.js"></script>
  19. </body>
  20. </html>
  1. System.register(['/src/a.js', 'myquery'], ((exports) => {
  2. let a, myquery;
  3. return {
  4. setters: [m => a = m, m => myquery=m],
  5. execute() {
  6. console.log('I am main')
  7. a.default()
  8. console.log(myquery)
  9. }
  10. }
  11. }))
  1. System.register(['/src/b.js'], (exports) => {
  2. let b;
  3. return {
  4. setters: [(module) => {
  5. b = module
  6. }],
  7. execute() {
  8. exports({
  9. default: () => {
  10. console.log('我是 a.js')
  11. b.sayHi()
  12. }
  13. })
  14. }
  15. }
  16. })
  1. System.register([], (exports) => {
  2. return {
  3. setters: [],
  4. execute() {
  5. exports({
  6. sayHi() {
  7. console.log('hi, i am b')
  8. }
  9. })
  10. }
  11. }
  12. })
  1. System.register([], (exports) => {
  2. return {
  3. setters: [],
  4. execute() {
  5. exports({
  6. name() {
  7. console.log('my name is myqueryjs')
  8. }
  9. })
  10. }
  11. }
  12. })

使用 http-server 运行如下:
截屏2022-12-14 上午11.38.12.png
网络请求如下:
截屏2022-12-14 上午11.38.26.png
文档结构如下:
截屏2022-12-14 上午11.40.01.png

基于 CMD 规范的 seajs

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>seajs demo</title>
  8. </head>
  9. <body>
  10. <script src="https://cdn.bootcdn.net/ajax/libs/seajs/3.0.3/sea.js"></script>
  11. <script>
  12. seajs.use('./src/main.js')
  13. seajs.config({
  14. alias:{
  15. 'myquery':'/deps/myquery.js'
  16. }
  17. });
  18. </script>
  19. </body>
  20. </html>
  1. define(function(require, exports, module){
  2. const a = require('./a.js'),
  3. myquery = require('myquery');
  4. console.log('I am main')
  5. a()
  6. console.log(myquery)
  7. })
  1. define(function(require, exports, module){
  2. const b = require('./b.js')
  3. module.exports = function() {
  4. console.log('我是 a.js')
  5. b.sayHi()
  6. }
  7. })
  1. define(function(require, exports, module){
  2. exports.sayHi = function() {
  3. console.log('hi, i am b')
  4. }
  5. })
  1. define(function(require, exports, module){
  2. exports.name = function() {
  3. console.log('my name is myqueryjs')
  4. }
  5. })

使用 http-server 运行如下:
截屏2022-12-16 下午1.43.57.png
网络请求如下:
截屏2022-12-16 下午1.44.05.png
文档结构如下:
截屏2022-12-16 下午1.45.24.png

模块打包器

目前 es6 模块化语法只在最新浏览器里面才能用,因此如果是浏览器版本满足的话,其实就可以直接使用 es6 模块语法。但是如果想兼容旧版本浏览器的话,就需要进行转换。

但是为了使用 es6 语法的模块化,便于将来浏览器全部支持或升级,一般会在开发状态直接使用 es6 语法和模块化语法(上述各种规范的语法也实在是难写)。利用模块打包器,将 es6 模块化语法进行转化,转化成上述的任意一种规范语法,然后使用对应的模块加载器进行加载运行即可。