教程:使用umd、commonjs和es三種模式製做本身的React 組件(庫)

建立 package.json 文件

執行命令npm init建立 package.json 文件,一步步輸入你本身的組件基本信息,下面是我建立的css

{
  "name": "react-code-previewer",
  "version": "0.0.1",
  "description": "基於 react 和 antd 的代碼預覽器組件",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com/zhangwei900808/react-code-previewer.git"
  },
  "keywords": [
    "react",
    "antd",
    "code-preview"
  ],
  "author": "zhangwei",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/zhangwei900808/react-code-previewer/issues"
  },
  "homepage": "https://github.com/zhangwei900808/react-code-previewer#readme"
}

初始化組件

首先在項目目錄下建立相應文件夾和基本配置文件,目錄結構以下所示:html

|-- com.github
    |-- .editorconfig
    |-- .gitignore
    |-- .npmignore
    |-- LICENSE
    |-- package.json
    |-- .react-code-previewer
    |-- components
    |   |-- index.js
    |   |-- code-previewer
    |       |-- index.js
    |       |-- style
    |           |-- index.js
    |           |-- index.scss
    |-- src

建立好以後,咱們開始製做該組件,在目錄 code-previewer/index.js 添加代碼,以下所示:前端

import React, { Component } from "react";

class CodePreviewer extends Component {
  render() {
    return (
      <div className="code-preview-container">
        <div className="cp-component">
          <div className="component-header">header</div>
          <div className="component-content">content</div>
          <div className="component-footer">footer</div>
        </div>
        <div className="cp-code">
          <div className="code-header">code header</div>
          <div className="code-tabs">code tabs</div>
          <div className="code-content">code content</div>
        </div>
      </div>
    );
  }
}

export default CodePreviewer;

在目錄 code-previewer/style/index.js 添加代碼,以下所示:node

import "./index.scss";

在目錄 code-previewer/style/index.scss 添加代碼,以下所示:react

.code-preview-container {
  .cp-component {
    .component-header {
      color: red;
    }

    .component-content {
      color: green;
    }

    .component-footer {
      color: yellow;
    }
  }

  .cp-code {
    .code-header {
      color: red;
    }

    .code-tabs {
      color: green;
    }

    .code-content {
      color: yellow;
    }
  }
}

咱們如今已經初始化組件內容,雖然很簡單,後面咱們再優化,如今咱們要對外暴露該組件,因此咱們在目錄 /components/index.js 添加代碼,以下:webpack

import CodePreview from "./code-previewer";

export { CodePreview };
export default { CodePreview };

umd 打包

首先咱們在目錄建立.react-code-preview/webpack.config.umd.js 文件,並添加以下代碼:git

const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const WebpackMd5Hash = require("webpack-md5-hash");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

const resolve = dir => path.join(__dirname, ".", dir);
const isProd = process.env.NODE_ENV === "production";
const { version, name, description } = require("../package.json");
const distDir = path.join(process.cwd(), "dist");

module.exports = {
  mode: "production",
  entry: { [name]: "./components/index.js" },
  output: {
    path: distDir,
    filename: "[name].min.js",
    // 採用通用模塊定義
    libraryTarget: "umd",
    library: name
  },
  devtool: "#source-map",
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  },
  resolve: {
    enforceExtension: false,
    extensions: [".js", ".jsx", ".json", ".less", ".css"]
  },
  // 注意:本地預覽的時候要註釋,不然報 require undefined
  // https://stackoverflow.com/questions/45818937/webpack-uncaught-referenceerror-require-is-not-defined
  externals: [nodeExternals()],
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [distDir]
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css"
    }),
    new WebpackMd5Hash(),
    new webpack.BannerPlugin(` \n ${name} v${version} \n ${description} ${fs.readFileSync(path.join(process.cwd(), "LICENSE"))}`)
  ],
  //壓縮js
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: "styles",
          test: /\.scss$/,
          chunks: "all",
          enforce: true
        }
      }
    },
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true
      }),
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.css\.*(?!.*map)/g, //注意不要寫成 /\.css$/g
        cssProcessor: require("cssnano"),
        cssProcessorOptions: {
          discardComments: { removeAll: true },
          safe: true,
          autoprefixer: false
        },
        canPrint: true
      })
    ]
  },
  node: {
    setImmediate: false,
    dgram: "empty",
    fs: "empty",
    net: "empty",
    tls: "empty",
    child_process: "empty"
  }
};

