Browserify - วิธีเรียกใช้ฟังก์ชันที่รวมอยู่ในไฟล์ที่สร้างผ่าน browserify ในเบราว์เซอร์


96

ฉันยังใหม่กับ nodejs และ browserify ฉันเริ่มต้นด้วยลิงค์นี้

ฉันมีไฟล์ main.js ที่มีรหัสนี้

var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

ตอนนี้ฉันติดตั้งโมดูล uniq ด้วย npm:

 npm install uniq

จากนั้นฉันรวมโมดูลที่ต้องการทั้งหมดโดยเริ่มจาก main.js เป็นไฟล์เดียวที่เรียกว่า bundle.js ด้วยคำสั่ง browserify:

browserify main.js -o bundle.js

ไฟล์ที่สร้างขึ้นมีลักษณะดังนี้:

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

},{"uniq":2}],2:[function(require,module,exports){
"use strict"

function unique_pred(list, compare) {
  var ptr = 1
    , len = list.length
    , a=list[0], b=list[0]
  for(var i=1; i<len; ++i) {
    b = a
    a = list[i]
    if(compare(a, b)) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique_eq(list) {
  var ptr = 1
    , len = list.length
    , a=list[0], b = list[0]
  for(var i=1; i<len; ++i, b=a) {
    b = a
    a = list[i]
    if(a !== b) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique(list, compare, sorted) {
  if(list.length === 0) {
    return []
  }
  if(compare) {
    if(!sorted) {
      list.sort(compare)
    }
    return unique_pred(list, compare)
  }
  if(!sorted) {
    list.sort()
  }
  return unique_eq(list)
}

module.exports = unique
},{}]},{},[1])

หลังจากรวมไฟล์ bundle.js ลงในหน้า index.htm แล้วฉันจะเรียกใช้ฟังก์ชัน logData ได้อย่างไร?


คุณต้องการเรียกมันว่าที่ไหน? แล้วทำไมถึงเรียกมันว่า?
artur grzesiak

2
@arturgrzesiak: ฉันต้องการใช้ฟังก์ชันนี้ในโปรเจ็กต์อื่นของฉันซึ่งฉันจะใช้งานในเบราว์เซอร์
SharpCoder

คำตอบ:


83

ตามค่าเริ่มต้น browserify จะไม่อนุญาตให้คุณเข้าถึงโมดูลจากภายนอกโค้ดที่เป็นเบราว์เซอร์หากคุณต้องการเรียกรหัสในโมดูลที่เป็นเบราว์เซอร์คุณควรจะเบราว์เซอร์โค้ดของคุณร่วมกับโมดูล ดูhttp://browserify.org/สำหรับตัวอย่างของสิ่งนั้น

แน่นอนคุณสามารถทำให้วิธีการของคุณสามารถเข้าถึงได้จากภายนอกอย่างชัดเจนเช่นนี้:

window.LogData =function(){
  console.log(unique(data));
};

จากนั้นคุณสามารถโทรได้LogData()จากที่อื่นในหน้านี้


1
ขอบคุณ. นี้ได้ผล หมายความว่าในขณะที่สร้างฟังก์ชันแทนที่จะพูด this.functionName ฉันควรเขียน window.functionName? เรามีงานอื่น ๆ เกี่ยวกับเรื่องนี้หรือไม่? เหตุผลใดในการใช้ window.functionName?
SharpCoder

21
"คุณควรจะเบราว์เซอร์โค้ดของคุณร่วมกับโมดูล" - ฮึฉันจะทำอะไรonclick="someFunction()"ได้บ้าง คุณไม่อาจเถียงได้ว่านั่นเป็นกรณีการใช้งานที่หายาก!?!
BlueRaja - Danny Pflughoeft

57
ไม่มีเอกสารประกอบอย่างจริงจังสำหรับผู้เริ่มต้นเกี่ยวกับวิธีใช้ Browserify บนไคลเอนต์
Oliver Dixon

1
ใช่เอกสารควรระบุอย่างชัดเจนว่านี่คือการตัดสินใจออกแบบที่ควรหลีกเลี่ยง แต่ระบุเส้นทางที่ชัดเจนเพื่อให้ใช้งานได้เมื่อคุณไม่มีทางเลือกอื่น (ในกรณีของฉันใช้ข้อมูลจากเทมเพลตเพื่อเติมข้อมูลวัตถุ JS) ... ขอบคุณ @thejh ที่ชี้แนวทางง่ายๆ! ;)
Alexandre Martini

1
ฉันไม่สามารถแม้แต่จะนึกถึงสถานการณ์ที่คุณไม่ต้องการให้ฟังก์ชันหลักของคุณใช้งานได้นอกโมดูล นี่ไม่ใช่พฤติกรรมเริ่มต้นอย่างไร เว็บแอปพลิเคชันแบบใดที่ไม่เรียกใช้ฟังก์ชัน
Cybernetic

101

ส่วนสำคัญของการรวมโมดูลแบบสแตนด์อโลนเข้ากับ Browserify คือ--sตัวเลือก มันแสดงสิ่งที่คุณส่งออกจากโมดูลของคุณโดยใช้โหนดmodule.exportsเป็นตัวแปรส่วนกลาง จากนั้นไฟล์จะรวมอยู่ใน<script>แท็กได้

คุณจะต้องทำสิ่งนี้ก็ต่อเมื่อคุณต้องการให้ตัวแปรส่วนกลางนั้นเปิดเผยด้วยเหตุผลบางประการ ในกรณีของฉันลูกค้าต้องการโมดูลแบบสแตนด์อโลนที่สามารถรวมไว้ในหน้าเว็บโดยที่พวกเขาไม่จำเป็นต้องกังวลเกี่ยวกับธุรกิจ Browserify นี้

นี่คือตัวอย่างที่เราใช้--sตัวเลือกกับอาร์กิวเมนต์module:

browserify index.js --s module > dist/module.js

moduleนี้จะเปิดเผยโมดูลของเราเป็นตัวแปรทั่วโลกชื่อ แหล่ง

อัปเดต: ขอบคุณ @fotinakis --standalone your-module-nameให้แน่ใจว่าคุณกำลังผ่าน หากคุณลืมว่า--standaloneใช้อาร์กิวเมนต์ Browserify อาจสร้างโมดูลว่างเปล่าขึ้นมาเนื่องจากไม่พบ

หวังว่านี่จะช่วยคุณประหยัดเวลาได้บ้าง


2
ฉันกำลังพยายามเบราว์เซอร์โค้ด ES6 ที่ไม่เป็นอันตราย แต่วัตถุแบบสแตนด์อโลนว่างเปล่าเมื่อฉันพยายามคอนโซลในเบราว์เซอร์ รหัส ES6 อย่างง่ายโดยไม่มีโมดูลใด ๆ ทำงานได้ดีในโหมดสแตนด์อโลน มีคำแนะนำเกี่ยวกับเรื่องนี้หรือไม่?
John

@jackyrudetsky ไม่รู้ฉันอยากจะแนะนำให้เพิ่มคำถามเกี่ยวกับ SO ดูเหมือนเป็นปัญหาที่น่าสนใจ อาจเกี่ยวข้องกับสิ่งนี้ github.com/substack/node-browserify/issues/1357
Matas Vaitkevicius

1
@fotinakis จริงๆแล้วมันเป็นปัญหาใน Browserify github.com/substack/node-browserify/issues/1537
John

3
IMO นี่ควรเป็นคำตอบที่ยอมรับได้ หากคุณใช้ฟังก์ชันส่วนกลางการมีเนมสเปซของคุณเองจะดีกว่าการแขวนทุกฟังก์ชันไว้นอกหน้าต่าง
VictorB

1
@VictorB ตัวแปรส่วนกลางทั้งหมดใน Javascript เป็นองค์ประกอบของหน้าต่างดังนั้นทั้งสองวิธีจึงบรรลุสิ่งเดียวกัน (การเพิ่มตัวแปรส่วนกลางลงในหน้าต่าง)
David Lopez

37

คำตอบของ @Matas Vaitkevicius พร้อมตัวเลือกแบบสแตนด์อโลนของ Browserifyนั้นถูกต้อง ( คำตอบของ @ thejh โดยใช้ตัวแปร global ของหน้าต่างก็ใช้ได้เช่นกัน แต่อย่างที่คนอื่น ๆ ตั้งข้อสังเกตมันก่อให้เกิดเนมสเปซทั่วโลกดังนั้นจึงไม่เหมาะ) ฉันต้องการเพิ่มรายละเอียดเพิ่มเติมเล็กน้อยเกี่ยวกับวิธีใช้ตัวเลือกแบบสแตนด์อโลน

ในสคริปต์ต้นทางที่คุณต้องการรวมกลุ่มตรวจสอบให้แน่ใจว่าได้เปิดเผยฟังก์ชันที่คุณต้องการเรียกใช้ผ่าน module.exports ในสคริปต์ไคลเอนต์คุณสามารถเรียกฟังก์ชั่นสัมผัสเหล่านี้ผ่านทาง<กำชื่อ>. <func ชื่อ> นี่คือตัวอย่าง:

ไฟล์ต้นทางของฉันsrc / script.jsจะมีสิ่งนี้:
module.exports = {myFunc: func};

คำสั่ง browserifyของฉันจะมีลักษณะดังนี้:
browserify src/script.js --standalone myBundle > dist/bundle.js

และสคริปต์ไคลเอ็นต์ของฉันdist / client.jsจะโหลดสคริปต์ที่แถมมา
<script src="bundle.js"></script>
จากนั้นเรียกใช้ฟังก์ชันที่เปิดเผยดังนี้:
<script>myBundle.myFunc();</script>


ไม่จำเป็นต้องกำหนดชื่อบันเดิลในสคริปต์ไคลเอนต์ก่อนที่จะเรียกใช้ฟังก์ชันที่เปิดเผยเช่น<script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script>ไม่จำเป็นและจะไม่ทำงาน

ในความเป็นจริงเช่นเดียวกับฟังก์ชันทั้งหมดที่มาพร้อมกับ browserify ที่ไม่มีโหมดสแตนด์อโลนฟังก์ชันที่ต้องการจะไม่สามารถใช้งานได้นอกสคริปต์ที่รวมอยู่ Browserify อนุญาตให้คุณใช้ฟังก์ชันโหนดฝั่งไคลเอ็นต์ แต่เฉพาะในสคริปต์ที่แถมมาเท่านั้น ไม่ได้หมายถึงการสร้างโมดูลแบบสแตนด์อโลนที่คุณสามารถนำเข้าและใช้ที่ใดก็ได้ในฝั่งไคลเอ็นต์นั่นคือเหตุผลที่เราต้องแก้ไขปัญหาพิเศษทั้งหมดนี้เพียงเพื่อเรียกใช้ฟังก์ชันเดียวนอกบริบทที่รวมไว้


3
ว้าว! ในที่สุดก็เป็นตัวอย่างที่ใช้ได้จริง
N73k

1
ตัวอย่างที่ดี แต่ตราบใดที่ "ก่อให้เกิดมลพิษต่อเนมสเปซทั่วโลกดังนั้นจึงไม่เหมาะ" จะไม่เป็นไปตามอัตโนมัติก็อาจยอมรับได้หากเป็นเพียงฟังก์ชันเดียว เพียงแค่สูบบุหรี่และกระจกแม้myBundleจะติดกับวัตถุหน้าต่าง window.myBundle.myFunc()แทนที่จะเป็น window.myFunc ()
ร่วม

1
ควรมีคะแนนพิเศษสำหรับผู้ที่ยกตัวอย่างแบบ end-to-end
Sharud

นั่นคือวิธีการเขียนเอกสาร
Ellery Leung

8

ฉันเพิ่งอ่านคำตอบและดูเหมือนว่าไม่มีใครพูดถึงการใช้ขอบเขตตัวแปรทั่วโลก? ซึ่งมีประโยชน์หากคุณต้องการใช้รหัสเดียวกันใน node.js และในเบราว์เซอร์

class Test
{
  constructor()
  {
  }
}
global.TestClass = Test;

จากนั้นคุณสามารถเข้าถึงTestClassได้ทุกที่

<script src="bundle.js"></script>
<script>
var test = new TestClass(); // Enjoy!
</script>

หมายเหตุ:จากนั้น TestClass จะพร้อมใช้งานทุกที่ ซึ่งเหมือนกับการใช้ตัวแปรหน้าต่าง

นอกจากนี้คุณสามารถสร้างมัณฑนากรที่เปิดเผยคลาสไปยังขอบเขตส่วนกลาง ซึ่งดีมาก แต่ทำให้ยากที่จะติดตามว่าตัวแปรถูกกำหนดไว้ที่ไหน


อย่างที่คุณพูดเองการเพิ่มฟังก์ชั่นเพื่อglobalสร้างเอฟเฟกต์เดียวกันกับการเพิ่มเข้าไปwindowซึ่ง thejh ครอบคลุมอยู่แล้ว คำตอบนี้ไม่ได้เพิ่มข้อมูลใหม่
Galen Long

@GalenLong บางทีคุณอาจลืมไปว่าไม่มีตัวแปรหน้าต่างใน node.js? และบางไลบรารีที่กำหนดเป้าหมายโหนดและเบราว์เซอร์อาจต้องการใช้โกลบอลแทน คำตอบของฉันได้รับการโหวตเพิ่มขึ้นเล็กน้อยและยังไม่ได้ลบดังนั้นฉันคิดว่ามันเป็นข้อมูลสำหรับคนอื่น ๆ ถ้าไม่ใช่สำหรับคุณ
DDD

คุณพูดถูก @Azarus มีคำตอบที่ซ้ำกันสองคำตอบในหน้านี้และฉันรวมคำตอบของคุณไว้ในพวงไม่ถูกต้อง ขอโทษด้วย.
Galen Long

เพียงต้องการทราบว่า parens แขวนที่นี่เป็นวิธีปฏิบัติที่ไม่ดีมากสำหรับจาวาสคริปต์เช่นใช้รูปแบบนี้กับคีย์เวิร์ด return และเตรียมร้องไห้ เช่นreturn {}แต่ปล่อยปีกกาเปิดลงไปที่บรรทัดถัดไป
Sgnl

1
@Azarus ฉันสร้างซอเพื่อแสดงให้เห็นว่าฉันหมายถึงอะไร - jsfiddle.net/cubaksot/1
Sgnl

6

อ่าน README.md ของ browserify เกี่ยวกับ--standaloneพารามิเตอร์หรือ google "browserify umd"


19
นี่เป็นคำแนะนำในการหาคำตอบมากกว่าคำตอบ
user2314737

สิ่งนี้นำฉันไปสู่โซลูชันที่ฉันกำลังมองหาเป็นเวลาสองวัน (วิธีใช้ browserify output จากสภาพแวดล้อม require.js) ขอบคุณ!
Flion

2

เพื่อให้ฟังก์ชันของคุณพร้อมใช้งานทั้งจาก HTML และจากโหนดฝั่งเซิร์ฟเวอร์:

main.js:

var unique = require('uniq');

function myFunction() {
    var data = [1, 2, 2, 4, 3];
    return unique(data).toString();
}
console.log ( myFunction() );

// When browserified - we can't call myFunction() from the HTML, so we'll externalize myExtFunction()
// On the server-side "window" is undef. so we hide it.
if (typeof window !== 'undefined') {
    window.myExtFunction = function() {
        return myFunction();
    }
}

main.html:

<html>
    <head>
        <script type='text/javascript' src="bundle.js"></script>
    <head>
    <body>
        Result: <span id="demo"></span>
        <script>document.getElementById("demo").innerHTML = myExtFunction();</script>
    </body>
</html>

วิ่ง:

npm install uniq
browserify main.js > bundle.js

และคุณควรได้ผลลัพธ์เดียวกันเมื่อเปิด main.html ในเบราว์เซอร์เหมือนกับเมื่อทำงาน

node main.js

2

ตัวอย่างที่รันได้น้อยที่สุด

โดยพื้นฐานแล้วจะเหมือนกับ: https://stackoverflow.com/a/43215928/895245แต่มีไฟล์ที่เป็นรูปธรรมซึ่งจะช่วยให้คุณสามารถเรียกใช้และทำซ้ำได้อย่างง่ายดายด้วยตัวคุณเอง

รหัสนี้มีให้ที่: https://github.com/cirosantilli/browserify-hello-world

index.js

const uniq = require('uniq');

function myfunc() {
  return uniq([1, 2, 2, 3]).join(' ');
}
exports.myfunc = myfunc;

index.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Browserify hello world</title>
</head>
<body>
<div id="container">
</body>
</div>
<script src="out.js"></script>
<script>
document.getElementById('container').innerHTML = browserify_hello_world.myfunc();
</script>
</html>

การใช้งาน Node.js:

#!/usr/bin/env node

const browserify_hello_world = require('./index.js');

console.log(browserify_hello_world.myfunc());

สร้างout.jsสำหรับการใช้งานเบราว์เซอร์:

npx browserify --outfile out.js --standalone browserify_hello_world index.js

ทั้งเบราว์เซอร์และบรรทัดคำสั่งแสดงผลลัพธ์ที่คาดหวัง:

1 2 3

ทดสอบกับ Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10


1
exports.myfunc.= myfuncส่วนหนึ่งของเรื่องนี้เป็นความสำคัญอย่างยิ่งและพลาดในคำตอบอื่น ๆ
parttimeturtle

2

มันง่ายมาก - แนวคิดทั้งหมดนี้เกี่ยวกับการห่อ

1. ทางเลือก - วัตถุ "นี้"

เพื่อจุดประสงค์นี้ฉันจะถือว่าคุณมี "เพียง 1 สคริปต์สำหรับทั้งแอป {{app_name}}" และ "1 ฟังก์ชัน {{function_name}}"

เพิ่มฟังก์ชัน {{function_name}} เพื่อคัดค้าน "this"

function {{function_name}}(param) {}
->
this.{{function_name}} = function(param) {}

จากนั้นคุณต้องตั้งชื่อวัตถุนั้นให้พร้อมใช้งาน - คุณจะเพิ่มพารามิเตอร์ "standalone with name" เหมือนที่คนอื่นแนะนำ

ดังนั้นหากคุณใช้"watchify" กับ "browserify" ให้ใช้สิ่งนี้

var b = browserify({
    ...
    standalone: '{{app_name}}'
});

หรือบรรทัดคำสั่ง

browserify index.js --standalone {{app_name}} > index-bundle.js

จากนั้นคุณสามารถเรียกใช้ฟังก์ชันของคุณจากเบราว์เซอร์

{{app_name}}.{{function_name}}(param);
window.{{app_name}}.{{function_name}}(param);

2. ทางเลือก - วัตถุ "หน้าต่าง"

เพิ่มฟังก์ชัน {{function_name}} ให้กับ object "window"

function {{function_name}}(param) {}
->
window.{{function_name}} = function(param) {}

จากนั้นคุณสามารถเรียกใช้ฟังก์ชันของคุณจากเบราว์เซอร์

{{function_name}}(param);
window.{{function_name}}(param);

-

บางทีฉันอาจจะช่วยใครสักคน


1

คุณมีทางเลือกไม่กี่ทาง:

  1. ให้ปลั๊กอินbrowserify-bridgeส่งออกโมดูลไปยังโมดูลรายการที่สร้างขึ้นโดยอัตโนมัติ สิ่งนี้มีประโยชน์สำหรับโครงการ SDK หรือสถานการณ์ที่คุณไม่ต้องติดตามสิ่งที่ส่งออกด้วยตนเอง

  2. ทำตามรูปแบบเนมสเปซหลอกสำหรับการเปิดรับภาพรวม:

ขั้นแรกจัดเรียงไลบรารีของคุณเช่นนี้โดยใช้ประโยชน์จากการค้นหาดัชนีในโฟลเดอร์:

/src
--entry.js
--/helpers
--- index.js
--- someHelper.js
--/providers
--- index.js
--- someProvider.js
...

ด้วยรูปแบบนี้คุณกำหนดรายการดังนี้:

exports.Helpers = require('./helpers');
exports.Providers = require('./providers');
...

สังเกตว่าจำเป็นต้องโหลด index.js โดยอัตโนมัติจากแต่ละโฟลเดอร์ย่อยที่เกี่ยวข้อง

ในโฟลเดอร์ย่อยของคุณคุณสามารถรวมรายการที่คล้ายกันของโมดูลที่มีอยู่ในบริบทนั้น:

exports.SomeHelper = require('./someHelper');

รูปแบบนี้ปรับขนาดได้ดีมากและช่วยให้สามารถติดตามบริบท (โฟลเดอร์ตามโฟลเดอร์) ของสิ่งที่จะรวมไว้ใน API แบบรวม


-1
window.LogData =function(data){
   return unique(data);
};

เรียกใช้ฟังก์ชันง่ายๆโดย LogData(data)

นี่เป็นเพียงการปรับเปลี่ยนเล็กน้อยสำหรับคำตอบของ thejhแต่เป็นสิ่งที่สำคัญ


การแก้ไขนี้ไม่เกี่ยวข้องกับข้อกังวลของผู้ถามและไม่ได้เพิ่มข้อมูลใหม่ใด ๆ จากคำตอบที่มีอยู่แล้ว
Galen Long

-2

เพื่อวัตถุประสงค์ในการดีบักฉันได้เพิ่มบรรทัดนี้ใน code.js ของฉัน:

window.e = function(data) {eval(data);};

จากนั้นฉันสามารถเรียกใช้อะไรก็ได้แม้จะอยู่นอกกลุ่ม

e("anything();");
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.