微信小程序中的"构建 npm"

2,022 阅读4分钟

前言

微信小程序支持使用 npm 安装第三方包,但是在使用前,需先构建 npm,这和我们常规的使用方式不太一样。

常规方式:npm install -> 打包编译(webpack)

此外,微信小程序在构建 npm 时也有很多“反常规”的操作,本文将详细介绍如何正确构建 npm 。

使用 npm 包

1.安装 npm 包

在小程序 package.json 所在的目录中执行命令安装 npm 包:

npm install

此处小程序对 package.json 文件所在的目录有要求,默认需要在 project.config.json 定义的 miniprogramRoot 之内。当然,小程序提供自定义规则能力,后文将详细介绍。

根据 package.json 的 dependencies 字段构建,所以声明在 devDependencies 里的包也可以在开发过程中被安装使用而不会参与到构建中。

2.构建 npm

点击开发者工具中的菜单栏:工具 --> 构建 npm

3.使用 npm 包

js 中引入 npm 包

const myPackage = require('packageName')

使用 npm 包中的自定义组件

{

    "usingComponents": {

        "componentName":"packageName/componentName"

    }

}

构建 npm 原理

核心点

  • 1.node_modules 目录不会参与编译、上传和打包
  • 2.会生成一个 miniprogram_npm 目录,里面会存放构建打包后的 npm 包,也就是小程序真正使用的 npm 包。
  • 3.构建打包分为两种:
    • 小程序 npm 包会直接拷贝构建文件生成目录(默认为 miniprogram_dist 目录)下的所有文件到 miniprogram_npm 中;(此处涉及小程序 npm 包发布,本文不做介绍,具体参见发布 npm 包
    • 其他 npm 包则会从入口 js 文件开始走一遍依赖分析和打包过程(类似 webpack)。

构建规则

核心就一句话:在每一份 project.config.json/miniprogramRoot 内开发者声明的 package.json 的最外层的 node_modules 的同级目录下会生成一个 miniprogram_npm 目录,里面会存放构建打包后的 npm 包。

虽然只有一句话,但是理解起来并不容易,下面举几个具体的案例来说明。

1.miniprogramRoot 字段不存在

当 miniprogramRoot 字段不存在时,miniprogramRoot 就是 project.config.json 所在的目录。

构建前:

|--node_modules
|    |--testComp // 小程序 npm 包
|    |    |--package.json
|    |    |--src
|    |    |--miniprogram_dist
|    |         |-index.js
|    |         |-index.json
|    |         |-index.wxss
|    |         |-index.wxml
|    |--testa // 其他 npm 包
|         |--package.json
|         |--lib
|         |    |--entry.js
|         |--node_modules
|              |--testb
|                   |--package.json
|                   |--main.js
|--pages
|--app.js
|--app.wxss
|--app.json
|--project.config.json

构建后:

|--node_modules
|--miniprogram_npm
|    |--testComp // 小程序 npm 包
|    |    |-index.js
|    |    |-index.json
|    |    |-index.wxss
|    |    |-index.wxml
|    |--testa // 其他 npm 包
|         |--index.js // 打包后的文件
|         |--miniprogram_npm
|              |--testb
|                   |--index.js // 打包后的文件
|                   |--index.js.map
|--pages
|--app.js
|--app.wxss
|--app.json
|--project.config.json

2.默认的构建方式

默认情况下,在 miniprogramRoot 内配置了 package.json 并执行 npm install 之后,其构建 npm 的结果是,为每一个 package.json 对应的 node_modules 构建一份 miniprogram_npm,并放置在对应 package.json 所在目录的子目录中。

构建前:

├── miniprogram
│   ├── app.js
│   ├── app.json
│   ├── app.wxss
│   ├── index
│   │   ├── 略
│   ├── node_modules // 可被默认方式构建 npm,因为它在 miniprogramRoot 内
│   ├── package.json
│   └── sub_package
│       ├── node_modules // 可被默认方式构建 npm,因为它在 miniprogramRoot 内
│       ├── package.json
│       └── sub_package_page
├── node_modules // 不被默认方式构建 npm,因为它不在 miniprogramRoot 内
├── package.json
└── project.config.json // 其中存在配置 `"miniprogramRoot": "./miniprogram"`

构建后:

├── miniprogram
│   ├── app.js
│   ├── app.json
│   ├── app.wxss
│   ├── index
│   │   ├── 略
│   ├── miniprogram_npm
│   ├── node_modules // 可被默认方式构建 npm,因为它在 miniprogramRoot 内 --> 同级的 miniprogram_npm 是这份 node_modules 的构建结果
│   ├── package.json
│   └── sub_package
│       ├── miniprogram_npm 
│       ├── node_modules // 可被默认方式构建 npm,因为它在 miniprogramRoot 内 --> 同级的 miniprogram_npm 是这份 node_modules 的构建结果
│       ├── package.json
│       └── sub_package_page
├── node_modules // 不被默认方式构建 npm,因为它不在 miniprogramRoot 内 --> 它并没有对应的 miniprogram_npm 生成
├── package.json
└── project.config.json // 其中存在配置 `"miniprogramRoot": "./miniprogram"`

3.自定义构建方式

以我们常规的开发习惯,node_modules 文件夹一般放在项目的根目录下,而不是放在主要源码(miniprogram、src等)的位置下。微信小程序允许我们自定义构建方式,此种方式需要开发者在 project.config.json 中指定 node_modules 的位置目标 miniprogram_npm 的位置

使用方法

  • 配置 project.config.json 的 setting.packNpmManually 为 true,开启自定义 node_modules 和 miniprogram_npm 位置的构建 npm 方式
  • 配置 project.config.json 的 setting.packNpmRelationList 项,指定 packageJsonPath 和 miniprogramNpmDistDir 的位置
"packNpmManually": true,
"packNpmRelationList": [
  {
    "packageJsonPath": "./package.json",
    "miniprogramNpmDistDir": "./miniprogram/"
  }
]
  • packageJsonPath 表示 node_modules 源对应的 package.json
  • miniprogramNpmDistDir 表示 node_modules 的构建结果目标位置

构建前:

├── miniprogram
│   ├── app.js
│   ├── app.json
│   ├── app.wxss
│   ├── index
│   ├── sitemap.json
│   └── sub_package
│       └── sub_package_page
├── project.config.json
├── src_node_modules_1
│   ├── node_modules
│   └── package.json
└── src_node_modules_2
    ├── node_modules
    └── package.json

其中 project.config.json 配置:

"setting": {
  "packNpmManually": true,
  "packNpmRelationList": [
    {
      "packageJsonPath": "./src_node_modules_1/package.json",
      "miniprogramNpmDistDir": "./miniprogram/"
    },
    {
      "packageJsonPath": "./src_node_modules_2/package.json",
      "miniprogramNpmDistDir": "./miniprogram/sub_package"
    }
  ]
}

构建后:

├── miniprogram
│   ├── app.js
│   ├── app.json
│   ├── app.wxss
│   ├── index
│   ├── miniprogram_npm // 由 src_node_modules_1/node_modules 构建得到
│   ├── sitemap.json
│   └── sub_package
│       ├── miniprogram_npm // 由 src_node_modules_2/node_modules 构建得到
│       └── sub_package_page
├── project.config.json
├── src_node_modules_1
│   ├── node_modules
│   └── package.json
└── src_node_modules_2
    ├── node_modules
    └── package.json

命令行构建

除了使用 IDE 来构建 npm ,还可以通过 miniprogram-ci 工具构建。需要特别注意的是:采用不同的构建方式,miniprogram-ci 的编译指令也不同。

默认构建方式

采用的命令为:ci.packNpm

const ci = require('miniprogram-ci')
;(async () => {
  const project = new ci.Project({
    appid: 'wxsomeappid',
    type: 'miniProgram',
    projectPath: 'the/project/path',
    privateKeyPath: 'the/path/to/privatekey',
    ignores: ['node_modules/**/*'],
  })
  // 在有需要的时候构建npm
  const warning = await ci.packNpm(project, {
    ignores: ['pack_npm_ignore_list'],
    reporter: (infos) => { console.log(infos) }
  })
  console.warn(warning)
  // 可对warning进行格式化
  /*
    warning.map((it, index) => {
            return `${index + 1}. ${it.msg}
    \t> code: ${it.code}
    \t@ ${it.jsPath}:${it.startLine}-${it.endLine}`
          }).join('---------------\n')
  */
  // 完成构建npm之后,可用ci.preview或者ci.upload
})()

自定义构建方式

采用的命令为:ci.packNpmManually

let packResult = await ci.packNpmManually({
  packageJsonPath: './lib/package.json',
  miniprogramNpmDistDir: './miniprogram-project/miniprogram/',
})

console.log('pack done, packResult:', packResult)
// 输出 pack done, packResult: { miniProgramPackNum: 0, otherNpmPackNum: 1, warnList: [] }

参考

npm 支持

miniprogram-ci