sdk封装--webpack+typescript+react

3,181 阅读6分钟

本文基于cesium封装sdk,旨在构建一个集成webpack+ts+react的sdk模板,封装部分工具后,将发布于npm。工具比较简单(工具内容将在github持续更新),重在梳理搭建发布的流程。

一、搭建sdk模板

技术栈选择:webpack 5.X:sdk打包工具;react 17.X:用于开发测试sdk功能的页面;typescript:检测类型,非常适合sdk开发。

1、初始化项目

npm init // 按提示填写,可先不配置git地址

2、安装typescript和react、react-dom及其Ts类型。

npm i typescript react react-dom
npm i @types/react @types/react-dom --save-dev

3、初始化tsconfig.json配置文件

tsc --init

此时根目录下已生成tsconfig.json,参考配置:【compilerOptions配置项手册】

{
  "compilerOptions": {
    "outDir": "./dist/", //指定输出文件夹
    "sourceMap": true, //编译时是否生成.map文件
    "removeComments": true, //删除所有注释,除了以 /!*开头的版权信息。
    "noImplicitAny": true, // 没有为值设置明确的类型时,设为false:编译器会默认认为值为any,设为true:报错
    "module": "es6",  //编译后的js要使用的模块标准
    "declaration": true, // 是否生成 .d.ts声明文件
    "declarationDir": "./dist/types/", // 声明文件打包的位置
    "target": "es5",  //编译后的js应遵循的js版本
    "jsx": "react",   //使用前不许转换,输出文件扩展名.js
    "allowJs": true,  //是否允许编译js文件
    "allowSyntheticDefaultImports": true, //允许模块默认导入
    "esModuleInterop": true, //未导入内容创建空间命名,实现commonjs和es模块的互操作性
    "moduleResolution": "node", //模块解析策略:node/classic
  },
  "include": ["./src/**/*","./index.ts","./typings.d.ts"], //指定编译的路径,支持文件夹、通配符等
  "exclude": ["node_modules"], //要排除的,不编译的文件,规则同include
}

4、创建目录结构

根目录下创建srctest两个文件夹。

  • src内放置sdk封装的工具。
  • test内放置测试sdk方法的页面(使用react编写),新建test/index.tsx配置React的DOM入口节点;新建test/App.tsx作为React主页面;新建test/public/index.html作为html主页面。
  • 根目录下新建index.ts文件,作为sdk工具的export文件。 index.ts:
export { Map } from "./src/Map"; //方法导出示例。

test/index.tsx

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "./assets/widgets.css"; //Cesium的样式文件
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

test/App.tsx

import React from "react";

export default function App() {
  return (
    <div>Hello!!!!</div>
  );
}

test/public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
    <link rel="shortcut icon" href="#" />
    <!-- 若引用本地文件,可将Cesium.js放在 /test/public文件夹,webpack.config.dev.js中 devServer.contentBase内配置:
         <script src="./Cesium.js" type="text/javascript"></script> -->
    <script src="http://www.supermapol.com/earth/Build/Cesium/Cesium.js"></script>
    <title>cesium-sdk 测试</title>
  </head>
  <body>
    <div id="root"></div>
    <div id="map"></div>
  </body>
</html>

5、安装webpack

npm i webpack --save-dev
npm i webpack-cli@3.3.12 --save-dev
npm i webpack-dev-server@3.11.2 --save-dev
npm i html-webpack-plugin clean-webpack-plugin --save-dev
  • webpack-cli:cli(Command Line Interface),用于命令行中运行webpack。要想兼容老版本的命令:在使用webpack4.X及以上版本时,可搭配3.X版本的webpack-cli
  • webpack-dev-server:用于webpack开发环境,使用3.X版本(理由同上)。功能:为静态文件提供web服务;自动(局部)刷新。
  • html-webpack-plugin:该插件会在html文件内新建script标签,并引入所有webpack生成的打包文件。
  • clean-webpack-plugin:用于重新打包时,清空打包文件夹内原来的文件。

6、选择webpack转译typescript的方案

本文使用ts-loader+babel-loader+fork-ts-checker-webpack-plugin的方案,转译typescriptWebpack 转译 Typescript 现有方案

npm i ts-loader babel-loader fork-ts-checker-webpack-plugin --save-dev
npm i @babel/core --save-dev
  • ts-loader:webpack 的 TypeScript 加载器。
  • babel-loader:允许使用 babel 和 webpack 编译.js文件。
  • fork-ts-checker-webpack-plugin:在单独的进程上运行 TypeScript 类型检查器的 Webpack 插件。
  • @babel/core:Babel 编译器核心。

7、安装style相关加载器(css、less、png等)

主要用于react测试页面的:

npm i css-loader style-loader less-loader url-loader --save-dev

8、webpack配置文件

本文封装的sdk以工具类为主,因此开发模式下只需要编译React编写sdk测试页面,生产环境下只编译(打包)src目录下的工具类。