在目錄根目錄下建立 .babelrc 文件,並添加以下代碼:github

{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
    }], "@babel/preset-react"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "useESModules": false
    }]
  ]
}

建立完成以後添加依賴包,並修改 package.json 文件,以下:web

"scripts": {
    "build:umd": "webpack --mode production --config .react-code-previewer/webpack.config.umd.js"
  },
"devDependencies": {
    "@babel/cli": "^7.5.5",
    "@babel/core": "^7.5.5",
    "@babel/plugin-transform-runtime": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/runtime": "^7.5.5",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.2.0",
    "fs": "^0.0.1-security",
    "mini-css-extract-plugin": "^0.8.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "path": "^0.12.7",
    "react": "^16.9.0",
    "sass-loader": "^7.2.0",
    "style-loader": "^1.0.0",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "webpack": "^4.39.2",
    "webpack-cli": "^3.3.6",
    "webpack-node-externals": "^1.7.2"
  }

添加完成以後,執行命令打包npm

yarn build:umd

clipboard.png

clipboard.png

成功在 dist 目錄下建立好了打包後的.js文件,目錄以下所示:

|-- com.github
    |-- .babelrc
    |-- .editorconfig
    |-- .gitignore
    |-- .npmignore
    |-- LICENSE
    |-- directoryList.md
    |-- package.json
    |-- yarn.lock
    |-- .react-code-previewer
    |   |-- webpack.config.umd.js
    |-- components
    |   |-- index.js
    |   |-- code-previewer
    |       |-- index.js
    |       |-- style
    |           |-- index.js
    |           |-- index.scss
    |-- dist
    |   |-- react-code-previewer.min.js
    |   |-- react-code-previewer.min.js.map
    |-- src

注意:由於後面組件會使用 bable-plugins-import 按需加載組件,因此上面的組件並無直接引用.scss文件,這樣也就不會在 dist 文件夾下打包.css文件了,可是.css 文件必定要有,後面會講gulp如何打包.css文件

commonjs 打包

上面講了如何使用webpack.config.umd.js 的 umd 模式打包 react 組件,接下來咱們講如何使用 commonjs 模式打包 react 組件,commonjs 模式打包咱們使用的是 babel 直接打包,而且要修改.babelrc 和 package.json,以下:

.babelrc

{
  "presets": [
    ["@babel/env", {
      "loose": true,
      "modules": "cjs"
    }], "@babel/preset-react"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "useESModules": false
    }],
  ]
}

package.json

"build:commonjs": "babel components -d lib --source-maps"

添加完成以後,執行命令打包

yarn build:commonjs

clipboard.png

clipboard.png

|-- com.github
    |-- .babelrc
    |-- .editorconfig
    |-- .gitignore
    |-- .npmignore
    |-- LICENSE
    |-- directoryList.md
    |-- package.json
    |-- yarn.lock
    |-- .react-code-previewer
    |   |-- webpack.config.umd.js
    |-- components
    |   |-- index.js
    |   |-- code-previewer
    |       |-- index.js
    |       |-- style
    |           |-- index.js
    |           |-- index.scss
    |-- dist
    |   |-- react-code-previewer.min.js
    |   |-- react-code-previewer.min.js.map
    |-- lib
    |   |-- index.js
    |   |-- index.js.map
    |   |-- code-previewer
    |       |-- index.js
    |       |-- index.js.map
    |       |-- style
    |           |-- index.js
    |           |-- index.js.map
    |-- src

