Awesome
说明
首先必须说明一下,该工具是基于webpack2的,所以很多配置都是需要遵守webpack2规范的。如果需要安装,直接运行下面的命令就可以了。而且,我们的入口文件都是在package.json中进行配置,当执行wcf命令会自动调用下面的三种模式中的一种完成打包。
npm install -g webpackcc//必须注意,我们局部安装的优先级要高于全局安装的
入口文件的配置:
"entry": {
"index": "./test/index.js"
}
1.该工具的三种打包模式
1.1 webpack-dev-server模式(Best Performance)
这种模式你只要在wcf后添加devServer参数,表明我们的文件应该使用webpack-dev-server来完成打包,此时所有的生成文件都会在内存中,而不会写入磁盘,效率比下面两种模式高,也是最推荐的一种打包模式(该模式绕过了写本地磁盘这一步,所以文件如果改变直接从内存中读取的速度要比其他方式高得多)。同时该模式会自动在output.path路径下通过html-webpack-plugin产生一个html(内存中不可见,同时需要加上--dev表明是开发模式),并自动加载我们的所有chunk.
wcf --devServer --dev
//此时打开localhost:8080就会看到我们使用test/index.html作为template的页面,如果你需要修改template请使用下面的htmlTemplate参数
1.2 webpack本身的watch模式(OK performance)
这种模式使用webpack自己的watch方法来完成,监听package.json中entry配置的文件的变化。你需要添加--watch --dev。如下:
wcf --watch --dev
该模式除了会监听entry文件的变化,而且当我们自定义的webpack.config.js(通过--config传入)文件内容变化的时候会自动退出编译,要求用户重启编译过程!
1.3 webpack普通模式
此时不会监听文件的变化,只是完成webpack的一次编译然后退出!
wcf --dev
2.生产模式 vs 开发模式
2.1 开发模式
你需要通过添加--dev
来开启开发模式。
如果模块本身是支持HMR的,那么我们就会采用不刷新的方式来更新页面,否则采用传统的livereload的方式。但是该模式会添加很多非用户指定的代码,如实现HMR的功能的客户端代码,所以不建议在生产模式使用。而且此时生成的css是内联的,是为了实现HMR的(style-loader完美支持HMR)!
2.2 生产模式
此时你不需要指定--dev
参数,去掉即可。该模式除了下面说的Plugin和开发环境不同以外,而且不再具有HMR的功能。所以打包生产的bundle较小!而且当你每次修改文件的时候需要手动刷新页面。此时会单独生成一个css文件(集成ExtractTextPlugin),而不是在开发模式模式下将css全部内联到html中!
2.该工具的配置参数
注意,该工具的所有的配置都是基于webpack的,各个参数的意义和webpack是一致的。
2.1 cli参数
--version
表示我们的版本号
-V/--vendor
表示我们是否添加commonChunkPlugin将引用次数过多的代码抽取出一个vendor.js
-w/--watch
表示是否启动webpack的watch模式,参数值可以是一个数字,默认是200ms。
-h/--hash
表示文件名是否应该包含hash值,注意这里如果传入这个参数那么文件名都会包含chunkhash而不是hash。
--dll <dllWebpackConfigFile>
此时你需要输入一个webpack.dll.js,通过这个文件我们产生一个json文件用于DllReferencePlugin
-m/--manifest <manifest.json>
此时你需要传入一个json文件给DllReferencePlugin,此时我们会自动添加DllReferencePlugin。需要了解上面两个选项可以阅读webpackDll
--publicPath <publicPath>
表示我们webpack的publicPath参数
--devtool <devtool>
用于指定sourceMap格式,默认是"cheap-source-map"
--stj <filename>
是否在output.path路径下产生stats.json文件,该文件可以参见这里用于分析本次打包过程
--dev
是否是开发模式,如果是我们会添加很多开发模式下才会用到的Plugin
--devServer
因为该工具集成了webpack-dev-server的打包模式,可以使用这个参数开启上面所说的webpack-dev-server模式。
--config <customConfigFile>
让使用者自己指定配置文件,配置文件内容会通过webpack-merge进行合并。
2.2 webpack默认参数
<cod>entry:</cod>
我们通过在package.json中配置entry字段来表示入口文件,比如:
"entry": {
"index": "./test/index.js"
}
这样就表示我们会将test目录下的index.js作为入口文件来打包,你也可以配置多个入口文件,其都会打包到output.path对应的目录下。当然,你也可以通过上面shell配置的config文件来更新或者覆盖入口文件。覆盖模式采用的就是上面说的webpack-merge。
<code>output.path:</code>
我们默认的打包输出路径是process.cwd()/dest,你可以通过我们的--config参数来覆盖。
<code>loaders:</code>
使用url-loader来加载png|jpg|jpeg|gif图片,小于10kb的文件将使用DataUrl方式内联:
{
test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i,
use: {
loader:require.resolve('url-loader'),
//If the file is greater than the limit (in bytes) the file-loader is used and all query parameters are passed to it.
//smaller than 10kb will use dataURL
options:{
limit : 10000
}
}
}
json文件采用json-loader加载:
{
test: /\.json$/,
loader:require.resolve('json-loader')
}
html文件采用html-loader来加载:
{
test: /\.html?$/,
use:{
loader: require.resolve('html-loader'),
options:{
}
}
}
sass文件采用如下三个loader顺次加载:
{
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"]
}
当然,你可以通过getWebpackDefaultConfig.js来查看更多的loader信息。其中内置了很多的功能,包括css module,压缩css,集成autoPrefixer,precss,直接import我们的css文件等等。
注意:上面任何内置的参数都是可以通过config(shell配置)文件来替换的!
3.内置plugins
为了保证开发环境的效率,在使用wcf的时候建议传入--dev表明是在开发环境中,这时候wcf会安装一些仅仅在开发环境使用的包。
开发环境独有的plugin:
HotModuleReplacementPlugin
//支持HMR
HtmlWebpackPlugin
//在output目录下产生一个index.html(生产环境下也会添加该plugin,只是不再支持HMR,并要求用户手动刷新页面)
生产环境独有的plugin:
UglifyJsPlugin//压缩JS
ImageminPlugin //压缩图片
ExtractTextPlugin//单独将css抽取出来,并要求用户手动刷新页面
共有的包:
CommonsChunkPlugin
//抽取公共的模块到common.js中
MinChunkSizePlugin
//减少chunk个数
LoaderOptionsPlugin
//兼容性要求
StatsPlugin
//shell参数传入--stj
DllPluginDync
//shell传入manifest
4.HMR功能的说明
在webpack-dev-server模式下,我们提供了HMR的功能,你不需要添加任何参数,默认开启。如果你需要体验该功能,只要在入口文件中加上下面的这句代码:(你也可以查看该项目对应的该git仓库,其test目录是完全支持HMR功能的,你修改任何test目录下的代码都会重新加载):
if (module.hot) {
module.hot.accept();
}
关于HMR的相关内容你可以参考这篇文章。注意,我们的devServer是如下的配置:
devServer: {
publicPath: '/',
open: true,
port: 8080,
contentBase: false,
hot: true//强制开启HMR的
}
此时你运行wcf --dev --devServer就会发现会自动打开页面,如果你不需要该功能可以通过自定义配置文件来覆盖open参数!
5.说明
(1)我们的webpack入口文件必须在package.json或者自定义的webpack配置文件中至少一处配置(多处配置会合并),如果两个地方都没有配置那么就会报错!
(2)我们的ExtractTextPlugin采用的是contentHash,而不是chunkHash,原因可以阅读Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案
6.新功能的添加
新功能的添加不再修改readme文件,其功能都会在changelog中说明。请查看该文件
7.可能出现的问题
(1)如果执行下面的命令不会自动打开浏览器,同时访问localhost:8080也无法访问
wcf --devServer
这时候请加上--dev,因为其会访问html内置的模板,其默认的目录在test目录下,因为这个例子是内置的HMR的例子,如果你不需要查看这个例子,请使用shell参数htmlTemplate参数来指定模板路径(依然需要添加--dev参数,因为html-webpack-plugin是开发插件)
(2)如果端口报错
new RangeError('"port" argument must be >= 0 and < 65536');//设置的端口必须是数值类型
(3)必须安装webpack作为依赖,可以是全局安装也可以是局部安装
npm install webpack -g//或者npm install webpack --save -dev
(4)无法打开URL
此时请确保你的参数有--dev,因为如果没有这个参数那么我们不会添加HMR的插件,同时也不会添加HtmlWebpackPlugin,所以不会自动打开页面。
8.与atool-build的区别
(1)wcf集成了三种打包模式
上面已经说过了,我们的wcf集成了三种打包模式,而且功能是逐渐增强的。webpack模式只是打包一次,然后退出,和webpack自己的打包方式是一样的。webpack watch模式会自动监听文件是否发生变化,然后重新打包。webpack-dev-server模式天然支持了HMR,支持无刷新更新数据。具体你可以阅读文档
(2)很好的扩展性
atool-build提供一个mergeCustomConfig函数来合并用户自定义的配置与默认的配置,并将用户配置作为参数传入函数进行修改,但是当要修改的配置项很多的时候就比较麻烦。wcf自己也集成了很多loader对文件进行处理,但是很容易进行拓展,只要你配置自己的扩展文件就可以了,内部操作都会自动完成。你可以通过两种方式来配置:
cli模式:
wcf --dev --devServer --config "Your custom webpack config file path"
//此时会自动合并用户自定义的配置与默认配置,通过webpack-merge完成,而不用逐项修改
Nodejs模式:
const build = require("webpackcc/lib/build");
const program = {
onlyCf : true,
//不启动打包,只是获取最终配置信息
cwd : process.cwd(),
dev : true,
//开发模式,不启动如UglifyJs等
config :"Your custom webpack config file path"
};
const finalConfig = build(program);
//得到最终的配置,想干嘛干嘛
通过nodejs模式,你可以获取webpack配置项用于其他地方。
下面给出一个完整的例子(假如下面给出的是我们自定义的配置文件):
module.exports = {
entry:{
'main': [
'webpack-hot-middleware/client?path=http://' + host + ':' + port + '/__webpack_hmr',
"bootstrap-webpack!./src/theme/bootstrap.config.js",
'./src/client.js'
]
},
output: {
path: assetsPath,
filename: '[name]-[hash].js',
chunkFilename: '[name]-[chunkhash].js',
publicPath: 'http://' + host + ':' + port + '/dist/'
},
plugins:[
new webpack.DefinePlugin({
__CLIENT__: true,
__SERVER__: false,
__DEVELOPMENT__: true,
__DEVTOOLS__: true
// <-------- DISABLE redux-devtools HERE
}),
new webpack.IgnorePlugin(/webpack-stats\.json$/),
webpackIsomorphicToolsPlugin.development()
],
module:{
rules:[
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
{
test: webpackIsomorphicToolsPlugin.regular_expression('images'),
use: {
loader: require.resolve("url-loader"),
options:{}
}
},
{
test: /\.jsx$/,
exclude :path.resolve("node_modules"),
use: [{
loader:require.resolve('babel-loader'),
options:updateCombinedBabelConfig()
}]
},
{
test: /\.js$/,
exclude :path.resolve("node_modules"),
use: [{
loader:require.resolve('babel-loader'),
options:updateCombinedBabelConfig()
}]
}]
}
}
注意:我们的wcf没有内置的entry,所以你这里配置的entry将会作为合并后的最终webpack配置的entry项。对于output来说,用户自定义的配置将会覆盖默认的配置(其他的也一样,除了module,plugins等)。对于plugin来说,我们会进行合并,此时不仅包含用户自定义的plugin,同时也包含内置的plugin。对于loader来说,如果有两个相同的loader,那么用户自定义的loader也会原样覆盖默认的loader。这样就很容易进行扩展。只要用户配置一个自定义配置文件的路径即可!
(3)dedupe
atool-build并没有对我们的plugin和loader进行去重,这样可能导致同一个plugin被添加了两次,这就要求用户必须了解内置那些plugin,从而不去添加它。同时也会导致某一个js文件的loader也添加了两次,得到如下的内容:
[ { test: { /\.jsx$/ [lastIndex]: 0 },
exclude:
{ [Function: exclude]
[length]: 1,
[name]: 'exclude',
[arguments]: null,
[caller]: null,
[prototype]: exclude { [constructor]: [Circular] } },
use: [ { loader: 'babel-loader', options: {} }, [length]: 1 ] },
{ test: { /\.jsx$/ [lastIndex]: 0 },
//对于jsx的loader又添加了一次
exclude:
{ [Function: exclude]
[length]: 1,
[name]: 'exclude',
[arguments]: null,
[caller]: null,
[prototype]: exclude { [constructor]: [Circular] } },
use: [ { loader: 'after', options: {} }, [length]: 1 ] },
[length]: 2 ]
这个问题你可以查看我给webpack-merge提出的issue。但是这些工作wcf已经做了,所以当你有两个相同的插件,或者两个相同的loader的情况下,都只会留下一个,并且用户自定义的优先级要高于默认配置的优先级。
(4)打包前进行钩子设置
如果在打包前,或者获取到最终配置之前,你要对最终配置做一个处理,比如删除某个plugin/loader,那么我们提供了一个钩子函数:
const program = {
onlyCf : true,
//此时不打包,只是为了获取最终配置用于nodejs
cwd : process.cwd(),
dev : true,
//不启动压缩
//下面这个hook用于去掉commonchunkplugin
hook:function(webpackConfig){
const commonchunkpluginIndex = webpackConfig.plugins.findIndex(plugin => {
return plugin.constructor.name == "CommonsChunkPlugin"
});
webpackConfig.plugins.splice(commonchunkpluginIndex, 1);
return webpackConfig;
}
};