React+Typescript+Less组件Webpack打包发布到npm仓库

597 阅读8分钟

前提:你得懂点前端的东西,略懂就行,不懂就别看了,学不会的,因为你可能要踩坑🙃。

一、初始化项目

新建你的项目文件夹,然后把你的项目文件夹初始化。

npm init

在使用初始化命令时,会提示输入项目的名称、版本号、关键词、作者等,如果还没想好这些信息那就combo回车,后续在package.json文件也可进行进行修改。然后你的文件夹就会出现这个package.json文件。 然后顺手添加三个开发、打包相关文件,demo、lib和src,如下

├── demo # 开发时预览代码
       ├── src # 示例代码目录
            ├── index.jsx/tsx # 入口 jsx/tsx 文件
            ├── index.html # 入口 html 文件
            └── index.less # 样式文件
├── lib # 组件打包结果目录
├── src # 组件源代码目录
     └── index.tsx  # 组件源代码

二、安装依赖

2.1 安装React相关依赖

npm i react react-dom -D

2.2 安装Webpack相关依赖(想看其他打包方式的就别往下看了,浪费生命)

提下这个webpack-merge包:生产环境和开发环境做区分,不重复写配置,保留一个 "common( 公共 )" 配置。配置合并。此工具会引用 “common” 配置,so就没必要再在环境特定env的配置中编写重复代码。

npm i webpack webpack-cli webpack-dev-server webpack-merge -D

顺手在你的项目文件下创建webpack配置相关文件,新增config文件夹,该文件夹下增加三个js配置文件,如下:

├── config  # webpack配置
     ├── webpack.base.js # 公共配置
     ├── webpack.dev.config.js # 开发环境配置
     └── webpack.prod.config.js # 生产-打包发布环境配置

2.3 安装css相关依赖

npm i postcss postcss-loader postcss-preset-env style-loader css-loader less-loader less mini-css-extract-plugin -D

2.4 安装babel相关依赖

npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react -D

2.5 安装typescript相关依赖

npm i @types/react @types/react-dom ts-loader @babel/preset-typescript
npm i babel-loader -D

到目前为止你的文件目录应该如下:

├── node_modules
├── config  # webpack配置
     ├── webpack.base.js # 公共配置
     ├── webpack.dev.config.js # 开发环境配置
     └── webpack.prod.config.js # 生产-打包发布环境配置
├── demo # 开发时预览代码
       ├── src # 示例代码目录
            ├── index.jsx/tsx # 入口 jsx/tsx 文件
            ├── index.html # 入口 html 文件
            └── index.less # 样式文件
├── lib # 组件打包结果目录
├── src # 组件源代码目录
     └── index.tsx  # 组件源代码
├── package.json
└── package-lock.json

三、配置webpackconfig

3.1 配置webpack.base.js

module.exports = {
  resolve: {
    // 定义 import 引用时可省略的文件后缀名
    extensions: [".js", ".jsx", ".ts", ".tsx"],
  },
  module: {
    rules: [
      {
        // 编译处理 js 和 jsx 文件
        test: /(\.js(x?))$/,
        use: [
          {
            loader: "babel-loader",
          },
        ],
        exclude: /node_modules/, // 只解析 src 目录下的文件
      },
      {
        // 编译处理 js 和 jsx 文件
        test: /(\.ts(x?))$/,
        use: [
          {
            loader: "ts-loader",
          },
        ],
        exclude: /node_modules/, // 只解析 src 目录下的文件
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [
          {
            loader: "url-loader",
            // options: {
            //     name: 'images/[name].[ext]',
            //     esModule: false,
            // },
          },
        ],
        // type: 'javascript/auto'
      },
    ],
  },
};

3.2 配置webpack.dev.config.js

const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js'); // 公共配置