es modules 打包

es modules打包和 babel 打包很相似也是經過 babel 打包,不一樣的是.babelrc 文件有所修改,代碼以下:

.babelrc

{
  "presets": [
    ["@babel/env", {
      "loose": true,
      "modules": false
    }], "@babel/preset-react"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "useESModules": true
    }]
  ]
}

package.json

"build:es": "babel components -d es --source-maps"

添加完成以後,執行下面命令打包

yarn build:es

clipboard.png

clipboard.png

|-- react-code-previewer
    |-- .babelrc
    |-- .editorconfig
    |-- .gitignore
    |-- .npmignore
    |-- LICENSE
    |-- package.json
    |-- yarn.lock
    |-- .react-code-previewer
    |   |-- webpack.config.umd.js
    |-- components
    |   |-- index.js
    |   |-- code-previewer
    |       |-- index.js
    |       |-- style
    |           |-- index.js
    |           |-- index.scss
    |-- dist
    |   |-- react-code-previewer.min.js
    |   |-- react-code-previewer.min.js.map
    |-- es
    |   |-- index.js
    |   |-- index.js.map
    |   |-- code-previewer
    |       |-- index.js
    |       |-- index.js.map
    |       |-- style
    |           |-- index.js
    |           |-- index.js.map
    |-- lib
    |   |-- index.js
    |   |-- index.js.map
    |   |-- code-previewer
    |       |-- index.js
    |       |-- index.js.map
    |       |-- style
    |           |-- index.js
    |           |-- index.js.map
    |-- src

這樣就完成了 umd、commonjs 和 es 的三種打包方式,可是這樣有個問題:每次打包都要修改.babelrc 文件,能不能直接經過一條命令打包三種模式的方法呢?下面咱們就來說講。

組合三種打包模式

經過三種打包模式咱們發現,.babelrc 文件跟三種模式打包有關,尤爲是其中幾個對象的設置,如:loose、modules 和 useESModules,其它的設置都同樣,因此咱們可使用cross-env BABEL_ENV=umd(commonjs 或者 es)增長 BABEL_ENV 環境變量進行判斷。還有,.babelrc 文件咱們要換成.babelrc.js,由於前者不能使用 js 語法,這裏有篇文章很好的分析.babelrc、.babelrc.js 和 babel.config.js文件之間的區別。代碼以下:

.babelrc.js

let babel_env = process.env["BABEL_ENV"];
let loose = false,
  modules = false,
  useESModules = false;

switch (babel_env) {
  case "commonjs":
    loose = true;
    modules = "cjs";
    useESModules = false;
    break;
  case "es":
    loose = true;
    modules = false;
    useESModules = true;
    break;
  case "umd":
    loose = false;
    modules = false;
    useESModules = false;
    break;
}

const presets = [["@babel/preset-env", { loose, modules }], "@babel/preset-react"];
const plugins = ["@babel/plugin-proposal-object-rest-spread", ["@babel/plugin-transform-runtime", { useESModules }]];

module.exports = { presets, plugins };

另外要注意的一點是,每次打包最好把以前的 dist、lib、es 目錄刪除防止相互干擾,刪除目錄咱們使用 rimraf 包,以下:

package.json

"build:umd": "cross-env BABEL_ENV=umd webpack --mode production --config .react-code-previewer/webpack.config.umd.js",
"build:commonjs": "rimraf lib && cross-env BABEL_ENV=commonjs babel components -d lib --source-maps",
"build:es": "rimraf es && cross-env BABEL_ENV=es babel components -d es --source-maps"

注意:由於 umd 是經過 webpack.config.umd.js 打包的因此咱們直接在該文件中刪除 dist 文件夾

plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [path.join(process.cwd(), "dist")]
    })
]

設置好以後,咱們分別打包一下,以下所示:

clipboard.png

三種打包模式都成功打包成 dist、es 和 lib 文件夾中,雖然都成功了可是每次執行三條命令很是麻煩,如何經過一條命令打包三種模式呢,其實也很簡單,代碼以下:

