字段 | 类型 | 含义 |
---|---|---|
name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose} |
filename | string | 指定导出的 remoteEntry 文件的名称。这个文件包含了模块联邦的运行时和引导代码 |
remotes | object | 定义其他应用程序的引用映射。这个参数是一个对象,键是别名,值是远程应用的 URL。当我们想在当前应用中引用其他应用的模块时,可以使用这个别名 |
exposes | object | 定义当前应用要暴露给其他应用的模块。这个参数是一个对象,键是别名,值是模块的相对路径。别名将用于其他应用在引用该模块时 |
shared | object | 定义哪些模块应该在应用程序之间共享。这个参数可以是一个数组,包含共享模块的名称,或者是一个对象,包含共享模块的名称及其配置选项。共享模块可以避免重复加载,从而减少应用的体积和加载时间 |
webpack-chain
的一些主要特性webpack-chain
使用了流行的链式 API 设计模式,允许您通过链式方法调用轻松地修改 Webpack 配置。这种设计模式提高了代码的可读性和维护性// 导入 'webpack-chain' 模块,它是一个构造函数,用于创建配置 API
const Config = require('./webpack-chain');
// 实例化一个新的配置 API 对象
const config = new Config();
// 使用链式 API 修改配置
config
// 添加一个入口点
.entry('index')
// 为入口点添加文件路径
.add('src/index.js')
// 结束对当前入口点的操作并返回 config 实例
.end()
// 修改输出设置
.output
// 设置输出目录
.path('dist')
// 设置输出文件名
.filename('[name].bundle.js');
// 将配置转换为 webpack 可以使用的配置对象
const options = config.toConfig();
// 打印配置对象
console.log(options);
/**
{
entry: { index: [ 'src/index.js' ] },
output: { path: 'dist', filename: '[name].bundle.js' }
}
*/
chain\webpack-chain\index.js
const ChainedMap = require('./ChainedMap');
const ChainedSet = require('./ChainedSet');
const Output = require('./Output');
class Config extends ChainedMap {
constructor(parent) {
super(parent);
this.entryPoints = new ChainedMap(this);
this.output = new Output(this);
}
entry(name) {
return this.entryPoints.getOrCompute(name, () => new ChainedSet(this));
}
toConfig() {
const entryPoints = this.entryPoints.entries();
return {
entry: Object.keys(entryPoints).reduce(
(acc, key) =>
Object.assign(acc, { [key]: entryPoints[key].values() }),
{},
),
output: this.output.entries()
}
}
}
module.exports = Config;
chain\webpack-chain\ChainedMap.js
const Chainable = require('./Chainable');
class ChainedMap extends Chainable {
constructor(parent) {
super(parent);
this.store = new Map();
}
extend(methods) {
this.shorthands = methods;
methods.forEach((method) => {
this[method] = (value) => this.set(method, value);
});
return this;
}
getOrCompute(key, fn) {
if (!this.has(key)) {
this.set(key, fn());
}
return this.get(key);
}
has(key) {
return this.store.has(key);
}
set(key, value) {
this.store.set(key, value);
return this;
}
get(key) {
return this.store.get(key);
}
entries() {
const entries = [...this.store].reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
return entries;
}
}
module.exports = ChainedMap
chain\webpack-chain\ChainedSet.js
const Chainable = require('./Chainable');
class ChainedSet extends Chainable {
constructor(parent) {
super(parent);
this.store = new Set();
}
add(value) {
this.store.add(value);
return this;
}
values() {
return [...this.store];
}
}
module.exports = ChainedSet;
chain\webpack-chain\Chainable.js
class Chainable {
constructor(parent) {
this.parent = parent;
}
end() {
return this.parent;
}
};
module.exports = Chainable;
chain\webpack-chain\Output.js
const ChainedMap = require('./ChainedMap');
module.exports = class extends ChainedMap {
constructor(parent) {
super(parent);
this.extend([
'path',
'filename'
]);
}
};
npm i -g @efox/emp
emp init -d https://static.zhufengpeixun.com/template_1680930323773.json
emp dev
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader commander fs-extra git-promise html-webpack-plugin inquirer nanospinner webpack webpack-chain webpack-cli webpack-dev-server axios --save
package.json
{
"bin": {
"zmp6": "./bin/zmp6.js"
}
}
bin\zmp6.js
// 声明一个 Node.js 脚本,并指定使用的解释器
#!/usr/bin/env node
// 引入 commander 库,用于处理命令行参数和选项
const program = require('commander');
// 引入 package.json 文件,以获取版本号等信息
const pkg = require('../package.json');
// 引入自定义的 CLI 模块
const cli = require('../cli');
// 设置版本号,并添加一个用于显示版本号的选项
program.version(pkg.version, '-v, --version').usage('<command> [options]');
// 添加 init 命令,用于初始化项目
program.command('init')
.description('初始化项目')
.option('-d, --data [data]', 'JSON数据 http地址或者文件路径相对、绝对路径')
.action((options) => {
cli.exec('init', options);
});
// 添加 dev 命令,用于启动开发服务器
program.command('dev')
.description('Dev Server')
.action(async (options) => {
console.log('dev', options);
});
// 解析命令行参数
program.parse(process.argv);
cli\index.js
// 定义一个名为 ZMPScript 的类
class ZMPScript {
// 定义一个名为 exec 的异步方法,接受两个参数:name 和 options
async exec(name, options) {
// 使用 require 动态加载指定名称的模块(文件),并调用其 setup 方法,传递 options 参数
await require(`./${name}`).setup(options);
}
}
// 创建一个 ZMPScript 类的实例,并导出该实例
module.exports = new ZMPScript();
cli\init.js
// 导入依赖模块
const { createSpinner } = require('nanospinner');
const git = require('git-promise');
const fs = require('fs-extra');
const path = require('path');
const axios = require('axios');
// 定义项目模板的URL
const templates = {
'remote': `https://gitee.com/zhufengpeixun/remote.git`,
'host': `https://gitee.com/zhufengpeixun/host.git`,
};
// 定义 Init 类
class Init {
templates = templates
// 检查传入的URL是否是HTTP地址,并返回数据
async checkData(url) {
if (/^http(s)?:\/\/.+/.test(url)) {
const { data } = await axios.get(url);
return data;
} else {
const filepath = path.join(process.cwd(), url);
this.templates = require(filepath);
}
}
// 设置模板
async setup(options) {
if (typeof options.data === 'string') {
const data = await this.checkData(options.data);
if (data) {
this.templates = data;
}
}
await this.selectTemplate();
}
// 选择模板
async selectTemplate() {
const inquirer = await (await import('inquirer')).default;
let answers = await inquirer.prompt([{
type: 'input',
name: 'name',
message: '请输入项目名:',
default: function () {
return 'zmp-project';
}
}, {
type: 'list',
name: 'template',
message: '请选择模板:',
choices: Object.keys(this.templates)
}]);
let downLoadUrl = this.templates[answers.template];
const downLoadName = answers.name;
await this.downloadRepo(downLoadUrl, downLoadName);
}
// 下载仓库
async downloadRepo(repoPath, localPath) {
const spinner = createSpinner().start();
spinner.start({ text: `[downloading]\n` });
await git(`clone ${repoPath} ./${localPath}`);
fs.removeSync(`./${localPath}/.git`);
spinner.success({
text: ` cd ${localPath} && npm i && npm run dev`
});
}
}
// 导出 Init 类的实例
module.exports = new Init();
bin\zmp6.js
#!/usr/bin/env node
const program = require('commander');
const pkg = require('../package.json');
const cli = require('../cli');
program.version(pkg.version, '-v, --version').usage('<command> [options]');
program.command('init')
.description('初始化项目')
.option('-d, --data [data]', 'JSON数据 http地址或者文件路径相对、绝对路径')
.action((options) => {
cli.exec('init', options);
});
program.command('dev')
.description('Dev Server')
.action(async (options) => {
+ cli.exec('dev',options);
});
program.parse(process.argv);
cli\dev.js
// 引入 webpack-dev-server 模块
const WebpackDevServer = require('webpack-dev-server');
// 引入 webpack 模块
const webpack = require('webpack');
// 引入 getConfig 方法,用于获取 webpack 配置
const { getConfig } = require('../config');
class devServer {
// 定义异步的 setup 方法
async setup() {
// 调用 setServer 方法来设置服务器
await this.setServer();
}
// 定义异步的 setServer 方法
async setServer() {
// 通过 getConfig 方法获取 webpack 配置
const config = getConfig();
// 使用 webpack 函数创建一个编译器实例
const compiler = webpack(config);
// 创建一个新的 webpack-dev-server 实例,传入 devServer 配置和编译器实例
this.server = new WebpackDevServer(config.devServer, compiler);
// 启动 webpack-dev-server
this.server.start();
}
}
// 导出 devServer 类的一个实例
module.exports = new devServer();
config\index.js
// 引入 path、webpack 和 WebpackChain 模块
const path = require('path');
const webpack = require('webpack');
const WebpackChain = require('webpack-chain');
// 导出 defineConfig 函数,接收一个配置对象并返回它
exports.defineConfig = (config) => {
return config;
}
// 导出 getConfig 函数,返回经过处理后的 webpack 配置对象
exports.getConfig = () => {
// 创建一个 webpack-chain 实例
const webpackChain = new WebpackChain();
// 解析 emp-config.js 配置文件,并获取其导出值
const configPath = path.resolve(process.cwd(), 'emp-config.js');
const configExport = require(configPath);
// 对默认配置进行处理,如将 server 属性提取到 devServer 属性中
const config = processDefault(configExport);
// 将处理后的配置与 webpack-chain 实例合并
webpackChain.merge(config);
// 将合并后的 webpack-chain 实例转换成 webpack 配置对象并返回
return webpackChain.toConfig();
}
// 处理默认配置的函数,返回处理后的配置对象
function processDefault(configExport) {
// 将 server 属性提取到 devServer 属性中
const devServer = configExport.server || {};
delete configExport.server;
// 将 empShare 属性的值作为 mfOptions 的一部分,并将 empShare 属性删除
const mfOptions = {
filename: "emp.js",
...configExport.empShare
}
delete configExport.empShare;
// 返回处理后的配置对象
return {
context: process.cwd(),
mode: 'development',
devtool: false,
devServer,
plugin: {
html: {
plugin: require('html-webpack-plugin'),
args: [{
template: path.join(__dirname, '../template/index.html')
}]
},
mf: {
plugin: webpack.container.ModuleFederationPlugin,
args: [mfOptions]
}
},
module: {
rule: {
compile: {
test: /\.js$/,
exclude: [/node_modules/],
use: {
'babel-loader': {
loader: require.resolve('babel-loader'),
options: {
presets: [
require.resolve('@babel/preset-env'),
require.resolve('@babel/preset-react')
]
}
}
}
}
}
},
...configExport,
}
}
template\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<div id="emp-root"></div>
</body>
</html>