脚手架具有如下功能:
- 开发组件时可以进行实时预览
- 处理组件内的各种资源(css、js、图片等)
- 一间打包发布
1、创建项目
- mkdir react-component-boilerplate
- cd react-component-boilerplate
- 使用yarn 命令初始化一个package.json文件
- yarn init
目录结构:
`react-component-boilerplate`
|-- config // webpack配置`
|-- example // 开发时预览用`
|-- dist // 打包结果`
|-- src // 源文件目录`
| -- assets // 存放图片等媒体文件`
| -- style // 存放样式,项目使用的是less来编写样式`
2、安装依赖
生产依赖
- 1、我们要使用react来开发组件所以必须要安装 react 和 react-dom
- yarn add react@16 react-dom@16.13.1 开发依赖
"devDependencies": {
// babel用于将你写的es6+的代码转换到es5
"@babel/core": "7.10.2",
"@babel/plugin-proposal-class-properties": "^7.0.0", // 用于支持class属性
"@babel/plugin-proposal-decorators":"^7.0.0" , // 支持decorator 装饰器语法
"@babel/preset-react": "7.10.1", // 让bable编译react语法
"@babel/plugin-transform-runtime":"7.0.0"
"@babel/preset-env":"7.0.0"
"babel-loader": "8.1.0",
"clean-webpack-plugin": "3.0.0",
"copy-webpack-plugin": "6.0.2",
"css-loader": "3.5.3",
"file-loader": "6.0.0",
"less-loader":"4.1.0"
"html-webpack-plugin": "4.3.0",
"mini-css-extract-plugin": "0.9.0", // 将css提取为一个单独的文件
"url-loader": "4.1.0", //将文件作为 data URI 内联到 bundle 中
"webpack": "4.43.0",
"webpack-cli": "3.3.10",// webpack4之后单独抽离了webpack-cli
"webpack-dev-server": "3.10.1",// 开发时的运行服务,监控文件变化自动刷新页面
"webpack-merge": "4.2.2"//用于合并webpack配置
},
3、配置babel和loader
3.1 配置webpack
在config目录下,建立三个webpack配置文件夹
- webpack.base.js
- webpack.config.dev.js // 本地开发的配置
- webpack.config.pro.js // 打包到生产环境的配置
webpack.base.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 清除 dist 目录
const CopyWebpackPlugin = require("copy-webpack-plugin"); // 处理静态资源
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 打包css文件
const config = {
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
"@": path.resolve(__dirname, 'src'),
_: __dirname, // 工程根目录
}
},
stats: {
colors: true,
modules: true,
children: true, // 打包时不显示子模块信息
},
module: {
rules: [
{
test: /\.(png)|(jpg)|(svg)|(bmp)|(eot)|(woff)|(ttf)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 1024,
name: 'static/[name].[ext]',
esModule: true
}
},
]
},
{
test: /\.(le|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "less-loader",
options: {
sourceMap: false
}
}
]
},
{ test: /\.(js)|(jsx)$/, exclude: /node_modules/,sideEffects: true, use: "babel-loader" },
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
// 打包 css 代码 到文件中
filename: "css/[name].css",
chunkFilename: "css/common.[hash:5].css", // 针对公共样式的文件名
}),
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, "public"), // 将public目录中的所有文件
to: "./", // 复制到 输出目录 的根目录
},
],
}),
]
}
module.exports = config
webpack.config.dev.js
const merge = require("webpack-merge");
const path = require("path");
const baseConfig = require("./webpack.config.js");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // 处理模板页面
// webpack的开发环境配置,从基本配置中合并
// 合并是利用 webpack-merge 完成的: https://github.com/survivejs/webpack-merge
const devConfig = {
entry:"./demo/index.js",
output: {
filename: 'demo.bundle.js', // 输出的文件名称
path: path.resolve(__dirname, '../demo') // 输出的文件目录
},
mode: "development",
devtool: "source-map",
devServer: {
open: true,
hot:true,
port: 8085,
proxy: {
// 如果开发环境中有跨域问题,在这里配置代理
},
stats: "minimal",
},
plugins: [
new HtmlWebpackPlugin({
template: "./demo/index.html",
}),
]
};
module.exports = merge(baseConfig, devConfig);
webpack.config.pro.js
const merge = require("webpack-merge");
const path = require("path");
const baseConfig = require("./webpack.config");
const proConfig = {
mode:"production",
entry: './src/index.js', //webpack编译文件入口
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget:'umd', // 采用通用模块定义,注意webpack4到目前为止依然不提供输出es module的方法,所以输出的结果必须使用npm安装到node_modules里再用,不然会报错
library:'react-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.exports = merge(baseConfig, proConfig)
注意:
- externals字段
- externals定义了外部依赖。将react和react-dom添加进该字段,说明我们的组件将依赖外部的react和react-dom,这样就可以避免把react和react-dom打包进去(不然组件会很大,防止页面重复引入react,造成重复声明变量的问题)
3.2 配置babel
我们需要把我的代码编译降级为ES5版本。需要写一些babel配置
```js
{
"presets": [
[
"@babel/preset-env",
{
"targets": "> 0.25%, not dead"
]
,"@babel/preset-react"]
"plugins": [ // 使用一些babel插件
"@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" // 支持es7的 ...操作符
]
} ```
注意:
- presets其中使用了preset-env, 规定了输出的代码目标环境是份额大于0.25%的浏览器
- 由于我们的项目里使用了react,presets中就要加入preset-react
3.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"]
4、编写组件
/* src/index.js */
import React from 'react';
import './style/style.less'; // 使用less的情况
export default class Hello extends React.Component {
render(){
return (<div>A new Component</div>)
}
}
在/src目录下新建一个index.js,这就是我们组件的入口文件了。
如果项目中要使用图片、css等,分类放到assets、style文件夹下
开发预览
在example目录的index.js文件中引入src/index.js的组件
import React from 'react';
import ReactDom from 'react-dom';
import Hello from '../src/index'
const Demo = () => {
return <div>
<h1>组件预览:</h1>
<Hello />
</div>
}
ReactDom.render(<Demo />, document.getElementById('root'));
发布npm&引用组件
- 1、没有npm账户,先注册npm账户
- 2、已注册,登录npm
- npm login
- npm whoami 如果不知道自己是否添加过npm账号,用此命令查看
- 3、正确修改package.json的组件名称、版本等信息
- 4、直接在命令行运行,直接打包并发布
- yarn pub
- 5、在项目中引入使用
总结
-
1、webpack打包时libraryTarget要使用umd
-
2、extenals字段,要把外部依赖配置好
-
3、如果要生成es module,可以使用gulp或rollup等工具
-
4、webpack4之后建议使用 MiniCssExtractPlugin来提取css