react组件从搭建脚手架到在npm发布的步骤实现


Posted in Javascript onJanuary 09, 2019

最近公司给公司里架设了私有的npm仓库,相应地也需要一个用来发布react组件用的脚手架,在这个过程中又又又又复习了一下webpack,在这里分享下脚手架搭建的过程。

首先,我们预期的脚手架具有如下功能

  • 开发组件时可以实时预览
  • 对组件各种资源进行打包(js/css/图片等)
  • 一键打包发布

1.创建项目

脚手架的名字暂时取react-simple-component-boilerplate。

首先创建一个新目录用于放我们的文件:

mkdir react-simple-component-boilerplate
cd react-simple-component-boilerplate

使用npm命令创建一个项目

npm init

接下来会提示你输入项目的名称、版本号、作者等,也可以一路回车,稍后修改。

这一步完成后,你的项目文件夹里应该有一个package.json文件了,这个文件保存了我们项目和组件的各种信息。

接下来创建如下的目录结构

react-simple-component-boilerplate
 |-- config // webpack配置
 |-- demo // 开发时预览用
 |-- dist // 打包结果
 |-- src  // 源文件目录
  | -- assets // 存放图片等媒体文件
  | -- style // 存放样式,项目使用的是less来编写样式

2.安装依赖

既然我们要发布的是react组件,那依赖里肯定少不了react。

使用npm install安装下面的依赖

npm install react react-dom --save

打包工具选择的是webpack,下面是开发依赖,也需要一并安装

"devDependencies": {
 // babel用于将你写的es6+的代码转换到es5
 "@babel/cli": "^7.0.0",
 "@babel/core": "^7.0.0",
 "@babel/plugin-proposal-class-properties": "^7.0.0", // 用于支持class属性
 "@babel/plugin-proposal-decorators": "^7.0.0", // 支持decorator
 "@babel/plugin-transform-modules-commonjs": "^7.0.0",
 "@babel/plugin-transform-runtime": "^7.0.0", // 自动polyfill es5不支持的api特性
 "@babel/preset-env": "^7.0.0", // 根据目标环境来按需转码
 "@babel/preset-react": "^7.0.0", // 让babel支持react语法
 "babel-loader": "^8.0.0",
 "css-loader": "^1.0.0",
 "file-loader": "^2.0.0",
 "html-loader": "^0.4.4",
 "less-loader": "^4.1.0", // 使用less来编写样式
 "mini-css-extract-plugin": "^0.5.0", // 将css提取成一个单独的文件
 "style-loader": "^0.23.0",
 "webpack": "^4.26.0",
 "webpack-cli": "^3.1.2", // webpack4之后需要额外安装webpack-cli
 "webpack-dev-server": "^3.1.14", // 开发时预览组件所用的服务,在文件变化时会自动刷新页面
 "webpack-merge": "^4.1.4" // 用于合并webpack配置
 },

3.编写组件

在/src目录下新建一个index.js,这就是我们组件的入口文件了。

如果项目中要使用图片、css等,分类放到assets、style文件夹下就好。

下面我们就在index.js中写一个简单的组件

/* src/index.js */

import React from 'react';
import './style/style.less'; // 使用less的情况
import testPng from './assets/test.png'; // 使用图片的情况

export default class MyComponent extends Component {
 render(){
  return (<div>A new Component</div>)
 }
}

接下来,我们在/demo目录下新建index.html和demo.js这两个文件用于在开发组件时预览组件效果。
index.html内容如下

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<div id="root"></div>
<script src="demo.bundle.js"></script>
</body>
</html>

在demo.js中,我们要使用一下刚刚写的组件(位于/src/index.js)看一下效果,开发中这个demo.js文件会被打包成demo.bundle.js,就是在上面index.html中引用的js。

import React from 'react';
import ReactDom from 'react-dom';
import MyComponent from '../src/index'

const Demo = () => {
 return <div>
 <h1>组件预览:</h1>
 <MyComponent />
 </div>
}

ReactDom.render(<Demo />, document.getElementById('root'));

4.配置webpack和babel

4.1 配置webpack

在/config下我们建立三个webpack配置文件

  • webpack.base.js
  • webpack.config.dev.js // 开发时的配置
  • webpack.config.prod.js // 打包发布时的配置

由于开发和发布打包时webpack的配置有一部分是公共而且重复的,我们把这部分的配置单独拿出来放到webpack.base.js中。
首先是公共配置webpack.base.js:

module.exports = {
 module: {
 rules: [
  { // 在webpack中使用babel需要babel-loader
  test: /\.js?$/,
  loader: 'babel-loader',
  exclude: '/node_modules/',
  },
  { // 用于加载组件或者css中使用的图片
  test: /\.(jpg|jpeg|png|gif|cur|ico|svg)$/,
  use: [{
   loader: 'file-loader', options: {
   name: "images/[name][hash:8].[ext]"
   }
  }]
  }
 ]
 }
}