package.json

"scripts": {
    "build:all": "yarn build:umd && yarn build:commonjs && yarn build:es"
  },

這樣咱們只要執行 yarn build:all 命令三種模式下的打包都能完成,很是方便。

gulp 打包 css

好了,到了這裏心急的小夥伴必定要說,你把 js都打包好了,那 css呢?如今咱們就來處理 css 打包,首先咱們建立 gulpfile.js文件,代碼以下:

const fs = require("fs");
const gulp = require("gulp");
const path = require("path");
const cleanCSS = require("gulp-clean-css");
const rename = require("gulp-rename");
const concat = require("gulp-concat");
const replace = require("gulp-replace");
const sass = require("gulp-sass");
const sourcemaps = require("gulp-sourcemaps");
const autoprefixer = require("gulp-autoprefixer");
const size = require("gulp-filesize");
const { name } = require("../package.json");

const resolve = dir => path.join(__dirname, ".", dir);
const distDir = resolve("../dist");
const libDir = resolve("../lib");
const esDir = resolve("../es");
const sassDir = resolve("../components/**/*.scss");
const indexJsDir = resolve("../components/**/style/index.js");

// 複製 sass 文件到 lib es 文件夾下
gulp.task("copy-sass", () => {
  return gulp
    .src(sassDir)
    .pipe(sourcemaps.init())
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(libDir))
    .pipe(gulp.dest(esDir));
});

// 根據 index.js 建立一個全新的 css.js 供按需加載 styel:'css' 使用
gulp.task("replace-indexjs", () => {
  return gulp
    .src(indexJsDir)
    .pipe(sourcemaps.init())
    .pipe(replace("scss", "css"))
    .pipe(
      rename(function(path) {
        path.basename = "css";
        path.extname = ".js";
      })
    )
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(libDir))
    .pipe(gulp.dest(esDir));
});

// 編譯 sass 文件到 es 和 lib 文件夾下
gulp.task("compile-sass", () => {
  return gulp
    .src(sassDir)
    .pipe(sourcemaps.init())
    .pipe(sass())
    .pipe(autoprefixer())
    .pipe(cleanCSS())
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(libDir))
    .pipe(gulp.dest(esDir));
});

// 編譯 sass 到 dist 文件夾下
gulp.task("dist-css", () => {
  return gulp
    .src(sassDir)
    .pipe(sourcemaps.init())
    .pipe(sass())
    .pipe(autoprefixer())

    .pipe(concat(`${name}.css`))
    .pipe(size())
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(distDir))

    .pipe(concat(`${name}.min.css`))
    .pipe(size())
    .pipe(cleanCSS())
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(distDir));
});

gulp.task("compile", gulp.series(gulp.parallel("copy-sass", "replace-indexjs", "compile-sass", "dist-css")));

package.json

"scripts": {
    "build:css": "cd .react-code-previewer && gulp compile",
    "build:umd": "cross-env BABEL_ENV=umd webpack --mode production --config .react-code-previewer/webpack.config.umd.js",
    "build:commonjs": "rimraf lib && cross-env BABEL_ENV=commonjs babel components -d lib --source-maps",
    "build:es": "rimraf es && cross-env BABEL_ENV=es babel components -d es --source-maps",
    "build:all": "yarn build:umd && yarn build:commonjs && yarn build:es"
  },
  "devDependencies": {
    "@babel/cli": "^7.5.5",
    "@babel/core": "^7.5.5",
    "@babel/plugin-transform-runtime": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/runtime": "^7.5.5",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "cross-env": "^5.2.0",
    "css-loader": "^3.2.0",
    "fs": "^0.0.1-security",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^6.1.0",
    "gulp-clean-css": "^4.2.0",
    "gulp-concat": "^2.6.1",
    "gulp-filesize": "^0.0.6",
    "gulp-rename": "^1.4.0",
    "gulp-replace": "^1.0.0",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "^2.6.5",
    "mini-css-extract-plugin": "^0.8.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "path": "^0.12.7",
    "react": "^16.9.0",
    "sass-loader": "^7.2.0",
    "style-loader": "^1.0.0",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "webpack": "^4.39.2",
    "webpack-cli": "^3.3.6",
    "webpack-node-externals": "^1.7.2"
  }

