当 webpackfis 遇到 canvas
Posted 前端新视野
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当 webpackfis 遇到 canvas相关的知识,希望对你有一定的参考价值。
本文摘要
本文主要解决 canvas 脚本文件对资源文件加载路径的特殊要求,从而需要 webpack 和 fis 针对性地做出线上打包优化,同时你会学到如何编写插件来任意地改变输出 index.html 中依赖文件的内容
错误原因也很直观:
canvas 的 API 都不允许跨域等对资源路的径限制操作
比如还包括:getImageData 等
我们的页面文件都存放在 A 域名,静态资源或托管到 B 域名
webpack 下面:
// webpack.prod.conf.js
// 在 output 里面
output: {
publicPath: config.build.publicPath
}
//config/index.js
build: {
publicPath: 'https://******'
}
fis 下面:
//fis-conf.js 文件的配置
fis.media('prod')
.match('*.{js,css,less,png,gif,jpg,svg}', {
domain : 'https://******'
})
webpack 下面:
我们分 2 个版本来看,压缩和未压缩的:
1、压缩版本:
// manifest.js
function f() {
b.onerror = b.onload = null, clearTimeout(r);
var c = d[e];
0 !== c && (c && c[1](new Error("Loading chunk " + e + " failed.")), d[e] = void 0)
}
// 这里和我们常面试的 jsonp 类似
// 创建 script 标签,加载一堆 chunk 文件
var n = document.getElementsByTagName("head")[0],
b = document.createElement("script");
b.type = "text/javascript",
b.charset = "utf-8",
b.async = !0,
b.timeout = 12e4,
c.nc && b.setAttribute("nonce", c.nc),
b.src = c.p + "static/js/" + e + "." + {
0: "69e1d542baafddafa73a"
}[e] + ".js";
var r = setTimeout(f, 12e4);
return b.onerror =
b.onload = f,
n.appendChild(b)
c.p = "https://******"
2、未压缩版本:
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "https://******";
来,跟着我们一起看源码:
// webpack/lib/MainTemplate.js
getPublicPath(options) {
return this.applyPluginsWaterfall("asset-path",
this.outputOptions.publicPath || "", options);
}
this.plugin("require-extensions", (source, chunk, hash) => {
const buf = [];
if(chunk.chunks.length > 0) {}
//...
const publicPath = this.getPublicPath({
hash: hash
})
buf.push("")
buf.push("// __webpack_public_path__")
buf.push(`${this.requireFn}.p = ${JSON.stringify(publicPath)};`)
return this.asString(buf)
}
网上也有很多人问一个同样的问题:
Q :
配置 output 的 publicPath 之后,最后编译的时候所有的资源文件都会被替换
但是:有的文件不想被替换
官网也给了很明显的解释:
For loaders that embed <script> or <link> tags or reference assets like images, publicPath is used as the href or url() to the file when it’s different than their location on disk (as specified by path).
// 在入口文件中使用:__webpack_public_path__
注意:但是它只能在 runtime 工作,还是解决不了我们 build 之后的
so ~~~
我们再来回顾一下整个 webpack 的编译流程,我们发现最终 index.html 的生成依赖插件:html-webpack-plugin,资源文件通过 inject 配置插入
而且之前我们做了很多自定义 index.html 的需求:
在国际化方案中自定了一些变量
inline 了 manifest.js
自定义打包文件写入 json
等等
所以我们又想到了采用 webpack 插件来单独处理最终写入 html 的资源文件的路径:
思路开始:
1.设计一个插件,在输出 html 之前替换全局的 publicPath
2.需要告诉插件一个标识,告知当前文件需要局部替换全局的 publicPath
实践开始:
1、在多页应用循环时候匹配模块,插入新的 conf.options
let conf = {
inject: true,
template: '**',
filename: '**'
...
}
// 匹配文件模块名
if (filename.indexOf('****') > -1) {
conf.needReplace = 'yes'
}
// 最终调用插件
new HtmlWebpackPlugin(conf)
2、插件替换
// 核心部分
compilation.plugin('html-webpack-plugin-alter-asset-tags', function (htmlPluginData, callback) {
if (!htmlPluginData.plugin.options.needReplace) {
return callback(null, htmlPluginData);
}
// 直接替换内容
htmlPluginData.body.forEach(function (item) {
if (item.attributes.type == 'text/javascript' && item.innerHTML) {
item.innerHTML = item.innerHTML.replace('https://***', '***')
}
})
}
我们选择在 html-webpack-plugin-alter-asset-tags 判断标识,然后替换 body 里面的内容。
这里为什么是 innerHTML?
是因为我们只是替换 chunk 文件,所以替换 c.p = "https://******" 为 c.p = "******"
效果如图:
下面我们看看 fis 的处理方案:
fis.media('prod')
.match('*.{js,css,less,png,gif,jpg,svg}', {
domain : 'https://******'
})
// 在替换一次特定目录的喽
.match('/images/zombie/*.{png,jpg,gif}', {
domain : '***'
})
扩展链接:
1、https://github.com/webpack/webpack/issues/443
2、https://developer.mozilla.org/zh-CN/docs/Web/HTML/CORS_enabled_image
以上是关于当 webpackfis 遇到 canvas的主要内容,如果未能解决你的问题,请参考以下文章