Webpack
遵循MVP
原则, 即最简化可实行产品
原则, 示例:
既有项目引入新的组件/库
假设现有项目用到了 react
, react-router
, antd
等库, 并且 controller
, router
, model
, view
已基本成型.
此时如果要引入 mobx
, 最佳实践步骤为:
- 新建一个空项目, 将既有库
react
,antd
等安装, 配置一个最简单的hello world
路由 - 安装
mobx
, 引入并测试通过 - 再在原有项目上进行功能扩充
既有项目打包优化
假设现有项目用到了 react
, react-router
, antd
, mobx
等库, 并且 controller
, router
, model
, view
已基本成型. webpack 打包过大, 应用性能较差.
最佳实践步骤:
- 新建一个空项目, 新建一个空的 webpack 配置
- 安装
react
(或antd
, 或mobx
等) 写一个简单示例引入项目 - 针对单一库进行
webpack
打包优化, 一般情况下, 除了 loader rules / vendor 需要每个库单独优化, 其他配置都能保证通用 - 一项优化完成后重复 2,3 步骤, 直到所有库优化完成
- 对原有项目的 webpack 配置进行替换, 不动项目源码
- 进一步优化, 比如
react-router-loader
之类的引入, 开始针对项目源码进行优化
示例: React/Antd 项目初始化
1. 配置 eslint
- 创建:
.eslintrc
和.eslintignore
- 安装:
yarn add --dev eslint eslint-config-dwing eslint-config-airbnb eslint-plugin-react eslint-plugin-jsx-a11y babel-eslint
// .eslintrc.js
module.exports = {
extends: ['eslint-config-dwing', 'eslint-config-airbnb-base/rules/strict', 'eslint-config-airbnb/rules/react'].map(require.resolve),
plugins: ['react'],
parser: 'babel-eslint',
env: {
browser: true
}
};
2. 配置 babel
- 创建:
.babelrc
- 安装:
yarn add --dev babel-preset-env babel-preset-react babel-plugin-transform-runtime babel-plugin-transform-decorators-legacy babel-plugin-import babel-plugin-transform-class-properties babel-plugin-transform-object-rest-spread babel-runtime babel-polyfill babel-core
// .babelrc
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions"]
}
}],
"react"
],
"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}],
"transform-decorators-legacy",
["import", { "libraryName": "antd", "style": true }],
"transform-class-properties",
"transform-object-rest-spread"
]
}
3. 安装 react/antd 等
- 安装:
yarn add react react-dom react-router antd mobx
- webpack 相关:
yarn add --dev babel-loader less less-loader css-loader postcss-loader autoprefixer
4. 配置 webpack
- 创建:
webpack.config.js
(用作产品)webpack.config.dev.js
(用作开发) - 安装:
yarn add --dev webpack html-webpack-plugin extract-text-webpack-plugin
该示例项目源码: https://github.com/AirDwing/webpack-lab/tree/antd
注意事项
1.最好不要用各类脚手架生成的 webpack 配置
原因有如下几点:
- 臃肿,夹杂了一大堆没用的第三方 npm 包,结构混乱, 难维护!!
webpack
更新速度较快, 现在已经到了3.x
版本了, 很多脚手架还停留在 1.x 或 2.x 的阶段- 知其然知其所以然, 不能仅做代码搬运的机器, 这样的话就失去了人的价值了. 只有在频繁地接触和使用过程中才能挖掘更优的配置
2.webpack 配置最佳实践
个人推荐以一个配置为base
(基准),其他进行微调.
如, 产品环境配置为:
// webpack.config.js
/* eslint-disable import/no-extraneous-dependencies */
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const theme = require('./antd.config');
module.exports = {
entry: {
app: path.resolve(__dirname, '../src/main.jsx'),
vendor: ['react', 'react-dom', 'react-router', 'mobx']
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.less$/,
loader: ExtractTextPlugin.extract(
`${require.resolve('css-loader')}?sourceMap&-autoprefixer!` +
`${require.resolve('less-loader')}?{"sourceMap":true,"modifyVars":${JSON.stringify(theme)}}`
)
}
]
},
resolve: {
modules: ['node_modules', path.resolve(__dirname, '../src')],
extensions: ['.js', '.json', '.jsx', '.css']
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
// 或灵活配置
// new webpack.DefinePlugin({
// 'process.env': {
// NODE_ENV: JSON.stringify(process.env.NODE_ENV)
// }
// }),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: 'common.js'
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.UglifyJsPlugin({
minimize: true,
output: {
comments: false
},
compress: {
warnings: false,
drop_console: false
}
}),
new ExtractTextPlugin('[name].css', {
disable: false,
allChunks: true
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/index.html')
})
]
};
开发环境配置可以是这样去写:
// webpack.config.dev.js
const webpack = require('webpack');
// 引入 base
const config = require('./webpack.config');
// 对 base 进行扩展
module.exports = Object.assign({}, config, {
entry: [
// 重新完整定义一个 entry, 当然一般情况下是用不着这么做的
],
output: Object.assign({}, config.output, {
// 仅修改 output 的 publicPath
publicPath: 'http://localhost/'
}),
// 比如, 在开发环境中需要多加一个 plugin
plugins: [
...config.plugins,
// 该插件仅用于示例
new webpack.optimize.ModuleConcatenationPlugin()
],
// 加一项新的配置
devtools: ''
});
当然你可能觉得这么写性能很低, 但对于只在启动时执行一次的代码来说没有什么, 而且你如果仔细研究一下,比如webpack-merge
或者其他的, 它们的底层实现也是这样, 还另外套了许多的封装.