Каков правильный способ загрузки полисов и прокладок с помощью Browserify

Я создаю веб-приложение, и я узнаю и люблю Browserify. Меня все-таки раздражало.

Я использую некоторые функции ES6, которые необходимо подстраивать/полифорировать в старых браузерах, таких как es6-promise и назначение object-assign (пакеты на npm).

В настоящее время я просто загружаю их в каждый модуль, который им нужен:

var assign  = require('object-assign');
var Promise = require('es6-promise');

Я знаю, что это определенно не путь. Трудно поддерживать, и я бы хотел использовать функции ES6 прозрачно, вместо того, чтобы "зависеть" от них по требованию.

Какой окончательный способ загрузки таких прокладок? Я видел несколько примеров в Интернете, но они все разные. Я мог бы:

  • загружать их извне:

    var bundle = browserify(); 
    bundle.require('s6-promise');
    // or should I use it bundle.add to make sure the code is runned???
    

    Проблема, которую я имею здесь, заключается в том, что я не знаю, в каком порядке будут загружены модули в браузере. Таким образом, полифорирование, возможно, еще не произошло на сайтах-узлах, которым нужна полизаполненная функциональность.

    У этого есть дополнительный недостаток, который код бэкэнд не может извлечь выгоду из этих полинаполнений (если только я чего-то не хватает).

  • используйте browserify-shim или что-то подобное. Я действительно не вижу, как это будет работать для функций ES6.

  • вручную настроить полизаполнение:

    Object.assign = require('object-assign');
    

Ответы

Ответ 1

Не требуйте полиполнений в ваших модулях, которые имеют анти-шаблон. Ваши модули должны предполагать, что время выполнения исправлено (если необходимо), и это должно быть частью контракта. Хорошим примером этого является ReactJS, где они явно определяют минимальное требование для среды выполнения, чтобы библиотека могла работать: http://facebook.github.io/react/docs/working-with-the-browser.html#browser -Поддержка-и-polyfills