下面是开发时所用的webpack配置,写在webpack.config.dev.js中

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

const devConfig = {
 entry: './demo/demo.js', // 入口文件
 mode: 'development', // 打包为开发模式
 output: {
 filename: 'demo.bundle.js', // 输出的文件名称
 path: path.resolve(__dirname, '../demo') // 输出的文件目录
 },
 devServer: { // 该字段用于配置webpack-dev-server
 contentBase: path.join(__dirname, '../demo'),
 compress: true,
 port: 9000, // 端口9000
 open: true // 自动打开浏览器
 },
 module: {
 rules: [
  { // 编译less
  test: /\.less$/,
  exclude: '/node_modules/',
  use: [{
   loader: 'style-loader'
  }, {
   loader: 'css-loader'
  }, {
   loader: 'less-loader'
  }]
  },
 ]
 },
}

module.exports = merge(devConfig, baseConfig); // 将baseConfig和devConfig合并为一个配置

需要注意的是,等会使用webpack-dev-sevrer启动开发服务时,并不会实际在demo文件夹下生成demo.bundle.js,打包好的文件是在内存中的,但并不影响我们使用。

下面是打包发布时所用的webpack配置,写在webpack.config.prod.js中

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

const devConfig = {
 entry: './src/index.js',
 mode: 'production',
 output: {
 path: path.resolve(__dirname, '../dist'),
 filename: 'index.js', // 输出文件
 libraryTarget: 'umd', // 采用通用模块定义, 注意webpack到4.0为止依然不提供输出es module的方法,所以输出的结果必须使用npm安装到node_modules里再用,不然会报错
 library: 'react-simple-component-boilerplate', // 库名称
 libraryExport: 'default', // 兼容 ES6(ES2015) 的模块系统、CommonJS 和 AMD 模块规范
 },
 externals: {
 react: {
  root: "React",
  commonjs2: "react",
  commonjs: "react",
  amd: "react"
 },
 "react-dom": {
  root: "ReactDOM",
  commonjs2: "react-dom",
  commonjs: "react-dom",
  amd: "react-dom"
 }
 },
 module: {
 rules: [{
  test: /\.(le|c)ss$/,
  use: [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
   loader: "less-loader",
   options: {
   sourceMap: false
   }
  }
  ]
 }
 ]
 },
 plugins: [
 new MiniCssExtractPlugin({
  filename: "main.min.css" // 提取后的css的文件名
 })
 ],
}

module.exports = merge(devConfig, baseConfig);

上面我们配置了externals字段,这一点非常重要。

externals定义了外部依赖。将react和react-dom添加进该字段,说明我们的组件将依赖外部的react和react-dom,这样就可以避免把react和react-dom打包进去(不然组件会很大)

4.1 配置babel

我们需要用babel把我们的代码编译成es5版本。在项目根目录新建一个.babelrc文件,输入以下内容。

{
 "presets": [
 [
  "@babel/preset-env",
  {
  "targets": "> 0.25%, not dead"
  }
 ],
 "@babel/preset-react"
 ],
 "plugins": [
 "@babel/plugin-transform-runtime",
 "@babel/plugin-transform-modules-commonjs",
 [
  "@babel/plugin-proposal-decorators",
  {
  "legacy": true
  }
 ],
 "@babel/plugin-proposal-class-properties",
 "@babel/plugin-proposal-object-rest-spread"
 ]
}

我们在presets其中使用了preset-env, 规定了输出的代码目标环境是份额大于0.25%的浏览器。另外由于我们的项目里使用了react,presets中就要加入preset-react。
同时,plugins配置了一些babel插件,用于支持装饰器展开操作符等类内直接定义属性等新的es特性。

4.3 配置启动命令

我们再次回到项目根目录下的package.json中,编辑如下

"scripts": {
 "build": "set NODE_ENV=production && webpack --config ./config/webpack.config.prod.js",
 "pub": "npm run build && npm publish",
 "dev": "webpack-dev-server --config ./config/webpack.config.dev.js"
 },
 "main": "dist/index.js",
 "files": ["dist"]
  • build 命令用于打包组件
  • dev 命令会使用webpack-dev-server启动一个开发服务用于预览组件效果
  • pub 命令进行打包组件并且发布到npm上
  • main字段指定了我们的组件的入口文件,files字段用于指定我们的npm包的文件目录。

5.试用和发布

要发布一个npm包,我们需使用如下命令添加一个npm的账号,如果已经添加过的这一步可以跳过。

npm adduser

如果已经有npm账号,可以使用npm login登陆。

如果不知道自己是否已经添加过了npm账号,使用npm whoami查看登陆信息即可

接下来就编辑package.json把组件的名称/版本/介绍等字段都填写一下。

好了,接下我们先使用npm run dev命令,此时会自动打开默认浏览器预览组件。

