npm创建React组件

编写时间:

2020年11月03日

前言:

开发npm项目,少不了要跟第三方组件打交道。趁着有时间就想自己搞一个中台框架组件,我最喜欢干这种闭门造轮子的事情。 然而打包组件这玩意水深得很,网上很难查找到趁手的资料,很多都是2018的资料了,加上前端本来就是个更新超快的玩意,结果可想而知。于是一边查资料一边总结,写下该篇文章,希望可以给后来的同学带来帮助。

相关资料

webpack 中文接口介绍:webpack详细中文文档:https://cloud.tencent.com/developer/doc/1250

正文:

一.创建项目

#创建项目目录
mkdir ReveryUI-Frame

#进入目录
cd ReveryUI-Frame

#创建基础项目
npm init

npm init 之后,会进入一个项目创建流程,将会提示以下内容:

> package name: (reveryui-frame)  #项目名称
> version: (1.0.0)  #当前版本
> description: #说明
> entry point: (index.js)  #项目默认入口 
> test command: 
> git repository:  #git地址,可空
> keywords:  
> author:  #作者
> license: (ISC) 
> About to write to /Users/zhuzejun/Documents/junDataMac/jsProject/ReveryUI-Frame/package.json:
> Is this OK? (yes)   #最后一步必须输入yes,才能创建成功。

项目创建成功后,该目录下会多出一个package.json的文件。

> ls
package.json

打开package.json,进行简单的修改,


{
  "name": "reveryui-frame",
  "version": "0.1.0",
  "description": "",
  "main": "index.js",
  "files":["dist"],
  "scripts": {
    "start": "webpack-dev-server --config ./config/webpack.dev.config.js",
    "dev": "npm run start",
    "pub": "npm publish",
    "build": "webpack --config ./config/webpack.prod.config.js"
  },
  "author": "浪荡小野狗",
  "license": "ISC",
  "dependencies":{},
  "devDependencies":{}
}

修改说明:

name:组件名称
version: 组件当前版本
description: 组件说明
main: 组件启动入口
files:组件发布时,上传的文件夹,如果没有,将会上传整个项目。
scripts :运行脚本,npm run 的入口。比如npm run start
author: 作者名称
license:授权许可形式
dependencies:项目依赖的第三方组件
devDependencies:项目依赖的第三方组件(与上面的区别是,这个位置的组件是编译时用的,不参与到打包目标里面去,不过据我了解,只要不在项目应用的话,在哪儿都没所谓。目前我也没发现有啥区别,这里只做说明,不深入讨论。)

二、安装打包需要用到的组件。

# 我这个组件是用react开发的,所以我加入了react的组件。vue的这里不介绍。
> npm i -D react react-dom

# 安装其他打包辅助工具
> npm i -D \
webpack@4.44.2 \
webpack-cli@3.3.12 \
webpack-dev-server \
webpack-node-externals \
@babel/core \
@babel/preset-env \
@babel/preset-react \
@babel/plugin-proposal-class-properties \
babel-loader \
babel-plugin-transform-jsx-url \
css-loader \
style-loader \
html-webpack-plugin \
clean-webpack-plugin \
mini-css-extract-plugin \
less \
less-loader \
optimize-css-assets-webpack-plugin

组件说明:

webpack: 编译工具主程序
webpack-cli: 编译工具脚手架
webpack-dev-server: 编译运行工具,npm run start 实际调用的方法
webpack-node-externals: 避免在编译期间node_modules中的错误
@babel/core: 把开发的nodejs编译成前端可以运行的js代码。
@babel/preset-env
@babel/preset-react: 把react编译成可执行js.
@babel/plugin-proposal-class-properties : 用于支持js 的 class 写法
babel-loader:js编译用到的loader,我理解是对上面@babel的调配工具。
babel-plugin-transform-jsx-url:处理react里面的资源,如img 里面的 src,把资源提取出来。
css-loader: 处理css的工具
style-loader: 处理 dom style 标签里面的css
html-webpack-plugin: 处理html的工具
clean-webpack-plugin: 用于清理之前编译的文件,防止造成过多的碎片。
mini-css-extract-plugin: 用于将css分割成独立文件,否则将会打包进js里面,造成js文件体积过大。
less:css 预处理工具(与less-loader 配套使用)
less-loader :css预处理工具
optimize-css-assets-webpack-plugin:压缩css的插件

工具介绍:

webpack5.x版本的确是一个吸引人的版本,这里推荐两篇文章(上下篇),有兴趣的可以看看,但是不建议直接用5.x部署项目,因为目前很多插件还没支持。文章链接:
https://juejin.im/post/6844903795286081550#heading-0

https://juejin.im/post/6850037279767920653

webpack 目前最新版本为(5.x+),这里为什么还在用4.x+?因为其他很多与webpack相关目前并不兼容5.x+版本, 实属于无奈之举。

故此,上面安装webpack和webpack-cil时,必须指定版本。否则会出现安装错误的情况。

ok , 工具准备好了,接下来开始进行,编码和进行打包测试。当前我的项目文件情况如下(我用的开发工具是idea,那个.idea是开发工具弄出来的文件夹,请无视):
npm创建React组件_第1张图片