根目录下新建webpack配置文件:webpack.config.js 配置项手册

  • webpack.config.dev.js【开发模式:/test/index.tsx作为入口文件】
  • webpack.config.js【生产模式:/index.ts作为入口文件】

webpack.config.dev.js:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");

module.exports = {
  // development:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。
  mode: "development",
  entry: "./test/index.tsx",
  //devtool: "source-map"可build代码,但是速度最慢。
  devServer: {
    hot: true, //热模块:仅更新更改内容;修改js/css后,立即更新浏览器
    contentBase: path.join(__dirname, "./test/public"), //告诉服务器从哪里提供内容
  },
  module: {
    //创建模块时,匹配请求的规则数组。
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          "babel-loader",
          {
            loader: "ts-loader",
            options: {
              // 关闭ts类型检查,只进行转译。使用fork-ts-checker-webpack-plugin做类型检查。
              transpileOnly: true,
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          {
            loader: "less-loader",
            options: {
              lessOptions: {
                javascriptEnabled: true, //支持内联js
              },
            },
          },
        ],
      },
      {
        test: /\.css$/,
        use: [{ loader: "style-loader" }, { loader: "css-loader" }],
      },
      {
        test: /\.(png|jpg|gif|svg|jpeg)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 1000240, //图片大小限制
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
    //按顺序解析后缀名。生产模式下可以不配置.tsx、.jsx。
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./test/public/index.html",
    }),
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        diagnosticOptions: {
          syntactic: true, //语法检查
          semantic: true,  //语义检查
          declaration: true, //声明检查
          global: true,  //全局
        },
      },
    }),
  ],
};

webpack.config.js:

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  // production:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。
  mode: "production",
  entry: "./index.ts",
  output: {
    path: path.resolve(__dirname, "dist"), //输出目录对应绝对路径;  '/dist'
    filename: "cesium-sdk.min.js", //决定bundle输出名称
    library: {
      type: "module", //同output.libraryTarget, 配置如何暴露library(影响打包文件的导出方式:变量/对象/模块)
    },
    // umdNamedDefine: true, //当library.type为'umd'时,umdNamedDefine为true,将命名由umd构建的amd模块
  },
  experiments: {
    outputModule: true, //library.type设置为'module'时,需要启用该项(webpack5供用户启用的实验性特性)
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          "babel-loader",
          {
            loader: "ts-loader",
            // 生产环境,可以选择保留声明文件,不设置transpileOnly
          },
        ],
      },
      //.less .css 和 图片的 rules 配置 同上。
      ...
    ],
  },
  resolve: {
    extensions: [".ts", ".js", ".json"], //可以只选择需要解析的文件类型
  },
  plugins: [new CleanWebpackPlugin()],
};

9、集成webpack配置(package.json)

在package.json文件原有配置基础上,添加以下options:package.json配置项手册

"main": "./dist/cesium-sdk.min.js", //程序的主要入口点。使用该包时,将进入的文件。
"types": "./dist/types/index.d.ts", //指定主声明文件(可选),此为ts的配置
"files": ["dist"], //当包作为依赖项安装时,需要包含的条目
"scripts": {
    "dev": "webpack-dev-server --config webpack.config.dev.js", //开发模式运行命令
    "build": "webpack --config webpack.config.js"  //生产模式运行命令
},

此时已完成sdk模板的配置,npm run dev查看测试页面效果。查看完整代码

二、上传npm

如果本地使用,直接在package.json的依赖中配置git仓库地址即可,这样方便调试,避免频繁更新npm包。

  • 准备工作:需要在npm官网注册账户。

1、项目根目录下,执行npm登陆命令

npm login
// ... 按提示输入用户名、密码、邮箱
npm who am  i // 可验证登陆是否成功

1629862144(1).jpg

  • :如果之前配置过淘宝镜像,这一步可能会报错500 Internal Server Error - PUT https://registry.npm.taobao.org... 解决方案:
npm config set registry https://registry.npmjs.org/    //切回npm.js源
curl https://registry.npmjs.org/     //检查地址
npm cache clean --force       //清除缓存

2、发布npm包

npm publish //每次publish 都要注意package.json里的version不要和上次相同
  • :发布时若403报错
403 Forbidden - PUT https://registry.npmjs.org/cesium-sdk - Forbidden
403 In most cases, you or one of your dependencies are requesting
403 a package version that is forbidden by your security policy.

可能导致原因:

  1. 包名/版本号冲突。可在package.json内修改包名/版本号,然后重新npm publish
  2. 注册npm账号时没有邮箱验证。打开www.npmjs.com/login,如果没有邮箱验证,会在页面的最顶端有邮箱未验证的英文提示,点击验证即可。(我在网上看到过有人对这个问题的解决方案是,新注册一个账户,差点笑死)

最后,在npm搜一下自己上传的包,验证是否上传成功(可能会稍有延迟)。