从0到1,一步步开发React的loading组件,并发布到npm上
没有发布过npm包的同学,可能会对NPM对开发有一种蜜汁敬畏,觉得这是一个很高大上的东西。甚至有次面试,面试官问我有没有发过npm包,当时只用过还没写过,我想应该挺难的,就小声说了没有,然后就让我回去了o(╯□╰)o。
其实,在现在的我看来,npm包就是一个我们平时经常写的一个export出来的模块而已,只不过跟其它业务代码耦合性低,具有较高的独立性。
当然,要发布一个npm包,除了写的模块组件外,还需要做一些基础的包装工作。
下面我就以最近开发的react-loading组件为例
源码地址,如果对你有帮助的话希望不要吝啬你的 Star
本文主要记录一下如何开发react组件(以react-loding为例),并在 npm 上发布。废话不多说,进入正题
创建组件
新建项目仓库并初始化
在github上新建一个仓库,名字可以自己取。确保你创建的组件名称没有在 npm 上被使用过, 这里我们用 wsm-loading作为示例。打开编辑器将项目拉到自己本地并初始化
git clone https://github.com/xxx/wsm-loading.git cd wsm-loading npm init
运行npm init问题提示列表可以根据自己的个人爱好填写,也可以采取默认的选项。
安装项目的依赖
我么需要开发的是一个React的loading组件,所以我们要先在项目中安装react依赖
npm i react react-dom -D
我们的项目将通过webpack4进行构建,安装项目所需的webpack依赖
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader babel-core babel-loader babel-preset-env babel-preset-react -D
在项目中我们经常会用到scss作为css的编译,因此我们在安装webpack
npm install sass sass-loader node-sass webpack -D
这时上面安装的依赖已经被添加到根目录下的 package.json中了,接下来我们添加一个 start的脚本,用于启动我们本地开发的服务器, start如下:
{ "name": "wsm-loading", "version": "0.0.1", "description": "这是一个react-loading组件", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --mode development", },
开发loading组件
现在让在我们的项目中创建组件和示例代码目录,目录树结构如下
├── examples // 示例代码存放目录 │ └── src ├── node_modules ├── package.json └── src // 组件源代码和样式存放目录 └── .babelrc // es6及jsx语法编译 └── .gitignore // git提交管理 └── package-lock.json └── package.json └── webpack.config.js webpack配置
lodaing组件接收两个props,一个size控制loading的大小,一个color控制loading的颜色
/*** src/index.js ***/ import React, { Component } from "react"; import "./index.scss"; export default class MyComponent extends Component { render() { const { color, size } = this.props; const sizeStyle = { width: `${size}px`, height: `${size}px` }; const colorStyle = { border: `1px solid ${color}`, borderColor: `${color} transparent transparent transparent` }; const ringStyle = Object.assign({}, colorStyle, sizeStyle); return ( <div className="loading" style={sizeStyle}> <div className="loading__ring" style={ringStyle} /> <div className="loading__ring" style={ringStyle} /> <div className="loading__ring" style={ringStyle} /> </div> ); } } MyComponent.defaultProps = { size: '36', color: '#000' }
loading组件的样式这里就不给了,可以直接从我的代码库中复制到你的项目里css。
接下来我们添加一个演示demo
<!-- examples/src/index.html --> <html> <head> <title>My Component Demo</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> </head> <body> <div id="root"></div> </body> </html>
/*** examples/src/index.js ***/ import React from 'react'; import { render} from 'react-dom'; import MyComponent from '../../src'; const App = () => ( <MyComponent size='36' color='red' /> ); render(<App />, document.getElementById("root"));
loading组件优化
以上就完成了一个基本的loading组件,但是我们还有一个疑问,就是现在引入的loading是在类名为root的元素之中,而一些被广泛使用的UI框架中的loading组件确实在body层,无论你在哪里引入,这样就可以防止loading组件受到父组件的样式的干扰。
我们平时普通的组件的html结构如上图,可见我们的loading是嵌套在我们的主体root元素中的。在仔细看下面这张图。
想要实现这种效果,我们必须得先了解React自带的特性:Portals(传送门)。这个特性是在16版本之后添加的。
我们哎src目录下新建一个newPortal/newPortal.js文件
import React from 'react'; import ReactDOM from 'react-dom'; class NewPortal extends React.Component { constructor(props) { super(props) this.node = document.createElement('div'); document.body.appendChild(this.node); } render() { const { children } = this.props; return ReactDOM.createPortal( children, this.node, ); } } export default NewPortal
将其引入到我们的loading组件中
import NewPortal from './newPortal/newPortal'
并对组件进行一层包裹
<NewPortal> <div className="loading" style={sizeStyle}> <div className="loading__ring" style={ringStyle} /> <div className="loading__ring" style={ringStyle} /> <div className="loading__ring" style={ringStyle} /> </div> </NewPortal>
这里作者是参考这篇文章对组件进行优化的。大家感兴趣可以去了解下。
配置webpack
接下来配置 webpack, 在项目根路径下创建 webpack.config.js文件。关于webpack配置不太了解或不熟悉的可以参考我之前之篇文章webpack详细配置,这里讲不在一一讲解。
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: path.join(__dirname, "examples/src/index.html"), filename: "./index.html" }); module.exports = { entry: path.join(__dirname, "examples/src/index.js"), output: { path: path.join(__dirname, "examples/dist"), filename: "bundle.js" }, module: { rules: [ { test: /\.(js|jsx)$/, use: "babel-loader", exclude: /node_modules/ }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, { test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] } ] }, plugins: [htmlWebpackPlugin], resolve: { extensions: [".js", ".jsx"] }, devServer: { port: 3001 } };
配置babelrc
最后需要指定 Babel 需要对哪些文件进行编译,毫无疑问 React 中使用的 JSX 文件需要被编译,让它转换成被主流浏览器都支持的 ES5 ,通用的配置也很简单,只需要添加一对 presets,在项目根目录下添加文件.babelrc
{ "presets": ["env", "react"] }
接下来运行 demo
npm start
启动完成后打开浏览器输入 http://localhost:3001,你将会在页面上看到你写的组件,你可以修改你的代码并保存,页面将会自动刷新,我们的开发环境已经处于监控模式
发布到npm上
我们要发布被 babel 编译且被压缩后的版本,要让没有使用 babel 的项目也能够正常的使用,比如不能出现 JSX 语法。
首先需要安装 babel cli
npm i babel-cli -D
现在我们添加 transpile脚本,以便使用 Babel 编译我们的源代码,同时拷贝一些静态文件(如:css 文件)到目标打包目录dist下
同时指定被编译后的版本为组件的主入口,更改后的 package.json如下
{ "name": "test", "version": "1.0.0", "description": "ceshi", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --mode development", "transpile": "babel src -d dist --copy-files" },
尝试编译
npm run transpile
最后让我们在项目的根目录下添加.npmignore文件,告诉 npm,我们项目中哪些文件和文件夹是在发布的包中被忽略掉的
# .npmignore src examples .babelrc .gitignore webpack.config.js
发布我们的组件到 npm 上
npm publish
提交npm之前需要去npm上开通自己的账号,并验证邮箱,这样才能正常的提交上去。在终端使用npm login登陆自己的信息
将代码上传到git
项目上传到git之前,我们需要配置下.gitignore,在项目的根目录下添加.npmignore文件
# .gitignore node_modules dist
这样我们上传时,不会上传node_modules和dist的目录
git add . git commit -m 'react-loading组件' git push
后语
当我们不停的陷于业务代码的开发中,何不尝抽出一点时间来做点自己喜欢做的事呢,比如将自己的业务组件抽出来做成一个npm,开源组件库不仅可以提高自己还能接触到原来接触不到东西。这只是一个简单的教程,希望能对你有所启发。
感谢你的阅读!