18、JavaScript 代码导入与导出及 npm 使用指南

JavaScript模块化与npm应用

JavaScript 代码导入与导出及 npm 使用指南

1. 利用导入和导出隔离功能

在 JavaScript 早期,所有代码都被放置在单个文件中,甚至开发者会将所有 JavaScript 代码放在 DOM 中的单个 <script> 标签下。后来,情况逐渐改善。有人创建了代码压缩和合并文件的工具,至少可以使用一个小的导入语句。接着,像 Require.js 和 CommonJS 这样的项目让开发者可以使用模块在文件间共享代码。如今,模块已简化为简单的导入和导出语句。

1.1 基本的导出和导入

最基本的,只需导出一个包含要共享数据的对象,这意味着可以导出函数、变量和类。也可以选择不导出某些内容,从而创建公共和私有函数。

示例代码如下:

// functions/context/method.js
const validator = {
    message: 'is invalid.',
    setInvalidMessage: field => `${field} ${this.message}`,
};
export { validator };

若要在另一个文件中使用该函数,使用 import 关键字和要导入的函数,并用花括号括起来,然后给出相对于当前文件的路径。

// architecture/import/single/bill.js
import { validator } from './context/method';
// 使用 validator

1.2 部分导出和导入

可以选择只导出部分函数,隐藏其他函数。

// architecture/import/single/util.js
function getPower(decimalPlaces) {
    return 10 ** decimalPlaces;
}
function capitalize(word) {
    return word[0].toUpperCase() + word.slice(1);
}
function roundToDecimalPlace(number, decimalPlaces = 2) {
    const round = getPower(decimalPlaces);
    return Math.round(number * round) / round;
}
export { capitalize, roundToDecimalPlace };

导入时,可以只导入需要的函数。

// architecture/import/single/name.js
import { capitalize } from './util';
function greet(name) {
    return `Hello, ${capitalize(name)}!`;
}
greet('ashley');
// Hello, Ashley!
export { greet };

1.3 导出变量和类

除了函数,还可以导出变量和类。

// architecture/import/single/math.js
const PI = 3.14;
const E = 2.71828;
export { E, PI };

1.4 其他导入方式

可以将所有导入作为对象的属性,使用 * 导入所有函数并指定变量名。

// architecture/import/each/name.js
import * as utils from './util';
function greet(name) {
    return `Hello, ${utils.capitalize(name)}!`;
}
greet('ashley');
// Hello, Ashley!
export { greet };

还可以重命名导入的函数或数据,使用 as 关键字。

1.5 快捷导出方式

可以在每个函数前添加 export 关键字,而不是在文件末尾声明对象。

// architecture/import/each/util.js
function getPower(decimalPlaces) {
    return 10 ** decimalPlaces;
}
export function capitalize(word) {
    return word[0].toUpperCase() + word.slice(1);
}
export function roundToDecimalPlace(number, decimalPlaces = 2) {
    const round = getPower(decimalPlaces);
    return Math.round(number * round) / round;
}

1.6 默认导出

当文件有单个入口点或某个函数更重要时,可以声明默认导出。

// architecture/import/default/address.js
import { capitalize } from '../single/util';
export function parseRegion(address) {
    const region = address.state || address.providence || '';
    return region.toUpperCase();
}
export function parseStreet({ street }) {
    return street.split(' ')
      .map(part => capitalize(part))
      .join(' ');
}
export default function normalize(address) {
    const street = parseStreet(address);
    const city = address.city;
    const region = parseRegion(address);
    return `${street} ${city}, ${region}`;
}

导入默认导出时,不使用花括号。

// architecture/import/default/mail.js
import normalize from './address';
function getAddress(user) {
    return normalize(user.address);
}
export default getAddress; 

若要同时导入默认函数和其他函数,可以混合使用导入语句。

// architecture/import/default/list.js
import normalize, { parseRegion } from './address';
function getAddress(user) {
    return normalize(user.address);
}
export function getAddressByRegion(users) {
    return users.reduce((regions, user) => {
        const { address } = user;
        const region = parseRegion(address);
        const addresses = regions[region] || [];
        regions[region] = [...addresses, normalize(address)];
        return regions;
    }, {});
}

1.7 类的默认导出

默认导入在类上特别有用,因为每个文件通常只有一个类。

// architecture/import/class/address.js
import { capitalize } from '../single/util';
export default class Address {
    constructor(address) {
        this.address = address;
    }
    normalize() {
        const street = this.parseStreet(this.address);
        const city = this.address.city;
        const region = this.parseRegion(this.address);
        return `${street} ${city}, ${region}`;
    }
    parseStreet({ street }) {
        return street.split(' ')
          .map(part => capitalize(part))
          .join(' ');
    }
    parseRegion(address) {
        const region = address.state || address.providence || '';
        return region.toUpperCase();
    }
}

以下是导出和导入的流程图:

graph TD;
    A[定义代码] --> B[导出代码];
    B --> C[导入代码];
    C --> D[使用代码];

1.8 总结

导入和导出非常直观,但随着项目增长,代码会分散在多个文件中。不过,这有助于更高效、逻辑地组织代码。

2. 使用 npm 利用社区知识

过去,如果想使用开源库,只能复制粘贴代码、下载库到项目或在标记中使用 <script> 标签包含外部依赖。现在,可以使用 Node 包管理器(npm)直接将代码下载到项目,控制版本,并使用熟悉的约定将代码导入到单个文件。

2.1 初始化项目

首先要安装 Node.js,安装时会同时安装 npm。安装完成后,打开终端,进入项目根目录,输入 npm init 初始化项目,这会启动一个配置工具,为你创建 package.json 文件。

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

