Làm thế nào tôi có thể nhập điều kiện một mô-đun ES6?


192

Tôi cần phải làm một cái gì đó như:

if (condition) {
    import something from 'something';
}
// ...
if (something) {
    something.doStuff();
}

Các mã trên không biên dịch; nó ném SyntaxError: ... 'import' and 'export' may only appear at the top level.

Tôi đã thử sử dụng System.importnhư hiển thị ở đây , nhưng tôi không biết từ đâu Systemđến. Đây có phải là một đề xuất ES6 cuối cùng không được chấp nhận? Liên kết đến "API lập trình" từ bài viết đó đưa tôi đến trang tài liệu không dùng nữa .


Chỉ cần nhập nó bình thường. Mô-đun của bạn cần nó bất kể.
Andy

Tôi thực sự không thấy bất kỳ lý do tại sao bạn sẽ không nhập bất kể điều kiện. Nó không giống như có một số loại trên cao. Trong một số trường hợp, bạn cần tệp, vì vậy nó không giống như trường hợp có thể bỏ qua hoàn toàn. Trong trường hợp đó, chỉ cần nhập nó vô điều kiện.
Cảm ơn bạn

8
Trường hợp sử dụng của tôi: Tôi muốn làm cho nó dễ dàng có một phụ thuộc tùy chọn. Nếu không cần dep, người dùng sẽ xóa nó khỏi package.json; gulpfilesau đó tôi kiểm tra xem sự phụ thuộc đó có tồn tại trước khi thực hiện một số bước xây dựng không.
ericsoco

1
Một trường hợp sử dụng khác: cho mục đích thử nghiệm. Tôi đang sử dụng webpackbabelchuyển mã es6 sang es5. Các dự án webpack-rewiretương tự và tương tự không phải là để giúp đỡ ở đây - github.com/jhnns/rewire-webpack/issues/12 . Một cách để đặt kiểm tra nhân đôi HOẶC để loại bỏ các phụ thuộc có vấn đề có thể là nhập có điều kiện.
Amio.io

3
+1. Có thể sử dụng một mô-đun trong nhiều môi trường nơi các phụ thuộc có thể hoạt động hoặc không hoạt động là rất quan trọng, đặc biệt khi các mô-đun có thể tham chiếu đến các phụ thuộc chỉ hoạt động trong trình duyệt (ví dụ: webpackđược sử dụng để chuyển đổi biểu định kiểu thành các mô-đun chèn các kiểu có liên quan vào DOM khi chúng được nhập) nhưng mô-đun cũng cần chạy bên ngoài trình duyệt (ví dụ: để kiểm tra đơn vị).
Jules

Câu trả lời:


142

Chúng tôi có đề xuất nhập khẩu năng động ngay bây giờ với ECMA. Đây là trong giai đoạn 3. Điều này cũng có sẵn như babel-preset .

Sau đây là cách để thực hiện kết xuất có điều kiện theo trường hợp của bạn.

if (condition) {
    import('something')
    .then((something) => {
       console.log(something.something);
    });
}

Điều này về cơ bản trả lại một lời hứa. Nghị quyết của lời hứa dự kiến ​​sẽ có mô-đun. Đề xuất cũng có các tính năng khác như nhập nhiều động, nhập mặc định, nhập tệp js, v.v. Bạn có thể tìm thêm thông tin về nhập động tại đây .


13
Cuối cùng, một câu trả lời thực sự, ES6! Cảm ơn @thecodejack. Trên thực tế ở giai đoạn 3 như bài viết này, theo bài báo đó bây giờ.
ericsoco

5
hoặc nếu bạn vừa đặt tên xuất khẩu, bạn có thể hủy cấu trúc:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
ivn

4
trên Firefox và trong khi chạy npm run buildtôi vẫn gặp lỗi:SyntaxError: ... 'import' and 'export' may only appear at the top level
ste

Điều này thất bại trong thử nghiệm tho, bất cứ ai có ý tưởng?
stackjlei

