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 相关文章推荐
收集的一些Array及String原型对象的扩展实现代码
Dec 05 Javascript
jquery一句话全选/取消全选
Mar 01 Javascript
ECMAScript6函数默认参数
Jun 12 Javascript
Bootstrap modal使用及点击外部不消失的解决方法
Dec 13 Javascript
node.js中express-session配置项详解
May 31 Javascript
10个最优秀的Node.js MVC框架
Aug 24 Javascript
微信小程序用户信息encryptedData详解
Aug 24 Javascript
javascript防抖函数debounce详解
Jun 11 Javascript
vue中get请求如何传递数组参数的方法示例
Nov 08 Javascript
node.js +mongdb实现登录功能
Jun 18 Javascript
Vue组件间数据传递的方式(3种)
Jul 13 Javascript
Vue实现指令式动态追加小球动画组件的步骤
Dec 18 Vue.js
微信小程序公用参数与公用方法用法示例
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
PHP 强制性文件下载功能的函数代码(任意文件格式)
2010/05/26 PHP
JavaScript随机排序(随即出牌)
2010/09/17 Javascript
小试JQuery的AutoComplete插件
2011/05/04 Javascript
Web开发之JavaScript
2012/03/29 Javascript
jquery的ajax()函数传值中文乱码解决方法介绍
2012/11/08 Javascript
在Javascript中 声明时用&quot;var&quot;与不用&quot;var&quot;的区别
2013/04/15 Javascript
JS和JQUERY获取页面大小,滚动条位置,元素位置(示例代码)
2013/12/14 Javascript
bootstrap改变按钮加载状态
2014/12/01 Javascript
jQuery中(function($){})(jQuery)详解
2015/07/15 Javascript
JS组件系列之Bootstrap Icon图标选择组件
2016/01/28 Javascript
jQuery焦点图轮播插件KinSlideshow用法分析
2016/06/08 Javascript
微信开发 微信授权详解
2016/10/21 Javascript
详细介绍RxJS在Angular中的应用
2017/09/23 Javascript
vue项目首屏加载时间优化实战
2019/04/23 Javascript
Vue实现购物车基本功能
2020/11/08 Javascript
讲解Python中的递归函数
2015/04/27 Python
Python的语言类型(详解)
2017/06/24 Python
Python实现的选择排序算法原理与用法实例分析
2017/11/22 Python
Python3解决棋盘覆盖问题的方法示例
2017/12/07 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
解决Django migrate No changes detected 不能创建表的问题
2018/05/27 Python
python实现石头剪刀布程序
2021/01/20 Python
在tensorflow中实现去除不足一个batch的数据
2020/01/20 Python
如何理解Python中包的引入
2020/05/29 Python
在python3.64中安装pyinstaller库的方法步骤
2020/06/02 Python
python如何处理程序无法打开
2020/06/16 Python
2020版Python学习路线图(附学习资料)
2020/09/15 Python
英国婚礼商城:Wedding Mall
2019/11/02 全球购物
模具设计与制造专业求职信
2014/07/19 职场文书
幼儿教师自我剖析材料
2014/09/29 职场文书
酒店仓管员岗位职责
2015/04/01 职场文书
针对吵架老公保证书
2015/05/08 职场文书
听证会主持词
2015/07/03 职场文书
国庆放假通知怎么写
2015/07/30 职场文书
解决pytorch读取自制数据集出现过的问题
2021/05/31 Python
http通过StreamingHttpResponse完成连续的数据传输长链接方式
2022/02/12 Python