您如何将聚合物项目投入生产?
How do you go into production with polymer project?
使用 polymer 构建然后将应用程序部署到用于生产的 Web 服务器上是否符合常识?
或者实际使用 polymer serve / polyserve 作为 Web 服务器是否有意义?
polymer serve
的问题是,如果它摔倒了,它不会重新启动,从而使您无法访问任何网站。它的真正用途是在开发中,因为它会在您开发单个元素时为您映射目录。
此外,您将如何处理 ajax 来电?
过去,我之前 运行 我的代码(一个定制的节点网络服务器)在 PM2 中。这些天我 运行 使用 docker
,特别是 docker-compose
,如果失败也会重新启动应用程序。
编辑以下是我如何从 Google Polymer Teams "Polymer Server" 复制(然后由我更改)动态代码,因此受该项目中给出的许可条件的约束.
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
该代码包含一些类似这些的支持函数
const parse5 = require('parse5');
const dom5 = require('dom5');
const LRU = require('lru-cache');
const babelCore = require('babel-core');
const transformLog = require('debug')('web:transform');
const babelTransformers = [
'babel-plugin-transform-es2015-arrow-functions',
'babel-plugin-transform-es2015-block-scoped-functions',
'babel-plugin-transform-es2015-block-scoping',
'babel-plugin-transform-es2015-classes',
'babel-plugin-transform-es2015-computed-properties',
'babel-plugin-transform-es2015-destructuring',
'babel-plugin-transform-es2015-duplicate-keys',
'babel-plugin-transform-es2015-for-of',
'babel-plugin-transform-es2015-function-name',
'babel-plugin-transform-es2015-literals',
'babel-plugin-transform-es2015-object-super',
'babel-plugin-transform-es2015-parameters',
'babel-plugin-transform-es2015-shorthand-properties',
'babel-plugin-transform-es2015-spread',
'babel-plugin-transform-es2015-sticky-regex',
'babel-plugin-transform-es2015-template-literals',
'babel-plugin-transform-es2015-typeof-symbol',
'babel-plugin-transform-es2015-unicode-regex',
'babel-plugin-transform-regenerator',
].map((name) => require(name));
const isInlineJavaScript = dom5.predicates.AND(
dom5.predicates.hasTagName('script'),
dom5.predicates.NOT(dom5.predicates.hasAttr('src')));
const babelCompileCache = LRU({
length: (n, key) => n.length + key.length
});
function compileHtml(source, location) {
const document = parse5.parse(source);
const scriptTags = dom5.queryAll(document, isInlineJavaScript);
for (const scriptTag of scriptTags) {
try {
const script = dom5.getTextContent(scriptTag);
const compiledScriptResult = compileScript(script);
dom5.setTextContent(scriptTag, compiledScriptResult);
} catch (e) {
// By not setting textContent we keep the original script, which
// might work. We may want to fail the request so a better error
// shows up in the network panel of dev tools. If this is the main
// page we could also render a message in the browser.
//eslint-disable-next-line no-console
console.warn(`Error compiling script in ${location}: ${e.message}`);
}
}
return parse5.serialize(document);
}
function compileScript(script) {
return babelCore
.transform(script, {
plugins: babelTransformers,
}).code;
}
function transform(request, body, isHtml) {
const source = body;
const cached = babelCompileCache.get(source);
if (cached !== undefined) {
transformLog('using the cache');
return cached;
}
if (isHtml) {
transformLog('compiling html');
body = compileHtml(source, request.path);
} else {
transformLog('compiling js');
body = compileScript(source);
}
babelCompileCache.set(source, body);
return body;
}
虽然中间件是中间件,但它有效地将自己插入传出流中,捕获传出 html 和 js 文件的所有块,并在必要时转换它们。
function transformResponse(transformNeeded) {
return (req, res, next) => {
let ended = false;
let _shouldTransform = null;
let isHtml = true;
// Note: this function memorizes its result.
function shouldTransform() {
if (_shouldTransform == null) {
const successful = res.statusCode >= 200 && res.statusCode < 300;
if (successful) {
const result = transformNeeded(req);
isHtml = result.isHtml;
_shouldTransform = !!result.transform;
} else {
_shouldTransform = false;
}
}
return _shouldTransform;
}
const chunks = [];
const _write = res.write;
res.write = function( chunk, enc, cb) {
if (ended) {
_write.call(this, chunk, enc, cb);
return false;
}
if (shouldTransform()) {
const buffer = (typeof chunk === 'string') ? new Buffer(chunk,enc) : chunk;
chunks.push(buffer);
return true;
}
return _write.call(this, chunk, enc, cb);
}.bind(res);
const _end = res.end;
res.end = function (chunk, enc, cb) {
if (ended)
return false;
ended = true;
if (shouldTransform()) {
if (chunk) {
const buffer = (typeof chunk === 'string') ? new Buffer(chunk,enc) : chunk;
chunks.push(buffer);
}
const body = Buffer.concat(chunks).toString('utf8');
let newBody = body;
try {
newBody = transform(req, body, isHtml);
} catch (e) {
//eslint-disable-next-line no-console
console.warn('Error', e);
}
// TODO(justinfagnani): re-enable setting of content-length when we know
// why it was causing truncated files. Could be multi-byte characters.
// Assumes single-byte code points!
// res.setHeader('Content-Length', `${newBody.length}`);
this.removeHeader('Content-Length');
return _end.call(this, newBody);
}
return _end.call(this,chunk, enc, cb);
}.bind(res);
next();
};
}
这个名为transformNeeded的例程如下(这是检测浏览器的位)
function transformNeeded(req) {
const pathname = url.parse(req.url).pathname;
const isHtml = pathname === '/' || pathname.slice(-5) === '.html';
if (isHtml || pathname.slice(-3) === '.js') {
//see if we need to compile as we have a .html or .js file
const splitPathName = pathname.split('/');
const isPolyfill = splitPathName.includes('webcomponentsjs') ||
splitPathName.includes('promise-polyfill');
if (!isPolyfill) {
const browser = new UAParser(req.headers['user-agent']).getBrowser();
const versionSplit = (browser.version || '').split('.');
const [majorVersion, minorVersion] = versionSplit.map((v) => v ? parseInt(v, 10) : -1);
const supportsES2015 = (browser.name === 'Chrome' && majorVersion >= 49) ||
(browser.name === 'Chromium' && majorVersion >= 49) ||
(browser.name === 'OPR' && majorVersion >= 36) ||
(browser.name === 'Mobile Safari' && majorVersion >= 10) ||
(browser.name === 'Safari' && majorVersion >= 10) ||
// Note: The Edge user agent uses the EdgeHTML version, not the main
// release version (e.g. EdgeHTML 15 corresponds to Edge 40). See
// https://en.wikipedia.org/wiki/Microsoft_Edge#Release_history.
//
// Versions before 15.15063 may contain a JIT bug affecting ES6
// constructors (see #161).
(browser.name === 'Edge' &&
(majorVersion > 15 || (majorVersion === 15 && minorVersion >= 15063))) ||
(browser.name === 'Firefox' && majorVersion >= 51);
requestLog(
'Browser is %s version %d,%d - supports ES2015? ',
browser.name,
majorVersion,
minorVersion,
supportsES2015
);
return {transform: !supportsES2015, isHtml: isHtml};
}
}
return {transform: false, isHtml: isHtml};
}
最后,我必须在建立网络服务器之前设置路由,然后告诉网络服务器使用我设置的路由。
const Router = require('router');
//sets up my API routes
manager.setRoutes(router);
router.use('/', transformResponse(this.transformNeeded));
router.use('/', staticFiles(clientPath));
this._start(router);
使用 polymer 构建然后将应用程序部署到用于生产的 Web 服务器上是否符合常识? 或者实际使用 polymer serve / polyserve 作为 Web 服务器是否有意义?
polymer serve
的问题是,如果它摔倒了,它不会重新启动,从而使您无法访问任何网站。它的真正用途是在开发中,因为它会在您开发单个元素时为您映射目录。
此外,您将如何处理 ajax 来电?
过去,我之前 运行 我的代码(一个定制的节点网络服务器)在 PM2 中。这些天我 运行 使用 docker
,特别是 docker-compose
,如果失败也会重新启动应用程序。
编辑以下是我如何从 Google Polymer Teams "Polymer Server" 复制(然后由我更改)动态代码,因此受该项目中给出的许可条件的约束.
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
该代码包含一些类似这些的支持函数
const parse5 = require('parse5');
const dom5 = require('dom5');
const LRU = require('lru-cache');
const babelCore = require('babel-core');
const transformLog = require('debug')('web:transform');
const babelTransformers = [
'babel-plugin-transform-es2015-arrow-functions',
'babel-plugin-transform-es2015-block-scoped-functions',
'babel-plugin-transform-es2015-block-scoping',
'babel-plugin-transform-es2015-classes',
'babel-plugin-transform-es2015-computed-properties',
'babel-plugin-transform-es2015-destructuring',
'babel-plugin-transform-es2015-duplicate-keys',
'babel-plugin-transform-es2015-for-of',
'babel-plugin-transform-es2015-function-name',
'babel-plugin-transform-es2015-literals',
'babel-plugin-transform-es2015-object-super',
'babel-plugin-transform-es2015-parameters',
'babel-plugin-transform-es2015-shorthand-properties',
'babel-plugin-transform-es2015-spread',
'babel-plugin-transform-es2015-sticky-regex',
'babel-plugin-transform-es2015-template-literals',
'babel-plugin-transform-es2015-typeof-symbol',
'babel-plugin-transform-es2015-unicode-regex',
'babel-plugin-transform-regenerator',
].map((name) => require(name));
const isInlineJavaScript = dom5.predicates.AND(
dom5.predicates.hasTagName('script'),
dom5.predicates.NOT(dom5.predicates.hasAttr('src')));
const babelCompileCache = LRU({
length: (n, key) => n.length + key.length
});
function compileHtml(source, location) {
const document = parse5.parse(source);
const scriptTags = dom5.queryAll(document, isInlineJavaScript);
for (const scriptTag of scriptTags) {
try {
const script = dom5.getTextContent(scriptTag);
const compiledScriptResult = compileScript(script);
dom5.setTextContent(scriptTag, compiledScriptResult);
} catch (e) {
// By not setting textContent we keep the original script, which
// might work. We may want to fail the request so a better error
// shows up in the network panel of dev tools. If this is the main
// page we could also render a message in the browser.
//eslint-disable-next-line no-console
console.warn(`Error compiling script in ${location}: ${e.message}`);
}
}
return parse5.serialize(document);
}
function compileScript(script) {
return babelCore
.transform(script, {
plugins: babelTransformers,
}).code;
}
function transform(request, body, isHtml) {
const source = body;
const cached = babelCompileCache.get(source);
if (cached !== undefined) {
transformLog('using the cache');
return cached;
}
if (isHtml) {
transformLog('compiling html');
body = compileHtml(source, request.path);
} else {
transformLog('compiling js');
body = compileScript(source);
}
babelCompileCache.set(source, body);
return body;
}
虽然中间件是中间件,但它有效地将自己插入传出流中,捕获传出 html 和 js 文件的所有块,并在必要时转换它们。
function transformResponse(transformNeeded) {
return (req, res, next) => {
let ended = false;
let _shouldTransform = null;
let isHtml = true;
// Note: this function memorizes its result.
function shouldTransform() {
if (_shouldTransform == null) {
const successful = res.statusCode >= 200 && res.statusCode < 300;
if (successful) {
const result = transformNeeded(req);
isHtml = result.isHtml;
_shouldTransform = !!result.transform;
} else {
_shouldTransform = false;
}
}
return _shouldTransform;
}
const chunks = [];
const _write = res.write;
res.write = function( chunk, enc, cb) {
if (ended) {
_write.call(this, chunk, enc, cb);
return false;
}
if (shouldTransform()) {
const buffer = (typeof chunk === 'string') ? new Buffer(chunk,enc) : chunk;
chunks.push(buffer);
return true;
}
return _write.call(this, chunk, enc, cb);
}.bind(res);
const _end = res.end;
res.end = function (chunk, enc, cb) {
if (ended)
return false;
ended = true;
if (shouldTransform()) {
if (chunk) {
const buffer = (typeof chunk === 'string') ? new Buffer(chunk,enc) : chunk;
chunks.push(buffer);
}
const body = Buffer.concat(chunks).toString('utf8');
let newBody = body;
try {
newBody = transform(req, body, isHtml);
} catch (e) {
//eslint-disable-next-line no-console
console.warn('Error', e);
}
// TODO(justinfagnani): re-enable setting of content-length when we know
// why it was causing truncated files. Could be multi-byte characters.
// Assumes single-byte code points!
// res.setHeader('Content-Length', `${newBody.length}`);
this.removeHeader('Content-Length');
return _end.call(this, newBody);
}
return _end.call(this,chunk, enc, cb);
}.bind(res);
next();
};
}
这个名为transformNeeded的例程如下(这是检测浏览器的位)
function transformNeeded(req) {
const pathname = url.parse(req.url).pathname;
const isHtml = pathname === '/' || pathname.slice(-5) === '.html';
if (isHtml || pathname.slice(-3) === '.js') {
//see if we need to compile as we have a .html or .js file
const splitPathName = pathname.split('/');
const isPolyfill = splitPathName.includes('webcomponentsjs') ||
splitPathName.includes('promise-polyfill');
if (!isPolyfill) {
const browser = new UAParser(req.headers['user-agent']).getBrowser();
const versionSplit = (browser.version || '').split('.');
const [majorVersion, minorVersion] = versionSplit.map((v) => v ? parseInt(v, 10) : -1);
const supportsES2015 = (browser.name === 'Chrome' && majorVersion >= 49) ||
(browser.name === 'Chromium' && majorVersion >= 49) ||
(browser.name === 'OPR' && majorVersion >= 36) ||
(browser.name === 'Mobile Safari' && majorVersion >= 10) ||
(browser.name === 'Safari' && majorVersion >= 10) ||
// Note: The Edge user agent uses the EdgeHTML version, not the main
// release version (e.g. EdgeHTML 15 corresponds to Edge 40). See
// https://en.wikipedia.org/wiki/Microsoft_Edge#Release_history.
//
// Versions before 15.15063 may contain a JIT bug affecting ES6
// constructors (see #161).
(browser.name === 'Edge' &&
(majorVersion > 15 || (majorVersion === 15 && minorVersion >= 15063))) ||
(browser.name === 'Firefox' && majorVersion >= 51);
requestLog(
'Browser is %s version %d,%d - supports ES2015? ',
browser.name,
majorVersion,
minorVersion,
supportsES2015
);
return {transform: !supportsES2015, isHtml: isHtml};
}
}
return {transform: false, isHtml: isHtml};
}
最后,我必须在建立网络服务器之前设置路由,然后告诉网络服务器使用我设置的路由。
const Router = require('router');
//sets up my API routes
manager.setRoutes(router);
router.use('/', transformResponse(this.transformNeeded));
router.use('/', staticFiles(clientPath));
this._start(router);