前端工程化之模块规范(2):node环境运行ESM

68 阅读3分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第7天

前言

紧接上文,继续学习模块规范。上文学习了常见的一个规范,而作为一个前端开发起步的程序猿,重点还是放在ESM上了,接下来就学习如何在node中运行ESM。

node对ESM的支持

在以往的node版本中,是不支持ESM规范的。再后来的V8.9.0版本中,执行下面的这行命令,node可以象征性地支持ESM

node --experimental-modules xxx.js

在后面的V13.2.0版本中,node开始支持并默认启动了--experimental-modules。

--experimental-modules特性

  • 使用type指定模块方案
    • 在package.json中,type指定为Commonjs,则使用CJS
    • 在package.json中,type指定为module,则使用ESM-
  • 使用--input-type指定入口文件的模块方案,与type一样
    • 命令中加上--input-type=commonjs,则使用CJS
    • 命令中加上--input-type=module,则使用ESM
  • 支持新文件后缀.cjs
    • 文件后缀使用.cjs,则使用CJS
  • 使用--es-module-specifier-resolution指定文件名称引用方式
    • 命令中加上--es-module-specifier-resolution=explicit,则引用模块时必须使用文件后缀(默认)
    • 命令中加上--es-module-specifier-resolution=node,则引用模块时无需使用文件后缀
  • 使用main根据type指定模块方案加载文件
    • package.json中指定mian后会根据type指定模块方案加载文件

node判断是否是ESM的规则

在V13.2.0版本后,相当于说node默认支持了ESM规范,那怎么判断是否是ESM呢?

  • 文件名后缀是.mjs(使用import/export一定要使用这个后缀,要么就是下面这种方法)
  • 如果文件名后缀是js,那需要在package.json中配置type为module(其他使用CJS的话,后缀就需要改成.cjs)
  • 命令中加上--input-type=module
  • 命令中加上--eval cmd 如果没有改后缀,也没有指定规则,.js文件默认解析为.cjs文件,采用CJS解析

node部署

大于13.2.0版本原生部署

需要指定node版本及npm的版本,不能低于这个node版本,这是为了原生node环境中能支持ESM。建议不要随便修改后缀,通过在package.josn文件中修改type来指定使用ESM。

在高版本的node中,默认情况下,对import命令引入的文件后缀名具有强制性,引入import "file"应该要写成import "file.js",而CJS的自动后缀处理行为可以通过命令行参数开启:--es-module-specifier-resolution=node

node环境下使用ESM,一些node的特性不能使用(__dirname__filenamerequiremoduleexports),且不能灵活引用json文件。

补救措施:

  • __filename__dirname可用import.meta对象重建
  • requiremoduleexports可用importexport代替
  • json文件的引用可用Fs模块readFileSyncJSON.parse()代替
import { readFileSync } from "fs"; 
import { dirname } from "path"; 
import { fileURLToPath } from "url"; 
const __filename = fileURLToPath(import.meta.url); 
const __dirname = dirname(__filename); 
console.log(__filename, __dirname); 
const json = readFileSync("./info.json"); 
const info = JSON.parse(json);

低版本Node编译部署

使用babel编译ESM代码是低版本node支持ESM的稳妥方式(高版本中也能用)。

  • @babel/cli:提供支持@babel/core的命令运行环境
  • @babel/core:提供转译函数
  • @babel/node:提供支持ESM的命令运行环境
  • @babel/preset-env:提供预设语法转换集成环境

安装上面的四个babel包,然后修改配置

{ 
    "scripts": { 
        "start": "babel-node src/index.js" 
    }, 
    "babel": { 
        "presets": [ "@babel/preset-env" ] 
    }
}

如果要兼容再更低版本,则需要:

{
    "babel": { 
        "presets": [ 
            [
                "@babel/preset-env",
                { 
                    "targets": { 
                        "node": "8.0.0" 
                    }
                }
            ]
        ]
    }
}

nodemon

是一个自动检测项目文件发生变化就重启服务的npm模块,是node开发的必备工具。修改配置如下:

{ 
    "nodemonConfig": { 
        "env": { 
            "NODE_ENV": "dev" 
        }, 
        "execMap": { 
            "js": "node --harmony" 
        }, 
        "ext": "js json", 
        "ignore": [ "dist/" ], 
        "watch": [ "src/" ]
    } 
}

执行命令:start换成nodemon -x babel-node src/index.js

  •  基于Node原生部署方案改造Node项目,适合在高版本Node环境中使用
  •  基于Node编译部署方案改造Node项目,适合在低版本Node环境任何版本Node环境中使用
  •  基于监听脚本自动重启命令改造Node项目,与Node编译部署方案一样的使用条件,中使用

参考资料:

掘金小册:从0到1落地前端工程化