Каков правильный способ загрузки полисов и прокладок с помощью 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()));
}
Как вы видете
Ответ 3
Или воспользуйтесь услугой polyfill на странице https://cdn.polyfill.io/v2/docs/.
Ответ 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;
};
}());
}