const devConfig = {
    mode: 'development', // 开发模式
    entry: path.join(__dirname, "../demo/src/index.jsx"), // 入口,处理资源文件的依赖关系
    output: {
        path: path.join(__dirname, "../demo/src/"),
        filename: "dev.js",
    },
    module: {
        rules: [
            {
                test: /.less$/,
                exclude: /.min.css$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: {
                                mode: "global"
                            }
                        }
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        'postcss-preset-env',
                                        {
                                            // 其他选项
                                        },
                                    ],
                                ],
                            },
                        },
                    },
                    { loader: 'less-loader' }
                ]
            },
            {
                test: /.min.css$/,
                use: [
                    { loader: 'style-loader' },
                    { loader: 'css-loader' },
                ]
            },
        ]
    },
    devServer: {
        static: path.join(__dirname, '../demo/src/'),
        compress: true,
        host: '127.0.0.1',
        port: 6666, // 启动端口
        open: true // 打开浏览器
    },
};
module.exports = merge(devConfig, baseConfig); // 合并配置

3.3 配置webpack.prod.config.js

const path = require("path");
const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.base.js"); // 引用公共的配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 用于将组件的css打包成单独的文件输出到`lib`目录中

const prodConfig = {
  mode: "production", // 生产模式
  entry: {
    index: path.join(__dirname, "../src/index.tsx"),
  },
  output: {
    path: path.join(__dirname, "../lib/"),
    filename: "index.js",
    libraryTarget: "umd", // 采用通用模块定义
    libraryExport: "default", // 兼容 ES6 Module、CommonJS 和 AMD 模块规范
  },
  module: {
    rules: [
      {
        test: /.less$/,
        exclude: /.min.css$/,
        use: [
          { loader: "style-loader" },
          { loader: MiniCssExtractPlugin.loader },
          {
            loader: "css-loader",
            options: {
              modules: {
                mode: "global",
              },
            },
          },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      // 其他选项
                    },
                  ],
                ],
              },
            },
          },
          {
            loader: "less-loader",
          },

        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "index.min.css", // 提取后的css的文件名
    }),
  ],
  externals: {
    // 定义外部依赖,避免把react和react-dom打包进去
    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(prodConfig, baseConfig); // 合并配置

3.4 添加并配置.babelrc

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

配置完成之后需要修改package.jsonsctrpts中的本地运行和打包命令换成上面的修改的配置,命令如下:

  "scripts": {
    ...
    "dev": "webpack-dev-server --config config/webpack.dev.config.js",
    "build": "webpack --config config/webpack.prod.config.js"
  },

到目前为止 接下来就是紧张刺激的组件开发环节了。先给项目添加相关文件,如下(之前写的漏了啥给忘了,尬,忽略吧)

补充 tsconfig文件配置-来自评论区

{  
    "compilerOptions": {  
        "target": "ES6",  
        "module": "commonjs",  
        "esModuleInterop":true,  
        "allowJs":true,  
        "jsx": "react"   
    },  
    "include": [  
        "src/**/*.ts",  
        "src/**/*.tsx",  
        "demo/**/*.tsx",  
        "demo/**/*.ts"  
    ],  
}

四、组件开发

4.1 开发组件(就像你正常开发一样哈,不赘述)

src/index.tsx中开发你的组件,在src/index.less开发你的样式

import React from 'react';
function YourCom() {
    return <div className="yourcom">YourCom</div>
}
export default YourCom
.yourcom {
    background:#dad;
}

4.2 本地调试组件(也像你正常开发一样,不赘述,如果你对你的组件很自信,这一步pass)

就像你正常开发一样,开发时实时预览组件的效果你是不是得本地运行,不是请pass,在demo目录下新建index.htmlindex.jsx/tsx两个文件,index.html的内容以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>YourCom</title>
    <style>
        html, body, p {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    //这里引用dev环境配置里面打包的对象文件
    <script src="dev.js"></script>
</body>
</html>

index.jsx/tsx内容就是应用你组件的react项目根节点,内容如下:

import React from "react"
import { createRoot } from 'react-dom/client';
import YourCom from '../../src/index';//你的组件
import './index.less' //这个是demo的index,别搞错了哈,这个样式文件自己随意写吧
const App = () => {
    return (
        <div className="container" style={{height:"100%",width:"100%"}}>
            <div className="text">
                <p>Demo</p>
            </div>
            <YourCom />
        </div>
    );
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App />);

如上文件的代码编写完,就可在根目录下打开cmd,执行npm run dev命令,进你的主页面(host和port在配置文件有,也可以自己配),能看到你组件,那就成功了,如果没有,要么你组件写的有问题,要么你上面配置没配对(抄作业都抄不好?)

4.3 打包和测试组件

在打包发布之前,还有一个坑要填写,如果你用的是严格模式"strict": true,你还得把你组件申明写好了!!!。不然你在引用的时候会提示,没有TS类型!! 首先修改你的tsconfig.json"noEmit"字段改成false, 然后在src下和组件index同级添加index.d.ts文件,如下:

declare module 'yourcomname'
declare module '*.less' { 
    const content: any; 
    export default content; 
}
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
1.打包组件

完成组件的开发,在发包以前,需要对本身组件的功能进行打包和测试。前面已经在package.json中添加了打包组件的命令,然而在执行npm run build以前,还需要在package.json中修改main字段,用作是声明组件的入口文件。

"main": "lib/index.js",

再给package.json润色下打包的配置,package.json的代码如下:

{
  "name": "yourcomname",   //你组件名字
  "version": "0.0.1",   //组件版本,npm pub的版本每次都得不一样
  "description": "HelloWorld",  // √ 组件简介
  "main": "lib/index.js",  //√ 打包之后的主入口就是你的lib
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "dev": "webpack-dev-server --config config/webpack.dev.config.js",
    "build": "webpack --config config/webpack.prod.config.js"
  },
  "keywords": [
    "react",
    "text-auto",
    "width-auto",
    "text-width-auto"
  ],  // √ 组件的关键词,便于搜索
  "author": "jacky010",  //作者原则上是你自己
  "license": "ISC",   //协议
  ... ...

接下来,则可使用npm run build命令打包开发好的组件,如果失败了,很遗憾你抄作业有抄错了,怎么肥四,如果成功,项目根目录的lib目录下会出现index.js文件(如果组件含有样式文件,那将会出现index.min.css文件,就是而配置文件中压缩的css这是个大坑,后面使用要填)。

2.测试打包组件

使用npm link把打包以后的组件引入到全局node-modules中; 然后进入demo/src文件夹下,执行npm link yourcomname命令(注意:这里的name一定要是package.json配置的name,必须一致!!!!!!);而后修改demo/src/index.jsx/tsx的内容:

// import YourCom from '../../src/index'; // 注释它引入组件
import YourCom from 'yourcomname'; // √ 引入node-modules中的组件

如果现在的页面和你之前直接开发编译的页面一样那么恭喜你,离成功还有一步之遥!!!!

五、编写Readme文档(不想写或者后期可以补的,直接pass)

手写也行用插件也行,这里采用的是readme-md-generator插件,readme自动生成工具。 执行npx readme-md-generator命令,接着可以做一些配置,也可以一路enter键,最后再去生成的文件中做详细配置。这个你自己去玩吧,下一步!!

六、发布!!!

发布npm包需要登录npm账号

npm adduser

按提示信息输入本身的用户名、密码及邮箱后,若是命令行输出Logged in as <user> on https://registry.npmjs.org/,则说明帐号注册并登录成功。 若是已经有npm帐号,则直接使用npm login登录。 切换你要上传的npm仓库地址,可使用npm config list查看当前使用的源地址。以下是官方地址。

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

使用命令npm pub默认的发包方式,执行package.json中的脚本:

npm pub

如果成功了那么恭喜你结束了,可以去搜搜你发布的组件了!!!

当然你可以把你的组件上传到git仓库,方便用户查看和提issue或者star

七、使用组件

找个空白的项目,安装你的组件。

npm i yourcomname
import YourCOm from "yourcomname";
import 'yourcomname/lib/index.min.css' //没有样式文件就不要,有的话得加你压缩的css,不然你样式就丢了

Over

PS:本文严重抄袭 原作者 黑土豆 的文章 (建议收藏)使用React+Typescript开发组件并发布到npm仓库

欢迎各位小伙伴批评指正😘