NodeJS系列(2)- 在 NPM 项目里使用 ECMAScript 6 (ES6) 规范

发布时间 2023-06-17 16:46:03作者: 垄山小站


NPM (Node Package Manager),NodeJS 包或模块管理工具,比较新的 NodeJS 版本一般内置 NPM 。NPM 有点类似于 Maven 在 Java 开发中的作用,NPM 项目也和 Maven 项目类似,包含了创建、编译、运行、打包、部署等功能。

ECMAScript 6 (ES6) 是最新的 JavaScript 语言的标准化规范,它的目标是使 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ES6 出现之前 Javascript 模块的加载方案,最主要的是 CommonJS 规范和 AMD 规范。CommonJS 规范应用于服务器,实现同步加载,比如 NodeJS。AMD 规范应用于浏览器,比如 requireJS (异步加载)。同时还有 CMD 规范,比如 seaJS (同步加载)。


1. 系统环境

    操作系统:Windows 10 (x64)
    NVM:1.1.11
    NodeJS: 14.21.3 LTS
    NPM:6.14.18
    工作目录:D:\workshop\nodejs

 

2. 创建 npm 项目

    进入工作目录 D:\workshop\nodejs,手动创建子目录 npmdemo,在命令行控制台进入该子目录,运行如下命令:

        D:\workshop\nodejs\npmdemo> npm init

            This utility will walk you through creating a package.json file.
            It only covers the most common items, and tries to guess sensible defaults.

            See `npm help init` for definitive documentation on these fields
            and exactly what they do.

            Use `npm install <pkg>` afterwards to install a package and
            save it as a dependency in the package.json file.

            Press ^C at any time to quit.
            package name: (npmdemo)
            version: (1.0.0)
            description:
            entry point: (index.js)
            test command:
            git repository:
            keywords:
            author:
            license: (ISC)
            About to write to D:\workshop\nodejs\npmdemo\package.json:

            {
                "name": "npmdemo",
                "version": "1.0.0",
                "description": "",
                "main": "index.js",
                "scripts": {
                    "test": "echo \"Error: no test specified\" && exit 1"
                },
                "author": "",
                "license": "ISC"
            }

            Is this OK? (yes)

        注:可以运行带 -y 参数的命令 npm init -y,即出现交互选择时都自动选 yes。init 命令运行完成后,npmdemo 目录先生成一个 package.json 文件。

    创建 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下:

        console.log("NPM demo");

    修改 D:\workshop\nodejs\npmdemo\package.json 文件 ,内容如下: 

        {
            "name": "npmdemo",
            "version": "1.0.0",
            "description": "",
            "main": "index.js",
            "scripts": {
                "test": "echo \"Error: no test specified\" && exit 1",
                "start":"node index.js"
            },
            "author": "",
            "license": "ISC"
        }

    运行

        D:\workshop\nodejs\npmdemo> npm run start

            > npmdemo@1.0.0 start D:\Workshop\dev-nodejs\npmdemo
            > node index.js

            NPM Demo         