修改好了咱們執行命令打包 css:

yarn build:css

clipboard.png

clipboard.png

這樣咱們就完成了 js 的三種模式和 css 的打包,不要忘了把 build:css也放到 build:all 命令裏面,以下:

"build:all": "yarn build:umd && yarn build:commonjs && yarn build:es && yarn build:css"

clipboard.png

接下來咱們會提交到 github以及發佈到 npm。

提交代碼到 github

咱們在 github上面建立一個新項目:react-code-previewer,而後在本地代碼執行 以下 git 命令:

git init
git remote add origin git@github.com:zhangwei900808/react-code-previewer.git
git add .
git commit -am 'your-commit-msg'
git push -u origin master

clipboard.png

發佈組件到 npm

完成了組件製做以後咱們就能夠發佈到 npm 其餘人也就能夠直接經過 npm install 你的插件了。

  1. 註冊一個 npm 帳號
  2. npm login
  3. npm version your-package-version
  4. npm publish

注意:發佈到 npm 的時候 npm 源要設置成官方的

npm set registry http://registry.npmjs.org

clipboard.png

clipboard.png

使用組件並按需加載 css 樣式

在你的另外的 react項目中咱們添加剛上傳的包:

yarn add react-code-previewer
import { CodePreviewer } from "react-code-previewer";

.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": [
    [
      "import",
      {
        "libraryName": "react-code-previewer",
        "libraryDirectory": "lib",
        "style": "css"
      }
    ]
  ]
}

按需加載咱們使用的是 antd 提供的babel-plugin-import插件,這裏要注意的是 style 屬性:

style: true 表示從style/index.scss 加載樣式
style: 'css' 表示從 style/index.css 加載樣式

完善組件代碼

上面雖然寫了一個react-code-previewer組件,可是裏面沒內容,主要是演示三種模式下打包和 css 打包,好了,如今咱們來完善下這個組件,併發布到 github pages預覽效果。

本地預覽(離線)

代碼都完成以後,咱們要在本地看看效果,首先添加webpack.config.dev.js文件,須要注意的是.dev.js和.umd.js文件相差不大.dev.js文件少了output多了HtmlWebpackPlugin配置,代碼以下:

const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const postcssPresetEnv = require("postcss-preset-env");

const resolve = dir => path.join(__dirname, ".", dir);
const isProd = process.env.NODE_ENV === "production";
const { version, name, description } = require("../package.json");
const docsDir = path.join(process.cwd(), "docs");

