webpack中打包后端模板的思路
整合jade输出到webpack
目前来说,由于seo需要,前端需要配合中间层使用后端渲染。当前的构建中,我们的构建会全量将jade移入相应的dest文件,修改后会对对应的文件进行重新复制移动,但当我们使用webpack的时候,后端模板的处理变成了一件头疼的事。
如果要用webpack,我们首先要处理的便是模板文件中引用的静态资源的问题。我们应该做的事情是想如何将jade资源通过自己的手段分析,达到webpack一同输出的目的。
由于我们的jade模板是通过后端渲染输出的,产生的模板必然需要是jade模板。此时模块市场上常用的html-webpack-plugin,html-webpack-plugin-pug,
pug-webpack-html-plugin等等插件并不能满足我们的需求。于是只好按着自己的思路造个方轮子了。。。
思路
入口区分
在我们的项目中,jade用作内容输入,布局,公共模块,混入方法等。但一个项目的入口一定在该项目下,一般叫index.jade。由于目前某些页面的入口模板命名并非index.jade,我们可以通过给文件加入入口前缀或者放入一个标志文件夹进行入口区分。
静态资源解析
在模板中,我们有时候会引用如下静态资源
// 通用js静态资源 +JS([ 'common/xxx/xxx.js' ], 'common/xxx/xxx.js') // 通用css静态资源 +CSS([ 'common/xxx/xxxx.css' ], [ 'common/xxx/xxxx.css' ]) // jade模板中使用的静态资源 img(src="aaaa/bbbb/ccc.png") div(style="background:url(aaaa/bbbb/cccc.png)") // jade扩展的布局模板 extends xxxxx.jade // jade包涵模块 include xxxxx.jade
先说js和css的处理。这个直接以vendor.app.js的入口形式进行打包,把这部分资源分离出来。
其次是其他资源,由于是jade模板引用,所以不会通过webpack走file-loader将静态资源移动到相应位置。此时我们就必须正则出来这些资源进行相应的移动处理了。
var jadeSource = fs.readFileSync('xxxx.jade', 'utf-8'); jadeSource.replace(/extends\s*(\S+)|include\s*(\S+)|src\s*\=\s*"(\S+)"|src\s*\=\s*'(\S+)'/g, function (matched, catched) { // 获取到catched Source,依据此文件进行路径分析和文件读取,继续进行文件分析,完成整个文件依赖读取 })
我们获得了这些资源,并不会去无穷无尽的一遍一遍读写,毕竟重复读写明显蛋疼。这个时候我们需要稍微做一个缓存系统,让多个共同资源不会重复读写。思路很简单,用map,可控参数cache,以及文件的最后修改时间来判断即可。
function JadeStat(path, cache) { this.path = path; this.cache = cache; this.stat = fs.statSync(path); if (this.cache) { this.file = fs.readFileSync(path, 'utf-8'); } this.deps = this.getDeps(); } JadeStat.prototype.update = function () { this.stat = this.getNowStat(); if (this.cache) { this.file = fs.readFileSync(this.path, 'utf-8'); } this.deps = this.getDeps(); } JadeStat.prototype.getName = function () { return path.relative(app.staticFolder, this.path); } JadeStat.prototype.getNowStat = function () { return fs.statSync(this.path); } JadeStat.prototype.getContent = function () { if (this.cache) { return this.file; } return fs.readFileSync(this.path, 'utf-8'); } JadeStat.prototype.getDeps = function () { var content = this.getContent(); var deps = []; content.replace(/extends\s*(\S+)|include\s*(\S+)|src\s*\=\s*"(\S+)"|src\s*\=\s*'(\S+)'/g, function (a, cachedSource) { deps.push(path.resolve(this.path, cachedSource)) }) return deps; } var cache = {}; function MyExampleWebpackPlugin(options = {}) { this.who = options.who; this.cache = options.cache === undefined ? true : options.cache; }; MyExampleWebpackPlugin.prototype.apply = function(compiler) { compiler.plugin('emit', (compilation, callback) => { // 递归处理所有模块以及依赖,并将其扁平化,这样我们在cache中就获取到了一个扁平的assetsMap,然后一一做处理即可 // 这里没有进行递归,仅仅是展示一下。 this.who.forEach((p) => { var firstInit = false; if (!cache[p]) { cache[p] = new JadeStat(p, this.cache); firstInit = true; } let jadestat = map[p]; if (!firstInit && jadestat.stat.mtime !== jadestat.getNowStat().stat.mtime) { jadestat.update(); } }) for (var stat of cache) { var source = stat.getContent(); compilation.assets[stat.getName()] = { source: function() { return source; }, size: function() { return source.length; } }; } callback(); }); };
后续问题
在我们的项目中,publicPath是指向www/${version}的,所以必须处理好output...