Node.js와 브라우저간에 코드를 공유하려면 어떻게해야합니까?


242

WebSocket을 사용하여 통신하는 JavaScript 클라이언트 (브라우저에서 실행)와 Node.js 서버로 작은 응용 프로그램을 만들고 있습니다.

클라이언트와 서버간에 코드를 공유하고 싶습니다. 방금 Node.js로 시작했으며 현대 JavaScript에 대한 내 지식은 조금 녹슨 것입니다. 그래서 나는 여전히 CommonJS require () 함수를 둘러보고 있습니다. 'export'객체를 사용하여 패키지를 작성하는 경우 브라우저에서 동일한 JavaScript 파일을 사용하는 방법을 볼 수 없습니다.

메시지 인코딩 및 디코딩, 기타 미러링 된 작업을 용이하게하기 위해 양쪽 끝에 사용되는 메서드 및 클래스 집합을 만들고 싶습니다. 그러나 Node.js / CommonJS 패키징 시스템은 양면에서 사용할 수있는 JavaScript 파일을 만드는 것을 방해합니다.

또한 JS.Class를 사용하여 더 단단한 OO 모델을 얻으려고 시도했지만 제공된 JavaScript 파일을 require ()와 함께 작동시키는 방법을 알 수 없기 때문에 포기했습니다. 내가 여기서 놓친 것이 있습니까?


4
이 질문에 대한 답변을 추가해 주셔서 감사합니다. 이것은 빠르게 변화하고 진화 할 주제입니다.
Simon Cave

답변:


168

당신이 두 클라이언트 측 및 서버 측을 사용할 수있는 모듈을 작성하려면, 나는 빠르고 쉬운 방법에 관한 짧은 블로그 게시물이 : 쓰기는 Node.js를 브라우저에 대한 본질적 다음은 (여기서 this과 동일 window) :