module.exports = {
  mode: "development",
  entry: { [name]: "./src/index.js" },
  // output: {
  //   // path: resolve("dist"), // 輸出目錄
  //   path: docsDir,
  //   filename: "static/js/[name].min.js",
  //   chunkFilename: "static/js/[name].chunk.js",
  //   umdNamedDefine: true, // 是否將模塊名稱做爲 AMD 輸出的命名空間
  //   //不加下面幾行,被引用會被報錯
  //   libraryTarget: "umd", // 採用通用模塊定義
  //   library: [name]
  // },
  devtool: "#source-map",
  module: {
    rules: [
      {
        test: /\.md$/,
        use: "raw-loader"
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(pc|sc|c)ss$/,
        use: [
          // fallback to style-loader in development
          {
            loader: "style-loader"
          },
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
              importLoaders: 1
            }
          },
          {
            loader: "postcss-loader",
            options: {
              ident: "postcss",
              sourceMap: true,
              plugins: () => [
                postcssPresetEnv({
                  stage: 3,
                  features: {
                    "custom-properties": true,
                    "nesting-rules": true
                  },
                  browsers: "last 2 versions"
                })
              ]
            }
          }
        ]
      },
      {
        test: /\.(jpe?g|png|gif|ogg|mp3)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 10 * 1000
            }
          }
        ]
      },
      {
        test: /\.(woff|svg|eot|ttf)\??.*$/,
        use: ["file-loader"]
      }
    ]
  },
  resolve: {
    enforceExtension: false,
    extensions: [".js", ".jsx", ".json", ".less", ".css"]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "static/css/[name].min.css",
      chunkFilename: "static/css/[name].chunk.css"
    }),
    //預覽
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "../public/index.html"), //指定要打包的html路徑和文件名
      filename: "./index.html" //指定輸出路徑和文件名
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ],
  //壓縮js
  optimization: {
    minimizer: [
      new TerserPlugin({
        cache: true,
        parallel: true,
        sourceMap: true
      }),
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.css\.*(?!.*map)/g, //注意不要寫成 /\.css$/g
        cssProcessor: require("cssnano"),
        cssProcessorOptions: {
          discardComments: { removeAll: true },
          safe: true,
          autoprefixer: false
        },
        canPrint: true
      })
    ]
  }
};

如今咱們修改下.babelrc.js使其可以支持ant-design

// 預覽狀況下添加對 antd 按需加載樣式的支持
if (babel_env === "preview") {
  plugins.push([
    "import",
    {
      libraryName: "antd",
      libraryDirectory: "es",
      style: "css" // `style: true` 會加載 less 文件
    },
    "ant-design"
  ]);
}

這樣咱們再修改下package.json就能夠本地預覽了,代碼以下:

"start": "cross-env BABEL_ENV=preview webpack-dev-server  --port 3002 --open --config scripts/webpack.config.dev.js",

執行命令yarn start便可預覽
image.png

在線預覽(github pages)

一、安裝gh-pages

yarn add gh-pages

二、添加webpack.config.prod.js文件,.prod.js比.umd.js文件多了個HtmlWebpackPlugin的配置其餘的沒有太大變化,代碼以下:

const path = require("path");
const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const InlineManifestWebpackPlugin = require("inline-manifest-webpack-plugin");
const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { version, name, description } = require("../package.json");
const postcssPresetEnv = require("postcss-preset-env");

const resolve = dir => path.join(__dirname, ".", dir);
const isProd = process.env.NODE_ENV === "production";
const docsDir = path.join(process.cwd(), "docs");