2.2 评估开源代码

npm 不会在代码发布前进行审核,所以需要自己评估要包含在项目中的代码。评估步骤如下:
1. 下载量 :每月下载量多的代码可能更安全。
2. 问题数量 :问题多可能表示项目维护不佳,但大型项目可能因使用量大而有更多问题。
3. 查看代码库 :查看最新提交日期和拉取请求。如果代码在过去六个月内没有提交,可能已被弃用;如果有陈旧的合并请求,说明代码管理不佳。
4. 查看实际代码 :代码结构整洁、遵循标准约定通常更安全。

2.3 安装和使用开源库

以 Lodash 为例,若要将其安装到项目中,运行 npm install --save lodash 。该命令会创建 node_modules 目录,复制包,更新 package.json 文件以包含版本号,并创建 package-lock.json 文件。

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

导入 Lodash 代码时,使用与之前相同的 import 命令,但无需给出路径。

// architecture/npm/utils/merge.js
import lodash, { fromPairs } from 'lodash';
export function mapToObject(map) {
    return fromPairs([...map]);
}
export function objectToMap(object) {
    const pairs = lodash.toPairs(object);
    return new Map(pairs);
}

2.4 开发依赖

有时需要在代码库上工作但不包含在生产构建中的代码,如测试运行器。以 Prettier 为例,它是用于格式化代码的工具,安装时使用 npm install --save-dev prettier

{
    "name": "test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "lodash": "^4.17.4"
    },
    "devDependencies": {
        "prettier": "^1.8.2"
    }
}

若要使用 Prettier,可以使用 npm 脚本。在 package.json scripts 字段中添加命令。

{
    "name": "test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "clean": "prettier --tab-width=4 --write ./code/*.js"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "lodash": "^4.17.4"
    },
    "devDependencies": {
        "prettier": "^1.8.2"
    }
}

在项目根目录下运行 npm run clean 即可执行该命令。

以下是使用 npm 的流程图:

graph TD;
    A[安装 Node.js 和 npm] --> B[初始化项目 npm init];
    B --> C[评估开源代码];
    C --> D[安装开源库 npm install --save];
    D --> E[安装开发依赖 npm install --save-dev];
    E --> F[使用 npm 脚本运行命令];

2.5 总结

npm 对 JavaScript 开发非常有价值,查看新项目时,应先浏览 package.json 文件。有了组合多个文件和开源项目代码的工具后,接下来可以考虑如何组织代码。

3. 代码组织与管理

3.1 组件架构的引入

随着项目规模的扩大,代码文件数量增多,为了更好地组织和管理代码,引入组件架构是很有必要的。组件架构可以将相关的文件聚集在一起,使代码结构更加清晰,便于维护和扩展。

3.2 组件架构的优势

优势 描述
模块化 每个组件可以独立开发、测试和维护,提高开发效率。
可复用性 组件可以在不同的地方重复使用,减少代码冗余。
易于理解 代码结构清晰,团队成员可以更快地理解和上手项目。

3.3 组件架构示例

假设有一个电商项目,包含商品列表、购物车、用户信息等功能。可以将这些功能拆分成不同的组件,每个组件包含自己的 HTML、CSS 和 JavaScript 文件。

e-commerce-project/
├── components/
│   ├── product-list/
│   │   ├── product-list.html
│   │   ├── product-list.css
│   │   ├── product-list.js
│   ├── shopping-cart/
│   │   ├── shopping-cart.html
│   │   ├── shopping-cart.css
│   │   ├── shopping-cart.js
│   ├── user-info/
│   │   ├── user-info.html
│   │   ├── user-info.css
│   │   ├── user-info.js
├── index.html
├── main.css
├── main.js

3.4 组件之间的通信

组件之间可能需要进行数据传递和通信。可以使用事件、回调函数、状态管理库等方式来实现组件之间的通信。

以下是一个简单的事件通信示例:

// product-list.js
const productList = document.getElementById('product-list');
productList.addEventListener('product-selected', (event) => {
    const selectedProduct = event.detail;
    // 处理选中的商品
});

// 触发事件
const product = { id: 1, name: 'Product 1' };
const event = new CustomEvent('product-selected', { detail: product });
productList.dispatchEvent(event);

3.5 组件架构的流程图

graph TD;
    A[项目根目录] --> B[组件目录];
    B --> C[组件 1];
    B --> D[组件 2];
    B --> E[组件 3];
    C --> F[HTML 文件];
    C --> G[CSS 文件];
    C --> H[JavaScript 文件];
    D --> I[HTML 文件];
    D --> J[CSS 文件];
    D --> K[JavaScript 文件];
    E --> L[HTML 文件];
    E --> M[CSS 文件];
    E --> N[JavaScript 文件];

4. 总结与建议

4.1 总结

通过使用导入和导出功能,可以在 JavaScript 文件之间共享代码,提高代码的复用性和可维护性。npm 作为一个强大的包管理工具,让我们可以轻松地下载和使用社区中的开源代码,同时还能管理项目的依赖和运行脚本。组件架构则帮助我们更好地组织和管理代码,使项目结构更加清晰。

4.2 建议

  • 在编写代码时,尽量将功能模块化,使用导入和导出功能将代码分离到不同的文件中。
  • 在选择开源库时,要仔细评估代码的质量和稳定性,避免引入有问题的代码。
  • 合理使用组件架构,将相关的文件聚集在一起,提高代码的可维护性和可扩展性。
  • 定期更新项目的依赖,确保使用的是最新版本的代码,以获取更好的性能和安全性。

希望这些内容能帮助你更好地进行 JavaScript 开发,提高代码质量和开发效率。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值