предисловие
Я уже писал немного об основах.строительные леса, оглядываться@vue/cli
Исходный код, должен сказать, действительно очень формален и стандартизирован. Обратитесь к некоторым функциям @vue/cli и измените скаффолдинг.
эффект каштана
Структура проекта
проектchuhc-react
Структура разделена на четыре части:
-
@chuhc/cli
Формируйте содержимое командной строки, инициализируйте проект с помощью команд и т. д. -
@chuhc/scripts
Проект компилирует и запускает упакованное содержимое и еще не подключен к процессу развертывания. -
@chuhc/template
файл шаблона. -
@chuhc/plugin-xxx
Плагины, упакованные в проект, такие как@chuhc/plugin-typescript
и т.п.
cli
непосредственно черезinquirer
Некоторые интерактивные команды , после получения информации, проходятdownload-git-repo
соответствоватьgithub
Вытащить файл шаблона относительно просто, но если шаблонов несколько, обслуживание будет затруднено.
пока в@vue/cli
Здесь он объединяется в общий шаблон с помощью нескольких плагинов.Во многих корневых каталогах скаффолдинга естьxxx.config.js
незащищенный. затем работаетnode
При командовании читать конфигурационный файл и выполнять соответствующие операции в соответствии с содержимым конфигурационного файла, например: использоватьwebpack-chain
Динамическая модификацияconfig
, и, наконец, вызовtoConfig
генерировать новыеwebpack
Настроить контент.
Оценка подключаемого модуля, создание пакета
базовыйpackage.json
Шаблоны, кроме обычных константversion
,private
,license
подожди, какname
,scripts
,dependencies
,devDependencies
Нам нужно добавить его вручную.
name
Просто используйте скаффолдинг для инициализации входящих параметров иscripts
успешно внедряет@chuhc/scripts
, используйте его для запуска команды.
как некоторые обязательные, например.react
,react-dom
, мы можем прямо положитьdependencies
в, покаdevDependencies
Как правило, он выбирается пользователем вручную во время инициализации.plugins
.
с помощьюinquirer
Перейдите к списку плагинов и позвольте пользователю выбрать, какие плагины импортировать.
const CHUHC_PLUGIN_CHECK = [
{
name: 'Typescript',
value: ['tsx', '@chuhc/plugin-typescript'],
},
{
name: 'Less',
value: ['less', '@chuhc/plugin-less'],
},
];
const { plugins } = await inquirer.prompt([
{
type: 'checkbox',
name: 'plugins',
message: 'Do you need these plugins',
choices: CHUHC_PLUGIN_CHECK, // 一个结构,自己觉得怎么处理方便,怎么来
},
]);
Тогда в соответствии с вышеизложеннымcode
могут получить то, что нужно пользователямplugin
Так что вы можете поставить этиplugin
вставитьjson
изdevDependencies
внутри.
Получить последнюю версию зависимостей
любой из вышеперечисленныхreact
ещеplugin
, Нам нужен номер версии, я здесь, чтобы использовать командную строку, чтобы получить последнюю версию, а затем как ееvalue
стоимость. Если вы перемещаетесь напрямую, чтобы запуститьexecSync
будет блокировать,ora
изloading
Тоже застрял, так что выбирайpromise
пробежатьexec
перезвонить до концаpromise
.
const forEachSetV = (list, obj, key) => {
const promises = [];
const manager = hasCnpm() ? 'cnpm' : 'npm'; // 判断选择cnpm还是npm
list.forEach(item => {
if (typeof item === 'object') {
return forEachSetV(item, obj, key);
}
const newPromise = new Promise(res => {
exec(`${manager} view ${item} version`, (err, stdout, stderr) => {
obj[key][item] = stdout.slice(0, stdout.length - 1);
res(0);
});
});
promises.push(newPromise);
});
return promises;
};
const promise = [
...forEachSetV(depe, pkg, 'dependencies'),
...forEachSetV(devD, pkg, 'devDependencies'),
];
await Promise.all(promise);
Затем, получив номер версии, заполните остальные данные вместе вjson
, используйте его какpackage.json
value в новом каталоге проекта создайте его.
const fs = require('fs-extra'); // fs-extra是系统fs模块的扩展
const path = require('path');
module.exports = (dir, files) => {
Object.keys(files).forEach(name => {
const pathName = path.join(dir, name);
fs.ensureDirSync(path.dirname(pathName)); // 如果没有文件夹则新建文件夹
fs.writeFileSync(pathName, files[name]); // 新建文件
});
};
writeFileTree(targetDir, {
'package.json': JSON.stringify(pkg, null, 2),
});
Выберите инструмент управления пакетами
потому чтоnpm
Скорость не идеальна и может рассматриваться как практический результат. Сначала определите, имеет ли текущая средаyarn
,cnpm
И так далее, а затем приоритеты выбирают первое, если нет, то повторно используйтеnpm
работать.
const PM_CONFIG = {
npm: {
install: ['install', '--loglevel', 'error'], // 打印error信息
remove: ['uninstall', '--loglevel', 'error'],
},
yarn: {
install: [],
remove: ['remove'],
},
};
PM_CONFIG.cnpm = PM_CONFIG.npm;
module.exports = class PackageManager {
constructor({ pkgName }) {
this.pkgName = pkgName;
if (hasYarn()) {
this.bin = 'yarn';
} else if (hasCnpm()) {
this.bin = 'cnpm';
} else {
this.bin = 'npm';
}
}
// 封装了下运行命令函数
runCommand(command, args = []) {
const _commands = [this.bin, ...PM_CONFIG[this.bin][command], ...args];
execSync(_commands.join(' '), { stdio: [0, 1, 2] });
}
install() {
try {
this.runCommand('install', ['--offline']); // offline指先去拉取缓存区里的,如果没有则去服务器拉
} catch (e) {
this.runCommand('install'); // 报错兜底
}
}
git() {
try {
execSync('git init');
return true;
} catch (e) {
return false;
}
}
};
судитьyarn
а такжеcnpm
О том, существует ли он в окружающей среде, можно судить поversion
Подождите, пока метод увидит, может ли он быть успешно выполнен, если он успешно выполнен, это означает, что он существует в среде, в противном случае - нет.
const judgeEnv = name => {
const envKey = `_has${name[0].toUpperCase()}${name.slice(1)}`; // 保存下结果
if (_env[envKey] !== null) {
return _env[envKey];
}
try {
execSync(`${name} --version`, { stdio: 'ignore' }); // 不打印信息
return (_env[envKey] = true);
} catch (e) {
return (_env[envKey] = false);
}
};
const hasYarn = judgeEnv.bind(this, 'yarn');
const hasCnpm = judgeEnv.bind(this, 'cnpm');
затем пройтиinstall
Способ установки зависимостей, а затем пройти несколько параметров@chuhc/template
Идите и скопируйте несколько основных шаблонов.
scripts
Поскольку это многостраничный проект,scripts
В основном он делает следующие вещи:
- пройти через
glob
чтобы соответствовать записи, затем используйте ее какentry
Динамическое входящее и динамическое входящее несколькоhtml-webpack-plugin
Датьplugins
. - Читая корневой каталог проекта
chuhc.config.js
файл для динамического измененияwebpack
Настройте контент и вызовите соответствующий плагин. - наконец сгенерировать окончательный
webpack
конфигурационный файл, переданныйwebpack
Перейдите к компиляции, запуску, упаковке и т. д.
вход в матч
Соответствующая запись в основном используетсяglob
Чтобы соответствовать, только если требования соответствия будут выполнены, он будет использоваться в качестве записи. Затем через согласованную информацию, чтобы сгенерировать соответствующийentry
содержание иplugin
содержание, переданноеwebpack
конфигурационный файл.
const SRC = './src/**/index.?(js|jsx|ts|tsx)';
/**
* get webpack entry
*/
const getEntries = () => {
if (entries) return entries;
entries = {};
const pages = glob.sync(SRC);
pages.forEach(page => { // 遍历传entry
const dirname = path.dirname(page);
const entry = path.basename(dirname);
entries[entry] = page;
});
return entries;
};
/**
* get pages info
* @param {Boolean} isProd
*/
const getPages = isProd => {
const plugins = [];
let entries = getEntries();
Object.keys(entries).map(dirname => { // 遍历传plugin
plugins.push(
new HtmlWebpackPlugin({
chunks: isProd ? ['libs', dirname] : [dirname],
filename: `./${dirname}/index.html`,
template: path.join(__dirname, './template/index.html')
})
);
});
return plugins;
};
цепочка конфигурации конфигурации
Рекомендуется цепная конфигурацияwebpack-chain,@vue/cli
также использовать его. Поскольку у нас уже есть некоторое базовое содержимое конфигурации, мы можем передатьconfig.merge
Объедините наш существующий объект конфигурации с экземпляром конфигурации.
Однако прямое преобразование не поддерживается, и нам необходимо вручную преобразовать некоторый контент конфигурации, например:module
. а такжеplugins
Не поддерживатьnew
изplugin
, моя обработка здесь заключается в том, чтобы пропустить вправоplugin
, и, наконец, используйтеwebpack-merge
Будуconfig.toConfig()
а такжеplugins
А затем объединены в окончательный объект конфигурации.
const Config = require('webpack-chain');
const chuhcConfig = require(`${process.cwd()}/chuhc.config.js`); // 读取根目录的配置文件
const { setBaseConfig } = require('./util/merge'); // 将已有的配置文件对象合并到配置实例
const BASE = require('./config/webpack.base'); // 配置对象base
const DEVE = merge(BASE, require('./config/webpack.deve')); // 配置对象 deve
const PROD = merge(BASE, require('./config/webpack.prod')); // 配置对象 prod
const config = new Config();
// 我这边就只是对plugin做一下处理,可以做其他很多事情,这里只是举个例子
const handleChuhcConfig = ({ plugins } = {}) => {
// to do sth.
if (plugins) {
plugins.forEach(plugin => {
require(plugin[0])(config, plugin[1]);
});
}
};
const getConfig = isDeve => {
config.clear(); // 清除配置
setBaseConfig(isDeve ? DEVE : PROD, config);
handleChuhcConfig(chuhcConfig);
return merge(config.toConfig(), {
plugins: isDeve ? DEVE.plugins : PROD.plugins
}); // 最后再合并
};
скомпилировать и запустить
в полученииwebpack config
, то он может быть основан наdev
команда все ещеbuild
команду, чтобы вызвать соответствующую функцию, скомпилировать, запустить, упаковать и так далее. (Аналогично, согласноprogram.command
)
// dev 运行
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const { getDeveConfig } = require('./config');
module.exports = () => {
const DEVE_CONFIG = getDeveConfig();
const { devServer } = DEVE_CONFIG;
const compiler = webpack(DEVE_CONFIG);
const server = new WebpackDevServer(compiler, { ...devServer });
server.listen(devServer.port);
};
// build
const webpack = require('webpack');
const { getProdConfig } = require('./config');
const logSymbols = require('log-symbols');
module.exports = () => {
const PROD_CONFIG = getProdConfig();
const compiler = webpack(PROD_CONFIG);
compiler.run((err, stats) => {
if (err) {
// 回调中接收错误信息。
console.error(err);
} else {
console.log(logSymbols.success, '打包成功!');
}
});
};
template
template
В основном через входящие параметры, чтобы определить, следует лиcopy
соответствующий файл, и в то же время согласноoptions
Приходите и измените содержимое и суффикс соответствующего файла. Код слишком скучный, поэтому я не буду его публиковать.
plugin-xx
plugin
Если это так, есть еще много вещей, которые можно сделать, я здесь только для того, чтобы изменить цепочкуwebpack
информация о конфигурации. Это только одна из функций, их гораздо больше, например: напишите самиwebpack plugin / loader
Проходить, делать что-то другое.
// example
module.exports = config => {
['.tsx', '.ts'].forEach(item => config.resolve.extensions.add(item));
config.module
.rule('js')
.test(/\.(js|ts|tsx|jsx)$/)
.use('babel-loader')
.tap(options => {
options.presets.push('@babel/preset-typescript');
return options;
});
};
github
PS: Начиная с сегодняшнего дня, продолжайте учиться. Еще недавно ничего не хотелось делать, каждый день после работы хотелось только лечь и растеряться.