(function(exports){

    // Your code goes here

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

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

또는 Marak의 gemini 와 같이 클라이언트 측에서 Node.js API를 구현하려는 일부 프로젝트가 있습니다 .

간단한 JSON 기반 네트워크 프로토콜을 사용하여 다른 시스템에서 JavaScript 함수를 호출 할 수 있도록 JavaScript 함수를 노출 할 수 있는 DNode에 관심이있을 수도 있습니다 .


우수한. 정보 감사합니다, Caolan.
Simon Cave

2
정말 좋은 기사 Caolan. 나는 그것을 이해했고, 효과가 있었고, 나는 다시 굴러 가고있다. 환상적인!
Michael Dausmann

2
내 자신의 프로젝트에서 RequireJs 를 사용 하고 있으므로 클라이언트와 서버에서 모듈을 공유 할 수 있습니다. 우리는 그것이 어떻게 작동하는지 볼 것입니다.
kamranicus

5
링크가 죽었다 @Caolan
Kamal Reddy

5
gemini 링크가 종료되었습니다.
borisdiakur

42

Epeli는 http://epeli.github.com/piler/ 여기 에 라이브러리 없이도 작동 하는 멋진 솔루션을 제공 합니다. 이것을 share.js라는 파일에 넣으십시오.

(function(exports){

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

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

서버 측에서는 다음을 사용하십시오.

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

share.test();

그리고 클라이언트 측에서 js 파일을로드 한 다음 사용하십시오.

share.test();

10
나는이 답변이 허용 된 답변보다 더 좋아서 나 같은 초보자에게 더 잘 설명되어 있기 때문입니다.
Howie

정적 (공용) 폴더 이외의 Express 폴더에는 'public'이라는 폴더도 있습니다.이 폴더는 'public'폴더와 같이 클라이언트에서도 액세스 할 수 있습니다 : app.use (express.static ( 'public')) ; app.use (express.static ( 'shared')); 그리고 귀하의 게시물은 클라이언트 및 서버와 파일을 공유한다는 아이디어를 확장합니다. 이것이 바로 내가 필요한 것입니다. 감사합니다!
결합

이 솔루션 + 자식 하위 트리 == 굉장합니다. 감사!
kevinmicke

@ broesch ES6에서 어떻게 작동합니까? ES6 관련 문제가 있는 새로운 질문 으로이 질문을 했지만 여기 편집 내용을 보게되어 기쁩니다!
Tedskovsky

15

Node.js 모듈 패턴, AMD 모듈 패턴 및 브라우저에서 전역 적으로 작동하도록하는 jQuery 소스 코드를 확인하십시오.

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

이것이 가장 좋은 방법입니다 (필요한 것). 다음은 내가 만든 실제 예제입니다. gist.github.com/drmikecrowe/4bf0938ea73bf704790f
Mike Crowe

13

JavaScript 함수의 문자열 표현은 해당 함수의 소스 코드를 나타냅니다. 함수와 생성자를 캡슐화 된 방식으로 작성하여 toString ()하고 클라이언트로 보낼 수 있습니다.

또 다른 방법은 빌드 시스템을 사용하고 공통 코드를 별도의 파일에 넣은 다음 서버와 클라이언트 스크립트 모두에 포함시키는 것입니다. 서버와 클라이언트가 본질적으로 동일한 게임 루프를 실행하고 클라이언트가 서버와 동기화하여 아무도 부정 행위를하지 않는 WebSockets를 통해 간단한 클라이언트 / 서버 게임 에이 접근법을 사용하고 있습니다.

게임의 빌드 시스템 은 C 전처리기를 통해 파일을 실행 한 다음 sed를 통해 정크 cpp 잎을 정리 하는 간단한 Bash 스크립트이므로 #include, #define, #ifdef와 같은 일반적인 전 처리기 항목을 모두 사용할 수 있습니다 등


2
문자열로 자바 스크립트 함수를 직렬화하면 나에게 결코 발생하지 않았습니다. 팁 고마워.
Simon Cave

13

Node.js 용 RequireJS 어댑터를 살펴 보는 것이 좋습니다 . 문제는 기본적으로 Node.js가 사용하는 CommonJS 모듈 패턴이 비동기 적이 지 않으므로 웹 브라우저에서로드를 차단한다는 것입니다. RequireJS는 어댑터를 사용하는 한 비동기 및 서버 및 클라이언트와 호환되는 AMD 패턴을 사용합니다 r.js.


비동기 라이브러리가있다
제이 섹 Pietal

11

어쩌면 이것이 질문과 완전히 일치하지는 않지만 공유 할 것이라고 생각했습니다.

String.prototype에 선언 된 두 개의 간단한 문자열 유틸리티 함수를 노드와 브라우저 모두에서 사용할 수 있도록 만들고 싶었습니다. 이 함수를 간단히 utilities.js (하위 폴더에있는) 파일에 보관하고 브라우저 코드의 스크립트 태그와 Node.js 스크립트에서 require (.js 확장자 생략)를 사용하여 쉽게 참조 할 수 있습니다. :

my_node_script.js

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

my_browser_code.html

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

이것이 다른 사람에게 유용한 정보가되기를 바랍니다.


1
이 방법이 마음에 들지만 정적 파일이 많이 움직입니다. 내가 찾은 한 가지 해결책은 모듈을 다시 내보내는 것입니다. 예를 들어, utilites.js한 줄로 작성하십시오 module.exports = require('./static/js/utilities');. 이런 식으로 주변을 뒤섞 으면 하나의 경로 만 업데이트하면됩니다.
Tom Makin

나는이 아이디어를 좋아한다. 알아내는 데 시간이 걸렸던 길에 대한 메모. 내가 utilities.js있는 shared프로젝트에서 폴더. 사용 require('/shared/utilities')하면 오류가 발생했습니다 Cannot find module '/shared/utilities'. require('./../../shared/utilities')작동하려면 이와 같은 것을 사용해야 합니다. 따라서 항상 현재 폴더에서 루트로 이동 한 다음 아래로 이동합니다.
newman

이제 정적 폴더에서 공유 모듈을 배치 할 위치를 봅니다. 정보 주셔서 감사합니다!
결합

9

브라우저에서 사용하기 위해 webpack 과 같은 모듈 번 들러 를 사용하여 JavaScript 파일을 번들로 제공하는 경우 브라우저에서 실행되는 프론트 엔드에 Node.js 모듈을 재사용하면됩니다. 즉, Node.js 모듈은 Node.js와 브라우저간에 공유 될 수 있습니다.

예를 들어 다음 코드 sum.js가 있습니다.

일반 Node.js 모듈 : sum.js

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

module.exports = sum

Node.js에서 모듈 사용

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

프론트 엔드에서 재사용

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

4

서버는 단순히 JavaScript 소스 파일을 클라이언트 (브라우저)에 보낼 수 있지만, 클라이언트가 미니 "내보내기"환경을 제공해야합니다. exec 코드를 작성하고 모듈로 저장 .

그러한 환경을 만드는 간단한 방법은 클로저를 사용하는 것입니다. 예를 들어, 서버가 HTTP와 같은 소스 파일을 제공한다고 가정하십시오 http://example.com/js/foo.js. 브라우저는 XMLHttpRequest를 통해 필요한 파일을로드하고 다음과 같이 코드를로드 할 수 있습니다.

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

핵심은 클라이언트가 외부 코드를 익명 함수로 래핑하여 즉시 실행 (닫기)하여 "내보내기"개체를 생성하고 반환함으로써 전역 네임 스페이스를 오염시키지 않고 원하는 곳에 할당 할 수 있다는 것입니다. 이 예제에서는 fooModule파일에서 내 보낸 코드를 포함 할 window 속성에 할당 됩니다 foo.js.


2
eval을 사용할 때마다 그놈을 죽입니다
Jacek Pietal

1
사용할 것 window.fooModule = {}; (new Function('exports', xhr.responseText))(window.fooModule)입니다.
GingerPlusPlus

2

이전 솔루션 중 어느 것도 CommonJS 모듈 시스템을 브라우저로 가져 오지 않습니다.

다른 답변에서 언급했듯이 Browserify 와 같은 자산 관리자 / 포장 솔루션이 있습니다. 또는 펠리은 과 같은 RPC 솔루션이 있습니다 dnode 또는 nowjs은 .

그러나 브라우저에 대한 CommonJS 구현 ( require()기능 및 exports/ module.exports객체 등 포함)을 찾을 수 없습니다 . : 나는 내 자신을 썼다 그래서, 단지 다른 사람이 더 나은 내가 가진 것보다 그것을 쓴 것을 나중에 발견 할 https://github.com/weepy/brequire . 이를 Brequire (브라우저 요구의 약자)라고합니다.

인기도에 따라 자산 관리자는 대부분의 개발자의 요구를 충족시킵니다. 그러나 CommonJS의 브라우저 구현이 필요한 경우 Brequire 가 청구서에 적합 할 것입니다.

2015 년 업데이트 : 더 이상 Brequire를 사용하지 않습니다 (몇 년 동안 업데이트되지 않았습니다). 작은 오픈 소스 모듈을 작성하고 누구나 쉽게 사용할 수 있기를 원한다면 Caolan의 답변과 유사한 패턴을 따라야합니다 (위) .2 년 동안 블로그 게시물을 썼습니다 전에.

그러나 개인적으로 사용하거나 Ampersand 커뮤니티 와 같이 CommonJS에서 표준화 된 커뮤니티를 위해 모듈을 작성하는 경우 CommonJS 형식으로 작성하고 Browserify를 사용 합니다 .


1

now.js 도 살펴볼 가치가 있습니다. 클라이언트 측에서 서버 측을 호출하고 서버 측에서 클라이언트 측 기능을 호출 할 수 있습니다.


1
프로젝트가 중단되었습니다. 좋은 대체품을 알고 있습니까? groups.google.com/forum/#!msg/nowjs/FZXWZr22vn8/UzTMPD0tdVQJ
Anderson Green

내가 아는 유일한 것은 다리 였고 같은 사람들에 의한 것이었다. socket.io의 0.9 버전은 이벤트에 대한 콜백도 지원하지만 now.js의 공유 코드와 같은 것은 아니지만 충분히 작동합니다.
balupton

sharejs도 있으며 적극적으로 유지 관리되고 있습니다. sharejs.org
앤더슨 그린

1

Node.js와 같은 스타일로 브라우저를 작성하려면 dualify 을 사용해보십시오 .

브라우저 코드 컴파일이 없으므로 제한없이 애플리케이션을 작성할 수 있습니다.


1

코드를 RequireJS 모듈 로 작성 하고 테스트를 Jasmine 테스트 로 작성하십시오 .

이 방법으로 RequireJS를 사용하여 코드를 어디서나로드 할 수 있으며 jasmine-html 및 jasmine-node 를 사용하여 브라우저에서 테스트를 실행할 수 있습니다. 코드 또는 테스트를 수정하지 않고도 Node.js의 를 사용 .

다음은 이에 대한 실제 예 입니다.


1

사용 사례 : Node.js와 브라우저간에 앱 구성을 공유합니다 (이것은 단지 예시 일 뿐이며 앱에 따라 최선의 방법은 아닙니다).

문제점 : 사용할 수 없습니다 window(Node.js에 존재하지 않음)global (브라우저에 존재하지 않음)

해결책:

  • 파일 config.js :

    var config = {
      foo: 'bar'
    };
    if (typeof module === 'object') module.exports = config;
  • 브라우저 (index.html)에서 :

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

    이제 개발 도구를 열고 전역 변수에 액세스 할 수 있습니다 config

  • Node.js (app.js)에서 :

    const config = require('./config');
    console.log(config.foo); // Prints 'bar'
  • Babel 또는 TypeScript를 사용하는 경우 :

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

1
감사합니다.
Microsis September

후속 :하자의 내가 server.js과 client.js간에 공유되는 두 개의 파일이 있다고 가정 : shared.jshelpers.js- shared.js에서 사용하는 기능 helpers.js이 필요하므로, const { helperFunc } = require('./helpers')작업 서버 측에 대한, 상단에. 문제는 클라이언트 require에 있으며 함수 가 아닌 것에 대해 불평 하지만 require 라인을에 줄 바꿈하면 if (typeof module === 'object') { ... }서버는 helperFunc ()가 정의되지 않았다고 말합니다 (if 문 외부). 두 가지 모두 작동시킬 아이디어가 있습니까?
Microsis

업데이트 : 이것을 맨 위에 배치하여 작동시키는 것 같습니다 shared.js: helperFunc = (typeof exports === 'undefined') ? helperFunc : require('./helpers').helperFunc;-불행히도 내 보낸 각 기능에 대해 줄이 필요하지만 좋은 해결책이 되길 바랍니다.
Microsis

1

나는 간단한 모듈을 썼다 은 클라이언트에서 서버에서 모두로드 모듈에 사용할 수있는, (두 노드에 필요하거나 브라우저에서 스크립트 태그 사용) 가져올 수 있습니다.

사용법 예

1. 모듈 정의

log2.js정적 웹 파일 폴더 안에 있는 파일에 다음을 배치하십시오.

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;

그렇게 간단합니다!

2. 모듈 사용

그것이이기 때문에 양자 모듈 로더, 우리는 양쪽 (클라이언트 및 서버)에서로드 할 수 있습니다. 따라서 다음을 수행 할 수 있지만 한 번에 둘 다 수행 할 필요는 없습니다 (특정 순서대로 단독으로 수행).

  • 노드에서

노드에서는 간단합니다.

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

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

이 반환해야합니다 2 .

파일이 노드의 현재 디렉토리에 없으면 loader.setRoot정적 웹 파일 폴더의 경로 (또는 모듈이있는 곳) 를 호출해야합니다 .

  • 브라우저에서 :

먼저 웹 페이지를 정의하십시오.

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

당신이 있는지 확인 하지 않는 브라우저에서 직접 파일을 열; AJAX를 사용하기 때문에 대신 Python 3의 http.server모듈 (또는 초고속, 명령 줄, 폴더 웹 서버 배포 솔루션이 무엇이든)을 살펴 보는 것이 좋습니다 .

모든 것이 잘되면 다음과 같이 나타납니다.

여기에 이미지 설명을 입력하십시오


0

나는 이것을 작성했다. 모든 변수를 전역 범위로 설정하려면 사용하는 것이 간단하다.

(function(vars, global) {
    for (var i in vars) global[i] = vars[i];
})({
    abc: function() {
        ...
    },
    xyz: function() {
        ...
    }
}, typeof exports === "undefined" ? this : exports);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.