三. 生产环境配置

在根目录下创建一个config文件夹

# 创建config文件夹
> mkdir config

在config文件夹下创建一个生产环境的配置文件 webpack.prod.config.js ,文件内容为:

/**
 * @author revery.Top zzjun
 * @date 11/2/20 01:30
 *
 * 说明: webpack 生产环境编译配置
 */
const path = require('path');

// js压缩和分割
const TerserPlugin = require('terser-webpack-plugin');

// 过滤node_modules内容,不编译进打包文件。
const nodeExternals = require('webpack-node-externals');

// 提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 压缩css插件
const optimizeCss = require('optimize-css-assets-webpack-plugin');

/**
 * webpack entry设置
 *
 * 打包入口
 * */
const getEntry = () => {
    return {
        index: './src/index.js'
    }
}

/**
 * webpack output设置
 *
 * --- 编译后,输出设置 ---
 *
 * */
const getOutput = () => {
    return {
        // 打包文件输出名称,默认为 main.js , [name]与 getEntry的设置有关,根据我上面的设置,最终打包结果为 index.js
        filename: '[name].js',

        // 打包文件输出目录
        path: path.resolve(__dirname, '../dist'),

        libraryTarget: 'commonjs2'
    };
}

/**
 * webpack plugins 设置
 *
 * webpack插件
 *
 * */
const getPlugins = () => {
    return [

        // 提取css插件
        new MiniCssExtractPlugin({
            filename: "[name].min.css"
        }),

        // 压缩css插件
        new optimizeCss({
            assetNameRegExp: /\.style\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorOptions: {discardComments: {removeAll: true}},
            canPrint: true
        }),

    ];
}

/**
 * rules 设置
 *
 * 编译过程中,读取到的每一个文件都会经过 rules 处理
 *      test: 符合预期需要处理正则表达式
 *      use: [] , 将文件交给某个处理器进行处理。
 *      exclude: 不处理的文件路径。
 * */
const getRules = () => {
    let rules = [];

    // 处理js
    rules.push({
        test: /\.(js|jsx)$/,
        use: [
            {
                loader: 'babel-loader',
                options: {
                    // babel默认不支持react的jsx语法,需要添加另外的编译器
                    presets: [
                        "@babel/preset-env",
                        "@babel/preset-react"
                    ],

                    // 用于识别jsx中的图片资源
                    plugins: [
                        [
                            "transform-jsx-url",
                            {
                                // 目录前缀,不知道是不是因为bug的原因,这个工具无法识别 "./" 这个当前目录标识符
                                root: '../src/',
                            }
                        ],
                        
                        // 支持js class形式写法
                        "@babel/plugin-proposal-class-properties",
                    ]
                }
            }
        ],

        // 这个可以忽略的,因为已经设置了全局过滤. 全局过滤设置在这里 -> externals: [nodeExternals()]
        exclude: /node_modules/,
    });

    // 处理css
    rules.push({
        test: /\.(css|less)$/,
        use: [
            MiniCssExtractPlugin.loader,
            "css-loader",
            {
                loader: "less-loader",
                options: {
                    // 用于识别 less 里面 @import 引用的 其他样式
                    lessOptions:{
                        javascriptEnabled: true
                    }
                }
            }
        ]
    });

    // 处理图片
    rules.push({
        test: /\.(png|gif|svg|ico|jpe?g)$/i,
        use: [
            {
                //这里用url-loader来处理图片
                // file-loader 与 url-loader 功能很像
                loader: 'url-loader',
                //options里面可以配置详细的处理信息
                options: {
                    /**
                     * limit: 3072,
                     * 大于3k的图片,输出成文件。少于3k的图片,编码成base64的图片格式,保存在js中。
                     * ---- 为了达到测试效果,我这里设置了,任何时候都输出文件。
                     * */
                    limit: 0,

                    /**
                     * 这个老哥一看就是输出文件的名称方式了
                     * */
                    name: './assets/[name].[ext]',     //  打包文件名
                }
            }
        ],
    });

    return {rules}
}

/**
 * 该属性为webpack4.x新增的优化项,具体参考官方文档:
 *      官方文档: https://webpack.js.org/configuration/optimization/#root
 * 这里实现了 js/css 压缩、分割
 * */
const getOptimization = () => {
    return {
        minimize: true, // 启用代码压缩
        minimizer: [
            // 压缩、分割(需要在下面定义分割内容) js
            new TerserPlugin(),

            // 压缩css
            new optimizeCss({})
        ],
        splitChunks: {
            minSize: 1, // 3kb = 3072 表示在压缩前的最小模块大小,默认值是30kb, 测试需要设置为了1b,任何时候都打包出独立文件。
            chunks: 'all',//同时分割同步和异步代码,推荐。
            automaticNameDelimiter: '_',//名称分隔符,默认是~
            cacheGroups: {  //默认的规则不会打包,需要单独定义
                // baseTool.js不会被打包到index.js里面
                baseTool: {
                    name: 'baseTool',
                    chunks: 'all',
                    test: /baseTool\.js/,
                    enforce: true
                }
            }
        }
    }
}

