feat: 完善 vite build

This commit is contained in:
winixt 2022-04-04 15:32:41 +08:00
parent e3d49d51c8
commit 049c9532de
24 changed files with 131 additions and 58 deletions

View File

@ -1,29 +1,47 @@
import legacy from '@vitejs/plugin-legacy';
import { getInnerCommonConfig } from '../../common/getConfig'; import { getInnerCommonConfig } from '../../common/getConfig';
/**
* polyfill @vitejs/plugin-legacy
* 确认 css 最终构建实现 autoprefixer postcss-safe-parser postcss-flexbugs-fixes
*/
export default async (api) => { export default async (api) => {
const { deepmerge } = api.utils; const { deepmerge, getTargetsAndBrowsersList } = api.utils;
const { build = {} } = api.config.viteOption; const { build = {} } = api.config.viteOption;
const { browserslist } = getTargetsAndBrowsersList({ config: api.config });
return deepmerge( const bundleConfig = deepmerge(
{ {
mode: 'production', mode: 'production',
css: { css: {
postcss: { postcss: {
plugins: [require('postcss-flexbugs-fixes'), require('postcss-safe-parser'), [require('autoprefixer'), {}]], plugins: [
require('postcss-flexbugs-fixes'),
require('postcss-safe-parser'),
require('autoprefixer')({
...api.config.autoprefixer,
overrideBrowserslist: browserslist,
}),
],
}, },
}, },
plugins: [
legacy({
targets: browserslist,
...api.config.viteLegacy,
}),
],
build: { build: {
...build, ...build,
target: build.target || 'es2015',
outDir: build.outDir || api.config.outputPath || 'dist', outDir: build.outDir || api.config.outputPath || 'dist',
assetsInlineLimit: build.assetsInlineLimit || api.config.inlineLimit || 8192, assetsInlineLimit: build.assetsInlineLimit || api.config.inlineLimit || 8192,
}, },
}, },
getInnerCommonConfig(api), getInnerCommonConfig(api),
); );
return api.applyPlugins({
type: api.ApplyPluginsType.modify,
key: 'modifyBundleConfig',
initialValue: bundleConfig,
args: {},
});
}; };

View File

