dahong
dahong copied to clipboard
boarder,快速开始一个webpack项目-优化篇
在介绍篇#7 中,我大概说了下boarder
这个命令行工具初始化出来的webpack项目模板有哪些功能,但是这些功能实在简单,所以想了想继续优化下,顺便解决下上篇留下来的问题
很多道友知道webpack打包现在很热门,但是不知其究竟,说实话我也不知道,上完班回去撸几把王者农药岂不快哉?但是最近webpack3又出来了,难道它想争夺版本帝?
本篇点到为止,可以根据关键字自行搜索,互联网的资料比我的更详细,易懂,有不明白的可以下面评论交流切磋,道友请留步!!!
上一篇中我已经介绍了目录结构,现在有所增强了
.
├── README.md
├── build
│ ├── webpack.base.js
│ ├── webpack.build.js
│ └── webpack.dev.js
├── dist
│ ├── index.html
│ └── static
│ └── js
├── package-lock.json
├── package.json
└── src
├── assets
├── index.html
├── index.js
└── pages
可以看到src
目录下增加pages这个文件夹,所以现在这个模板是支持多页面的,现在SPA大放异彩,但是有些项目中或多或少的都会涉及到其他的页面,为了方便,我们当然更希望一个npm run build
后就可以发布上线了
好了,废话不多说,正题开始:
css预编译支持(stylus|less|sass)
很多项目书写css已经不是.css文件了,大多都是styl,scss和less了,所以我也要来支持下,在webpack.base.js中添加对应的loader,例如:
{
test: /\.styl/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'stylus-loader'
]
}),
}
前提是你需要npm install对应的loader
ExtractTextPlugin是为了分离css的,但是开发中切勿使用,别问我怎么知道的,我的热更新啊,我的泪啊
所以它提供了一个参数可以让我们可以来禁用它,disable就是来决定是否启用,我们根据环境来判断,环境是什么,提供个关键字给你node process
,
new ExtractTextPlugin({
filename: 'static/css/[name].[contenthash].css',
allChunks: true,
disable: isPro ? false : true
})
css预编译可以使用了,并且可以利用ExtractTextPlugin提供给我们的contenthash
做长效缓存,至于hash
和css的contenthash
和js的chunkhash
的区别自行了解,大家需要知道一般不用hash
,开发中直接使用[name].js
这种就行
多页面支持
假如我们除了主页还有一个about
和setting
页面,那么pages
目录结构应该是这样的
├── about
│ ├── index.html
│ └── index.js
└── setting
├── index.html
└── index.js
我们多页面的页面数实际开发中是不可预料的,所以我们按照约定的结构实现自动化即可,我是一个有经验的程序
/**
*
*
* @param {string} globPath
* @returns {object}
*/
function getEntries(globPath) {
var files = glob.sync(globPath),
entries = {};
files.forEach(function (filepath) {
var split = filepath.split('/');
var name = split[split.length - 2];
entries[name] = filepath;
});
return entries;
}
var entries = getEntries('./src/pages/*/index.js');
var hot = 'webpack-hot-middleware/client?reload=true';
entries['index'] = './src/index.js'
我们利用glob
来找到这个多页面的主入口(index.js),并且以文件夹的名字作为webpack entry
的入口,最后再手动加上主页index的入口,运行起来的entries
应该是这样的
{
about: './src/pages/about/index.js',
setting: './src/pages/setting/index.js',
index: './src/index.js'
}
找到入口之后我们要添加到webpack的配置中
baseConfig = {
entry: {},
……
}
var hot = 'webpack-hot-middleware/client?reload=true';
Object.keys(entries).forEach(function (name) {
baseConfig.entry[name] = isPro ? entries[name] : [hot, entries[name]];
var htmlPlugin = new HtmlWebpackPlugin({
filename: name + '.html',
template: name === 'index' ? './src/index.html' : './src/pages/' + name + '/index.html',
inject: true,
chunksSortMode: 'dependency',
});
baseConfig.plugins.push(htmlPlugin);
})
这样的话我们webpack的入口就会有index,about和setting的入口,如果有更多的页面,放马过来
解决了第一个入口问题,那我们就要解决资源的加载问题了,我们理想的状态是多页面共享的依赖剥离出来,然后每个页面的依赖和业务代码再剥离出来,这样就会有这么些文件,index.[chunkhash] .js,vendor.[chunkhash].js,index.vendor.[chunkhash].js
等等,index.[chunkhash].js
是主页面的业务代码,vendor.[chunkhash].js
是多页面共享的依赖,index.vendor.[chunkhash].js
是主页面的依赖,因为依赖的代码会很少更新,所以我们分离出来可以利用chunkhash来帮我们做到长效的缓存
长效缓存
chunkhash
是根据模块的内容计算出来的hash,但是模块的ID是一个递增的数,我们并不确定依赖的数量,所以业务代码的变化也会导致依赖代码的chunkhash改变,所以我们可以使用webpack提供的HashedModuleIdsPlugin
来帮我们稳定模块的ID,
new Webpack.HashedModuleIdsPlugin()
HashedModuleIdsPlugin
是干什么的?HashedModuleIdsPlugin就是为了避免这种无顺序的模块ID,他是根据模块的相对路径来产生的,有了HashedModuleIdsPlugin就可以了么,还不行,因为webpack runtime
代码块中是包含chunks ID
和 chunkhash
的,所以我们也要把webpack runtime
代码抽出来
baseConfig.plugins.push(new Webpack.optimize.CommonsChunkPlugin({
name: ['manifest'],
minChunks: Infinity
}))
用一个manifest来保存webpack runtime
代码,虽然会多一个文件,但是我们可以用长效缓存的优点来掩盖这一切,因为webpack runtime
很少,我们没有必要单独去加载这个js,可以内联到html的头部,这个我找了下插件发现没有合适的,要么就是使用ejs,要么就是不容易变通,所以我自己借鉴了下webpack插件,写了个基于HtmlWebpackPlugin
的内联资源插件html-webpack-inline-assets-plugin
new HtmlWebpackInlineAssetsPlugin({
head: 'manifest.',
// body: 'manifest.'
})
插件使用还是很简单的,npm install之后直接使用,两个参数,内联到head或者body中,参数是正则表达式用来匹配资源的
至此,基本上这个项目所涵盖的点都有设计,也会有不完美的地方,希望在实践中能够弥补,让它更具有扩展性
如果你有什么要求,可以去对应的项目中给我提issues 命令行工具boarder webpack模板wdeme 资源内联插件html-webpack-inline-assets-plugin
最近刚开始试着用webpack来打包项目 ,getEntries() 这个方法很不错,动态添加entery,学习了,非常感谢你的分享!
嗯嗯,这个boarder不好,相当于下载器,直接下载这个模板wdemo,package.json的一些参数没有改,还是wdemo的,所以不是很完善,我写这个主要就是平时想立即用webpack的时候没有好的脚手架,只能把以前的项目配置拿过来,很麻烦,所以写了个这个
刚开始学习webpack。。小白一枚,请教下,在开发环境下,a链接跳转路径、图片引用路径,在build之后路径就不对了,这种问题该怎么处理呀
@myhuangqiang 你在开发环境用的是相对路径还是绝对路径?你用绝对路径应该是可以的,相对路径的话要配置url-loader,你可以看我的url-loader配置
@shaodahong 非常感谢,a标签跳页面应该是根据build之后的路径把,刚试了一把,要开发环境下的目录跟打包之后href地址是不一样的^-^
@myhuangqiang 是的,但是你描述的不是很清楚, 你可以上代码看下。相对路径肯定是相对于你a标签的页面的,比如你是组件,但是你build后加载是在index.html里面的,那么它会相对这个index.html来找的
开发环境下首页有个链接<a href="./setting/index.html">vvv</a>
应该是会跳转到setting目录下的页面,但是会报找不到这个页面。写成vvv却能找到对应的页面
<a href="./setting.html">vvv</a>
@myhuangqiang 不会吧,<a href="./setting.html">vvv</a>
和<a href="./setting/index.html">vvv</a>
不是一个页面吧