From 8326cb9183d69b5bb76ea5eeadf9dc563c9ddfa0 Mon Sep 17 00:00:00 2001 From: hubcarl Date: Wed, 5 Jul 2017 15:48:44 +0800 Subject: [PATCH] feat:support injectRes config --- README.md | 1 + config/config.default.js | 5 ++ lib/resource.js | 111 +++++++++++++++++++++++++++++++++------ package.json | 2 +- 4 files changed, 101 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 1b8d9a4..75ec2c7 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ exports.vuessr = { // buildConfig: path.join(app.baseDir, 'config/buildConfig.json'), // injectCss: true, // injectJs: true, + // injectRes: [] // fallbackToClient: true, // fallback to client rendering after server rendering failed // afterRender: (html, context) => { // return html; diff --git a/config/config.default.js b/config/config.default.js index 2393405..4ebeb72 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -12,6 +12,10 @@ module.exports = app => { * @property {String} [manifest=${baseDir}/config/buildConfig.json] - compile config, include `publicPath` and `commonsChunk` * @property {Boolean} [injectCss] whether inject href css * @property {Boolean} [injectJs] whether inject src script + * @property {Array} [injectRes] inline/inject css or js to file head or body. include location and src config + * inline {Boolean} true or false, default false + * location {String} headBefore, headAfter, bodyBefore, bodyAfter insert location, default headBefore + * url {String} inline file absolution path * @property {Function} afterRender hook html after render * `publicPath`: static resource prefix path, so cdn domain address prefix or local prefix path(`/`) * `commonsChunk`: common js or css filename, so `vendor` @@ -22,6 +26,7 @@ module.exports = app => { buildConfig: path.join(app.baseDir, 'config/buildConfig.json'), injectCss: true, injectJs: true, + injectRes: [], fallbackToClient: true, // fallback to client rendering if server render failed afterRender: (html, context) => { /* eslint no-unused-vars:off */ return html; diff --git a/lib/resource.js b/lib/resource.js index e945fe7..622ff91 100644 --- a/lib/resource.js +++ b/lib/resource.js @@ -12,18 +12,60 @@ class Resource { } init() { + this.initConfig(); + // local dev mode, webpack build finish update buildConfig and manifest + if (process.env.NODE_ENV !== 'production') { + this.app.messenger.on('webpack_manifest_save_success', () => { + delete require.cache[this.config.buildConfig]; + delete require.cache[this.config.manifest]; + this.initConfig(); + }); + } + } + + initConfig() { if (this.isValidJSONConfig(this.config.buildConfig)) { this.buildConfig = require(this.config.buildConfig); } - if (!this.resourceDeps && this.isValidJSONConfig(this.config.manifest)) { + if (this.isValidJSONConfig(this.config.manifest)) { const manifest = require(this.config.manifest); this.resourceDeps = this.getResourceDeps(manifest, this.buildConfig); } + this.initInline(); + } + + initInline() { + if (Array.isArray(this.config.injectRes)) { + this.config.injectRes.forEach(item => { + if (item.inline && this.isExist(item.url)) { + const content = this.readFile(item.url); + if (/\.js$/.test(item.url)) { + item.content = ``; + } else if (/\.css$/.test(item.url)) { + item.content = ``; + } + } else { + if (/\.js$/.test(item.url)) { + item.content = this.createScriptSrcTag(item.url); + } else if (/\.css$/.test(item.url)) { + item.content = this.createCssLinkTag(item.url); + } + } + }); + } + } + + isExist(filepath) { + return fs.existsSync(filepath); + } + + readFile(filepath) { + return fs.readFileSync(filepath, 'utf8'); } isValidJSONConfig(filepath) { if (!filepath) return false; - if (fs.existsSync(filepath)) { + if (this.isExist(filepath)) { try { return require(filepath); } catch (err) { @@ -33,35 +75,70 @@ class Resource { return false; } + createCssLinkTag(url) { + return ``; + } + + createScriptSrcTag(url) { + return ``; + } + + injectHead(resArr) { + this.config.injectRes.forEach(item => { + if (item.content) { + if (item.location === 'headBefore' || (item.location === undefined && /\.(css|js)$/.test(item.url))) { + resArr.unshift(item.content); + } else if (item.location === 'headAfter') { + resArr.push(item.content); + } + } + }); + } + + injectBody(resArr) { + this.config.injectRes.forEach(item => { + if (item.content) { + if (item.location === 'bodyBefore') { + resArr.unshift(item.content); + } else if (item.location === 'bodyAfter') { + resArr.push(item.content); + } + } + }); + } + + inject(name, html, context, config, options) { - this.init(); const fileKey = name; const fileManifest = this.resourceDeps[fileKey]; if (fileManifest) { + const headInject = []; + const bodyInject = []; const publicPath = this.buildConfig.publicPath; if (config.injectCss && (options.injectCss === undefined || options.injectCss)) { - const cssInject = fileManifest.css.map(item => { - return ``; - }); - html = html.replace(this.headRegExp, match => { - return cssInject.join('') + match; + fileManifest.css.forEach(item => { + headInject.push(this.createCssLinkTag(publicPath + item)); }); } else { - html = html.replace(this.headRegExp, match => { - return context.styles + match; - }); + headInject.push(context.styles); } if (config.injectJs) { - const scriptInject = fileManifest.script.map(item => { - return ``; + fileManifest.script.forEach(item => { + bodyInject.push(this.createScriptSrcTag(publicPath + item)); }); if (!/window.__INITIAL_STATE__/.test(html)) { - scriptInject.unshift(``); + bodyInject.unshift(``); } - html = html.replace(this.bodyRegExp, match => { - return scriptInject.join('') + match; - }); } + this.injectHead(headInject); + html = html.replace(this.headRegExp, match => { + return headInject.join('') + match; + }); + + this.injectBody(bodyInject); + html = html.replace(this.bodyRegExp, match => { + return bodyInject.join('') + match; + }); } return config.afterRender(html, context); } diff --git a/package.json b/package.json index e6c6814..a6fd301 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "egg-view-vue-ssr", - "version": "2.0.0", + "version": "2.0.1", "description": "vue server side render solution for egg-view-vue", "eggPlugin": { "name": "vuessr",