webpack手动搭建react发布组件

238 阅读4分钟

脚手架具有如下功能:

  • 开发组件时可以进行实时预览
  • 处理组件内的各种资源(css、js、图片等)
  • 一间打包发布

1、创建项目

  • mkdir react-component-boilerplate
  • cd react-component-boilerplate
  • 使用yarn 命令初始化一个package.json文件
    • yarn init

目录结构:

`react-component-boilerplate`

    |-- config // webpack配置`

    |-- example // 开发时预览用`

    |-- dist // 打包结果`

    |-- src  // 源文件目录`

        | -- assets // 存放图片等媒体文件`

        | -- style // 存放样式,项目使用的是less来编写样式`

2、安装依赖

生产依赖

  • 1、我们要使用react来开发组件所以必须要安装 react 和 react-dom
    • yarn add react@16 react-dom@16.13.1 开发依赖
  "devDependencies": {
     // babel用于将你写的es6+的代码转换到es5
    "@babel/core": "7.10.2",
    "@babel/plugin-proposal-class-properties": "^7.0.0", // 用于支持class属性
    "@babel/plugin-proposal-decorators":"^7.0.0" ,  // 支持decorator 装饰器语法
    "@babel/preset-react": "7.10.1",  // 让bable编译react语法
    "@babel/plugin-transform-runtime":"7.0.0"
    "@babel/preset-env":"7.0.0"
    "babel-loader": "8.1.0",
    "clean-webpack-plugin": "3.0.0",
    "copy-webpack-plugin": "6.0.2",
    "css-loader": "3.5.3",
    "file-loader": "6.0.0",
    "less-loader":"4.1.0"
    "html-webpack-plugin": "4.3.0",
    "mini-css-extract-plugin": "0.9.0",  // 将css提取为一个单独的文件
    "url-loader": "4.1.0",   //将文件作为 data URI 内联到 bundle 中
    "webpack": "4.43.0",
    "webpack-cli": "3.3.10",// webpack4之后单独抽离了webpack-cli
    "webpack-dev-server": "3.10.1",// 开发时的运行服务,监控文件变化自动刷新页面
    "webpack-merge": "4.2.2"//用于合并webpack配置
  },

3、配置babel和loader

3.1 配置webpack

在config目录下,建立三个webpack配置文件夹

  • webpack.base.js
  • webpack.config.dev.js // 本地开发的配置
  • webpack.config.pro.js // 打包到生产环境的配置

webpack.base.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 清除 dist 目录
const CopyWebpackPlugin = require("copy-webpack-plugin"); // 处理静态资源
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 打包css文件
const config = {
    resolve: {
        extensions: ['.js', '.jsx', '.json'],
        alias: {
            "@": path.resolve(__dirname, 'src'),
            _: __dirname,    // 工程根目录
        }
    },
    stats: {
        colors: true,
        modules: true,
        children: true, // 打包时不显示子模块信息
    },
    module: {
        rules: [
            {
                test: /\.(png)|(jpg)|(svg)|(bmp)|(eot)|(woff)|(ttf)$/i,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: 'static/[name].[ext]',
                            esModule: true
                        }
                    },
                ]
            },
            {
            test: /\.(le|c)ss$/,
            use: [
            MiniCssExtractPlugin.loader,
            "css-loader",
            {
             loader: "less-loader",
             options: {
             sourceMap: false
             }
            }
            ]
           },
            { test: /\.(js)|(jsx)$/, exclude: /node_modules/,sideEffects: true, use: "babel-loader" },
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            // 打包 css 代码 到文件中
            filename: "css/[name].css",
            chunkFilename: "css/common.[hash:5].css", // 针对公共样式的文件名
          }),
        new CopyWebpackPlugin({
            patterns: [
                {
                    from: path.resolve(__dirname, "public"), // 将public目录中的所有文件
                    to: "./", // 复制到 输出目录 的根目录
                },
            ],
        }),
    ]
}
module.exports = config

webpack.config.dev.js

const merge = require("webpack-merge");
const path = require("path");
const baseConfig = require("./webpack.config.js");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 处理模板页面


