2020年11月03日
开发npm项目,少不了要跟第三方组件打交道。趁着有时间就想自己搞一个中台框架组件,我最喜欢干这种闭门造轮子的事情。 然而打包组件这玩意水深得很,网上很难查找到趁手的资料,很多都是2018的资料了,加上前端本来就是个更新超快的玩意,结果可想而知。于是一边查资料一边总结,写下该篇文章,希望可以给后来的同学带来帮助。
webpack 中文接口介绍:webpack详细中文文档:https://cloud.tencent.com/developer/doc/1250
#创建项目目录
mkdir ReveryUI-Frame
#进入目录
cd ReveryUI-Frame
#创建基础项目
npm init
npm init 之后,会进入一个项目创建流程,将会提示以下内容:
> package name: (reveryui-frame) #项目名称
> version: (1.0.0) #当前版本
> description: #说明
> entry point: (index.js) #项目默认入口
> test command:
> git repository: #git地址,可空
> keywords:
> author: #作者
> license: (ISC)
> About to write to /Users/zhuzejun/Documents/junDataMac/jsProject/ReveryUI-Frame/package.json:
> Is this OK? (yes) #最后一步必须输入yes,才能创建成功。
项目创建成功后,该目录下会多出一个package.json的文件。
> ls
package.json
打开package.json,进行简单的修改,
{
"name": "reveryui-frame",
"version": "0.1.0",
"description": "",
"main": "index.js",
"files":["dist"],
"scripts": {
"start": "webpack-dev-server --config ./config/webpack.dev.config.js",
"dev": "npm run start",
"pub": "npm publish",
"build": "webpack --config ./config/webpack.prod.config.js"
},
"author": "浪荡小野狗",
"license": "ISC",
"dependencies":{},
"devDependencies":{}
}
修改说明:
name:组件名称
version: 组件当前版本
description: 组件说明
main: 组件启动入口
files:组件发布时,上传的文件夹,如果没有,将会上传整个项目。
scripts :运行脚本,npm run 的入口。比如npm run start
author: 作者名称
license:授权许可形式
dependencies:项目依赖的第三方组件
devDependencies:项目依赖的第三方组件(与上面的区别是,这个位置的组件是编译时用的,不参与到打包目标里面去,不过据我了解,只要不在项目应用的话,在哪儿都没所谓。目前我也没发现有啥区别,这里只做说明,不深入讨论。)
# 我这个组件是用react开发的,所以我加入了react的组件。vue的这里不介绍。
> npm i -D react react-dom
# 安装其他打包辅助工具
> npm i -D \
webpack@4.44.2 \
webpack-cli@3.3.12 \
webpack-dev-server \
webpack-node-externals \
@babel/core \
@babel/preset-env \
@babel/preset-react \
@babel/plugin-proposal-class-properties \
babel-loader \
babel-plugin-transform-jsx-url \
css-loader \
style-loader \
html-webpack-plugin \
clean-webpack-plugin \
mini-css-extract-plugin \
less \
less-loader \
optimize-css-assets-webpack-plugin
组件说明:
webpack: 编译工具主程序
webpack-cli: 编译工具脚手架
webpack-dev-server: 编译运行工具,npm run start 实际调用的方法
webpack-node-externals: 避免在编译期间node_modules中的错误
@babel/core: 把开发的nodejs编译成前端可以运行的js代码。
@babel/preset-env
@babel/preset-react: 把react编译成可执行js.
@babel/plugin-proposal-class-properties : 用于支持js 的 class 写法
babel-loader:js编译用到的loader,我理解是对上面@babel的调配工具。
babel-plugin-transform-jsx-url:处理react里面的资源,如img 里面的 src,把资源提取出来。
css-loader: 处理css的工具
style-loader: 处理 dom style 标签里面的css
html-webpack-plugin: 处理html的工具
clean-webpack-plugin: 用于清理之前编译的文件,防止造成过多的碎片。
mini-css-extract-plugin: 用于将css分割成独立文件,否则将会打包进js里面,造成js文件体积过大。
less:css 预处理工具(与less-loader 配套使用)
less-loader :css预处理工具
optimize-css-assets-webpack-plugin:压缩css的插件
工具介绍:
webpack5.x版本的确是一个吸引人的版本,这里推荐两篇文章(上下篇),有兴趣的可以看看,但是不建议直接用5.x部署项目,因为目前很多插件还没支持。文章链接:
https://juejin.im/post/6844903795286081550#heading-0
https://juejin.im/post/6850037279767920653
webpack 目前最新版本为(5.x+),这里为什么还在用4.x+?因为其他很多与webpack相关目前并不兼容5.x+版本, 实属于无奈之举。
故此,上面安装webpack和webpack-cil时,必须指定版本。否则会出现安装错误的情况。
ok , 工具准备好了,接下来开始进行,编码和进行打包测试。当前我的项目文件情况如下(我用的开发工具是idea,那个.idea是开发工具弄出来的文件夹,请无视):
在根目录下创建一个config文件夹
# 创建config文件夹
> mkdir config
在config文件夹下创建一个生产环境的配置文件 webpack.prod.config.js ,文件内容为:
/**
* @author revery.Top zzjun
* @date 11/2/20 01:30
*
* 说明: webpack 生产环境编译配置
*/
const path = require('path');
// js压缩和分割
const TerserPlugin = require('terser-webpack-plugin');
// 过滤node_modules内容,不编译进打包文件。
const nodeExternals = require('webpack-node-externals');
// 提取css插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 压缩css插件
const optimizeCss = require('optimize-css-assets-webpack-plugin');
/**
* webpack entry设置
*
* 打包入口
* */
const getEntry = () => {
return {
index: './src/index.js'
}
}
/**
* webpack output设置
*
* --- 编译后,输出设置 ---
*
* */
const getOutput = () => {
return {
// 打包文件输出名称,默认为 main.js , [name]与 getEntry的设置有关,根据我上面的设置,最终打包结果为 index.js
filename: '[name].js',
// 打包文件输出目录
path: path.resolve(__dirname, '../dist'),
libraryTarget: 'commonjs2'
};
}
/**
* webpack plugins 设置
*
* webpack插件
*
* */
const getPlugins = () => {
return [
// 提取css插件
new MiniCssExtractPlugin({
filename: "[name].min.css"
}),
// 压缩css插件
new optimizeCss({
assetNameRegExp: /\.style\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: {discardComments: {removeAll: true}},
canPrint: true
}),
];
}
/**
* rules 设置
*
* 编译过程中,读取到的每一个文件都会经过 rules 处理
* test: 符合预期需要处理正则表达式
* use: [] , 将文件交给某个处理器进行处理。
* exclude: 不处理的文件路径。
* */
const getRules = () => {
let rules = [];
// 处理js
rules.push({
test: /\.(js|jsx)$/,
use: [
{
loader: 'babel-loader',
options: {
// babel默认不支持react的jsx语法,需要添加另外的编译器
presets: [
"@babel/preset-env",
"@babel/preset-react"
],
// 用于识别jsx中的图片资源
plugins: [
[
"transform-jsx-url",
{
// 目录前缀,不知道是不是因为bug的原因,这个工具无法识别 "./" 这个当前目录标识符
root: '../src/',
}
],
// 支持js class形式写法
"@babel/plugin-proposal-class-properties",
]
}
}
],
// 这个可以忽略的,因为已经设置了全局过滤. 全局过滤设置在这里 -> externals: [nodeExternals()]
exclude: /node_modules/,
});
// 处理css
rules.push({
test: /\.(css|less)$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "less-loader",
options: {
// 用于识别 less 里面 @import 引用的 其他样式
lessOptions:{
javascriptEnabled: true
}
}
}
]
});
// 处理图片
rules.push({
test: /\.(png|gif|svg|ico|jpe?g)$/i,
use: [
{
//这里用url-loader来处理图片
// file-loader 与 url-loader 功能很像
loader: 'url-loader',
//options里面可以配置详细的处理信息
options: {
/**
* limit: 3072,
* 大于3k的图片,输出成文件。少于3k的图片,编码成base64的图片格式,保存在js中。
* ---- 为了达到测试效果,我这里设置了,任何时候都输出文件。
* */
limit: 0,
/**
* 这个老哥一看就是输出文件的名称方式了
* */
name: './assets/[name].[ext]', // 打包文件名
}
}
],
});
return {rules}
}
/**
* 该属性为webpack4.x新增的优化项,具体参考官方文档:
* 官方文档: https://webpack.js.org/configuration/optimization/#root
* 这里实现了 js/css 压缩、分割
* */
const getOptimization = () => {
return {
minimize: true, // 启用代码压缩
minimizer: [
// 压缩、分割(需要在下面定义分割内容) js
new TerserPlugin(),
// 压缩css
new optimizeCss({})
],
splitChunks: {
minSize: 1, // 3kb = 3072 表示在压缩前的最小模块大小,默认值是30kb, 测试需要设置为了1b,任何时候都打包出独立文件。
chunks: 'all',//同时分割同步和异步代码,推荐。
automaticNameDelimiter: '_',//名称分隔符,默认是~
cacheGroups: { //默认的规则不会打包,需要单独定义
// baseTool.js不会被打包到index.js里面
baseTool: {
name: 'baseTool',
chunks: 'all',
test: /baseTool\.js/,
enforce: true
}
}
}
}
}
/**
* webpack 编译配置
* */
const webpackConfig = {
// 打包模式,development,开发模式。production 生产模式。
mode: 'production',
// 打包入口
entry: getEntry(),
// 编译后的文件输出保存设置
output: getOutput(),
// 打包用到的插件
plugins: getPlugins(),
// 打包过程遇到的非js,进行其他预处理。webpack只能编译js文件,其他css、图片等,需要通过这个配置,设置给其他工具去处理。
module: getRules(),
// 设置 js/css 压缩、分割
optimization: getOptimization(),
// 这个是用来过滤编译时,无视 node_modules 等文件用的。
// 也就是文章中提及的 webpack-node-externals 工具。
externals: [nodeExternals()]
};
module.exports = webpackConfig;
文档写到这里断断续续,已经写了好多天了,本来还想把配置的步骤一步步写出来的,后来想想还是算了,具体的过程,我都写在了上面配置文件的备注里面,有兴趣的学习的,可以把每个组件都百度一遍,很多都有介绍。(2020年11月06日)
唯一找不到介绍的组件 babel-plugin-transform-jsx-url ,这个组件是把React里面图片的url识别出来。我测试过,这个工具只会识别属于本地开发的图片,网络图片会忽略,这个很好。
相信能看到这里的,都是真爱了,都是有缘之人。我将会在文章末尾,添加git地址,可以拉下来看看我最终的部署成果。所有代码跟本文章一致!
到此! 生产环境的打包过程部署好了。下面开始进行开发环境的配置。
在config文件夹下创建一个生产环境的配置文件 webpack.dev.config.js ,文件内容为:
const path = require('path');
const devConfig = {
entry: './demo/index.js', // 入口文件, 最终打开的是这个页面
mode: 'development', //打包为开发模式
output: {
path: path.resolve(__dirname, '../demo'), // 输出的文件目录
filename: 'demo.bundle.js' // 输出的文件名称, 在 /demo/index.html 里面必须要调用 demo.bundle.js 这个文件
},
module: {
rules: [
{ // 在webpack中使用babel需要babel-loader
test: /\.(js|jsx)$/,
use: [
{
loader: 'babel-loader',
options: {
// babel默认不支持react的jsx语法,需要添加另外的编译器
presets: [
"@babel/preset-env",
"@babel/preset-react"
],
// 用于识别jsx中的图片资源
plugins: [
[
"transform-jsx-url",
{
// 目录前缀, 这里是相对于当前配置文件的位置,所以要返回上一层的src目录获取图片
root: '../src',
}
],
]
},
}
]
},
// 用于解析css,less
{
test: /\.(css|less)$/,
use: [
"style-loader", //使代码中的 import './index.less' 能生效, 生产环境因为要打包出独立css,所以不使用这个组件。
"css-loader",
{
loader: "less-loader",
options: {
// 用于识别 less 里面 @import 引用的 其他样式
lessOptions:{
javascriptEnabled: true
}
}
}
]
},
// 用于加载组件或者css中使用的图片
{
test: /\.(png|gif|svg|ico|jpe?g)$/i,
use: [
{
//这里用file-loader来处理图片
loader: 'file-loader',
options: {
/**
* 如果不设置这个,图片的src 就会变成一个Module对象。
* 因为transform-jsx-url会把 转换为
* */
esModule: false,
name: '[name].[ext]',
outputPath: './assets'
}
}
],
}
]
},
// 该字段用于配置webpack-dev-server
devServer: {
contentBase: path.join(__dirname, '../demo'),
compress: true,
port: 9000, // 端口9000
open: false // 自动打开浏览器
}
}
module.exports = devConfig;
调用编译得到最终结果
> npm run build
最后献上最终开发示例
gitee: https://gitee.com/junos2/npm-react-component-example
#设置正确的npm地址,有些人用了阿里云或者网易之类的第三方镜像地址的。必须重置回npm的地址
> npm config set registry https://registry.npmjs.org/
# 创建一个npm账号,输入用户名,密码,邮箱
> npm adduser
# 登陆
> npm login
.......
# 上传组件
> npm publish
- 到这里要说明一下,创建账号后,不能直接就可以上传的,需要到npm官方去激活一下账号,并创建一个packages包。
- 具体操作流程:到npm官方登陆后,在右上角的头像箭头里面,鼠标移上去,有个下拉菜单,选择里面的packages,然后在里面添加你要创建的组件名称,要更你上传的组件名称一致。
- 每次上传更新时,必须修改 package.json 里面的版本号
- npm官方地址:https://www.npmjs.com/