如果没什么问题的话,接下来使用npm run pub进行打包和发布。

等待发布完成后,我们就下载安装一下。

npm i your-component // 假设你的包名字叫your-component

使用自己发布的组件

import YourComponent from 'your-component';
import 'your-component/dist/main.min.css'; // 如果给组件写了样式,需要手动导入css文件

6.总结

到这里,一个非常非常简单的用于发布react小组件的脚手架就搭好了,总结一下其中要注意的地方:

  • webpack打包时libraryTarget要使用umd
  • externals 里要把外部依赖配置好
  • 如果还要生成es module,可以额外使用gulp或rollup等工具
  • webpack4 之后建议使用MiniCssExtractPlugin来提取css

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery live( type, fn ) 委派事件实现
Oct 11 Javascript
左右悬浮可分组的网站QQ在线客服代码(可谓经典)
Dec 21 Javascript
js动态添加表格数据使用insertRow和insertCell实现
May 22 Javascript
基于jQuery实现动态搜索显示功能
May 05 Javascript
基于Javascript倒计时效果
Dec 22 Javascript
简单实现IONIC购物车功能
Jan 10 Javascript
AngularJS实现根据不同条件显示不同控件
Apr 20 Javascript
jQuery实现html双向绑定功能示例
Oct 09 jQuery
解决layui的input独占一行的问题
Sep 10 Javascript
layui表格数据复选框回显设置方法
Sep 13 Javascript
vue keep-alive实现多组件嵌套中个别组件存活不销毁的操作
Oct 30 Javascript
JavaScript实现消消乐的源代码
Jan 12 Javascript
微信小程序公用参数与公用方法用法示例
Jan 09 #Javascript
微信小程序实现的日期午别医生排班表功能示例
Jan 09 #Javascript
Windows下Node爬虫神器Puppeteer安装记
Jan 09 #Javascript
jQuery简单实现根据日期计算星期几的方法
Jan 09 #jQuery
jQuery实现根据身份证号获取生日、年龄、性别等信息的方法
Jan 09 #jQuery
爬虫利器Puppeteer实战
Jan 09 #Javascript
puppeteer库入门初探
Jan 09 #Javascript
You might like
laravel学习教程之关联模型
2016/07/30 PHP
yii2.0框架场景的简单使用示例
2020/01/25 PHP
jQuery 1.0.4 - New Wave Javascript(js源文件)
2007/01/15 Javascript
jQuery 性能优化指南 (1)
2009/05/21 Javascript
慎用 somefunction.prototype 分析
2009/06/02 Javascript
五段实用的js高级技巧
2011/12/20 Javascript
jQuery实现锚点scoll效果实例分析
2015/03/10 Javascript
关于使用js算总价的问题
2017/06/23 Javascript
vue+mockjs模拟数据实现前后端分离开发的实例代码
2017/08/08 Javascript
JavaScript设计模式之享元模式实例详解
2019/01/17 Javascript
JavaScript数组去重的几种方法
2019/04/07 Javascript
微信小程序中使用echarts的实现方法
2019/04/24 Javascript
layui table 多行删除(id获取)的方法
2019/09/12 Javascript
解决angular 使用原生拖拽页面卡顿及表单控件输入延迟问题
2020/04/21 Javascript
JavaScript冒泡算法原理与实现方法深入理解
2020/06/04 Javascript
Element Collapse 折叠面板的使用方法
2020/07/26 Javascript
[28:57]EG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/16 DOTA
python 读取视频,处理后,实时计算帧数fps的方法
2018/07/10 Python
pycharm安装和首次使用教程
2018/08/27 Python
Python 经典算法100及解析(小结)
2019/09/13 Python
python logging添加filter教程
2019/12/24 Python
python实现人机五子棋
2020/03/25 Python
Python figure参数及subplot子图绘制代码
2020/04/18 Python
如何在sublime编辑器中安装python
2020/05/20 Python
NUK奶瓶美国官网:NUK美国
2016/09/26 全球购物
NFL墨西哥官方商店:Tienda NFL
2017/11/28 全球购物
英国户外玩具儿童游乐设备网站:TP Toys(蹦床、攀爬框架、秋千、滑梯和游戏屋)
2018/04/09 全球购物
德国高端单身人士交友网站:ElitePartner
2018/12/02 全球购物
Jacques Lemans德国:奥地利钟表品牌
2019/12/26 全球购物
工程力学硕士生的自我评价范文
2013/11/16 职场文书
教师网络培训感言
2014/03/09 职场文书
社区清明节活动总结
2014/07/04 职场文书
纪念九一八爱国演讲稿600字
2014/09/14 职场文书
布达拉宫导游词
2015/02/02 职场文书
Vue详细的入门笔记
2021/05/10 Vue.js
Win10 heic文件怎么打开 ? Win10 heic文件打开教程
2022/04/06 数码科技