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",