@ -24,12 +24,13 @@ export default async (api, args) => {
args: {}, args: {},
}); });
return deepmerge( const bundleConfig = deepmerge(
{ {
mode: 'development', mode: 'development',
plugins: [viteMiddlewarePlugin(beforeMiddlewares, middlewares)], plugins: [viteMiddlewarePlugin(beforeMiddlewares, middlewares)],
server: { server: {
...server, ...server,
proxy: server?.proxy || api.config.proxy,
port, port,
host: hostname, host: hostname,
https: process.env.HTTPS || args.https, https: process.env.HTTPS || args.https,
@ -37,4 +38,11 @@ export default async (api, args) => {
}, },
getInnerCommonConfig(api), getInnerCommonConfig(api),
); );
return api.applyPlugins({
type: api.ApplyPluginsType.modify,
key: 'modifyBundleConfig',
initialValue: bundleConfig,
args: {},
});
}; };

View File

@ -3,11 +3,6 @@ import getDevConfig from './getDevConfig';
/** /**
* TODO * TODO
*
* analyze: rollup-plugin-visualizer
*
* 其他插件如何对内部配置进行修改
* *
* 共享 webpack vite 的部分配置降低熟悉 vite 的成本 * 共享 webpack vite 的部分配置降低熟悉 vite 的成本
*/ */

View File

@ -5,11 +5,8 @@ import { createHtmlPlugin } from 'vite-plugin-html';
import SFCConfigBlockPlugin from './SFCConfigBlockPlugin'; import SFCConfigBlockPlugin from './SFCConfigBlockPlugin';
import getDefine from './getDefine'; import getDefine from './getDefine';
// TODO
// * 如何处理 html (改 mountId or title 等)(比较麻烦,晚点再看看有无更好的方案)
export function getInnerCommonConfig(api) { export function getInnerCommonConfig(api) {
const { deepmerge } = api.utils; const { deepmerge, resolveRuntimeEnv } = api.utils;
const { server, build, define, base, ...otherViteOption } = api.config.viteOption; const { server, build, define, base, ...otherViteOption } = api.config.viteOption;
const publicPath = base || api.config.publicPath || '/'; const publicPath = base || api.config.publicPath || '/';
@ -27,10 +24,10 @@ export function getInnerCommonConfig(api) {
createHtmlPlugin({ createHtmlPlugin({
minify: true, minify: true,
entry: join(api.paths.absTmpPath, 'fes.js'), entry: join(api.paths.absTmpPath, 'fes.js'),
template: 'public/index.html', template: 'index.html',
inject: { inject: {
data: { data: {
title: 'Fes.js', ...resolveRuntimeEnv(publicPath),
mountElementId: api.config.mountElementId, mountElementId: api.config.mountElementId,
}, },
}, },

View File

@ -3,10 +3,24 @@ export default (api) => {
key: 'viteAnalyze', key: 'viteAnalyze',
config: { config: {
schema(joi) { schema(joi) {
return joi.object({}).unknown(true); return joi.object();
}, },
default: {}, default: {},
}, },
enableBy: () => !!process.env.ANALYZE, enableBy: () => !!process.env.ANALYZE,
}); });
api.modifyBundleConfig((memo) => {
memo.plugins.push(
require('rollup-plugin-visualizer').visualizer({
filename: './.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
...api.viteAnalyze,
}),
);
return memo;
});
}; };

View File

@ -0,0 +1,12 @@
export default (api) => {
api.describe({
key: 'viteHtml',
config: {
schema(joi) {
return joi.object();
},
default: {},
},
enableBy: () => !!process.env.ANALYZE,
});
};

View File

@ -0,0 +1,12 @@
export default (api) => {
api.describe({
key: 'viteLegacy',
config: {
schema(joi) {
return joi.object();
},
default: {},
},
enableBy: () => !!process.env.ANALYZE,
});
};

View File

@ -1,10 +1,15 @@
export default function () { export default function () {
return { return {
plugins: [ plugins: [
require.resolve('./registerMethods'),
require.resolve('./registerType'),
// bundle configs // bundle configs
require.resolve('./features/viteOption'), require.resolve('./features/viteOption'),
require.resolve('./features/viteVueJsx'), require.resolve('./features/viteVueJsx'),
require.resolve('./features/viteVuePlugin'), require.resolve('./features/viteVuePlugin'),
require.resolve('./features/viteAnalyze'),
require.resolve('./features/viteLegacy'),
// commands // commands
require.resolve('./commands/build'), require.resolve('./commands/build'),

View File

@ -0,0 +1,5 @@
export default function (api) {
['modifyBundleConfig'].forEach((name) => {
api.registerMethod({ name });
});
}

View File

@ -0,0 +1,8 @@
import { name } from '../package.json';
export default function (api) {
api.addConfigType(() => ({
source: name,
build: ['ViteBuildConfig'],
}));
}

View File

@ -1,10 +1,14 @@
import type {UserConfig} from 'vite'; import type {UserConfig} from 'vite';
import type {Options} from '@vitejs/plugin-vue' import type {Options} from '@vitejs/plugin-vue'
import {Options as PolyfillOptions } from '@vitejs/plugin-legacy'
import createPlugin from '@vitejs/plugin-vue-jsx' import createPlugin from '@vitejs/plugin-vue-jsx'
import {createHtmlPlugin} from 'vite-plugin-html'
export interface ViteBuildConfig { export interface ViteBuildConfig {
viteOption: UserConfig; viteOption: UserConfig;
viteVuePlugin: Options; viteVuePlugin: Options;
viteVueJsx: Parameters<typeof createPlugin>[0]; viteVueJsx: Parameters<typeof createPlugin>[0];
viteLegacy: PolyfillOptions;
viteHtml: Parameters<typeof createHtmlPlugin>[0]
} }

View File

@ -25,7 +25,6 @@ export default function () {
require.resolve('./plugins/features/outputPath'), require.resolve('./plugins/features/outputPath'),
require.resolve('./plugins/features/postcssLoader'), require.resolve('./plugins/features/postcssLoader'),
require.resolve('./plugins/features/publicPath'), require.resolve('./plugins/features/publicPath'),
require.resolve('./plugins/features/targets'),
require.resolve('./plugins/features/terserOptions'), require.resolve('./plugins/features/terserOptions'),
require.resolve('./plugins/features/nodeModulesTransform'), require.resolve('./plugins/features/nodeModulesTransform'),
require.resolve('./plugins/features/vueLoader'), require.resolve('./plugins/features/vueLoader'),

View File

@ -167,8 +167,6 @@ export function printFileSizes(stats, dir) {
console.log(`${ui.toString()}\n\n ${chalk.gray('Images and other types of assets omitted.')}\n`); console.log(`${ui.toString()}\n\n ${chalk.gray('Images and other types of assets omitted.')}\n`);
if (orderedAssets?.some((asset) => asset.suggested)) { if (orderedAssets?.some((asset) => asset.suggested)) {
// We'll warn for bundles exceeding them.
// TODO: use umi docs
console.log(); console.log();
console.log(chalk.yellow('The bundle size is significantly larger than recommended.')); console.log(chalk.yellow('The bundle size is significantly larger than recommended.'));
console.log(chalk.yellow('Consider reducing it with code splitting')); console.log(chalk.yellow('Consider reducing it with code splitting'));

View File

@ -9,24 +9,6 @@ import createDefineWebpackConfig from './define';
import createMinimizerWebpackConfig from './minimizer'; import createMinimizerWebpackConfig from './minimizer';
import createHtmlWebpackConfig from './html'; import createHtmlWebpackConfig from './html';
function getTargetsAndBrowsersList({ config }) {
let targets = config.targets || {};
targets = Object.keys(targets)
.filter((key) => targets[key] !== false)
.reduce((memo, key) => {
memo[key] = targets[key];
return memo;
}, {});
const browserslist = targets.browsers || Object.keys(targets).map((key) => `${key} >= ${targets[key] === true ? '0' : targets[key]}`);
return {
targets,
browserslist,
};
}
const DEFAULT_EXCLUDE_NODE_MODULES = [ const DEFAULT_EXCLUDE_NODE_MODULES = [
'vue', 'vue',
'vuex', 'vuex',
@ -139,7 +121,7 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
esModule: false, esModule: false,
}); });
const { targets, browserslist } = getTargetsAndBrowsersList({ config }); const { targets, browserslist } = api.utils.getTargetsAndBrowsersList({ config });
const babelOpts = await getBabelOpts({ const babelOpts = await getBabelOpts({
cwd, cwd,
config, config,

View File

@ -15,8 +15,6 @@ import mergeDefault from './utils/mergeDefault';
const CONFIG_FILES = ['.fes.js']; const CONFIG_FILES = ['.fes.js'];
// TODO:
// 1. custom config file
export default class Config { export default class Config {
cwd; cwd;

View File

@ -21,6 +21,7 @@ export default function () {
require.resolve('./plugins/features/plugins'), require.resolve('./plugins/features/plugins'),
require.resolve('./plugins/features/proxy'), require.resolve('./plugins/features/proxy'),
require.resolve('./plugins/features/singular'), require.resolve('./plugins/features/singular'),
require.resolve('./plugins/features/targets'),
// route // route
require.resolve('./plugins/route'), require.resolve('./plugins/route'),

View File

@ -2,13 +2,16 @@ import { extname } from 'path';
import historyFallback from 'connect-history-api-fallback'; import historyFallback from 'connect-history-api-fallback';
const ASSET_EXT_NAMES = ['.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg']; const ASSET_EXT_NAMES = ['.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg'];
const SKIP_PATHS_PREFIX = ['/@vite', '/@id'];
const proxyMiddleware = (api) => (req, res, next) => { const proxyMiddleware = (api) => (req, res, next) => {
const proxyConfig = api.config.proxy; const proxyConfig = api.config.proxy;
if (proxyConfig && Object.keys(proxyConfig).some((path) => req.url.startsWith(path))) { if (proxyConfig && Object.keys(proxyConfig).some((path) => req.url.startsWith(path))) {
return next(); return next();
} }
if (SKIP_PATHS_PREFIX.find((prefix) => req.url.startsWith(prefix))) {
return next();
}
if (ASSET_EXT_NAMES.includes(extname(req.url))) { if (ASSET_EXT_NAMES.includes(extname(req.url))) {
return next(); return next();
} }

View File

@ -3,11 +3,10 @@ export default (api) => {
key: 'targets', key: 'targets',
config: { config: {
default: { default: {
chrome: 49, chrome: 56,
firefox: 64, firefox: 67,
safari: 10, safari: 10.4,
edge: 13, edge: 13,
ios: 10,
}, },
schema(joi) { schema(joi) {
return joi.object(); return joi.object();

View File

@ -1,4 +1,3 @@
// TODO 其他 API
export { export {
useRoute, useRoute,
useRouter, useRouter,
@ -10,7 +9,7 @@ export {
createWebHashHistory, createWebHashHistory,
createWebHistory, createWebHistory,
createMemoryHistory, createMemoryHistory,
createRouter createRouter,
} from 'vue-router'; } from 'vue-router';
export { default as Plugin, ApplyPluginsType } from './plugin'; export { default as Plugin, ApplyPluginsType } from './plugin';

View File

@ -11,15 +11,12 @@
<meta name="viewport" <meta name="viewport"
content="viewport-fit=cover,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"> content="viewport-fit=cover,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
<title> <title>
<%= title %> Fes & vite
</title> </title>
<link rel="shortcut icon" type="image/x-icon" href="./logo.png">
</head> </head>
<body ontouchstart=""> <body ontouchstart="">
<div id="<%= mountElementId %>"></div> <div id="<%= mountElementId %>"></div>
<script type="module" src="/src/.fes/fes.js"></script>
<!-- built files will be auto injected -->
</body> </body>
</html> </html>

View File

@ -4,7 +4,8 @@
"description": "fes vite 构建模版", "description": "fes vite 构建模版",
"scripts": { "scripts": {
"prod": "FES_ENV=prod fes build", "prod": "FES_ENV=prod fes build",
"dev": "fes dev" "dev": "fes dev",
"analyze": "ANALYZE=1 fes build"
}, },
"keywords": [ "keywords": [
"管理端", "管理端",

View File

@ -1,4 +1,3 @@
// TODO
// 时间格式化 // 时间格式化
// js 数字精度计算 // js 数字精度计算
// 手机号、身份证号 等的校验 // 手机号、身份证号 等的校验

View File

@ -0,0 +1,17 @@
export default function getTargetsAndBrowsersList({ config }) {
let targets = config.targets || {};
targets = Object.keys(targets)
.filter((key) => targets[key] !== false)
.reduce((memo, key) => {
memo[key] = targets[key];
return memo;
}, {});
const browserslist = targets.browsers || Object.keys(targets).map((key) => `${key} >= ${targets[key] === true ? '0' : targets[key]}`);
return {
targets,
browserslist,
};
}

View File

@ -31,6 +31,7 @@ import changePort from './changePort';
import getHostName from './getHostName'; import getHostName from './getHostName';
import resolveRuntimeEnv from './resolveRuntimeEnv'; import resolveRuntimeEnv from './resolveRuntimeEnv';
import stringifyObjValue from './stringifyObjValue'; import stringifyObjValue from './stringifyObjValue';
import getTargetsAndBrowsersList from './getTargetsAndBrowsersList';
export { export {
chalk, chalk,
@ -68,4 +69,5 @@ export {
getHostName, getHostName,
resolveRuntimeEnv, resolveRuntimeEnv,
stringifyObjValue, stringifyObjValue,
getTargetsAndBrowsersList,
}; };