Node.js ile tarayıcı arasında nasıl kod paylaşabilirim?


242

WebSocket kullanarak iletişim kurarak bir JavaScript istemcisi (tarayıcıda çalışır) ve Node.js sunucusuyla küçük bir uygulama oluşturuyorum.

İstemci ve sunucu arasında kod paylaşmak istiyorum. Az önce Node.js ile yeni başladım ve modern JavaScript bilgim biraz paslı. Bu yüzden hala kafamı CommonJS gerektiren () fonksiyonu etrafında alıyorum. Paketlerimi 'dışa aktar' nesnesini kullanarak oluşturuyorsam, tarayıcıda aynı JavaScript dosyalarını nasıl kullanabileceğimi göremiyorum.

Kodlama ve kod çözme ve diğer yansıtılmış görevleri kolaylaştırmak için her iki uçta kullanılan yöntemler ve sınıflar kümesi oluşturmak istiyorum. Ancak, Node.js / CommonJS paketleme sistemleri, her iki tarafta da kullanılabilecek JavaScript dosyaları oluşturmamı engelliyor gibi görünüyor.

Ayrıca daha sıkı bir OO modeli almak için JS.Class kullanmayı denedim, ancak vazgeçtim çünkü sağlanan JavaScript dosyalarını birlikte (() ile çalışmak için nasıl elde edemeyiz anlayamadım. Burada özlediğim bir şey var mı?


4
Bu soruya ek cevaplar gönderdiğiniz için herkese teşekkürler. Bu açıkça hızla değişecek ve gelişecek bir konudur.
Simon Cave

Yanıtlar:


168

Hem istemci tarafında hem de sunucu tarafında kullanılabilecek bir modül yazmak istiyorsanız, hızlı ve kolay bir yöntemle ilgili kısa bir blog yayınım var : Node.js ve tarayıcı için yazım , esasen aşağıdakiler (burada thisaynıdır window) :

(function(exports){

    // Your code goes here

   exports.test = function(){
        return 'hello world'
    };

})(typeof exports === 'undefined'? this['mymodule']={}: exports);

Alternatif olarak Marak en olarak istemci tarafında node.js API, uygulamaya yönelik bazı projeler vardır ikizler .

Ayrıca , basit bir JSON tabanlı ağ protokolü kullanarak başka bir makineden çağrılabilmesi için bir JavaScript işlevini açığa çıkarmanızı sağlayan DNode ile de ilgilenebilirsiniz .


Mükemmel. Bilgi için teşekkürler, Caolan.
Simon Cave

2
Gerçekten harika bir makale Caolan. Anladım, işe yaradı, şimdi tekrar yuvarlanıyorum. Fantastik!
Michael Dausmann

2
Modüllerimi istemci ve sunucuda paylaşmama izin verecek olan kendi projemde RequireJ kullanıyorum . Nasıl çalıştığını göreceğiz.
kamranicus

5
@Caolan bu bağlantı öldü
Kamal Reddy

5
İkizler bağlantısı öldü.
borisdiakur

42

Epeli'nin burada kütüphane olmadan bile çalışan güzel bir çözümü var http://epeli.github.com/piler/ , bunu share.js adlı bir dosyaya koymanız yeterli

(function(exports){

  exports.test = function(){
       return 'This is a function from shared module';
  };

}(typeof exports === 'undefined' ? this.share = {} : exports));

Sunucu tarafında şunları kullanın:

var share = require('./share.js');

share.test();

Ve istemci tarafında sadece js dosyasını yükleyin ve kullanın

share.test();

10
Bu yanıtı kabul edilenden daha çok seviyorum çünkü benim gibi yeni başlayanlar için daha iyi açıklanıyor.
Howie

Statik (genel) klasörünün yanındaki Express klasörümde, istemciden 'public' klasörü gibi de erişilebilen 'paylaşılan' adında bir klasör var: app.use (express.static ('public')) ; app.use (express.static () 'ortak); Ve gönderiniz dosyaları istemci ve sunucu ile paylaşma fikrimi genişletiyor. Tam da ihtiyacım olan şey bu. Teşekkür ederim!
birleştirin

Bu çözüm + git subtree == awesome. Teşekkürler!
kevinmicke

@broesch Bu ES6'da nasıl çalışır? Bunu ES6'ya özgü bazı problemlerle yeni bir soru olarak sordum , ancak burada bir düzenleme görmekten memnuniyet duyarım!
Tedskovsky

15

Bu çalışmayı Node.js modül modelinde, AMD modül modelinde ve tarayıcıda global yapan jQuery kaynak koduna bakın:

(function(window){
    var jQuery = 'blah';

    if (typeof module === "object" && module && typeof module.exports === "object") {

        // Expose jQuery as module.exports in loaders that implement the Node
        // module pattern (including browserify). Do not create the global, since
        // the user will be storing it themselves locally, and globals are frowned
        // upon in the Node module world.
        module.exports = jQuery;
    }
    else {
        // Otherwise expose jQuery to the global object as usual
        window.jQuery = window.$ = jQuery;

        // Register as a named AMD module, since jQuery can be concatenated with other
        // files that may use define, but not via a proper concatenation script that
        // understands anonymous AMD modules. A named AMD is safest and most robust
        // way to register. Lowercase jquery is used because AMD module names are
        // derived from file names, and jQuery is normally delivered in a lowercase
        // file name. Do this after creating the global so that if an AMD module wants
        // to call noConflict to hide this version of jQuery, it will work.
        if (typeof define === "function" && define.amd) {
            define("jquery", [], function () { return jQuery; });
        }
    }
})(this)

Bu en iyi yöntemdir (ihtiyacım olan şey için). İşte oluşturduğum çalışan bir örnek: gist.github.com/drmikecrowe/4bf0938ea73bf704790f
Mike Crowe

13

Bir JavaScript işlevinin dize olarak temsil edilmesinin, o işlevin kaynak kodunu temsil ettiğini unutmayın. Fonksiyonlarınızı ve kurucularınızı kapsüllenmiş bir şekilde yazabilirsiniz, böylece toString () 'd olabilir ve istemciye gönderilebilirler.

Bunu yapmanın başka bir yolu, bir derleme sistemi kullanmak, ortak kodu ayrı dosyalara koymak ve bunları hem sunucuya hem de istemci komut dosyalarına dahil etmektir. WebSockets aracılığıyla basit bir istemci / sunucu oyunu için bu yaklaşımı kullanıyorum; burada sunucu ve istemci her ikisi de aynı oyun döngüsünü çalıştırıyor ve istemci kimsenin hile yapmadığından emin olmak için sunucu her kene ile senkronize oluyor.

Oyun için derleme sistemim , dosyaları C önişlemcisi ve sonra sed aracılığıyla bazı önemsiz cpp yapraklarını temizlemek için çalıştıran basit bir Bash betiğidir, böylece #include, #define, #ifdef gibi tüm normal önişlemci öğelerini kullanabilirim , vb.


2
Javascript işlevlerinin seri hale getirilmesi, dizeler olarak bana hiç gelmedi. Bahşiş için teşekkürler.
Simon Cave

13

Node.js için RequireJS bağdaştırıcısına bakmanızı tavsiye ederim . Sorun, Node.js'nin varsayılan olarak kullandığı CommonJS modül kalıbının eşzamansız olmaması ve web tarayıcısında yüklemeyi engellemesidir. RequireJS, r.jsadaptörü kullandığınız sürece hem eşzamansız hem de sunucu ve istemciyle uyumlu olan AMD desenini kullanır .


async kütüphanesi var
Jacek Pietal

11

Belki bu tamamen soruya uygun değildir, ama bunu paylaşacağımı düşündüm.

Ben hem düğüm hem de tarayıcı için kullanılabilir String.prototype bildirilen basit dize yardımcı programı işlevleri bir çift yapmak istedim. Bu işlevleri basitçe utilities.js adlı bir dosyada (bir alt klasörde) saklıyorum ve hem tarayıcı kodumdaki bir komut dosyası etiketinden hem de Node.js komut dosyamdaki requir (.js uzantısını atlayarak) kullanarak kolayca başvurabilirim :

my_node_script.js

var utilities = require('./static/js/utilities')

my_browser_code.html

<script src="/static/js/utilities.js"></script>

Umarım bu benden başka biri için yararlı bir bilgidir.


1
Bu yaklaşımı seviyorum ama statik dosyalarımın çok hareket ettiğini görüyorum. Bulduğum bir çözüm, modülü yeniden dışa aktarmak. Örneğin, utilites.jstek bir satırla oluşturun module.exports = require('./static/js/utilities');. Bu şekilde, etrafında bir şey karıştırırsanız yalnızca bir yolu güncellemeniz gerekir.
Tom Makin

Bu fikri seviyorum. Sadece yolu bulmam biraz zaman alan bir not. Benim utilities.jsise sharedproje kapsamında klasöründe. Kullanmak require('/shared/utilities')bana hata verdi Cannot find module '/shared/utilities'. require('./../../shared/utilities')Çalışması için böyle bir şey kullanmalıyım . Yani, her zaman geçerli klasörden gider ve önce kök sonra aşağı aşağı seyahat.
newman

Şimdi paylaşılan modülün nereye yerleştirileceğini görüyorum - statik klasöre. Bilgi için teşekkürler!
birleştirin

9

Aşağıdaki gibi kullanım modülü bundlers kullanırsanız WebPack bir tarayıcıda kullanım için JavaScript dosyalarını paketlemek için, sadece bir tarayıcıda çalışan kullanıcı arabirimi için sizin node.js modülünü yeniden kullanabilirsiniz. Başka bir deyişle, Node.js modülünüz Node.js ve tarayıcı arasında paylaşılabilir.

Örneğin, şu sum.js koduna sahipsiniz:

Normal Node.js modülü: sum.js

const sum = (a, b) => {
    return a + b
}

module.exports = sum

Node.js dosyasındaki modülü kullanın

const sum = require('path-to-sum.js')
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

Ön uçta yeniden kullanın

import sum from 'path-to-sum.js'
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

4

Sunucu, istemciye (tarayıcı) JavaScript kaynak dosyalarını gönderebilir, ancak işin püf noktası, istemcinin execkodu kodlayabilmesi ve bir modül olarak depolayabilmesi için mini bir "dışa aktarma" ortamı sağlaması gerekecektir .

Böyle bir ortam yapmanın basit bir yolu bir kapatma kullanmaktır. Örneğin, sunucunuzun HTTP gibi kaynak dosyalar sağladığını varsayalım http://example.com/js/foo.js. Tarayıcı gerekli dosyaları XMLHttpRequest ile yükleyebilir ve kodu şöyle yükleyebilir:

ajaxRequest({
  method: 'GET',
  url: 'http://example.com/js/foo.js',
  onSuccess: function(xhr) {
    var pre = '(function(){var exports={};'
      , post = ';return exports;})()';
    window.fooModule = eval(pre + xhr.responseText + post);
  }
});

Anahtar, istemcinin yabancı kodu hemen çalıştırılmak üzere anonim bir işleve (bir kapatma) satabildiği anlamına gelir. Bu örnekte, fooModuledosya tarafından dışa aktarılan kodu içerecek olan window özelliğine atanmıştır foo.js.


2
eval kullandığınız her zaman bir gnome öldürmek
Jacek Pietal

1
Kullanırdım window.fooModule = {}; (new Function('exports', xhr.responseText))(window.fooModule).
GingerPlusPlus

2

Önceki çözümlerin hiçbiri CommonJS modül sistemini tarayıcıya getirmez.

Diğer yanıtlar belirtildiği gibi, gibi varlık yöneticisi / packager çözümler vardır Browserify veya İstifler ve benzeri RPC çözümler vardır dnode veya nowjs .

Ancak tarayıcı için bir CommonJS uygulaması bulamadım (bir require()işlev ve exports/ module.exportsnesneler vb. Dahil ). Bu yüzden kendim yazdım, ancak daha sonra bir başkasının benden daha iyi yazdığını keşfetmek için: https://github.com/weepy/brequire . Buna Brequire denir (Tarayıcı gereksiniminin kısaltmasıdır).

Popülerliğe göre, varlık yöneticileri çoğu geliştiricinin ihtiyaçlarına uygundur. Bununla birlikte, CommonJS'nin tarayıcı uygulamasına ihtiyacınız varsa, Brequire muhtemelen tasarıyı uyacaktır.

2015 Güncellemesi: Artık Brequire kullanmıyorum (birkaç yıl içinde güncellenmedi). Sadece küçük, açık kaynaklı bir modül yazıyorsam ve herkesin kolayca kullanabilmesini istiyorsanız, Caolan'ın cevabına benzer bir desen izleyeceğim (yukarıda) - bunun hakkında birkaç yıl boyunca bir blog yazısı yazdım önce.

Ancak, özel kullanım veya CommonJS'de ( Ampersand topluluğu gibi) standartlaştırılmış bir topluluk için modüller yazıyorsanız, bunları sadece CommonJS formatında yazacağım ve Browserify kullanacağım .


1

now.js de bir göz atmaya değer. Sunucu tarafından istemci tarafından, istemci tarafında işlevleri sunucu tarafından çağırmanıza olanak tanır


1
Proje durduruldu - bunun için iyi bir değişiklik var mı? groups.google.com/forum/#!msg/nowjs/FZXWZr22vn8/UzTMPD0tdVQJ
Anderson Green

bildiğim diğer tek köprü ve aynı insanlar tarafından yapıldı, bu yüzden de terk edildi. Socket.io'nun 0.9 sürümü de etkinlikler için geri çağrıları destekler - ancak now.js'nin paylaşım kodu gibi bir şey değildir, ancak yeterince iyi çalışır.
balupton

Aktif olarak bakımı yapılan paylaşımlar da var. sharejs.org
Anderson Green

1

Tarayıcınızı Node.js benzeri bir tarzda yazmak istiyorsanız dualify'ı deneyebilirsiniz .

Tarayıcı kodu derlemesi yoktur, bu nedenle uygulamanızı sınırlama olmadan yazabilirsiniz.



1

Kullanım örneği: Uygulama yapılandırmanızı Node.js ve tarayıcı arasında paylaşın (bu yalnızca bir gösterimdir, muhtemelen uygulamanıza bağlı olarak en iyi yaklaşım değildir).

Sorun: kullanamazsınız window(Node.js'de globalmevcut değildir ) veya (tarayıcıda mevcut değildir).

Çözüm:

  • Config.js dosyası:

    var config = {
      foo: 'bar'
    };
    if (typeof module === 'object') module.exports = config;
  • Tarayıcıda (index.html):

    <script src="config.js"></script>
    <script src="myApp.js"></script>

    Artık geliştirme araçlarını açabilir ve global değişkene erişebilirsiniz. config

  • Node.js'de (app.js):

    const config = require('./config');
    console.log(config.foo); // Prints 'bar'
  • Babel veya TypeScript ile:

    import config from './config';
    console.log(config.foo); // Prints 'bar'

1
Bunun için teşekkür ederim.
Mikroz

Takip: Diyelim ki server.js ve client.js arasında paylaşılan iki dosyam var: shared.jsve helpers.js- shared.jsişlevlerini kullanıyor helpers.js, bu yüzden const { helperFunc } = require('./helpers')sunucu tarafında çalışması için en üstte olması gerekiyor . Sorun istemcide, requirebir işlev olmama hakkında şikayet ediyor , ancak ben gerekli satırı sarın if (typeof module === 'object') { ... }, sunucu helperFunc () tanımlanmadığını (if deyiminin dışında) diyor. Her ikisinde de çalışmasını sağlayacak herhangi bir fikir var mı?
Mikroz

Güncelleme: Bunu en üste yerleştirerek işe yaramış gibi görünüyor shared.js:helperFunc = (typeof exports === 'undefined') ? helperFunc : require('./helpers').helperFunc; - Maalesef ihraç edilen her işlev için bir satıra ihtiyacımız olacak ama umarım iyi bir çözüm mü?
Mikroz

1

Modülleri hem istemciden hem de sunucudan yüklemek için kullanabileceğiniz basit bir modül yazdım (ya Düğümde gereksinim ya da tarayıcıda komut dosyası etiketleri kullanarak).

Örnek kullanım

1. Modülün tanımlanması

Aşağıdakileri log2.jsstatik web dosyaları klasörünüzün içindeki bir dosyaya yerleştirin:

let exports = {};

exports.log2 = function(x) {
    if ( (typeof stdlib) !== 'undefined' )
        return stdlib.math.log(x) / stdlib.math.log(2);

    return Math.log(x) / Math.log(2);
};

return exports;

Bu kadar basit!

2. Modülü kullanma

Bir olduğundan ikili modül yükleyici, her iki tarafın (istemci ve sunucu) den yükleyebilirsiniz. Bu nedenle, aşağıdakileri yapabilirsiniz, ancak ikisini birden yapmanıza gerek yoktur (belirli bir sırayla bırakın):

  • Düğümde

Düğümde basit:

var loader = require('./mloader.js');
loader.setRoot('./web');

var logModule = loader.importModuleSync('log2.js');
console.log(logModule.log2(4));

Bu geri dönmeli 2.

Dosyanız Düğümün mevcut dizininde değilse, loader.setRoot , statik web dosyaları klasörünüzün yolunu (veya modülünüzün olduğu her yeri) .

  • Tarayıcıda:

İlk olarak, web sayfasını tanımlayın:

<html>
    <header>
        <meta charset="utf-8" />
        <title>Module Loader Availability Test</title>

        <script src="mloader.js"></script>
    </header>

    <body>
        <h1>Result</h1>
        <p id="result"><span style="color: #000088">Testing...</span></p>

        <script>
            let mod = loader.importModuleSync('./log2.js', 'log2');

            if ( mod.log2(8) === 3 && loader.importModuleSync('./log2.js', 'log2') === mod )
                document.getElementById('result').innerHTML = "Your browser supports bilateral modules!";

            else
                document.getElementById('result').innerHTML = "Your browser doesn't support bilateral modules.";
        </script>
    </body>
</html>

Dosyayı doğrudan tarayıcınızda açmadığınızdan emin olun ; AJAX kullandığından, Python 3'ün http.servermodülüne (veya süper hızlı, komut satırınıza, klasör web sunucusu dağıtım çözümünüz ne olursa olsun) bir göz atmanızı öneririm .

Her şey yolunda giderse, bu görünecektir:

resim açıklamasını buraya girin


0

Bunu yazdım, tüm değişkenleri küresel kapsamda ayarlamak istiyorsanız kullanımı basit:

(function(vars, global) {
    for (var i in vars) global[i] = vars[i];
})({
    abc: function() {
        ...
    },
    xyz: function() {
        ...
    }
}, typeof exports === "undefined" ? this : exports);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.