Npm 是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。 虽然大多数情况下你都是这些开放模块的使用者,但有时候你也许会成为贡献者,开发一个模块上传到 Npm 仓库。
发布到 Npm 仓库的模块有以下几个特点:
package.json
Webpack 不仅可用于构建运行的应用,也可用于构建上传到 Npm 的模块。 接下来用教大家如何用 Webpack 构建一个可上传的 Npm 仓库的 React 组件,具体要求如下:
在开始前先看下最终发布到 Npm 仓库的模块的目录结构:
node_modules/hello-webpack ├── lib │ ├── index.css (组件所有依赖的 CSS 都在这个文件中) │ ├── index.css.map │ ├── index.js (符合 CommonJS 模块化规范的 ES5 代码) │ └── index.js.map ├── src (ES6 源码) │ ├── index.css │ └── index.js └── package.json (模块描述文件)
由于本节的重点不在于 React 而在于 Webpack,所以写一个最简单的 React 组件,src/index.js 内容如下:
src/index.js
import React, { Component } from 'react'; import './index.css'; // 导出该组件供给其它模块使用 export default class HelloWebpack extends Component { render() { return <h1 className="hello-component">Hello,Webpack</h1> } }
要使用该模块时只需要这样:
// 通过 ES6 语法导入 import HelloWebpack from 'hello-webpack'; import 'hello-webpack/lib/index.css'; // 或者通过 ES5 语法导入 var HelloWebpack = require('hello-webpack'); require('hello-webpack/lib/index.css'); // 使用 react-dom 渲染 render(<HelloWebpack/>);
接下来用 Webpack 一条条来解决上面抛出问题的4点要求。
devtool: 'source-map'
output.libraryTarget='commonjs2'
相关的 Webpack 配置代码如下:
module.exports = { output: { // 输出的代码符合 CommonJS 模块化规范,以供给其它模块导入使用。 libraryTarget: 'commonjs2', }, // 输出 Source Map devtool: 'source-map', };
const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { module: { rules: [ { // 增加对 CSS 文件的支持 test: /\.css/, // 提取出 Chunk 中的 CSS 代码到单独的文件中 use: ExtractTextPlugin.extract({ use: ['css-loader'] }), }, ] }, plugins: [ new ExtractTextPlugin({ // 输出的 CSS 文件名称 filename: 'index.css', }), ], };
此步引入了3个新依赖:
# 安装 Webpack 构建所需要的新依赖 npm i -D style-loader css-loader extract-text-webpack-plugin
例如下面这段 ES6 代码
class HelloWebpack extends Component{ }
在被转换成能正常运行的 ES5 代码时需要以下2个辅助函数:
babel-runtime/helpers/createClass
babel-runtime/helpers/inherits
默认的情况下 Babel 会在每个输出文件中内嵌这些依赖的辅助函数的代码,如果多个源代码文件都依赖这些辅助函数,那么这些辅助函数的代码将会重复的出现很多次,造成代码冗余。 为了不让这些辅助函数的代重复出现,可以在依赖它们的时候通过 require('babel-runtime/helpers/createClass') 的方式去导入,这样就能做到只让它们出现一次。 babel-plugin-transform-runtime 插件就是用来做这个事情的。
require('babel-runtime/helpers/createClass')
修改 .babelrc 文件,为其加入 transform-runtime 插件:
.babelrc
{ "plugins": [ [ "transform-runtime", { // transform-runtime 默认会自动的为你使用的 ES6 API 注入 polyfill // 假如你在源码中使用了 Promise,输出的代码将会自动注入 require('babel-runtime/core-js/Promise') 语句 // polyfill 的注入应该交给模块使用者,因为使用者可能在其它地方已经注入的其它的 Promise polyfill 库 // 所以关闭该功能 "polyfill": false } ] ] }
由于加入 babel-plugin-transform-runtime 后生成的代码中会大量出现类似 require('babel-runtime/helpers/createClass') 这样的语句,所以输出的代码将依赖 babel-runtime 模块。
babel-runtime
# 安装 Webpack 构建所需要的新依赖 npm i -D babel-plugin-transform-runtime # 安装输出代码运行时所需的新依赖 npm i -S babel-runtime
Externals 用来告诉 Webpack 要构建的代码中使用了哪些不用被打包的模块,也就是说这些模版是外部环境提供的,Webpack 在打包时可以忽略它们。
module.exports = { // 通过正则命中所有以 react 或者 babel-runtime 开头的模块 // 这些模块使用外部的,不能被打包进输出的代码里 externals: /^(react|babel-runtime)/, };
完成以上4步后最终的 Webpack 完整配置代码如下:
const path = require('path'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { // 模块的入口文件 entry: './src/index.js', output: { // 输出文件的名称 filename: 'index.js', // 输出文件的存放目录 path: path.resolve(__dirname, 'lib'), // 输出的代码符合 CommonJS 模块化规范,以供给其它模块导入使用。 libraryTarget: 'commonjs2', }, // 通过正则命中所有以 react 或者 babel-runtime 开头的模块 // 这些模块使用外部的,不能被打包进输出的代码里 externals: /^(react|babel-runtime)/, module: { rules: [ { test: /\.js$/, use: ['babel-loader'], // 排除 node_modules 目录下的文件 // node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换 exclude: path.resolve(__dirname, 'node_modules'), }, { // 增加对 CSS 文件的支持 test: /\.css/, // 提取出 Chunk 中的 CSS 代码到单独的文件中 use: ExtractTextPlugin.extract({ use: ['css-loader'] }), }, ] }, plugins: [ new ExtractTextPlugin({ // 输出的 CSS 文件名称 filename: 'index.css', }), ], // 输出 Source Map devtool: 'source-map', };
重新执行构建后,你将会在项目目录下看到一个新目录 lib,里面放着要发布到 Npm 仓库的最终代码。
lib
在把构建出的代码发布到 Npm 仓库前,还需要确保你的模块描述文件 package.json 是正确配置的。
由于构建出的代码的入口文件是 ./lib/index.js,需要修改 package.json 中的 main 字段如下:
./lib/index.js
main
{ "main": "lib/index.js", "jsnext:main": "src/index.js" }
其中 jsnext:main 字段用于指出采用 ES6 编写的模块入口文件所在的位置。
jsnext:main
修改完毕后在项目目录下执行 npm publish 就能把构建出的代码发布到 Npm 仓库中(确保已经 npm login 过)。
npm publish
npm login
本实例提供项目完整代码
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8