Вы можете использовать услугу polyfill (например: https://cdn.polyfill.io/), чтобы включить оптимизированный тег сценария в верхней части страницы, чтобы гарантировать, что время выполнения исправлено правильно с помощью фрагментов, которые вам нужны, в то время как современные браузеры будут не наказываться.

Ответ 2

Одним из решений, которые работали для меня, было использование bundle.add

Я разделил свой пакет на 2 части, app.js для кода приложения и appLib.js для библиотек (этот будет кэшироваться, поскольку он не изменяется часто).

См. Https://github.com/sogko/gulp-recipes/tree/master/browserify-separating-app-and-vendor-bundles.

Для appLibs.js я использую bundle.add для polyfills, так как они должны загружаться при загрузке скрипта, в то время как я использую bundle.require для других libs, которые будут загружаться только тогда, когда это требуется внутри app.js.

polyfills.forEach(function(polyfill) {
   b.add(polyfill);
});
libs.forEach(function(lib) {
  b.require(lib);
});

Страница загружает эти 2 пакета по порядку:

<head>
    ...
    <script type="text/javascript" src="appLibs.js" crossorigin></script>
    <script type="text/javascript" src="app.js" crossorigin></script>
    ...
</head>

Таким образом, кажется безопасным предположить, что все полисы будут загружены еще до инициализации других библиотек. Не уверен, что это лучший вариант, но это сработало для меня.

Моя полная настройка:

"use strict";

var browserify   = require('browserify');
var gulp         = require('gulp');
var gutil        = require('gulp-util');
var handleErrors = require('../util/handleErrors');
var source       = require('vinyl-source-stream');
var watchify     = require("watchify");
var livereload   = require('gulp-livereload');
var gulpif       = require("gulp-if");
var buffer       = require('vinyl-buffer');
var uglify       = require('gulp-uglify');


// polyfills should be automatically loaded, even if they are never required
var polyfills = [
    "intl"
];

var libs = [
    "ajax-interceptor",
    "autolinker",
    "bounded-cache",
    "fuse.js",
    "highlight.js",
    "imagesloaded",
    "iscroll",
    "jquery",
    "keymaster",
    "lodash",
    "medium-editor",
    "mime-db",
    "mime-types",
    "moment",
    "packery",
    "q",
    "rangy",
    "spin.js",
    "steady",
    "store",
    "string",
    "uuid",
    "react-dnd"
];


// permits to create a special bundle for vendor libs
// See https://github.com/sogko/gulp-recipes/tree/master/browserify-separating-app-and-vendor-bundles
gulp.task('browserify-libs', function () {
    var b = browserify({
        debug: true
    });

    polyfills.forEach(function(polyfill) {
        b.add(polyfill);
    });
    libs.forEach(function(lib) {
        b.require(lib);
    });

    return b.bundle()
        .on('error', handleErrors)
        .pipe(source('appLibs.js'))
        // TODO use node_env instead of "global.buildNoWatch"
        .pipe(gulpif(global.buildNoWatch, buffer()))
        .pipe(gulpif(global.buildNoWatch, uglify()))
        .pipe(gulp.dest('./build'));
});



// Inspired by http://truongtx.me/2014/08/06/using-watchify-with-gulp-for-fast-browserify-build/

gulp.task('browserify',['cleanAppJs','browserify-libs'],function browserifyShare(){
    var b = browserify({
        cache: {},
        packageCache: {},
        fullPaths: true,
        extensions: ['.jsx'],
        paths: ['./node_modules','./src/'],
        debug: true
    });
    b.transform('reactify');

    libs.forEach(function(lib) {
        b.external(lib);
    });

    // TODO use node_env instead of "global.buildNoWatch"
    if ( !global.buildNoWatch ) {
        b = watchify(b);
        b.on('update', function() {
            gutil.log("Watchify detected change -> Rebuilding bundle");
            return bundleShare(b);
        });
    }
    b.on('error', handleErrors);

    //b.add('app.js'); // It seems to produce weird behaviors when both using "add" and "require"

    // expose does not seem to work well... see https://github.com/substack/node-browserify/issues/850
    b.require('app.js',{expose: 'app'});

    return bundleShare(b);
});



function bundleShare(b) {
    return b.bundle()
        .on('error', handleErrors)
        .pipe(source('app.js'))
        .pipe(gulp.dest('./build'))
        // TODO use node_env instead of "global.buildNoWatch"
        .pipe(gulpif(!global.buildNoWatch, livereload()));
}

Как вы видете

Ответ 4

Это метод, который я использую. Ключ в том, что вы должны правильно экспортировать полиполк в верхней части основного файла записи.

Следующие действия не будут работать:

// Using ES6 imports
import './polyfill';

// Using CommonJS style
require('./polyfill');

... // rest of your code goes here

Вам действительно нужно экспортировать полипол:

// Using ES6 export
export * from './polyfill';

// Using CommonJS style
var polyfill = require('./polyfill');

... // rest of your code goes here

Ваши полисы будут загружаться правильно, если вы будете использовать один из последних методов.

Ниже вы можете найти примеры моих полиполков.

polyfill.js:

import './polyfill/Array.from';
import './polyfill/Object.assign';

Object.assign:

if (typeof Object.assign !== 'function') {
  (function iife() {
    const ObjectHasOwnProperty = Object.prototype.hasOwnProperty;

    /**
     * Copy the values of all enumerable own properties from one source
     * object to a target object. It will return the target object.
     * @param  {Object}  target  The target object.
     * @param  {Object}  source  The source object.
     * @return  {Object}  The target object.
     */
    function shallowAssign(target, source) {
      if (target === source) return target;
      Object.keys(source).forEach((key) => {
        // Avoid bugs when hasOwnProperty is shadowed
        if (ObjectHasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      });
      return target;
    }

    /**
     * Copy the values of all enumerable own properties from one source
     * object to a target object. It will return the target object.
     * @param  {Object}  target  The target object.
     * @param  {Object}  source  The source object.
     * @return  {Object}  The target object.
     */
    Object.assign = function assign(target, ...sources) {
      if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
      sources.forEach((source) => {
        if (source !== null) { // Skip over if undefined or null
          shallowAssign(Object(target), Object(source));
        }
      });
      return target;
    };
  }());
}