对webpack的运用做一些解释
如何支持多出口?
在用webpack打包的时候,我们经常遇到一个问题,那就是由于path的设置,导致我们不能分目录打包我们想要的文件,这样的问题怎么办呢?
其实这样的问题很好解决。我们只需要在entry里做一些改变即可。
以我们的项目为例。我需要对资源生成一张资源地图,按照资源地图来制作入口配置。
var path = require('path');
function getSourceMap(list) {
var entry = Object.create(null);
list.forEach(function (src) {
// 砍掉./static/前缀和.extname后缀
// 我们的项目结构,读者可以自行配置,不用拘泥于伪代码
entry[src.slice(9, src.lastIndexOf('.'))] = src;
});
return entry;
}
生成的资源入口最终变成了
entry = {
'market/js/pages/appIndexPage': './static/market/js/pages/appIndexPage.js',
'market/js/pages/appOrderPage': './static/market/js/pages/appOrderPage.js'
...
}
这时候,只要简单的将输入静态资源的出口规定好,我们的多出口输出配置就完成了
output: {
path: path.resolve(__dirname, 'www/static-mobile/' + version),
filename: "[name].js"
}
最终输出的文件结构如下
+www
+static-mobile
+dev
+market
+ js
+pages
appIndexPage.js
appOrderPage.js
....
如何将webpackDevServer与thinkjs的服务结合起来
本地构建的过程中,我们的webpackDevServer其实只充当着静态资源服务器的角色。这个时候我们就不可避免的要访问thinkjs的服务来支撑每个页面的展示。
怎么办呢?如果启用nginx,对于前端来说,又是一坨没有接触过的东西,不如用我们熟悉的东西来做。
var d = webpack(options);
var WebpackDevServer = require('webpack-dev-server');
var compiler = webpack(options);
var server = new WebpackDevServer(compiler, {
contentBase:path.join(__dirname, 'www/'),
compress: true,
hot: true,
open: true,
stats: { colors: true }
,
proxy: [
{
path: '/static\-mobile',
target: 'http://localhost:3000',
pathRewrite: {
'^/static-mobile': '/qietv-mobile'
}
},
{
path: '*',
target: 'http://localhost:8361',
context: function (pathname, req) {
if (/^\/(static\-mobile|qietv\-mobile)/.test(pathname)) {
return false;
}
return true;
},
}
]
});
我们所处的项目的thinkjs服务端口是8361,其实只要将非静态文件夹全部转到thinkjs服务上就行。在网上有很多其他答案,会告诉你只用跟着他的答案写就行了,却没有告诉你为什么。要搞清楚proxy的详细配置方法,还是需要去看webpack-dev-server的文档和http-proxy-middleware的文档(因为devServer使用的是http-proxy-middleware)。
什么?我的热更新找不到输出文件?
同学们可能会疑惑,我启动了devServer.hot = true,入口也填入了webpack-dev-server/client,plugin也插入了,依然找不到文件,为什么?这个问题在于publicPath设置的错误(publicPath需要和output.publicPath一致)
var server = new WebpackDevServer(compiler, {
contentBase:path.join(__dirname, 'www/'),
compress: true,
hot: true,
inline: true,
// publicPath不填默认是'/',
// 在这里不填的话,我们原本输出到./www/qietv-mobile/dev/client/js/app.js
// 访问的时候就变成了 'http://localhost:3000/client/js/app.js'才访问得到.
// 加上一个访问path,就可以成功写出了。
// publicPath: "/qietv-mobile/" + version + '/'
}
什么?我设置了publicPath以后依然无法热更新?
在设置完publicPath以后记得在pulgin里加入new webpack.HotModuleReplacementPlugin()。除此之外,你还需要在入口文件里允许需要的模块热更新才行。
// 允许所有子模块热更新
if (module.hot) {
module.hot. accept();
// 只允许某一个模块热更新
// module.hot. accept('./a');
// 允许多个指定模块热更新
// module.hot. accept(['./a', './b']);
}
但是开发的时候,我们并不是很想写这个东西对吧,所以此时我们可以借助插件webpack-module-hot-accept(这个插件是一个loader,用于给经过的js增加一个module.hot.accept(xxx)的包装)。用法请自行百度该插件
webpack如何和别的观察系统结合
我们有这么一种需求,需要将jade文件观察(jade并不由我们的webpack引入),此时如果jade更新,我们需要刷新页面。作为一个懒人我们又懒得按下f5去刷新,这时候怎么办呢?
var webpack = require('webpack');
var config = require('./webpack.config.js');
var webpackDevServer = require('webpack-dev-server');
var server = webpackDevServer(webpack(config), {
// someoptions
});
var fs = require('fs-extra');
// 伪代码,
watch('/watchedDir', function (event) {
if (event.filepath.test(/\.jade$/)) {
fs.copy(event.filepath, targetDist)
.then(function () {
server.sockWrite(server.sockets, "content-changed");
})
}
})
webpack-dev-server向外暴露了sockWrite接口,用于指挥开发页面执行一些命令(webpack-dev-server使用sockjs和页面进行通信)。当发布content-changed事件的时候,页面接收到,会被直接刷新。因为content-changed事件对应的操作如下(入口应加入webpack-dev-server/client):
"content-changed": function() {
log("info", "[WDS] Content base changed. Reloading...")
self.location.reload();
},
哪些资源可以使用取巧的方式使本地开发更加便捷
如果我们的页面上有一些资源并不需要编译,仅仅只是简单的移动一下位置,那么我们简单做一个快捷方式过去就行了。。。这样可以省却本地开发复制移动这种无聊的事。。。
var views = glob.sync(path.resolve(p, '**/views'));
views.forEach(function (viewSrc) {
fs.ensureSymlinkSync(viewSrc, path.resolve(__dirname, 'view', viewSrc.match(/\/([a-zA-Z0-9\-]+)\/views$/)[1]));
});
我跟你说这个只是个符号连接,小心你操作文件的时候把文件搞空了。
什么时候需要用到happypack
实际上,happypack是为了解决大量计算速度上不来的问题。我们做文件移动,复制,这些事情的时候基本用不上。那什么时候用呢?首先我们要明白happypack到底是什么东西。happypack本身管理了一些线程,充分利用cpu来讲计算密集的事情时间缩短(事实上他这个说法有误,他用的是chird_process,使用的是子进程,通信用的是IPC,当然应该是进程池。但是大家写这种东西习惯性的取这种名字,那就这样咯)。我们在做什么事情的时候会用到cpu计算呢?在解析es6文本,解析其他预编译语言的时候,就会用到cpu进行计算。所以在这种时候,我们需要上happypack进行构建。
happypack参与构建的姿势参考以下片段
// 例子是掘金上挖过来的,webpack1的写法,见谅
var babelQuery = webpackConfig.babel;
// 这个size可以使用cpu num + 1进行代替
var happyThreadPool = HappyPack.ThreadPool({ size: 25 });
function createHappyPlugin(id, loaders) {
console.log('id', id)
return new HappyPack({
id: id,
loaders: loaders,
threadPool: happyThreadPool,
// disable happy caching with HAPPY_CACHE=0
cache: true,
// make happy more verbose with HAPPY_VERBOSE=1
verbose: process.env.HAPPY_VERBOSE === '1',
});
}
webpackConfig.module = {};
webpackConfig.module = {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=js',
},
{
test: /\.jsx$/,
loader: 'happypack/loader?id=jsx',
},
{
test(filePath) {
return /\.css$/.test(filePath) && !/\.module\.css$/.test(filePath);
},
loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=cssWithoutModules')
},
{
test: /\.module\.css$/,
loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=cssWithModules')
},
{
test(filePath) {
return /\.less$/.test(filePath) && !/\.module\.less$/.test(filePath);
},
loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=lessWithoutModules')
},
{
test: /\.module\.less$/,
loader: ExtractTextPlugin.extract('style', 'happypack/loader?id=lessWithModules')
}
],
}
if (!!handleFontAndImg) {
webpackConfig.module.loaders.concat([
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=woff2' },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=ttf' },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=eot' },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'happypack/loader?id=svg' },
{ test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, loader: 'happypack/loader?id=img' },
{ test: /\.json$/, loader: 'happypack/loader?id=json' },
{ test: /\.html?$/, loader: 'happypack/loader?id=html' }
])
} else {
webpackConfig.module.loaders.concat([
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/font-woff' },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=application/octet-stream' },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&minetype=image/svg+xml' },
{ test: /\.(png|jpg|jpeg|gif)(\?v=\d+\.\d+\.\d+)?$/i, loader: 'url?limit=10000' },
{ test: /\.json$/, loader: 'json' },
{ test: /\.html?$/, loader: 'file?name=[name].[ext]' }
])
}
webpackConfig.plugins.push(createHappyPlugin('js', ['babel?'+JSON.stringify(babelQuery)]))
webpackConfig.plugins.push(createHappyPlugin('jsx', ['babel?'+JSON.stringify(babelQuery)]))
webpackConfig.plugins.push(createHappyPlugin('cssWithoutModules', ['css?sourceMap&-restructuring!postcss']))
webpackConfig.plugins.push(createHappyPlugin('cssWithModules', ['css?sourceMap&-restructuring&modules&localIdentName=[local]___[hash:base64:5]!postcss']))
webpackConfig.plugins.push(createHappyPlugin('lessWithoutModules', ['css?sourceMap!postcss!less-loader?sourceMap']))
webpackConfig.plugins.push(createHappyPlugin('lessWithModules', ['css?sourceMap&modules&localIdentName=[local]___[hash:base64:5]!postcss!less-loader?sourceMap']))
if (!!handleFontAndImg) {
webpackConfig.plugins.push(createHappyPlugin('woff', ['url?limit=10000&minetype=application/font-woff']))
webpackConfig.plugins.push(createHappyPlugin('woff2', ['url?limit=10000&minetype=application/font-woff']))
webpackConfig.plugins.push(createHappyPlugin('ttf', ['url?limit=10000&minetype=application/octet-stream']))
webpackConfig.plugins.push(createHappyPlugin('eot', ['file']))
webpackConfig.plugins.push(createHappyPlugin('svg', ['url?limit=10000&minetype=image/svg+xml']))
webpackConfig.plugins.push(createHappyPlugin('img', ['url?limit=10000']))
webpackConfig.plugins.push(createHappyPlugin('json', ['json']))
webpackConfig.plugins.push(createHappyPlugin('html', ['file?name=[name].[ext]']))
}
本地开发需要全量构建吗?
本地开发追求的是开发速度,并不需要全量构建,甚至可以说,我们只需要指定子目录运行webpackdev即可。在启动本地开发的构建服务的时候,最初只会构建一次,用于访问其他项目模块使用。这样的代码是修改其他模块并不会被watch,但我们追求的是开发一个模块就专心一个模块的开发即可。这样才能十分明显的提高效率。
为什么我的css不更新!!
有这么一个经典的场景,a君问:
module: {
loaders: [
{//抽离css
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader',
publicPath: '/dist/' })
}
我代码这样写用了ExtractTextPlugin为啥更新不了啊!
首先要说,webpack官方直接就告诉你了,我们在开发环境下,不推荐你用ExtractTextPlugin。因为1是开发环境不需要考虑加载速度和连接数量,2是ExtractTextPlugin这个插件也是将option合并以后返回给webpack进行处理,本身输出的非js模块,不支持热替换。这种情况下直接使用其他loader打包成js明显才是正道(支持热更新了)。在进行全量构建或者输出构建的时候再将css添加进去会比较明智。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。