1
Hàm nhập động có điều kiện đó không có khả năng chi tiết tốt để chỉ nhập các phần tử cụ thể mà "nhập X từ Y" có. Trong thực tế, khả năng hạt mịn thậm chí còn quan trọng hơn trong tải động (trái ngược với gói tiền xử lý)
Craig Hicks

101

Nếu bạn muốn, bạn có thể sử dụng yêu cầu. Đây là một cách để có một tuyên bố yêu cầu có điều kiện.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}

1
Tôi nghĩ rằng một cái gì đó và các biến khác được khai báo bằng cách sử dụng const được chặn trong phạm vi, do đó, nếu điều kiện thứ hai sẽ ném thứ gì đó không được xác định
Mohammed Essehemy

Sẽ tốt hơn khi sử dụng let và khai báo hai biến bên ngoài khối thay vì sử dụng 'var' và tránh phạm vi khối hoàn toàn.
Vorcan

Liệu cẩu có ảnh hưởng gì trong trường hợp này? Tôi đã gặp phải một số vấn đề trong đó việc nâng hàng có nghĩa là tôi đã nhập một thư viện một cách bất thường khi theo một mô hình gần với điều này nếu bộ nhớ phục vụ.
Thomas

11
Cần phải chỉ ra rằng đó require()không phải là một phần của JavaScript tiêu chuẩn - đó là một hàm tích hợp trong Node.js, vì vậy chỉ hữu ích trong môi trường đó. OP không đưa ra dấu hiệu làm việc với Node.js.
Velojet

53

Bạn không thể nhập có điều kiện, nhưng bạn có thể làm ngược lại: xuất một cái gì đó có điều kiện. Nó phụ thuộc vào trường hợp sử dụng của bạn, vì vậy công việc này có thể không dành cho bạn.

Bạn có thể làm:

api.js

import mockAPI from './mockAPI'
import realAPI from './realAPI'

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

Tôi sử dụng điều đó để giả lập các lib phân tích như mixpanel, v.v ... bởi vì hiện tại tôi không thể có nhiều bản dựng hoặc giao diện của chúng tôi. Không phải là thanh lịch nhất, nhưng làm việc. Tôi chỉ có một vài 'nếu' ở đây và ở đó tùy thuộc vào môi trường vì trong trường hợp mixpanel, nó cần khởi tạo.


39
Giải pháp này khiến các mô-đun không mong muốn được tải, vì vậy không phải là một giải pháp tối ưu, tôi nghĩ vậy.
ismailarilik

5
Như đã nêu trong câu trả lời, đây là một cách giải quyết. Vào thời điểm đó, đơn giản là không có giải pháp. Nhập khẩu ES6 không năng động, đây là do thiết kế. Đề xuất chức năng nhập động ES6, được mô tả trong câu trả lời hiện được chấp nhận, có thể thực hiện được. JS đang phát triển :)
Kev

9

Có vẻ như câu trả lời là, cho đến bây giờ, bạn không thể.

http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api

Tôi nghĩ rằng mục đích là để cho phép phân tích tĩnh càng nhiều càng tốt, và các mô-đun được nhập có điều kiện phá vỡ điều đó. Cũng đáng đề cập - Tôi đang sử dụng Babel và tôi đoán rằng điều đó Systemkhông được Babel hỗ trợ vì API trình tải mô-đun không trở thành tiêu chuẩn ES6.


@FelixKling đưa ra câu trả lời của riêng mình và tôi sẽ vui vẻ chấp nhận nó!
ericsoco

4

require()là một cách để nhập một số mô-đun vào thời gian chạy và nó đủ điều kiện để phân tích tĩnh như importnếu được sử dụng với các đường dẫn theo chuỗi. Điều này được yêu cầu bởi bundler để chọn phụ thuộc cho gói.

const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;

Để phân giải mô-đun động với hỗ trợ phân tích tĩnh hoàn chỉnh, các mô-đun chỉ mục đầu tiên trong một bộ chỉ mục (index.js) và bộ chỉ mục nhập trong mô-đun máy chủ.

// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';

// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);

7
Cần phải chỉ ra rằng đó require()không phải là một phần của JavaScript tiêu chuẩn - đó là một hàm tích hợp trong Node.js, vì vậy chỉ hữu ích trong môi trường đó. OP không đưa ra dấu hiệu làm việc với Node.js.
Velojet

2

Sự khác biệt quan trọng nếu bạn sử dụng chế độ Webpack nhập động eager:

if (normalCondition) {
  // this will be included to bundle, whether you use it or not
  import(...);
}

if (process.env.SOMETHING === 'true') {
  // this will not be included to bundle, if SOMETHING is not 'true'
  import(...);
}

Nhưng importtrả lại một lời hứa.
newguy ngày 5 tháng

0

che khuất nó trong một eval làm việc cho tôi, giấu nó khỏi máy phân tích tĩnh ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}

3
Có ai có thể giải thích tại sao câu trả lời này bị hạ cấp? Có bất kỳ nhược điểm thực sự hay đó chỉ là phản ứng tiêu cực tự động đối với từ khóa ác 'eval'?
Yuri Gor

3
Downvote tự động để sử dụng từ khóa eval gớm ghiếc. Tránh xa ra
Tormod Haugene

1
Bạn có thể giải thích điều gì thực sự sai khi sử dụng evalở đây không, @TormodHaugene?
Adam Barnes

MDN tổng hợp khá nhiều lý do tại sao evalkhông nên sử dụng . Nói chung: nếu bạn thấy cần phải sử dụng eval, có lẽ bạn đang làm sai và nên lùi lại một bước để xem xét các lựa chọn thay thế của mình. Có thể có một số tình huống sử dụng evallà chính xác, nhưng rất có thể bạn chưa gặp phải một trong những tình huống đó.
Tormod Haugene

5
Cần phải chỉ ra rằng đó require()không phải là một phần của JavaScript tiêu chuẩn - đó là một hàm tích hợp trong Node.js, vì vậy chỉ hữu ích trong môi trường đó. OP không đưa ra dấu hiệu làm việc với Node.js.
Velojet

0

Tôi đã có thể đạt được điều này bằng cách sử dụng một hàm được gọi ngay lập tức và yêu cầu câu lệnh.

const something = (() => (
  condition ? require('something') : null
))();

if(something) {
  something.doStuff();
}

5
Cần phải chỉ ra rằng đó require()không phải là một phần của JavaScript tiêu chuẩn - đó là một hàm tích hợp trong Node.js, vì vậy chỉ hữu ích trong môi trường đó. OP không đưa ra dấu hiệu làm việc với Node.js.
Velojet

0

Nhập khẩu có điều kiện cũng có thể đạt được với một ternary và require()s:

const logger = DEBUG ? require('dev-logger') : require('logger');

Ví dụ này được lấy từ các tài liệu yêu cầu toàn cầu của ES Lint: https://eslint.org/docs/rules/global-require


5
Cần phải chỉ ra rằng đó require()không phải là một phần của JavaScript tiêu chuẩn - đó là một hàm tích hợp trong Node.js, vì vậy chỉ hữu ích trong môi trường đó. OP không đưa ra dấu hiệu làm việc với Node.js.
Velojet


0

Không, bạn không thể!

Tuy nhiên, việc gặp phải vấn đề đó sẽ khiến bạn phải suy nghĩ lại về cách bạn tổ chức mã của mình.

Trước các mô-đun ES6, chúng tôi đã có các mô-đun CommonJS sử dụng cú pháp Yêu cầu (). Các mô-đun này là "động", có nghĩa là chúng tôi có thể nhập các mô-đun mới dựa trên các điều kiện trong mã của chúng tôi. - nguồn: https://bitsofco.de/what-is-tree-shaking/

Tôi đoán một trong những lý do khiến họ bỏ hỗ trợ trên ES6 trở đi là việc biên dịch nó sẽ rất khó hoặc không thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.