3. ES6 模块

    ES6 的模块设计思想是静态化的,它在编译时确定模块的依赖关系以及输入和输出的变量,也就是说它在编译时就完成了模块加载,我们称为 “编译时加载” 或者静态加载,效率上比 CommonJS 要高。
    
    ES6 新增了 export 和 import 语法(或命令、关键字)来实现模块之间的功能调用(或数据复用),而且逐渐取代 CommonJS/AMD 规范的 exports 和 require 语法,现已成为浏览器和服务器通用的模块解决方案。

    示例,在 npmdemo 项目下,创建 common.js 模块使用 export 导出,在 index.js 使用 import 导入 common.js 导出的函数、对象和变量。

        创建一个 D:\workshop\nodejs\npmdemo\common.js 文件,内容如下

            function func() {
                console.log("common -> func()")
            }
            
            let obj = {
                name: "common -> obj"
            }
            
            let str = "common -> str"
            
            export {
                func,obj,str
            }


        修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下

            import * as common from "./common.js"

            console.log("type: " + typeof(common))
            common.func()
            console.log(common.obj.name)
            console.log(common.str)

        运行

            D:\workshop\nodejs\npmdemo> node index.js

            或

            D:\workshop\nodejs\npmdemo> npm run start

                (node:22624) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
                (Use `node --trace-warnings ...` to show where the warning was created)
                D:\Workshop\nodejs\npmdemo\index.js:1
                import * as common from "./common.js"
                ^^^^^^

                SyntaxError: Cannot use import statement outside a module
                    at wrapSafe (internal/modules/cjs/loader.js:1029:16)
                    at Module._compile (internal/modules/cjs/loader.js:1078:27)
                    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1143:10)
                    at Module.load (internal/modules/cjs/loader.js:979:32)
                    at Function.Module._load (internal/modules/cjs/loader.js:819:12)
                    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12)
                    at internal/main/run_main_module.js:17:47


        NodeJS 默认支持 CommonJS 模块规范,export 和 import 属于 ES6 的模块化语法,需要在 package.json 中声明 "type": "module"。修改 package.json,内容如下:

            {
                ...

                "author": "",
                "license": "ISC",
                "type": "module"
            }


        再次运行

            D:\workshop\nodejs\npmdemo> npm run start

                type: object
                common -> func()
                common -> obj
                common -> str

 


4. export

    一个模块就是一个独立的文件,该文件内部的资源,外部无法访问,export 用于放开访问限制,将 export 放在变量、函数、类或对象等的前面,允许其它模块访问。

    export 规定的是对外接口,必须与模块内部变量/函数/类等建立一一对应的关系。export 输出的接口和其对应的值都是动态绑定的关系,即通过该接口取到的都是模块内部实时的值。

    export 必须在模块顶层,可以位于模块顶层的任意位置,但是不能在其它作用域内(比如:函数作用域内)。

    1) export 变量

        // 格式1,等效于导出对象 {a}
        export var a = 1

        // 格式2
        var b = 2
        export {b}

        // 格式3
        var c = 3
        export {c as d}

        // 报错,没有模块内部变量
        export {4}

        // 报错,导出格式有错
        export 5

        // 报错,导出格式有错
        var e = 6
        export e

 

    2) export 函数

        // 格式1
        export function f() {}

        // 格式2
        function f() {}
        export {f}

        // 报错,导出格式有错
        function f() {}
        export f


    3) export default

        export default 就是输出一个 default 变量/函数/类,在 import 时可以为它取任意名字。

        示例,修改 D:\workshop\nodejs\npmdemo\common.js 文件,内容如下

            var s = 'default value'

            export default s

            export var v = 99

            var obj = {"name": "nodeJS"}

            export {obj} 


        修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下

            import anyname from "./common.js"

            console.log("type: " + typeof(anyname))
            console.log(anyname)

        运行

            D:\workshop\nodejs\npmdemo> node index.js

                type: string
                default value

            注:import anyname 默认导入了 common.js 里 export default 导出的变量 s。


5. import

    import 用于从其它模块导入变量、函数、对象等资源。import 分为命名式导入(名称导入)和 定义式导入(默认导入)。import 必须放在文件的最开始,且前面不允许有其他逻辑代码,和 Java 、Python 类似。

    示例,以上文 export default 部分的 common.js 文件为例

        使用 import 命名式导入全部变量,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下

            import * as common from "./common.js"

            console.log("type: " + typeof(common))
            console.log(common.obj.name)
            console.log(common.v)
            console.log(common.default)


        运行

            D:\workshop\nodejs\npmdemo> node index.js

                type: object
                nodeJS
                99
                default value


        使用 import 命名式导入 obj 和 v,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下

            import {obj, v} from "./common.js"

            console.log(obj.name)
            console.log(v)


        运行

            D:\workshop\nodejs\npmdemo> node index.js

                nodeJS
                99

        使用 import 定义式导入 default,修改 D:\workshop\nodejs\npmdemo\index.js 文件,内容如下

            import anyname from "./common.js"

            console.log("type: " + typeof(anyname))
            console.log(anyname)


        运行

            D:\workshop\nodejs\npmdemo> node index.js

                type: string
                default value