webpack单独打包react组件

3,503 阅读3分钟

本文主要介绍react组件库开发时通过webpack打包过程的关键注意点。

组件打包要求:
  1. 针对公共组件打包处理,并且保证按源文件结构输出。
  2. 支持ES6 module、AMD、CommonJs、Script标签 引入使用组件。

js部分webpack配置

const webpackConfig = {
  // 要打包的组件,多入口配置
  entry: {
    'button1': path.resolve(__dirname, `../src/components/button1/index.tsx`),
    'button2': path.resolve(__dirname, `../src/components/button2/index.tsx`)
  },
  output: {
    // filename指定为一个方法可以拿到chunk.name,分别是'button1'和'button2'
    // filename最终的结果 => 'button1/index.js'
    filename: (pathData) => {
      return `${pathData.chunk.name}/index.js`
    },
    // 输出到根目录的 lib文件夹下面
    // 最终的路径就是 path + filename => '../lib/button1/index.js', 如此js可以按照源文件路径输出了
    path: path.resolve(__dirname, '../lib'),
    libraryTarget: 'umd',
    library: '[name]',
    libraryExport: "default"
  },
  modules: {
    rules: [
     {
     	test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          'babel-loader',
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true,
            },
          }
        ]
      }
    ]
  }
}

libraryTarget:'umd'。 可以在ES6 module、CommonJS、AMD 环境下运行,也可以通过全局访问。

library:暴露出模块的变量名称,上述配置的名称取的是文件名'button1'、'button2',可根据需求自己定义,具体使用如下。

  • ES2015 模块。例如 import button1 from 'button1'。
  • CommonJS 模块。例如 require('button1')。
  • 全局变量,在通过 script 标签引入可以直接访问到button1这个变量。 libraryExport:'default', 如果不给libraryExport赋值默认导出整个命名空间对象Module,例如想要调用button1(这里button1是个方法),需要这样button1.default()。如果定义libraryExport:'default',那么可以直接调用button1()。

到这里js部分的打包处理完成了,同时最开始需求的第2点也满足了,下面开始处理css。

css部分webpack配置

module: {
    rules: [
      {
        test: /\.(scss|sass)$/,
        exclude: /node_modules/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      }
   ]
},
plugins: [
  new MiniCssExtractPlugin({
    filename: (pathData) => {
      return `${pathData.chunk.name}/style/index.css`
    }
  })
]

css配置相对比较简单,filename和js配置类似,都是通过chunk.name拼接出最终要输出的路径。

images部分webpack配置

  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        exclude: /node_modules/,
        use: {
          loader: 'file-loader',
          options: {
            context: path.resolve(__dirname, '../src/components'),
            // 这里name最终结果 => 文件名.jpg
            name: '[name].[ext]',
            outputPath: (url, resourcePath, context) => {
              const relativePath = path.relative(context, resourcePath);
              return `../lib/${relativePath}`
            },
            publicPath: '../images',
            esModule: false
          }
        }
      }
    ]
  }

context: 当前执行上下文,可以理解为当前打包时nodeJs执行所在的工作目录,默认时webpack的context,如果指定一个目录那么工作目录就是指定的文件夹位置。

outputPath:打包结果输出路径。这里要保持打包后的目录结构和打包前一致所以outputPath使用来一个方法来处理,此时之前设置的context就可以用上了。如下:

outputPath: (url, resourcePath, context) => {
  // 以我电脑输出为例
  // resourcePath => /Users/admin/Documents/webpack/src/components/button1/images/dog1.png 
  // context => /Users/admin/Documents/webpack/src/components 
  // 这里的context就是上面配置的context: path.resolve(__dirname, '../src/components')
  // 通过path.relative就能得到相对路径,这个路径就是要输出的路径 => 'button1/images/dog1.png'
  const relativePath = path.relative(context, resourcePath);
  // 返回最终图片打包输出的路径 => '../lib/button1/images/dog1.png'
  return `../lib/${relativePath}`
}

publicPath: 打包后静态资源的访问路径。例如在css中引用了一个图片:

background-image: url(../images/dog1.png);

如果打包出来dog1.png位于lib/static中,那么可以这样配置publicPath

options: {
  name: '[name].[ext]',
  publicPath: '../static',
}

可以通过配置publicPath来自定义打包后静态资源的引用路径,这样打包后的图片引用路径就会被修改,结果如下

background-image: url(../static/dog1.png);

esModule:false。当前file-loader版本为v6.2.0,默认打包输出结果是使用ES模块语法的JS模块,如下

background-image:url([object Module])

所以要想显示正常的url,需要把esModule置为false。

排除react、react-dom

因为是组件打包,所以不能把react相关的代码打包进去,所以最后需要把不需要的代码排除掉。

externals: {
  'react': {
    commonjs: 'react', // CommonJS 模块
    commonjs2: 'react', // CommonJS 模块
    amd: 'react',       // AMD 模块
    root: 'React',     // 全局变量访问
  },
  'react-dom': {
    commonjs: 'react-dom',
    commonjs2: 'react-dom',
    amd: 'react-dom',
    root: 'ReactDOM',
  },
}

上面externals配置解决了2个问题:

  1. 排除react和react-dom,让组件打包时不会把这两个库的代码打进去。
  2. 解决组件内使用react和react-dom问题。 组件内通常这么引用react
import React from 'react'

externals配置就是保证了组件内部引用react时能够正常运行,如上配置使用require或者全局变量访问react此处也是可以的。

到此组件打包基础功能已经实现,看下图:左侧是src下的源码,右侧是打包后的输出文件

本片文章的相关代码webpack-build-components有兴趣可以参考。如果文章内容有误欢迎指正,谢谢。