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 相关文章推荐
扩展String功能方法
Sep 22 Javascript
Javascript 检测键盘按键信息及键码值对应介绍
Jan 03 Javascript
js确认删除对话框效果的示例代码
Feb 20 Javascript
类似天猫商品详情随浏览器移动的示例代码
Feb 27 Javascript
javacript使用break内层跳出外层循环分析
Jan 12 Javascript
JQuery节点元素属性操作方法
Jun 11 Javascript
Markdown与Bootstrap相结合实现图片自适应属性
May 04 Javascript
全屏滚动插件fullPage.js使用实例解析
Oct 21 Javascript
angular和BootStrap3实现购物车功能
Jan 25 Javascript
微信小程序开发之数据存储 参数传递 数据缓存
Apr 13 Javascript
关于jQuery中fade(),show()起始位置的一点小发现
Apr 25 jQuery
vue-cli webpack模板项目搭建及打包时路径问题的解决方法
Feb 26 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
PHP教程 基本语法
2009/10/23 PHP
php解析url并得到url中的参数及获取url参数的四种方式
2015/10/26 PHP
PHP实现数据分页显示的简单实例
2016/05/26 PHP
ThinkPHP和UCenter接口冲突的解决方法
2016/07/25 PHP
安装PHP扩展时解压官方 tgz 文件后没有configure文件无法进行配置编译的问题
2020/08/26 PHP
contains和compareDocumentPosition 方法来确定是否HTML节点间的关系
2011/09/13 Javascript
js 动态加载事件的几种方法总结
2013/12/25 Javascript
自己使用jquery写的一个无缝滚动的插件
2014/04/30 Javascript
js换图片效果可进行定时操作
2014/06/09 Javascript
js调试系列 源码定位与调试[基础篇]
2014/06/18 Javascript
轻松创建nodejs服务器(5):事件处理程序
2014/12/18 NodeJs
Javascript中3个需要注意的运算符
2015/04/02 Javascript
微信公众号 摇一摇周边功能开发
2016/12/08 Javascript
JavaScript中变量提升机制示例详解
2019/12/27 Javascript
解决Vue 移动端点击出现300毫秒延迟的问题
2020/07/21 Javascript
[33:15]2018DOTA2亚洲邀请赛3月30日 小组赛B组 VP VS Mineski
2018/03/31 DOTA
编程语言Python的发展史
2014/09/26 Python
介绍Python中几个常用的类方法
2015/04/08 Python
Django数据库操作的实例(增删改查)
2017/09/04 Python
tensorflow实现简单的卷积网络
2018/05/24 Python
python实现词法分析器
2019/01/31 Python
django基于cors解决跨域请求问题详解
2019/08/06 Python
Tensorflow之MNIST CNN实现并保存、加载模型
2020/06/17 Python
vivo智能手机官方商城:vivo
2016/09/22 全球购物
澳大利亚优惠网站:Deals.com.au
2019/07/02 全球购物
2014新课程改革心得体会
2014/03/10 职场文书
财务科长个人对照检查材料
2014/09/18 职场文书
法英专业大学生职业生涯规划书范文
2014/09/22 职场文书
党政领导班子群众路线对照检查材料思想汇报
2014/09/27 职场文书
农村党员学习党的群众路线教育实践活动心得体会
2014/11/04 职场文书
离婚协议书范本
2015/01/26 职场文书
幼儿园园长六一致辞
2015/07/31 职场文书
观看《筑梦中国》纪录片心得体会
2016/01/18 职场文书
解决golang 关于全局变量的坑
2021/05/06 Golang
python基于tkinter实现gif录屏功能
2021/05/19 Python
浅析Python实现DFA算法
2021/06/26 Python