feat: cli 支持yarn | 支持路由懒加载 | 支持gzip

This commit is contained in:
bac-joker 2020-09-16 19:01:58 +08:00 committed by harrywan
parent a8e33ddd8c
commit 31d9c13681
10 changed files with 110 additions and 87 deletions

View File

@ -15,6 +15,7 @@ module.exports = {
'vue/comment-directive': 'off',
'no-param-reassign': 'off',
'func-names': 'off',
'global-require': 'off',
'class-methods-use-this': 'off'
}
};

View File

@ -23,6 +23,6 @@
"lerna": "^3.18.4"
},
"devDependencies": {
"@webank/eslint-config-webank": "^0.1.6"
"@webank/eslint-config-webank": "^0.1.7"
}
}

View File

@ -7,7 +7,7 @@ const generateConfig = require('../build/helpers/config');
const log = require('../build/helpers/log');
commander.usage('<command> [options]')
.version(pkg.version)
.version(pkg.version, '-v, --vers')
.option('-e, --env <env>', '配置环境 local(本地) | sit(测试) | prod(生产)')
.description(pkg.description);

View File

@ -4,14 +4,26 @@ const merge = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const FriendlyErrorsPlugin = require('@soda/friendly-errors-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const OptimizeCssnanoPlugin = require('@intervolga/optimize-cssnano-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlPlugin = require('html-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const autoprefixer = require('autoprefixer');
const browsers = require('../helpers/browser');
function handleGzipCompress(compress) {
if (!compress) return null;
if (typeof compress === 'boolean') {
return {};
}
return compress;
}
module.exports = function webpackConfig(configs, webpack, mode) {
let template = path.resolve(
configs.folders.PROJECT_DIR,
@ -24,36 +36,26 @@ module.exports = function webpackConfig(configs, webpack, mode) {
const isDev = mode === 'dev';
const isBuild = mode === 'build';
const projectNodeModulesDir = path.resolve(
configs.folders.PROJECT_DIR,
'./node_modules'
);
const cliNodeModulesDir = path.resolve(
configs.folders.CLI_DIR,
'./node_modules'
);
const nodeModulesDir = ['node_modules', projectNodeModulesDir, cliNodeModulesDir];
const gzipCompress = handleGzipCompress(configs.compress);
const presets = [
[
path.resolve(cliNodeModulesDir, '@babel/preset-env'),
{
modules: false,
useBuiltIns: 'entry',
corejs: 3,
targets: {
browsers
}
}
require.resolve('@babel/preset-env')
]
];
const plugins = [
path.resolve(cliNodeModulesDir, '@babel/plugin-proposal-object-rest-spread'),
path.resolve(cliNodeModulesDir, '@babel/plugin-syntax-dynamic-import')
[
require.resolve('@babel/plugin-transform-runtime'), {
corejs: 3
}
],
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-syntax-dynamic-import')
];
const cssloaders = [
isDev
? {
loader: 'vue-style-loader',
loader: require.resolve('vue-style-loader'),
options: {
sourceMap: false,
shadowMode: false
@ -66,17 +68,19 @@ module.exports = function webpackConfig(configs, webpack, mode) {
}
},
{
loader: 'css-loader',
loader: require.resolve('css-loader'),
options: {
sourceMap: false,
importLoaders: 2
}
},
{
loader: 'postcss-loader',
loader: require.resolve('postcss-loader'),
options: {
config: {
path: path.resolve(configs.folders.CLI_DIR, 'build/configs/postcss.config.js')
postcssOptions: {
plugins: [
autoprefixer({ browsers })
]
},
sourceMap: false
}
@ -85,22 +89,18 @@ module.exports = function webpackConfig(configs, webpack, mode) {
const baseConfig = {
mode: isDev ? 'development' : 'production',
context: path.resolve(configs.folders.PROJECT_DIR),
entry: {
app: [
path.resolve(configs.folders.CLI_DIR, './node_modules/babel-polyfill'),
// path.resolve(configs.folders.CLI_DIR, './build/utils/create-nonce'),
path.resolve(configs.folders.FES_DIR, './src/app.js')
]
},
resolve: {
extensions: ['.js', '.fes', '.vue', '.json'],
modules: nodeModulesDir,
alias: {
projectRoot: configs.folders.PROJECT_DIR,
'@': path.resolve(configs.folders.PROJECT_DIR, 'src'),
@ -113,10 +113,6 @@ module.exports = function webpackConfig(configs, webpack, mode) {
}
},
resolveLoader: {
modules: nodeModulesDir
},
output: {
globalObject: 'this',
filename: isDev ? 'js/[name].js' : 'js/[name].[contenthash:8].js',
@ -134,13 +130,13 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.vue|fes$/,
use: [
{
loader: 'cache-loader',
loader: require.resolve('cache-loader'),
options: {
cacheDirectory: path.resolve(configs.folders.PROJECT_DIR, 'node_modules/.cache/vue-loader')
}
},
{
loader: 'vue-loader',
loader: require.resolve('vue-loader'),
options: {
compilerOptions: {
preserveWhitespace: false
@ -156,11 +152,11 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'url-loader',
loader: require.resolve('url-loader'),
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
loader: require.resolve('file-loader'),
options: {
name: isDev ? 'img/[name].[ext]' : 'img/[name].[hash:8].[ext]'
}
@ -175,7 +171,7 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'file-loader',
loader: require.resolve('file-loader'),
options: {
name: isDev ? 'img/[name].[ext]' : 'img/[name].[hash:8].[ext]'
}
@ -188,11 +184,11 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: 'url-loader',
loader: require.resolve('url-loader'),
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
loader: require.resolve('file-loader'),
options: {
name: isDev ? 'media/[name].[ext]' : 'media/[name].[hash:8].[ext]'
}
@ -207,11 +203,11 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
loader: require.resolve('url-loader'),
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
loader: require.resolve('file-loader'),
options: {
name: isDev ? 'fonts/[name].[ext]' : 'fonts/[name].[hash:8].[ext]'
}
@ -238,7 +234,7 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.scss$/,
use: cssloaders.concat([
{
loader: 'sass-loader',
loader: require.resolve('sass-loader'),
options: {
sourceMap: false
}
@ -251,7 +247,7 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.sass$/,
use: cssloaders.concat([
{
loader: 'sass-loader',
loader: require.resolve('sass-loader'),
options: {
sourceMap: false,
indentedSyntax: true
@ -265,7 +261,7 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.less$/,
use: cssloaders.concat([
{
loader: 'less-loader',
loader: require.resolve('less-loader'),
options: {
sourceMap: false,
javascriptEnabled: true
@ -279,7 +275,7 @@ module.exports = function webpackConfig(configs, webpack, mode) {
test: /\.styl(us)?$/,
use: cssloaders.concat([
{
loader: 'stylus-loader',
loader: require.resolve('stylus-loader'),
options: {
sourceMap: false,
preferPathResolver: 'webpack'
@ -291,18 +287,19 @@ module.exports = function webpackConfig(configs, webpack, mode) {
/* config.module.rule('js') */
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'cache-loader',
loader: require.resolve('cache-loader'),
options: {
cacheDirectory: path.resolve(configs.folders.PROJECT_DIR, 'node_modules/.cache/babel-loader')
}
},
{
loader: 'thread-loader'
loader: require.resolve('thread-loader')
},
{
loader: 'babel-loader',
loader: require.resolve('babel-loader'),
options: {
presets,
plugins
@ -402,6 +399,14 @@ module.exports = function webpackConfig(configs, webpack, mode) {
/* config.plugin('friendly-errors') */
new FriendlyErrorsPlugin(),
isBuild && gzipCompress && new CompressionWebpackPlugin({ // gzip 压缩
filename: '[path][base].gz',
test: /\.js$|\.html$|\.css/,
threshold: 10240,
minRatio: 0.8,
...gzipCompress
}),
/* config.plugin('index.html') */
new HtmlPlugin({
template,

View File

@ -1 +1,8 @@
module.exports = ['>1%', 'last 2 versions', 'safari >= 7', 'ie >= 9'];
module.exports = [
'Chrome >= 46',
'Firefox >= 45',
'Safari >= 10',
'Edge >= 13',
'iOS >= 10',
'Electron >= 0.36'
];

View File

@ -44,6 +44,8 @@ function generateConfig(command, env) {
const fesCofig = require(path.join(config.folders.PROJECT_DIR, 'fes.config.js'));
config.CDN = fesCofig.env[config.env].cdn;
config.needCDN = !!config.CDN;
config.compress = fesCofig.compress;
config.lazyRouter = fesCofig.lazyRouter;
} catch (e) {
config.needCDN = false;
}

View File

@ -3,13 +3,15 @@ const
const webpack = require('webpack');
const express = require('express');
const opn = require('opn');
const path = require('path');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackDevMiddleware = require('webpack-dev-middleware');
const initMock = require('../mock/init.js');
module.exports = function createDevServer(port, defaultConfig) {
defaultConfig.entry.app.unshift('webpack-hot-middleware/client?reload=true');
const hotMiddlewarePath = require.resolve('webpack-hot-middleware');
defaultConfig.entry.app.unshift(`${hotMiddlewarePath.replace(path.basename(hotMiddlewarePath), '')}client?reload=true`);
defaultConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
defaultConfig.plugins.push(new webpack.NamedModulesPlugin());
@ -19,6 +21,7 @@ module.exports = function createDevServer(port, defaultConfig) {
// devServer 自带支持,添加自定义插件。
app.use(webpackDevMiddleware(compiler, {
lazy: false,
logLevel: 'silent',
watchOptions: {
aggregateTimeout: 300,
poll: 1000
@ -31,7 +34,9 @@ module.exports = function createDevServer(port, defaultConfig) {
publicPath: defaultConfig.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));
app.use(webpackHotMiddleware(compiler, {
log: false
}));
app.use('/static', express.static('src/static'));

View File

@ -27,7 +27,6 @@ const main = {
app.use(cookieParser());
this.customRoute();
this.defaultRoute();
},
customRoute() {
@ -92,20 +91,6 @@ const main = {
log.message('[INFO] mock.js 发生变化');
loadRouteConfig();
});
},
defaultRoute() {
const app = this.app;
setTimeout(() => {
app.use((err, req, res) => {
res.json({
status: err.status || 500,
message: err.message,
err
});
});
});
}
};

View File

@ -17,17 +17,30 @@ export default {{routes}};
const routes = getRoute(config.folders.PROJECT_PAGE_DIR, config.folders.PROJECT_PAGE_DIR);
const componentsTemplate = [];
routes.components.forEach((item) => {
componentsTemplate.push(render(IMPORT_TEMPLATE, {
name: item.name,
path: item.path
}));
});
let template = '';
if (config.lazyRouter) {
const componentsObj = {};
routes.components.forEach((item) => {
componentsObj[item.name] = item.path;
});
// component: () => import( /* webpackChunkName: "home" */ '../views/Home.vue')
template = render(MAIN_TEMPLATE, {
include: '',
routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, ($0, $1) => `"component": () => import( /* webpackChunkName: "${$1}" */ '${componentsObj[$1]}')`)
});
} else {
routes.components.forEach((item) => {
componentsTemplate.push(render(IMPORT_TEMPLATE, {
name: item.name,
path: item.path
}));
});
const template = render(MAIN_TEMPLATE, {
include: componentsTemplate.join(endOfLine),
routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, '"component": $1')
});
template = render(MAIN_TEMPLATE, {
include: componentsTemplate.join(endOfLine),
routes: JSON.stringify(routes.newRoutes).replace(/"component":"(.+?)"/g, '"component": $1')
});
}
fs.outputFileSync(OUTPUT_PATH, template);
}

View File

@ -32,17 +32,19 @@
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/runtime": "^7.7.2",
"@babel/runtime-corejs3": "^7.11.2",
"@intervolga/optimize-cssnano-plugin": "^1.0.6",
"@soda/friendly-errors-webpack-plugin": "^1.7.1",
"autoprefixer": "^8.1.0",
"babel-loader": "^8.0.6",
"babel-polyfill": "^6.26.0",
"body-parser": "^1.5.2",
"cache-loader": "^4.1.0",
"case-sensitive-paths-webpack-plugin": "^2.2.0",
"chalk": "^1.1.1",
"chalk": "^4.1.0",
"chokidar": "^1.7.0",
"clean-webpack-plugin": "^3.0.0",
"commander": "^4.1.0",
"compression-webpack-plugin": "^6.0.0",
"cookie-parser": "^1.4.3",
"copy-webpack-plugin": "^5.0.4",
"cross-spawn": "^2.1.0",
@ -59,27 +61,31 @@
"hash-sum": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"http-proxy": "^1.12.0",
"is-ci": "^1.0.10",
"json-templater": "^1.2.0",
"less": "^3.12.2",
"less-loader": "^7.0.1",
"lodash": "^4.17.4",
"mini-css-extract-plugin": "^0.8.0",
"mockjs": "^1.1.0",
"morgan": "^1.2.2",
"node-plus-string": "^1.0.1",
"node-sass": "^4.13.0",
"node-sass": "^4.14.1",
"normalize-path": "^1.0.0",
"on-finished": "^2.3.0",
"opn": "^4.0.2",
"path": "^0.12.7",
"postcss-loader": "^3.0.0",
"postcss": "^7.0.32",
"postcss-loader": "^4.0.1",
"prompts": "^2.3.0",
"request": "^2.81.0",
"require-dir": "^0.3.0",
"sass-loader": "^8.0.0",
"sass-loader": "^10.0.2",
"shelljs": "^0.5.3",
"string-replace-loader": "^2.2.0",
"strip-indent": "^2.0.0",
"style-loader": "^1.0.0",
"stylus": "^0.54.8",
"stylus-loader": "^3.0.2",
"tar": "^6.0.5",
"tar-fs": "^1.16.0",
"terser-webpack-plugin": "^2.2.1",
@ -91,7 +97,6 @@
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.9.0",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.2",
"yargs": "^3.31.0"