您如何将聚合物项目投入生产?

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);