/**
 * webpack 编译配置
 * */
const webpackConfig = {
    // 打包模式,development,开发模式。production 生产模式。
    mode: 'production',

    // 打包入口
    entry: getEntry(),

    // 编译后的文件输出保存设置
    output: getOutput(),

    // 打包用到的插件
    plugins: getPlugins(),

    // 打包过程遇到的非js,进行其他预处理。webpack只能编译js文件,其他css、图片等,需要通过这个配置,设置给其他工具去处理。
    module: getRules(),

    // 设置 js/css 压缩、分割
    optimization: getOptimization(),

    // 这个是用来过滤编译时,无视 node_modules 等文件用的。
    // 也就是文章中提及的 webpack-node-externals 工具。
    externals: [nodeExternals()]

};

module.exports = webpackConfig;
  1. 文档写到这里断断续续,已经写了好多天了,本来还想把配置的步骤一步步写出来的,后来想想还是算了,具体的过程,我都写在了上面配置文件的备注里面,有兴趣的学习的,可以把每个组件都百度一遍,很多都有介绍。(2020年11月06日)

  2. 唯一找不到介绍的组件 babel-plugin-transform-jsx-url ,这个组件是把React里面图片的url识别出来。我测试过,这个工具只会识别属于本地开发的图片,网络图片会忽略,这个很好。

  3. 相信能看到这里的,都是真爱了,都是有缘之人。我将会在文章末尾,添加git地址,可以拉下来看看我最终的部署成果。所有代码跟本文章一致!

  4. 到此! 生产环境的打包过程部署好了。下面开始进行开发环境的配置。

四. 开发环境配置

在config文件夹下创建一个生产环境的配置文件 webpack.dev.config.js ,文件内容为:


const path = require('path');

const devConfig = {
    entry: './demo/index.js', // 入口文件, 最终打开的是这个页面
    mode: 'development', //打包为开发模式
    output: {
        path: path.resolve(__dirname, '../demo'), // 输出的文件目录
        filename: 'demo.bundle.js' // 输出的文件名称, 在 /demo/index.html 里面必须要调用 demo.bundle.js 这个文件
    },
    module: {
        rules: [
            { // 在webpack中使用babel需要babel-loader
                test: /\.(js|jsx)$/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            // babel默认不支持react的jsx语法,需要添加另外的编译器
                            presets: [
                                "@babel/preset-env",
                                "@babel/preset-react"
                            ],

                            // 用于识别jsx中的图片资源
                            plugins: [
                                [
                                    "transform-jsx-url",
                                    {
                                        // 目录前缀, 这里是相对于当前配置文件的位置,所以要返回上一层的src目录获取图片
                                        root: '../src',
                                    }
                                ],
                            ]
                        },
                    }
                ]
            },


            // 用于解析css,less
            {
                test: /\.(css|less)$/,
                use: [
                    "style-loader", //使代码中的 import './index.less' 能生效, 生产环境因为要打包出独立css,所以不使用这个组件。
                    "css-loader",
                    {
		                loader: "less-loader",
		                options: {
		                    // 用于识别 less 里面 @import 引用的 其他样式
		                    lessOptions:{
		                        javascriptEnabled: true
		                    }
		                }
		            }
                ]
            },

            // 用于加载组件或者css中使用的图片
            {
                test: /\.(png|gif|svg|ico|jpe?g)$/i,
                use: [
                    {
                        //这里用file-loader来处理图片
                        loader: 'file-loader',
                        options: {
                            /**
                             * 如果不设置这个,图片的src 就会变成一个Module对象。
                             *   因为transform-jsx-url会把  转换为 
                             * */
                            esModule: false,

                            name: '[name].[ext]',
                            outputPath: './assets'
                        }
                    }
                ],
            }
        ]
    },

    // 该字段用于配置webpack-dev-server
    devServer: {
        contentBase: path.join(__dirname, '../demo'),
        compress: true,
        port: 9000, // 端口9000
        open: false // 自动打开浏览器
    }
}

module.exports = devConfig;


五. 测试

调用编译得到最终结果


> npm run build

npm创建React组件_第2张图片

六. 示例

最后献上最终开发示例
gitee: https://gitee.com/junos2/npm-react-component-example

七. 上传npm
#设置正确的npm地址,有些人用了阿里云或者网易之类的第三方镜像地址的。必须重置回npm的地址
> npm config set registry https://registry.npmjs.org/

# 创建一个npm账号,输入用户名,密码,邮箱
> npm adduser

# 登陆
> npm login

.......

# 上传组件
> npm publish
  1. 到这里要说明一下,创建账号后,不能直接就可以上传的,需要到npm官方去激活一下账号,并创建一个packages包。
  2. 具体操作流程:到npm官方登陆后,在右上角的头像箭头里面,鼠标移上去,有个下拉菜单,选择里面的packages,然后在里面添加你要创建的组件名称,要更你上传的组件名称一致。
  3. 每次上传更新时,必须修改 package.json 里面的版本号
  4. npm官方地址:https://www.npmjs.com/

你可能感兴趣的:(js前端,npm+raeat,开发组件,webpack,node,javascript)