如何使用Elm和Webpack读取文本文件

时间:2018-04-15 08:33:47

标签: javascript webpack loader elm ports

我用Elm编写了一个简单的quizz应用程序。正如教程中所解释的那样,Elm访问外部文件的唯一方法是使用带有Javascript的端口。所以我在我的Elm文件中包含了端口,现在我必须将它们添加到我用作入口点的index.js文件中。我使用webpack构建完整的应用程序 但是,我没有得到webpack逻辑。这是我的文件树:

resources
    |---- images
    |---- questions
              |---- question_1.txt
              |---- question_2.txt
              |---- ...
    |---- scores
              |---- scores_table.json
src
    |---- MyElm.elm
    |---- Questions.elm
    |---- index.js
    |---- index.html

webpack.config.js

我的JS组件需要阅读questions文件夹中的所有可能问题,以确定问题的总数并通过端口将它们提供给Elm。 同样,JS组件需要解析scores_table.json文件以将结果发送到Elm应用程序。

我可以在index.js应用中使用哪些内容来阅读这些文件?我尝试使用require,但我认为我没有正确使用它。

这是我关于Stack Overflow的第一个问题,所以如果有任何遗漏,请告诉我。

最小例子

这是我所拥有的简化版本:

webpack.config.js

var path = require("path");

module.exports = {
  entry: {
    app: [
      './src/index.js'
    ]
  },

  output: {
    path: path.resolve(__dirname + '/dist'),
    filename: '[name].js',
  },

  module: {
    rules: [
      {
        test: /\.txt$/,
        use: 'raw-loader'
      },
      {
        test: /\.(css|scss)$/,
        loaders: [
          'style-loader',
          'css-loader',
        ]
      },
      {
        test:    /\.html$/,
        exclude: /node_modules/,
        loader:  'file-loader?name=[name].[ext]',
      },
      {
        test:    /\.elm$/,
        exclude: [/elm-stuff/, /node_modules/],
        loader:  'elm-webpack-loader?verbose=true&warn=true',
      },
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'url-loader?limit=10000&mimetype=application/font-woff',
      },
      {
        test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader',
      },
    ],

    noParse: /\.elm$/,
  },

  devServer: {
    inline: true,
    stats: { colors: true },
  },

};

index.js

const RESOURCES_DIR = "../resources/";
const IMAGES_DIR = RESOURCES_DIR + "images/"
const QUESTIONS_DIR = RESOURCES_DIR + "questions/"
const SCORES_DIR = RESOURCES_DIR + "scores/"

require("./index.html");
const scores_table =
    require(SCORES_DIR + "scores_table.json");

var total_question_nb = 0;
var questions_table = [];
var end = false;

while (!end) {
    try {
        data = require(
            "raw!" +
            QUESTIONS_DIR +
            "question_${total_question_nb}.txt");
        questions_table.push(data);
        total_question_nb += 1;
    } catch (e) {
        end = true;
    }
}

console.log(questions_table[0]);
console.log(total_question_nb);

var Elm = require("./MyElm.elm");
var mountNode = document.getElementById("elm-app");

var app = Elm.MyElm.embed(mountNode);

// Need to add port gestion there

MyElm.elm

...
import Questions
...

Questions.elm

...
-- (current_question_no, ans_id)
port ask_question_score : (Int, Int) -> Cmd msg

-- next_question_no
port ask_next_question : Int -> Cmd msg

-- question_score
port receive_question_score : (List Int -> msg) -> Sub msg

-- (next_question_no, total_question_nb, next_question_text)
port receive_next_question : ((Int, Int, String) -> msg) -> Sub msg

-- ()
port receive_end_question : (() -> msg) -> Sub msg
...

这是我使用Webpack加载页面时得到的结果:

Uncaught Error: Cannot find module "../resources/scores/scores_table.json".
    at r (app.js:1)
    at Object.<anonymous> (app.js:1)
    at r (app.js:1)
    at Object.<anonymous> (app.js:1)
    at r (app.js:1)
    at app.js:1
    at app.js:1

2 个答案:

答案 0 :(得分:1)

TLDR 您需要设置Webpack的code splitting with dynamic imports才能启用动态require

Webpack旨在将所有源文件压缩为一个“构建”文件。当然,这依赖于识别您要导入的文件。将表达式而不是普通字符串传递给require时,webpack可能无法正确识别所需的文件。

为了明确告诉webpack要包含哪些内容,您可以使用“动态”导入,即code splitting。我会说代码拆分比较先进,如果你想避免它,只需硬编码你要导入的文件。如果文件不经常更改,那应该没问题。

实施例

如果您知道文件名:

const scores = require('../resources/scores/scores_table.json')
// or
import scores from '../resources/scores/scores_table.json'

// The `import()` function could be used here, but it returns a Promise
// I believe `require()` is blocking, which is easier to deal with here
// (though not something I'd want in a production application!)
const questions = [
  require('../resources/questions/question_1.txt'),
  require('../resources/questions/question_2.txt'),
]

如果你想动态导入文件(就像你可能会对问题做的那样):

// Utility function
// Example:
//     arrayOfLength(4) -> [0, 1, 2, 3]
const arrayOfLength = length =>
  (new Array(length)).fill(0).map((val, i) => i)

const questionsCount = 100 // <- You need to know this beforehand

// This would import questions "question_0.txt" through "question_99.txt"
const questionPromises = arrayOfLength(questionsCount)
  .map(i =>
    import(`../resources/questions/question_${i}.txt`)
      .catch(error => {
        // This allows other questions to load if one fails
        console.error('Failed to load question ' + i, error)
        return null
      })
  )

Promise.all(questionPromises)
  .then(questions => { ... })

使用动态导入,您需要处理承诺。您还可以使用async / await使其看起来更好(在所有浏览器中都不支持 - 需要进行转换设置)

如果文件 经常更改,这意味着您经常修改问题和/或分数表,您可能应该使用数据库而不是动态导入。

答案 1 :(得分:0)

这是我在React项目中使用的。它允许您在不使用webpack进行重建的情况下更改文件:

config/index.js

中的

const CONFIG_FILE = '/config.json';

let loadedConfig = {};

export const loadConfig = () => {
  const headers = new Headers();

  headers.append('Content-type', 'application/json');
  try {
    return fetch(CONFIG_FILE, { headers })
      .then(response => response.json())
      .then((json) => { loadedConfig = json; });
  } catch (error) {
    console.log(error); // eslint-disable-line no-console
  }
};

export default () => ({ ...loadedConfig });

index.jsx我加载它:

import React from 'react';
import ReactDOM from 'react-dom';

import { loadConfig } from 'config';

loadConfig().then(() => {
  require.ensure([], (require) => {
    const App = require('App').default;
    ReactDOM.render(
      <App />,
      document.getElementById('root'),
    );
  });
});

然后我导入它并使用

import config from 'config';

console.log(config().SOME_VAR_FROM_JSON);