上一篇我們說到了gulp任務流,這一篇繼續(xù)說
查看package.json卖宠,可以看到對于antd-tools用到了以下幾個命令:
"lint:ts": "npm run tsc && antd-tools run ts-lint"
"lint-fix:ts": "npm run tsc && antd-tools run ts-lint-fix"
"dist": "antd-tools run dist"
"compile": "antd-tools run compile"
"predeploy"
pub
prepublish
npm run lint:ts
:編譯typescript并檢測typescript語法規(guī)范;
npm run lint-fix:ts
:編譯typescript并檢測typescript語法規(guī)范并且根據(jù)配置文件自動格式化代碼;
npm run dist
:
首先看dist任務的源碼
function dist(done) {
rimraf.sync(path.join(cwd, 'dist'));
process.env.RUN_ENV = 'PRODUCTION';
const webpackConfig = require(path.join(cwd, 'webpack.config.js'));
webpack(webpackConfig, (err, stats) => {
if (err) {
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
console.error(info.errors);
}
if (stats.hasWarnings()) {
console.warn(info.warnings);
}
const buildInfo = stats.toString({
colors: true,
children: true,
chunks: false,
modules: false,
chunkModules: false,
hash: false,
version: false,
});
console.log(buildInfo);
done(0);
});
}
簡單概括來說就是根據(jù)項目目錄的webpack.config.js讓webpack去處理前端資源打包工作,并輸出必要信息祖乳。下面是webpack.config.js源碼:
// This config is for building dist files
const webpack = require('webpack');
const getWebpackConfig = require('antd-tools/lib/getWebpackConfig');
const WebpackBar = require('webpackbar');
// noParse still leave `require('./locale' + name)` in dist files
// ignore is better
// http://stackoverflow.com/q/25384360
function ignoreMomentLocale(webpackConfig) {
delete webpackConfig.module.noParse;
webpackConfig.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/));
}
function addLocales(webpackConfig) {
let packageName = 'antd-with-locales';
if (webpackConfig.entry['antd.min']) {
packageName += '.min';
}
webpackConfig.entry[packageName] = './index-with-locales.js';
webpackConfig.output.filename = '[name].js';
}
function externalMoment(config) {
config.externals.moment = {
root: 'moment',
commonjs2: 'moment',
commonjs: 'moment',
amd: 'moment',
};
}
function usePrettyWebpackBar(config) {
// remove old progress plugin.
config.plugins = config.plugins
.filter((plugin) => {
return !(plugin instanceof webpack.ProgressPlugin)
&& !(plugin instanceof WebpackBar);
});
// use brand new progress bar.
config.plugins.push(
new WebpackBar({
name: '?? Webpack',
minimal: false,
})
);
}
const webpackConfig = getWebpackConfig(false);
if (process.env.RUN_ENV === 'PRODUCTION') {
webpackConfig.forEach((config) => {
ignoreMomentLocale(config);
externalMoment(config);
addLocales(config);
usePrettyWebpackBar(config);
});
}
module.exports = webpackConfig;
可以看到這里針對生產(chǎn)環(huán)境逗堵,進行了以下調(diào)整:
1.忽略momenjs本地化模塊
2.對于momentjs不進行打包,同時設定全局變量眷昆,在runtime通過外部引入
3.增加螞蟻金服antd自己的本地化模塊
4.使用更美觀的webpack進度條
下面再來看看'antd-tools/lib/getWebpackConfig'
蜒秤,這里是ant-design的webpack核心配置:
const path = require('path');
const webpack = require('webpack');
const WebpackBar = require('webpackbar');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const deepAssign = require('deep-assign');
const replaceLib = require('./replaceLib');
const postcssConfig = require('./postcssConfig');
module.exports = function (modules) {
const pkg = require(path.join(process.cwd(), 'package.json'));
const babelConfig = require('./getBabelCommonConfig')(modules || false);
const pluginImportOptions = [
{
style: true,
libraryName: pkg.name,
libraryDirectory: 'components',
},
];
if (pkg.name !== 'antd') {
pluginImportOptions.push({
style: 'css',
libraryDirectory: 'es',
libraryName: 'antd',
});
}
babelConfig.plugins.push([
require.resolve('babel-plugin-import'),
pluginImportOptions,
]);
if (modules === false) {
babelConfig.plugins.push(replaceLib);
}
const config = {
devtool: 'source-map',
output: {
path: path.join(process.cwd(), './dist/'),
filename: '[name].js',
},
resolve: {
modules: ['node_modules', path.join(__dirname, '../node_modules')],
extensions: [
'.web.tsx',
'.web.ts',
'.web.jsx',
'.web.js',
'.ts',
'.tsx',
'.js',
'.jsx',
'.json',
],
alias: {
[pkg.name]: process.cwd(),
},
},
node: [
'child_process',
'cluster',
'dgram',
'dns',
'fs',
'module',
'net',
'readline',
'repl',
'tls',
].reduce((acc, name) => Object.assign({}, acc, { [name]: 'empty' }), {}),
module: {
noParse: [/moment.js/],
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: babelConfig,
},
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
options: babelConfig,
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
],
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: Object.assign(
{},
postcssConfig,
{ sourceMap: true }
),
},
],
}),
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: Object.assign(
{},
postcssConfig,
{ sourceMap: true }
),
},
{
loader: 'less-loader',
options: {
sourceMap: true,
},
},
],
}),
},
],
},
plugins: [
new ExtractTextPlugin({
filename: '[name].css',
disable: false,
allChunks: true,
}),
new CaseSensitivePathsPlugin(),
new webpack.BannerPlugin(`
${pkg.name} v${pkg.version}
Copyright 2015-present, Alipay, Inc.
All rights reserved.
`),
new WebpackBar({
name: '?? Webpack',
minimal: false,
}),
],
};
if (process.env.RUN_ENV === 'PRODUCTION') {
const entry = ['./index'];
config.entry = {
[`${pkg.name}.min`]: entry,
};
config.externals = {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react',
},
'react-dom': {
root: 'ReactDOM',
commonjs2: 'react-dom',
commonjs: 'react-dom',
amd: 'react-dom',
},
};
config.output.library = pkg.name;
config.output.libraryTarget = 'umd';
const uncompressedConfig = deepAssign({}, config);
config.plugins = config.plugins.concat([
new webpack.optimize.UglifyJsPlugin({
output: {
ascii_only: true,
},
compress: {
warnings: false,
},
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.LoaderOptionsPlugin({
minimize: true,
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
}),
]);
uncompressedConfig.entry = {
[pkg.name]: entry,
};
uncompressedConfig.plugins.push(new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}));
return [config, uncompressedConfig];
}
return config;
};
下面是對antd-tools的webpack配置文件的解析
首先:const pkg = require(path.join(process.cwd(), 'package.json'));
汁咏,我們拿到項目的package.json文件,然后通過getBabelCommonConfig
函數(shù)拿到babel的配置作媚,babel配置如下:
'use strict';
module.exports = function (modules) {
const plugins = [
require.resolve('babel-plugin-transform-es3-member-expression-literals'),
require.resolve('babel-plugin-transform-es3-property-literals'),
require.resolve('babel-plugin-transform-object-assign'),
require.resolve('babel-plugin-transform-class-properties'),
require.resolve('babel-plugin-transform-object-rest-spread'),
];
plugins.push([require.resolve('babel-plugin-transform-runtime'), {
polyfill: false,
}]);
return {
presets: [
require.resolve('babel-preset-react'),
[require.resolve('babel-preset-env'), {
modules,
targets: {
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
},
}],
],
plugins,
};
};
之后針對拿到的babel配置攘滩,添加并配置babel-plugin-import
插件;
用過ant-design的前端工程師們應該都知道這是一個按需加載的babel插件纸泡,有了它你就可以加載相應使用的組件的樣式和js漂问,而不是將ant-design全部加載打包,節(jié)省了打包后的體積女揭。
之后是一個騷操作:
if (modules === false) {
babelConfig.plugins.push(replaceLib);
}
這個操作用node-modules中的.js文件去替換項目中引用的庫文件;
下面是replaceLib代碼:(用到了高深的babel的AST知識)
'use strict';
const { join, dirname } = require('path');
const fs = require('fs');
const cwd = process.cwd();
function replacePath(path) {
if (path.node.source && /\/lib\//.test(path.node.source.value)) {
const esModule = path.node.source.value.replace('/lib/', '/es/');
const esPath = dirname(join(cwd, `node_modules/${esModule}`));
if (fs.existsSync(esPath)) {
path.node.source.value = esModule;
}
}
}
function replaceLib() {
return {
visitor: {
ImportDeclaration: replacePath,
ExportNamedDeclaration: replacePath,
},
};
}
module.exports = replaceLib;
后面是正式的webpack配置了蚤假,比較常規(guī),挑幾個有特點的說說:
transpileOnly: true,
:tsx編譯成jsx或js時吧兔,不寫入文件磷仰,保存在內(nèi)存中,加快編譯速度境蔼。
對于樣式文件灶平,antd-tools還使用了postcssConfig
,以下是它的配置
const rucksack = require('rucksack-css');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
rucksack(),
autoprefixer({
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
}),
],
};
可以看到它用了rucksack插件,可以很方便的寫css了.
在plugins中還使用了CaseSensitivePathsPlugin
:打包時賦予變量提升優(yōu)化瀏覽器運行性能;
new webpack.BannerPlugin
:在每個打包出來的文件寫上版權(quán)信息;
別的配置都是常規(guī)操作箍土,還是很容易看懂的逢享。