Потрібен стиль вузла для вбудованого браузера javascript? [зачинено]


86

Чи існують бібліотеки для JavaScript у браузері, що забезпечують таку ж гнучкість / модульність / простоту використання, що і Node require?

Щоб надати докладніше: причина requireнастільки вагома, що вона:

  1. Дозволяє динамічно завантажувати код з інших місць (що, на мій погляд, стилістично краще, ніж зв’язування всього вашого коду в HTML)
  2. Він забезпечує послідовний інтерфейс для побудови модулів
  3. Модулям легко залежати від інших модулів (щоб я міг написати, наприклад, API, який вимагає jQuery, щоб я міг використовувати jQuery.ajax()
  4. Завантажений javascript масштабується , це означає, що я міг завантажувати var dsp = require("dsp.js");і мав би доступ dsp.FFT, що не заважало б моєму локальномуvar FFT

Я ще не знайшов бібліотеки, яка б це ефективно робила. Рішення, якими я схильний користуватися, є:

  • coffeescript-concat - досить просто вимагати інших js, але вам потрібно його скомпілювати, а це означає, що він менш придатний для швидкої розробки (наприклад, побудова API в тесті)

  • RequireJS - Він популярний, простий і вирішує 1-3, але відсутність обсягу дійсно порушує дію (я вважаю, що head.js схожий тим, що йому бракує масштабу, хоча у мене ніколи не було жодної нагоди використовувати його. Подібним чином LABjs може завантажувати та усунути.wait() проблеми залежності, але все одно не робить масштабування)

Наскільки я можу зрозуміти, здається, існує багато рішень для динамічного та / або асинхронного завантаження javascript, але вони, як правило, мають такі ж проблеми з обсягом, як просто завантаження js з HTML. Більше за все інше, я хотів би завантажити javascript, який взагалі не забруднює глобальний простір імен, але все ж дозволяє завантажувати та використовувати бібліотеки (як це вимагає вузол).

ОНОВЛЕННЯ 2020: Модулі тепер є стандартними для ES6, а станом на середину 2020 року підтримуються більшістю браузерів . Модулі підтримують як синхронне, так і асинхронне (з використанням Promise) завантаження. Поточна моя рекомендація полягає в тому, що в більшості нових проектів слід використовувати модулі ES6 та використовувати транслятор, щоб повернутися до одного файлу JS для застарілих браузерів.

Як загальний принцип, пропускна здатність сьогодні також набагато ширша, ніж коли я спочатку задавав це питання. Отже, на практиці ви можете обґрунтовано вибрати завжди використовувати транспілер з модулями ES6 і зосередити свої зусилля на ефективності коду, а не на мережі.

ПОПЕРЕДНЕ РЕДАКТУВАННЯ (або якщо вам не подобаються модулі ES6): З моменту написання цього тексту я широко використовував RequireJS (який тепер має набагато чіткішу документацію). На мою думку, RequireJS справді був правильним вибором. Я хотів би пояснити, як система працює для людей, які так само розгублені, як і я:

Ви можете використовувати requireв повсякденному розвитку. Модулем може бути будь-що, що повертається функцією (як правило, об’єктом або функцією) і визначається як параметр. Ви також можете скомпілювати свій проект в один файл для розгортання r.js(на практиці це майже завжди швидше, хоча requireпаралельно можна завантажувати сценарії).

Основна відмінність між RequireJS та стилем node вимагає, як browserify (класний проект, запропонований tjameson), - це спосіб розробки та необхідності модулів:

  • RequireJS використовує AMD (Визначення асинхронного модуля). В AMD requireбере список модулів (файли javascript) для завантаження та функцію зворотного виклику. Коли він завантажує кожен з модулів, він викликає зворотний виклик з кожним модулем як параметр зворотного виклику. Таким чином, це справді асинхронно і, отже, добре підходить для Інтернету.
  • Вузол використовує CommonJS. У CommonJS require- це виклик блокування, який завантажує модуль і повертає його як об'єкт. Це чудово працює для Node, оскільки файли зчитуються з файлової системи, що досить швидко, але погано працює в Інтернеті, оскільки синхронне завантаження файлів може зайняти набагато більше часу.

На практиці багато розробників використовували Node (а отже і CommonJS) ще до того, як побачити AMD. Крім того, багато бібліотек / модулів написано для CommonJS (шляхом додавання речей до exportsоб'єкта), а не для AMD (поверненням модуля з defineфункції). Тому багато розробників веб-вузлів хочуть використовувати бібліотеки CommonJS в Інтернеті. Це можливо, оскільки завантаження з <script>тегу блокує. Такі рішення, як browserify, беруть модулі CommonJS (Node) і обгортають їх, щоб ви могли включати їх за допомогою тегів сценаріїв.

Тому, якщо ви розробляєте власний багатофайловий проект для Інтернету, я настійно рекомендую RequireJS, оскільки це справді модульна система для Інтернету (хоча при чесному розкритті я вважаю AMD набагато природнішим, ніж CommonJS). Останнім часом розрізнення стає менш важливим, оскільки RequireJS тепер дозволяє по суті використовувати синтаксис CommonJS. Крім того, RequireJS можна використовувати для завантаження модулів AMD у Node (хоча я віддаю перевагу node-amd-loader ).


1
Примітка. RequireJS насправді підтримує модульність і може охоплювати область. З моменту запитання я використав це трохи ширше. На мою думку, він має багато функціональних можливостей, але для ефективного використання вимагає багато читання документації, і перед перфектним використанням потрібна якась форма першокласного синхронного завантаження.
Алекс Черчілль,

1
Чи відрізняє асинхронність від цього значення? всякий раз, коли мені потрібен код, мені в основному блокують можливість продовжувати, оскільки він визначає функції, і тому мені потрібно робити що завгодно ...
Майкл

Відповідь на це запитання слід взяти і поставити як фактичну відповідь. Див. Чи можу я відповісти на власне запитання?
Ліам

Відповіді:


17

Перевірте ender . Це робить багато цього.

Крім того, browserify досить непоганий. Я використовував require-kiss ¹ і це працює. Ймовірно, є й інші.

Я не впевнений щодо RequireJS. Це просто не те саме, що і вузол. Можуть виникнути проблеми із завантаженням з інших місць, але це може спрацювати. Поки існує метод надання або щось, що можна викликати.

TL; DR - Я б порекомендував переглянути або поцілувати.


Оновлення:

1: require-kiss тепер мертвий, і автор видалив його. З тих пір я без проблем використовую RequireJS. Автор require-kiss написав pakmanager і pakman . Повне розкриття інформації, я працюю з розробником.

Особисто мені більше подобається RequireJS. Це набагато простіше налагоджувати (ви можете мати окремі файли в розробці та один розгорнутий файл у виробництві) і побудовано на надійному "стандартному".


Посилання на require-kiss здається мертвим. Простий (пере) пошук нікуди не привів - куди він подівся?
Джоель Пурра,

@JoelPurra - require-kiss видалено і замінено pakmanager. Я рекомендую вимагати js зараз. Я оновив відповідь.
beatgammit

приємна відповідь тут, людина :), не забувайте перевірити запитання, яке я щойно зробив, схоже на це (але одночасно різне)? stackoverflow.com/questions/43237875 / ...
Webeng

16

Я написав невеликий скрипт, який дозволяє асинхронне та синхронне завантаження файлів Javascript, які тут можуть бути корисними. Він не має залежностей і сумісний з Node.js & CommonJS. Установка досить проста:

$ npm install --save @tarp/require

Потім просто додайте наступні рядки у свій HTML, щоб завантажити основний модуль:

<script src="/node_modules/@tarp/require/require.min.js"></script>
<script>Tarp.require({main: "./scripts/main"});</script>

Усередині основного модуля (і будь-якого підмодуля, звичайно) ви можете використовувати його, require()як вам відомо, від CommonJS / NodeJS. Повні документи та код можна знайти на GitHub .


як ви використовуєте функцію, яка знаходиться в main.js? Наприклад main.js має простий myFunctionв alert("hello"). Чи дзвоню я main.myFunction()? Це не призведе до сповіщення?
Brian Wiley

Чи потрібно використовувати, Tarp.require({ expose: true });щоб це працювало? Як у вашому тесті?
Brian Wiley

10

Я розумію, що можуть бути новачки, які прагнуть упорядкувати свій код. Це 2020 рік , і якщо ви розглядаєте модульний додаток JS, вам слід розпочати роботу з npm та Webpack прямо зараз.

Ось кілька простих кроків, щоб розпочати:

  1. У кореневій частині проекту запустіть npm init -yініціалізацію проекту npm
  2. Завантажте пакет модулів Webpack: npm install webpack webpack-cli
  3. Створіть файл index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
</head>
<body>
    
    <script src="_bundle.js"></script>
</body>
</html>

Зверніть особливу увагу на _bundle.jsфайл - це буде остаточний файл JS, згенерований webpack, ви не будете змінювати його безпосередньо (продовжуйте читати).

  1. Створіть файл, <project-root>/app.jsв який ви імпортуєте інші модулі:
const printHello = require('./print-hello');

printHello();
  1. Створіть зразок print-hello.jsмодуля:
module.exports = function() {
    console.log('Hello World!');
}
  1. Створіть <project-root>/webpack.config.jsта скопіюйте та вставте наступне:
var path = require('path');

module.exports = {
  entry: './app.js',
  output: {
    path: path.resolve(__dirname),
    filename: '_bundle.js'
  }
};

У наведеному вище коді є 2 пункти:

  • запис app.js- це місце, де ви будете писати свій код JS. Він імпортує інші модулі, як показано вище.
  • output _bundle.js- це ваш остаточний комплект, згенерований webpack. Це те, що побачить ваш html в кінці.

-7. Відкрийте свій файл package.jsі замініть scriptsйого наступною командою:

  "scripts": {
    "start": "webpack --mode production -w"
  },
  1. І , нарешті , запустити годинник сценарій app.jsі генерувати _bundle.jsфайл, виконавши: npm start.
  2. Насолоджуйтесь кодуванням!

2
у 2020 році це слід позначити як правильну відповідь
p13rnd

9

Варіант чудової відповіді Іллі Харламова , з деяким кодом, щоб він добре грав з інструментами розробника chrome.

//
///- REQUIRE FN
// equivalent to require from node.js
function require(url){
    if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
    if (!require.cache) require.cache=[]; //init cache
    var exports=require.cache[url]; //get from cache
    if (!exports) { //not cached
            try {
                exports={};
                var X=new XMLHttpRequest();
                X.open("GET", url, 0); // sync
                X.send();
                if (X.status && X.status !== 200)  throw new Error(X.statusText);
                var source = X.responseText;
                // fix (if saved form for Chrome Dev Tools)
                if (source.substr(0,10)==="(function("){ 
                    var moduleStart = source.indexOf('{');
                    var moduleEnd = source.lastIndexOf('})');
                    var CDTcomment = source.indexOf('//@ ');
                    if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
                    source = source.slice(moduleStart+1,moduleEnd-1); 
                } 
                // fix, add comment to show source on Chrome Dev Tools
                source="//@ sourceURL="+window.location.origin+url+"\n" + source;
                //------
                var module = { id: url, uri: url, exports:exports }; //according to node.js modules 
                var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
                anonFn(require, exports, module); // call the Fn, Execute the module
                require.cache[url]  = exports = module.exports; //cache obj exported by module
            } catch (err) {
                throw new Error("Error loading module "+url+": "+err);
            }
    }
    return exports; //require returns object exported by module
}
///- END REQUIRE FN

5
(function () {
    // c is cache, the rest are the constants
    var c = {},s="status",t="Text",e="exports",E="Error",r="require",m="module",S=" ",w=window;
    w[r]=function R(url) {
        url+=/.js$/i.test(url) ? "" : ".js";// to allow loading without js suffix;
        var X=new XMLHttpRequest(),module = { id: url, uri: url }; //according to the modules 1.1 standard
        if (!c[url])
            try {
                X.open("GET", url, 0); // sync
                X.send();
                if (X[s] && X[s] != 200) 
                    throw X[s+t];
                Function(r, e, m, X['response'+t])(R, c[url]={}, module); // Execute the module
                module[e] && (c[url]=module[e]);
            } catch (x) {
                throw w[E](E+" in "+r+": Can't load "+m+S+url+":"+S+x);
            }
        return c[url];
    }
})();

Краще не використовувати на виробництві через блокування. (У node.js, require () - це блокуючий виклик).


не повинно бути "export: {}" властивістю "module"? і дзвінок be (R, module.exports, module)
Lucio M. Tato

@ LucioM.Tato Я не впевнений, не бачу жодної згадки про module.exports у стандарті Modules 1.1 . Ви завжди можете зателефонувати за вимогою (module.id), щоб отримати експорт
Ілля Харламов

Так. Ви праві, я думав про реалізацію модулів node.js. Я додав деякі зміни до коду у вашій справді хорошій відповіді, щоб зробити його приємним для роботи з Chrome Dev Tools (я використовую його як IDE часу налагодження). Я розміщу код як чергову відповідь на це запитання, якщо це буде корисно комусь іншому.
Lucio M. Tato

1

Require-stub - забезпечує сумісність вузлів requireу браузері, вирішує як модулі, так і відносні шляхи. Використовує техніку, подібну до TKRequire (XMLHttpRequest). Отриманий код можна повністю перевірити, оскільки він require-stubможе замінити watchify.



-1

Ось розширення фантастичної відповіді Люсіо М. Тато, яке дозволяє рекурсивне завантаження модулів із відносними шляхами.

Ось проект github для розміщення рішення та приклад його використання:

https://github.com/trausti/TKRequire.js

Щоб використовувати TKRequire.js, додайте наступний рядок у свій заголовок

<script type="text/javascript" src="./TKRequire.js"></script>

Потім завантажте модулі так само, як у node.js:

var MyModule = require("./relative/path/to/MyModule.js");

Спасибі Траусті. Якщо ви використовуєте javascript, вам слід перевірити github.com/luciotato/LiteScript (бета-версія). PD: На якому рівні CC Saga ви зараз граєте? : P
Lucio M. Tato

посилання мертві, як ми могли взяти і поглянути на ваш код?
ПА.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.