在 express js 服务器上使用 @react-pdf/renderer

Using @react-pdf/renderer on an express js server

我有一个 react 网络应用程序,我希望它必须呈现 pdf 并在按下按钮时通过电子邮件发送。为了测试起见,当在反应前端按下按钮时,它会向我的 expressjs 后端发送请求并尝试通过 @react-pdf/renderer.

生成静态 pdf

我对 ES 模块和 CommonJS 没有清楚的了解,但我的服务器使用的是 CommonJS,所以我将 "type": "module" 添加到服务器的 package.json 并更新了 [=18] 中的导入=].但是,编译器在 server.jsmaterials.js 中抱怨 SyntaxError: Unexpected token '<'(取决于设置)。我做错了什么?

(下面的代码已被清理为匿名)

编辑 Here 是在 Node

上使用 react-pdf/renderer 的示例

server.js:

// const express = require('express');
// const cors = require('cors');
// const fs = require('fs')
// const react = require('react')
// const reactpdf = require('@react-pdf/renderer');
// const materials = require('./pdf/src/materials');
import express from 'express';
import cors from 'cors';
import fs from 'fs';
import react from 'react';
import ReactPDF from '@react-pdf/renderer';
import MaterialsList from './materials.js';

const app = express();
const port = 5000;

app.use(cors())

app.use(express.urlencoded());
app.use(express.json());

app.listen(port, function () {
    console.log(`server running on http://localhost:${port}`);
})

app.post('/sendmaterials', cors(), function (req, res) {
    // const pdf = req.body;
    // console.log(pdf);

    // reactpdf.render(pdf, `${__dirname}/trial.pdf`);
    reactpdf.render(<MaterialsList />, `${__dirname}/trial.pdf`);
    // fs.writeFile('trial.pdf', pdf, (err) => {
    //     // throws an error, you could also catch it here
    //     if (err) throw err;
    
    //     // success case, the file was saved
    //     console.log('PDF saved!');
    // });
})

package.json:

{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "dependencies": {
    "@react-pdf/renderer": "^1.6.13",
    "concurrently": "^5.3.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "firebase": "^9.0.0-beta.1",
    "nodemon": "^2.0.6",
    "react": "^17.0.2"
  },
  "devDependencies": {
    "husky": "^5.1.3",
    "lint-staged": "^10.5.4",
    "prettier": "^2.2.1",
    "pretty-quick": "^3.1.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "concurrently \"nodemon server.js\" \"cd frontend && yarn start\"",
    "init": "concurrently \"yarn\" \"cd frontend && yarn\"",
    "install-initial": "concurrently \"yarn install\" \"cd frontend && yarn install\"",
    "start-frontend": "cd frontend && yarn start",
    "start-server": "nodemon server.js",
    "pretty-quick": "pretty-quick",
    "prepare": "husky install"
  },
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged"
    }
  },
  "lint-staged": {
    "linters": {
      "src/**/*.{js,css}": [
        "prettier --write",
        "git add"
      ]
    }
  }
}

materials.js:

import React from 'react';
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  Image,
  // Font,
} from '@react-pdf/renderer';
import Logo from './images/img.png'
// import Trebuchet from './fonts/Trebuchet/trebuchet-ms.ttf'

// Font.register({
//   family: 'Trebuchet MS',
//   src: Trebuchet,
// })

// Create styles
const styles = StyleSheet.create({
  page: {
    padding: 30,
  },
  container: {
    marginTop: 20,
    marginBottom: 20,
    flex: 0,
    flexDirection: 'row',
    '@media max-width: 400': {
      flexDirection: 'column',
    },
  },
  image: {
    marginTop: 20,
  },
  mainTitle: {
    // fontFamily: 'Trebuchet MS',
    paddingLeft: 15,
    fontWeight: 'bold',
    color: "#000000",
    fontSize: 19,
  },
  title: {
    // fontFamily: 'Trebuchet MS',
    fontWeight: 'bold',
    color: "#000000",
    fontSize: 16,
    textDecoration: 'underline',
  },
  regGrey: {
    // fontFamily: 'Trebuchet MS',
    color: "#8d8d8d",
    fontSize: 12,
  },
  regBlack: {
    // fontFamily: 'Trebuchet MS',
    color: "#101010",
    fontSize: 12,
    marginBottom: 15,
  },
  column: {
    flexDirection: 'column',
    width: 180,
    paddingLeft: 15,
    paddingRight: 15,
    '@media max-width: 400': {
      width: '100%',
      paddingRight: 0,
    },
    '@media orientation: landscape': {
      width: 200,
    },
  },
  table: {
      width: '100%',
      alignContent: 'center',
      borderWidth: 0,
      display: 'flex',
      flexDirection: 'column',
      paddingTop: 10,
  },
  header: {
    backgroundColor: "#4c4c4c",
    fontWeight: 'bold',
    color: "#fdfdfd",
    flexWrap: 'wrap'
  },
  tableRow:{
    display: 'flex',
    flexDirection: 'row',
  },
  lightRow:{
    display: 'flex',
    flexDirection: 'row',
    backgroundColor: "#cfcfcf",
  },
  darkRow:{
    display: 'flex',
    flexDirection: 'row',
    backgroundColor: "#aeaeae",
  },
  cell: {
    fontColor: "#101010",
    // fontFamily: 'Trebuchet MS',
    fontSize: 12,
    borderWidth: 0,
    display: 'flex',
    justifyContent: 'center',
    alignContent: 'center',
    textAlign: 'center',
    flexWrap: 'wrap'
  },
});

// Create table object
const Table = ({header, alternate, children, col}) => (
    <View style={styles.table}>
        {children.map((row, ind) =>
            <View key={ind} style={[styles.tableRow,
              header && ind === 0 ? styles.header: {},
              alternate && ind % 2 === 0 && ind !== 0 ? styles.lightRow: {},
              alternate && ind % 2 !== 0 && ind !== 0 ? styles.darkRow: {},
            ]}>
                {row.map((cell, j) =>
                    <View key={j} style={[styles.cell, {width:col[j], height: 40}]}>
                        {
                            typeof(cell) === 'string' || typeof(cell) === 'number' ?
                            <Text>{cell}</Text> : cell
                        }
                    </View>
                )}
            </View>
        )}
    </View>
)

// Create Document Component
const MaterialsList = () => (
  <Document>
    <Page style={styles.page}>
      <View style={styles.container}>
        <View style={styles.column}>
          <Text style={styles.title}>
            Steels
          </Text>
          <Table
            col={['60%', '40%']}
            header
            alternate
            children={[
              ['Item', 'Quantity'],
              ['Steel', '10'],
              ['U Channel', '10'],
              ['Gate Insert', '10'],
            ]} />
        </View>
      </View>
    </Page>
  </Document>
);

export default MaterialsList

问题出在 package.json 文件中,我缺少 babel 库和配置文件。我有一个 example repo showing the correct setup and a link to the discussion on the react-pdf/renderer github discussion.