module.exports = {
  mode: "production",
  // 預覽
  entry: { main: "./src/index.js" },
  output: {
    // path: resolve("dist"), // 輸出目錄
    path: docsDir,
    filename: "static/js/[name].min.js",
    chunkFilename: "static/js/[name].chunk.js",
    umdNamedDefine: true, // 是否將模塊名稱做爲 AMD 輸出的命名空間
    //不加下面幾行,被引用會被報錯
    libraryTarget: "umd", // 採用通用模塊定義
    library: [name]
  },
  devtool: "#source-map",
  module: {
    rules: [
      {
        test: /\.md$/,
        use: "raw-loader"
      },
      {
        test: /\.(pc|le|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: { importLoaders: 1, sourceMap: true }
          },
          {
            loader: "postcss-loader",
            options: {
              ident: "postcss",
              sourceMap: true,
              plugins: () => [
                postcssPresetEnv({
                  stage: 3,
                  features: {
                    "custom-properties": true,
                    "nesting-rules": true
                  },
                  browsers: "last 2 versions"
                })
              ]
            }
          }
        ]
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(jpe?g|png|gif|ogg|mp3)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 10 * 1000
            }
          }
        ]
      }
    ]
  },
  resolve: {
    extensions: [".js", ".jsx"]
  },
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [docsDir]
    }),
    new MiniCssExtractPlugin({
      filename: "static/css/[name].min.css",
      chunkFilename: "static/css/[name].chunk.css"
    }),
    //預覽
    new HtmlWebpackPlugin({
      //指定要打包的html路徑和文件名
      template: path.join(__dirname, "../public/index.html"),
      //指定輸出路徑和文件名
      filename: "./index.html"
    }),
    new InlineManifestWebpackPlugin(),
    new HtmlWebpackInlineSourcePlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ],
  //壓縮js
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendors",
          minSize: 30000,
          minChunks: 1,
          chunks: "initial",
          priority: 1 // 該配置項是設置處理的優先級,數值越大越優先處理
        },
        commons: {
          test: /[\\/]src[\\/]common[\\/]/,
          name: "commons",
          minSize: 30000,
          minChunks: 3,
          chunks: "initial",
          priority: -1,
          reuseExistingChunk: true // 這個配置容許咱們使用已經存在的代碼塊
        }
      }
    },
    // 生成運行時.js 文件,並寫入到.html
    runtimeChunk: "single",
    minimizer: [
      new TerserPlugin({ cache: true, parallel: true, sourceMap: true }),
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.css\.*(?!.*map)/g, //注意不要寫成 /\.css$/g
        cssProcessor: require("cssnano"),
        cssProcessorOptions: {
          //生成.css.map 文件
          map: true,
          discardComments: { removeAll: true },
          // 避免 cssnano 從新計算 z-index
          safe: true,
          // 關閉autoprefixer功能
          // 使用postcss cssnano的autoprefixer功能
          autoprefixer: false
        },
        canPrint: true
      })
    ]
  }
};

二、配置package.json

"build:prod": "cross-env BABEL_ENV=preview webpack --mode production --config scripts/webpack.config.prod.js",
 "deploy:docs": "yarn build:prod && gh-pages -d docs"

執行yarn deploy:docs便可編譯預覽代碼到build文件夾,而後gh-pages建立gh-pages分支到github,而後咱們到github設置gh-pages爲github pages便可,這樣就完成了在線預覽了。
image.png

總結

一、三種打包方式的不一樣umd、commonjs和es modules,所修改的webpack文件和對應的bable.js文件的區別,理解和總結以後之後能夠用相同的方法打造本身的組件庫。
二、js sourcemap使用--source-maps生成,css sourcemap使用生成

const sourcemaps = require("gulp-sourcemaps");
...
.pipe(sourcemaps.init())
.pipe(sourcemaps.write("."))
...

三、gulp能夠對png的打包支持

const imgDir = resolve("../components/**/*.png");

// 複製 img 文件到 lib es 文件夾下
gulp.task("copy-img", () => {
  return gulp
    .src(imgDir)
    .pipe(gulp.dest(libDir))
    .pipe(gulp.dest(esDir));
});

四、webpack4和babel7跟以前版本有所區別,如webpack4有optimization屬性,babel7使用的包是@babel開頭等等
五、若是你想使用本身的域名,那麼你要在public裏面添加 CNAME 文件,而後添加yarn add copy-webpack-plugin插件,再修改plugins屬性new CopyPlugin([{ from: "./public/CNAME", to: "." }])便可,做用就是把CNAME文件從public打包到build文件夾下,這樣發佈到github pages以後就會被其識別就能夠訪問了。
六、404
七、github pages history模式

參考

react-code-previewer
hiynn-design
JavaScript模塊化CommonJS/AMD/CMD/UMD/ES6Module的區別
前端構建工具發展及其比較
cuke-ui
ant-landing)
黃瓜 UI: 一個即插即用的 React 組件庫
webpack4 SplitChunks實現代碼分隔詳解
Show me the code,babel 7 最佳實踐!
使用模塊化工具打包本身開發的JS庫(webpack/rollup)對比總結
gulp實戰(2) - 自動化打包CSS文件(完整版)
打包平臺Webpack(V3-V4)和Babel(V6-V7)升級~
[babel.config.js、.babelrc、.babelrc.js 區別

相關文章
相關標籤/搜索