// webpack的开发环境配置,从基本配置中合并
// 合并是利用 webpack-merge 完成的: https://github.com/survivejs/webpack-merge
const devConfig = {
  entry:"./demo/index.js",
  output: {
    filename: 'demo.bundle.js', // 输出的文件名称
    path: path.resolve(__dirname, '../demo') // 输出的文件目录
    },
  mode: "development",
  devtool: "source-map",
  devServer: {
    open: true,
    hot:true,
    port: 8085,
    proxy: {
      // 如果开发环境中有跨域问题,在这里配置代理
    },
    stats: "minimal",
  },
  plugins: [
    new HtmlWebpackPlugin({
        template: "./demo/index.html",
    }),
]
};
module.exports = merge(baseConfig, devConfig);

webpack.config.pro.js

const merge = require("webpack-merge");
const path = require("path");
const baseConfig = require("./webpack.config");

const proConfig = {
    mode:"production",
    entry: './src/index.js',    //webpack编译文件入口
    output: {
        filename: 'index.js',
        path: path.resolve(__dirname, 'dist'),
        libraryTarget:'umd',       // 采用通用模块定义,注意webpack4到目前为止依然不提供输出es module的方法,所以输出的结果必须使用npm安装到node_modules里再用,不然会报错
        library:'react-component-boilerplate', // 组件库的名称
        libraryExport:'default'  // 兼容 ES6(ES2015) 的模块系统、CommonJS 和 AMD 模块规范
    },
    externals: {
        react: {
         root: "React",
         commonjs2: "react",
         commonjs: "react",
         amd: "react"
        },
        "react-dom": {
         root: "ReactDOM",
         commonjs2: "react-dom",
         commonjs: "react-dom",
         amd: "react-dom"
        }
        },
}

module.exports = merge(baseConfig, proConfig)

注意:

  • externals字段
    • externals定义了外部依赖。将react和react-dom添加进该字段,说明我们的组件将依赖外部的react和react-dom,这样就可以避免把react和react-dom打包进去(不然组件会很大,防止页面重复引入react,造成重复声明变量的问题)

3.2 配置babel

我们需要把我的代码编译降级为ES5版本。需要写一些babel配置

```js
{
"presets": [
[
"@babel/preset-env",
{
    "targets": "> 0.25%, not dead"
]
,"@babel/preset-react"]
 "plugins": [   // 使用一些babel插件
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-modules-commonjs",
[
 "@babel/plugin-proposal-decorators",
 {
 "legacy": true
 }
],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"   // 支持es7的 ...操作符
]

} ```

注意:

  • presets其中使用了preset-env, 规定了输出的代码目标环境是份额大于0.25%的浏览器
  • 由于我们的项目里使用了react,presets中就要加入preset-react

3.3 配置package.json 启动命令

"scripts": {
    "build": "set NODE_ENV=production && webpack --config ./config/webpack.config.prod.js",
    "pub": "npm run build && npm publish",
    "dev": "webpack-dev-server --config ./config/webpack.config.dev.js"
    },
    "main": "dist/index.js",
    "files": ["dist"]

4、编写组件

/* src/index.js */
 
import React from 'react';
import './style/style.less'; // 使用less的情况
 
export default class Hello extends React.Component {
 render(){
      return (<div>A new Component</div>)
 }
}

在/src目录下新建一个index.js,这就是我们组件的入口文件了。

如果项目中要使用图片、css等,分类放到assets、style文件夹下

开发预览

在example目录的index.js文件中引入src/index.js的组件

 import React from 'react';
  import ReactDom from 'react-dom';
  import Hello from '../src/index'
   
  const Demo = () => {
   return <div>
   <h1>组件预览:</h1>
       <Hello />
   </div>
  }
   
  ReactDom.render(<Demo />, document.getElementById('root'));

发布npm&引用组件

  • 1、没有npm账户,先注册npm账户
  • 2、已注册,登录npm
    • npm login
    • npm whoami 如果不知道自己是否添加过npm账号,用此命令查看
  • 3、正确修改package.json的组件名称、版本等信息
  • 4、直接在命令行运行,直接打包并发布
    • yarn pub
  • 5、在项目中引入使用

总结

  • 1、webpack打包时libraryTarget要使用umd

  • 2、extenals字段,要把外部依赖配置好

  • 3、如果要生成es module,可以使用gulp或rollup等工具

  • 4、webpack4之后建议使用 MiniCssExtractPlugin来提取css