您的位置:首页 > Web前端 > Webpack

towebp-loader诞生记~ 一个webpack 图片loader

2017-03-21 08:55 591 查看
http://www.jianshu.com/u/9760e0846302

在上篇Vuejs webp图片支持,插件开发过程~ 中我做了一个图片转换成webp的组件,并且使用了相关的loader,但是我们在使用过程中发现webp和原图的hash是不一样的,这样我们就没有办法保留版本的概念了,临时我使用时间戳来作为替代,但是这样每次上传都会重新上传所有文件,有点浪费硬盘,于是我今天做了这个
towebp-loader


功能

towebp-loader
 可以在webpack中根据图片类型转换成一份
webp
和原图两份图片,并且集成了
url-loader
的功能
支持url的
limit
功能和
file-loader
文件名的功能。


使用方法

{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'towebp?' + JSON.stringify({
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
})
},


配合vue-webp指令使用效果更佳哦

vue-webp

使用方法
![](abc.jpg)


关键源码讲解

……
// 获取loader中的查询字符串 使用webpack loader api
var query = loaderUtils.parseQuery(this.query);
// 保存源文件的路径如果穿 name就使用不然使用默认hash.ext模式
var url = loaderUtils.interpolateName(this, query.name || "[hash].[ext]", {
content: content,
regExp: query.regExp
});
// webp 文件的保存路径
var webpUrl = url.substring(0, url.lastIndexOf('.')) + '.webp';
// limit参数来自url-loader 如果小于这个值使用base64字符串替换图片
if (query.limit) {
limit = parseInt(query.limit, 10);
}
var mimetype = query.mimetype || query.minetype || mime.lookup(this.resourcePath);
if (limit <= 0 || content.length < limit) {
return "module.exports = " + JSON.stringify("data:" + (mimetype ? mimetype + ";" : "") + "base64," + content.toString("base64"));
}
……
// 转换原图成webp
imagemin.buffer(content, { plugins: [imageminWebp(options)] }).then(file => {
// 保存原图
this.emitFile(url, content);
// 保存压缩后的webp图片
this.emitFile(webpUrl, file);
callback(null, "module.exports = __webpack_public_path__ + " + JSON.stringify(url) + ";");
}).catch(err => {
callback(err);
});
……


主要代码都在这里了。还有一些webpack loader的API 大家仔细去读相关文档


参考

webp-loader
url-loader
file-loade

本人已经使用vue.js半年多了,在做一些Html5页面的时候发现很多页面都是图片组成的,如果能有效的压缩图片的体积那么整个项目体积就会减少很多,这是为什么写这个简单东西的起点。

Webp 百度百科上已经讲清楚在保持原画质的情况呀体积可以压缩到原来的60%这是很牛逼的一件事。看看webp的兼容情况,下图是caniuse上面最新的webp支持情况



webp兼容情况

兼容情况还是不那么乐观,不过chrome和安卓阵营已经全部支持。所以我还是做了这件事。


源码github

Vue.js
 的自定义指令系统十分强大是我做这件事的根本原因之一,所以我的设想是在一个指令中传入图片链接,然后在页面渲染的时候根据浏览器是否支持
webp
格式的图片选择下载那个图片,这里就需要判断浏览器是否支持
webp
了,这里我用到的是
canvas
方法,代码如下
var canUseWebp = (function() {
var elem = document.createElement('canvas');
if (!!(elem.getContext && elem.getContext('2d'))) {
return elem.toDataURL('image/webp').indexOf('data:image/webp') === 0;
} else {
return false;
}
})();


这时候就非常简单了指令在
update
的时候根据是否支持然后选择不同的图片
function update(el, option) {
var attr = option.arg || 'src';
if (el.tagName.toLowerCase() === 'img' && option.value) {
el.setAttribute(attr, option.value);
}
};


然而事情的这个时候发现一些小的图标不见了,原来我的
webpack
配置中设置了小于
10k
的图片使用
base64
编码,

所以最终我的更新代码是这样的
function update(el, option) {
var attr = option.arg || 'src';
if (el.tagName.toLowerCase() === 'img' && option.value) {
if (option.value.indexOf('data:image') < 0) {
var tmp = option.value.substring(0, option.value.lastIndexOf('.')) + '.webp';
el.setAttribute(attr, canUseWebp ? tmp : option.value);
} else {
el.setAttribute(attr, option.value);
}

}
};


这个时候
vue.js 2.0
发布了。我有针对 2.0版本做了支持,由于我的指令非常简单,所以代码很轻松
var isVueNext = Vue.version.split('.')[0] === '2';
if (isVueNext) {
Vue.directive('webp', function(el, binding) {
update(el, {
arg: binding.arg,
value: binding.value
});
})
} else {
Vue.directive('webp', {
bind: function() {},
update: function(val, old) {
update(this.el, {
arg: this.arg,
value: val
});
},
unbind: function() {}
})
}
};


这样我的
vue-webp
指令就算完成了。

只有指令可不行,每次都要自己生成一份
webp
格式的图片,这太不友好了。我有查找一番,发现一个
webp-loader
可以在
webpack
打包和dev的时候自动生成相应的
webp
文件,太好了。使用原作者的
webp-loader
发现文件的
hash
不一样,我又用
imagemin
最新版本升级了一下,上传到
npm
webpn-loader
(原谅我不会命名),

具体使用方法可以参考我的 Vue.js 2.0 后台项目 模板项目

谢谢大家,看到这里。欢迎各种star
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: