添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

react-cropper + antdesign +dva 实现裁剪图片并上传的功能

一. 首先安装react-cropper插件
npm install --save react-cropper
执行该命令以后,下载react-cropper依赖信息自动更新到package.json中
在使用该插件的代码中需要进行引入

import "cropperjs/dist/cropper.css"
import Cropper from 'react-cropper'

二.如下列出相应插件的文档以供参考

react-cropper文档:https://www.npmjs.com/package/react-cropperjs
官方cropper文档:https://fengyuanchen.github.io/cropper/
一个作者总结的相关插件的中文版文档:https://blog.csdn.net/weixin_38023551/article/details/78792400

三.主要的实现的功能

使用antdesign的upload组件进行上传图片,并调用react-cropper组件实现固定长和宽的截图,将截图的结果以FILE格式传给后台。实现效果如下。
在这里插入图片描述
可以上传多张图片。
在这里插入图片描述
四.主要的思路
首先构建一个CropperUpload组件,该组件接收截图框的长,宽,Upload样式模式,初始图片列表四个参数。该组件主要实现上传可截图的图片的功能,在Upload组件的beforeUpload中用FileReader读取要上传的文件的base64码,并返回false阻止Upload组件自动上传,将文件码赋值给cropper组件,用cropper进行截图,截图之后,将cropper返回的base64码转换成File格式传向后台。

五.具体函数的实现
1.首先需要一个ant的Upload组件,在这里主要用到这个组件的三个重要参数,beforeUpload上传图片之前触发的回调,onRemove删除图片的回调,fileList已上传图片的列表。

	<Upload
          name="files"
          listType={this.state.pattern === 1 ? " " : this.state.pattern === 2 ? "picture-card" : "picture"}
          className={this.state.pattern === 3 ? styles.uploadinline : ""}
          beforeUpload={this.beforeUpload.bind(this)}
          onRemove={this.handleRemove.bind(this)}
          fileList={this.state.fileList}
          {botton}
     </Upload>

其他的listType主要是支持Upload不同的样式模式。如图所示的三种模式。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
点击上传图片按钮会触发beforeUpload函数,如果想获取通过input选择的文件的数据,我们就需要使用到js封装好的FileReader对象。并通过input.files[0]来获取点击上传的文件对象,在这里因为用了Upload组件,该组件已经封装好了,此时会通过beforeUpload的file参数传递进来,文件对象里面包括文件大小(size),文件的名字(name)和文件类型(type),可以通过该文件类型来判断上传的文件是否是图片,判断的正则如下

/image\/\w+/.test(file.type)

通过调用FileReader来读取文件,文件读取结束之后会调用onload回调,在onload回调中,在通过image.onload回调(该回调是异步的)来获取图片的长宽,图片的长宽必须大于裁剪框的长宽,最后通过e.target.result来获取图片的base64码,将该码通过setSate设置给reactCropper的src中,src为裁剪插件的图片的源,在将包裹裁剪框的Modal框弹出,通过将Modal的visible设置为true,Model的visible在这里用state中的editImageModalVisible来控制,此时将弹出裁剪图片的裁剪框。

	// 特产介绍图片Upload上传之前函数
  beforeUpload(file, fileList) {
    //当打开同一张图片的时候清除上一次的缓存
    if (this.refs.cropper) {
      this.refs.cropper.reset();
      this.refs.cropper.setData({
        width: this.state.width,
        height: this.state.height,
      });
    const isLt10M = file.size / 1024 / 1024 < 10;
    if (!isLt10M) { //添加文件限制
      MsgBox.error({ content: '文件大小不能超过10M' });
      return false;
    var reader = new FileReader();
    const image = new Image();
    var height;
    var width;
    //因为读取文件需要时间,所以要在回调函数中使用读取的结果
    reader.readAsDataURL(file); //开始读取文件
    reader.onload = (e) => {
      image.src = reader.result;
      image.onload = () => {
        height = image.naturalHeight;
        width = image.naturalWidth;
        if (height < this.state.height || width < this.state.width) {
          message.error('图片尺寸不对 宽应大于:'+this.state.width+ '高应大于:' +this.state.height );
          this.setState({
            editImageModalVisible: false, //打开控制裁剪弹窗的变量,为true即弹窗
        else{
          this.setState({
            srcCropper: e.target.result, //cropper的图片路径
            selectImgName: file.name, //文件名称
            selectImgSize: (file.size / 1024 / 1024), //文件大小
            selectImgSuffix: file.type.split("/")[1], //文件类型
            editImageModalVisible: true, //打开控制裁剪弹窗的变量,为true即弹窗
          if (this.refs.cropper) {
            this.refs.cropper.replace(e.target.result);
    return false;

裁剪框的设置如下,src是裁剪框图片的路径,ref为该组件真实实例的引用,viewMode在这里选择了模式1,zoomable是否允许放大图像,movable是否允许移动图像,在这里设置了ready函数,当一个cropper实例完全构建时,这个事件就会发生,用于在每一次裁剪之前都重新设置一下裁剪框大小,否则他会自动的恢复默认原图像0.8比例。

 		<Cropper
            src={this.state.srcCropper} //图片路径,即是base64的值,在Upload上传的时候获取到的
            ref="cropper"
            viewMode={1} //定义cropper的视图模式
            zoomable={true} //是否允许放大图像
            movable={true}
            guides={true} //显示在裁剪框上方的虚线
            background={false} //是否显示背景的马赛克
            rotatable={false} //是否旋转
            style={{ height: '100%', width: '100%' }}
            cropBoxResizable={false}//是否可以拖拽
            cropBoxMovable={true




    
}//是否可以移动裁剪框
            dragMode="move"
            center={true}
            ready={this._ready.bind(this)}

裁剪框父组件设置如下,可见与不可见通过页面的stated的editImageModalVisible来控制,当上传图片时此状态设置为true显示裁剪框,当上传图片成功,或者关闭裁剪框的时候将此状态设置为false关闭裁剪框,Modal组件的高度是自适应组件内的图片高度的,宽度并不自适应图片的宽度,若不设置将会有默认的宽度,因此在这里设置为100%,当裁剪完成点击保存触发saveImg函数回调,当点击取消的时候触发handleCancle回调。

       <Modal
          key="cropper_img_icon_key"
          visible={this.state.editImageModalVisible}
          width="600"
          footer={[
            <Button type="primary" onClick={this.saveImg.bind(this)} >保存</Button>,
            <Button onClick={this.handleCancel.bind(this)} >取消</Button>
        </Modal>

点击保存按钮之后,触发saveImg函数,要使用FormData向后台传数据,并且传输的是格式是FILE格式的,因此现将截图的结果取出,取出的时候使用this.refs.cropper.getCroppedCanvas().toDataURL(),最终取出的是截图后图片的base64码,通过dataURLtoFile函数将base64转换成FILE,封装到formdata中,调用后台的一个接口postimage。后台将返回一个图片的路径给前台。前台将路径取出,并按照Upload组件图片列表默认显示的格式来构建,最终push到fileList中,显示在Upload组件上。并将state中editImageModalVisible的状态置为false,关闭Modal裁剪框。

saveImg() {
    const { dispatch } = this.props;
    var formdata = new FormData();
    formdata.append("files", this.dataURLtoFile(this.refs.cropper.getCroppedCanvas().toDataURL(), this.state.selectImgName));
    dispatch({
      type: 'getData/postimage',
      payload: formdata,
      callback: () => {
        const { success, msg, obj } = this.props.imagePictures;
        if (success) {
          let imageArry = this.state.pattern == 3 ? this.state.fileList.slice() : [];
          imageArry.push({
            uid: Math.random() * 100000,
            name: this.state.selectImgName,
            status: 'done',
            url: obj[0].sourcePath,
            // url:obj[0].sourcePath,
            thumbUrl: this.refs.cropper.getCroppedCanvas().toDataURL(),
            thumbnailPath: obj[0].thumbnailPath,
            largePath: obj[0].largePath,
            mediumPath: obj[0].mediumPath,
            upload: true,
          this.setState({
            fileList: imageArry,
            editImageModalVisible: false, //打开控制裁剪弹窗的变量,为true即弹窗
            srcCropper: this.state.srcCropper, //cropper的图片路径
          this.props.onChange(imageArry);
        else {
          message.error(msg);
    });

在modal中调用getData的postimage向后台发送数据,现在附上请求头的设置部分,因为传递的数据已经用formdata封装上了,因此直接就传递给后台就可以。

export default function request(url, options) {
  const defaultOptions = {
    credentials: 'include',
  const newOptions = { ...defaultOptions, ...options };
  if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
      newOptions.method = 'POST'
  return fetch(url, newOptions)
    .then(checkStatus)
    .then((response) => {
      if (newOptions.method === 'DELETE' || response.status === 204) {
        return response.text();
      return response.json();
    .catch((e) => {
      const { dispatch } = store;
      const status = e.name;
      if (status === 401) {
        dispatch(routerRedux.push('/user/login'));
        return;
    });

向后台发送图片的请求头:
在这里插入图片描述
后传传输回数据:图片存储的路径,分为不同种的格式,大图,小图,中图,原图。
在这里插入图片描述
遇到的问题
上面流程看似简单,可是在实际开发中遇到了无数的困难!下面给大家分享我的经历。
1.beforeUpload组件问题
当Upload组件调用beforeUpload函数的时候,如果函数的返回值是false的时候,就会清空fileList,所以每次参数fileList之中都只有当前的图片,没有之前上传过的,这是因为当beforeUpload函数返回时false的时候,会自动触发onChange函数,触发时fileList中只有当前的上传的图片,因此若再用onChange函数中调用setState来维护fileList就会将之前的图片都清除。因此在这种完全阻止自动上传的情景下就应该将onChange函回调函数删除。

 <Upload
          name="files"
          action="/hyapi/resource/image/multisize/upload"
          beforeUpload={this.beforeUpload.bind(this)}
          onRemove={this.handleRemove.bind(this




    
)}
          fileList={this.state.fileList}
          //onChange={this.onChange}//这里将不使用onChange回调
          {botton}
        </Upload>

2.对同一张图片进行截图的问题
当对一张图片进行截图,调整了图片的位置和缩放,下次再打开同一张图片,还会保留有上一次的记录。
第一次打开的位置:
在这里插入图片描述
改变图片大小和位置:
在这里插入图片描述
再次打开时候还是上图所示位置,cropper对同一张图片没有重新的渲染。cropper组件有一个ready函数,每一次截图准备完毕之前都会调用,在ready里面打印,发现同一张图片连续上传,只有第一次会调用ready。

  _ready() {
    this.refs.cropper.setData({
      width: this.state.width,
      height: this.state.height,
    });

这说明对同一张图片,cropper不会重新渲染,所以就要手动在每次弹出截图框的时候进行reset一下,因此在beforeUpload函数中进行恢复初始化操作。调用this.refs.cropper.reset(),并重新设置裁剪框的宽和高,否则会恢复默认原图的0.8比例。

beforeUpload(file, fileList) {
  //当打开同一张图片的时候清除上一次的缓存
  if (this.refs.cropper) {
    this.refs.cropper.reset();
    this.refs.cropper.setData({
      width: this.state.width,
      height: this.state.height,
    });

3.多次截图后面图片会变小,容器变成默认的200*100
第一次上传图片截图时正常,图片大小为原始图片大小
在这里插入图片描述
后来在截图发现图片会压缩成很小,查看样式发现为cropper-container恢复默认值200*100
在这里插入图片描述
发现单纯的通过setState改变cropper的src有时并不能使图片将cropper容器撑开,达到重新渲染组件的目的,后来查看了官方文档,发现有一个参数replace(url),作用替换图像的src并重新构建cropper,尝试了一下发现可以重新构建cropper,达到图片撑开容器而不是显示容器默认值得效果。并且加上这个参数之后。连续上传同一张图片也不会显示上一次默认的值了,同一张图片也会当做一张新的图片重新渲染,所以每次给cropper赋值的时候,调用replace函数十分重要,而不是单纯的靠改变cropper的src来实现重新渲染裁剪插件。

this.setState({
          srcCropper: e.target.result, //cropper的图片路径
          selectImgName: file.name, //文件名称
          selectImgSize: (file.size / 1024 / 1024), //文件大小
          selectImgSuffix: file.type.split("/")[1], //文件类型
          editImageModalVisible: true, //打开控制裁剪弹窗的变量,为true即弹窗
        if (this.refs.cropper) {
          this.refs.cropper.replace(e.target.result);

4.用fetch向后台传输File格式文件,头文件设置问题
将File绑定在formdata里面之后,向后台传递时候,起初手动设置了头文件Content-Type=multipart/form-data,向后台发送数据失败,头文件显示如下,显示的结果没有正常情况下的boundary
在这里插入图片描述
看了原生ajax传递数据的代码如下,将contentType设置为false,不设置头文件。因此尝试在fetch时候,不设置contentType的内容。

$.ajax({
          url: "upload.ashx",
                type: "POST",
                data: formData,
                *必须false才会自动加上正确的Content-Type
                contentType: false,
                * 必须false才会避开jQuery对 formdata 的默认处理
                * XMLHttpRequest会对 formdata 进行正确的处理
                processData: false,
                success: function (data) {
                    if (data.status == "true") {
                        alert("上传成功!");
                    if (data.status == "error") {
                        alert(data.msg);
                    $("#imgWait").hide();
                error: function () {
                    alert("上传失败!");
                    $("#imgWait").hide();

之后再向后台传递的时候,头文件就显示为正常,自动设置了boundary
在这里插入图片描述
以上是所有开发中遇到的问题,欢迎大家一起来讨论

七.完整代码部分
自己封装的cropper-upload组件完整代码

import React, { PureComponent } from 'react';
import moment from 'moment';
import { routerRedux, Route, Switch, Link } from 'dva/router';
import { Upload, Button, Modal, Icon, message } from 'antd';
import "cropperjs/dist/cropper.css"
import Cropper from 'react-cropper'
import { connect } from 'dva';
import styles from './Upload.less';
@connect(state => ({
imagePictures: state.getData.imagePictures,
}))
class CropperUpload extends PureComponent {
constructor(props) {
  super(props);
  console.log(props);
  console.log("props");
  this.state = {
    width: props.width,
    height: props.height,
    pattern: props.pattern,
    fileList: props.fileList ? props.fileList : [],
    editImageModalVisible: false,
    srcCropper: '',
    selectImgName: '',
componentWillReceiveProps(nextProps) {
  if ('fileList' in nextProps) {
    this.setState({
      fileList: nextProps.fileList ? nextProps.fileList : [],
    });
handleCancel = () => {
  this.setState




    
({
    editImageModalVisible: false,
  });
// 图片Upload上传之前函数
beforeUpload(file, fileList) {
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) { //添加文件限制
    MsgBox.error({ content: '文件大小不能超过10M' });
    return false;
  var reader = new FileReader();
  const image = new Image();
  var height;
  var width;
  //因为读取文件需要时间,所以要在回调函数中使用读取的结果
  reader.readAsDataURL(file); //开始读取文件
  reader.onload = (e) => {
    image.src = reader.result;
    image.onload = () => {
      height = image.naturalHeight;
      width = image.naturalWidth;
      if (height < this.state.height || width < this.state.width) {
        message.error('图片尺寸不对 宽应大于:'+this.state.width+ '高应大于:' +this.state.height );
        this.setState({
          editImageModalVisible: false, //打开控制裁剪弹窗的变量,为true即弹窗
      else{
        this.setState({
          srcCropper: e.target.result, //cropper的图片路径
          selectImgName: file.name, //文件名称
          selectImgSize: (file.size / 1024 / 1024), //文件大小
          selectImgSuffix: file.type.split("/")[1], //文件类型
          editImageModalVisible: true, //打开控制裁剪弹窗的变量,为true即弹窗
        if (this.refs.cropper) {
          this.refs.cropper.replace(e.target.result);
  return false;
handleRemove(file) {
  this.setState((state) => {
    const index = state.fileList.indexOf(file);
    const newFileList = state.fileList.slice();
    newFileList.splice(index, 1);
    this.props.onChange(newFileList);
    return {
      fileList: newFileList,
  });
//将base64码转化成blob格式
convertBase64UrlToBlob(base64Data) {
  var byteString;
  if (base64Data.split(',')[0].indexOf('base64') >= 0) {
    byteString = atob(base64Data.split(',')[1]);
  } else {
    byteString = unescape(base64Data.split(',')[1]);
  var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
  var ia = new Uint8Array(byteString.length);
  for (var i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  return new Blob([ia], { type: this.state.selectImgSuffix });
//将base64码转化为FILE格式
dataURLtoFile(dataurl, filename) {
  var arr = dataurl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  return new File([u8arr], filename, { type: mime });




    

_ready() {
  this.refs.cropper.setData({
    width: this.state.width,
    height: this.state.height,
  });
saveImg() {
  const { dispatch } = this.props;
  var formdata = new FormData();
  formdata.append("files", this.dataURLtoFile(this.refs.cropper.getCroppedCanvas().toDataURL(), this.state.selectImgName));
  dispatch({
    type: 'getData/postimage',
    payload: formdata,
    callback: () => {
      const { success, msg, obj } = this.props.imagePictures;
      if (success) {
        let imageArry = this.state.pattern == 3 ? this.state.fileList.slice() : [];
        imageArry.push({
          uid: Math.random() * 100000,
          name: this.state.selectImgName,
          status: 'done',
          url: obj[0].sourcePath,
          // url:obj[0].sourcePath,
          thumbUrl: this.refs.cropper.getCroppedCanvas().toDataURL(),
          thumbnailPath: obj[0].thumbnailPath,
          largePath: obj[0].largePath,
          mediumPath: obj[0].mediumPath,
          upload: true,
        this.setState({
          fileList: imageArry,
          editImageModalVisible: false, //打开控制裁剪弹窗的变量,为true即弹窗
        this.props.onChange(imageArry);
      else {
        message.error(msg);
  });
render() {
  const botton = this.state.pattern == 2 ?
      <Icon type="plus" />
      <div className="ant-upload-text">Upload</div>
    </div> :
    <Button>
      <Icon type="upload" />选择上传</Button>
  return (
      <Upload
        name="files"
        action="/hyapi/resource/image/multisize/upload"
        listType={this.state.pattern === 1 ? " " : this.state.pattern === 2 ? "picture-card" : "picture"}
        className={this.state.pattern === 3 ? styles.uploadinline : ""}
        beforeUpload={this.beforeUpload.bind(this)}
        onRemove={this.handleRemove.bind(this)}
        fileList={this.state.fileList}
        {botton}
      </Upload>
      <Modal
        key="cropper_img_icon_key"
        visible={this.state.editImageModalVisible}
        width="100%"
        footer={[
          <Button type="primary" onClick={this.saveImg.bind(this)} >保存</Button>,
          <Button onClick={this.handleCancel.bind(this)} >取消</Button>
        <Cropper
          src={this.state.srcCropper} //图片路径,即是base64的值,在Upload上传的时候获取到的
          ref="cropper"
          preview=".uploadCrop"
          viewMode={1} //定义cropper的视图模式
          zoomable={true} //是否允许放大图像
          movable={true}
          guides={true} //显示在裁剪框上方的虚线
          background={false} //是否显示背景的马赛克
          rotatable={false} //是否旋转
          style={{ height: '100%', width: '100%' }}
          cropBoxResizable={false}
          cropBoxMovable={true}
          dragMode="move"
          center={true}
          ready={this._ready.bind(this)}
      </Modal>
    </div>
export default CropperUpload;

页面具体调用范例:

<CropperUpload
     pattern={1}//模式分为三种,1为只能上传一张图片,2为上传多张图片
     width={375}
     height={120}
     onChange={this.ChangeIconImage.bind(this)}
     fileList={this.state.iconUrl}
                    react-cropper + antdesign +dva 实现裁剪图片并上传的功能一.首先安装react-cropper插件npm install --save react-cropper执行该命令以后,下载react-cropper依赖信息自动更新到package.json中在使用该插件的代码中需要进行引入import "cropperjs/dist/cropper.css"im...
				
React + React-Router 4 + Redux + Ant Design + Webpack 4带有热重载和redux-devtools-extension入门 :warning_selector: Ant Design仍在升级到ReactJS 16.3新生命周期,因此,应用目前无法正常运行 完整的Ant Design入门 您喜欢Bootstrap吗? 等待并查看 。 您现在明白我的意思了吗? ReactJS(16.4.x + - ) 蚂蚁设计(3.8+ ) 具有内置主题自定义(更改var以进行自定义) 图标字体在本地加载 Redux(随着应用程序的增长,管理状态将是一个严
npm install --save react-cropper 您的项目中需要cropper.css ,来自 。 由于此项目依赖于 ,因此,对于更高版本的npm 3.0.0 ,它位于/node_modules/react-cropper/node_modules/cropperjs/dist/cropper.css或node_modules/cropperjs/dist/cropper.css中 重大更改(版本> = 2.0.0) 对ref支持已被删除。 使用onInitialized方法来获取cropper实例。 在2. 在交互式监视模式下启动测试运行程序。 有关更多信息,请参见关于的部分。 npm run build或yarn build 构建生产到应用程序build文件夹。 它在生产模式下正确捆绑了React,并优化了构建以获得最佳性能。 最小化构建,文件名包含哈希。 您的应用已准备好进行部署! 有关更多信息,请参见有关的部分。 npm run eject或yarn eject 注意:这是单向操作。 eject ,您将无法返回! 如果您对构建工具和配置选择不满意,则可以随时eject 。 此命令将从项目中删除单个构建依赖项。 而是将所有配置
react-cropper react-crepper的github:https://github.com/react-cropper/react-cropper 不想看官方文档的,就直接看如何使用吧。 npm install --save react-cropper //或者 yarn add react-cropper ** 组件内使用** import React, { useRef } from 'react'; /** 引入react-cropper */ import Cropper
1. 安装 react-cropper npm install --save react-cropper //或者 yarn add react-cropper 2. 组件内引入 import Cropper from "react-cropper"; // 引入Cropper import "cropperjs/dist/cropper.css"; // 引入Cropper对应的css 3. 函数式组件 1. jsx文件 import { useState } from 'react'; :gem_stone:优雅美观:基于Ant Design架构精心设计 :rocket:最新技术栈:使用React / dva / antd等前端前沿技术开发 :input_numbers: 模拟数据:实用的本地数据调试方案 roadhog版本升级2.0 $ git clone https://github.com/sosout/react-antd-dva.git $ cd react-antd-dva $ yarn install $ yarn start
react+antd+react-cropper+lrz 实现图片剪裁后压缩上传 需要安装的依赖 npm install react-cropper 图片裁剪 npm install lrz 图片压缩 关于react-cropper图片裁剪器 参考官方文档https://github.com/roadmanfong/react-cropper,另外本文也会对其一些常用的功能进行注释 关于l...
最近公司项目在做一个求职工具类的小程序,小程序端有企业Logo的显示,为了保证图片在小程序端的展示效果,所以要求在后台管理系统进行企业Logo上传时需要限定上传图片尺寸和比例。 为了减少管理人员的录入成本,需要在Logo上传前进行裁剪操作,保证上传进来的图片都是规则的正方。,此处用到了react-cropper组件,本文主要介绍了react-cropper组件在Ant Design Pro项目中...
2. 在webpack配置中启用sass 在项目根目录下找到config文件夹,找到webpack.config.dev.js和webpack.config.prod.js文件,分别进行以下操作: - 在module.rules中添加对sass文件的处理: ```javascript test: /\.scss$/, use: [ loader: "sass-loader", options: { implementation: require("node-sass") 3. 全局变量 在src文件夹中创建一个variables.scss文件,并在其中定义要使用的全局变量,例如: ```scss $primary-color: #007bff; body { background-color: $primary-color; 4. 在组件中使用sass文件 在需要使用sass文件的组件中,使用import语句导入变量和样式: ```jsx import './MyComponent.scss'; import variables from './variables.scss'; 然后就可以在组件中正常使用定义好的全局变量和样式了: ```jsx const MyComponent = () => { return ( <div className="my-component"> <h1 style={{ color: variables.primaryColor }}>Hello World</h1> 这样就可以在create-react-app中配置sass并使用全局变量了。 m0_37119354: 博主,我也遇到了这样的问题,但是我检查过我的入口文件,没有什么问题,入口文件只是挂载了一个简单的div元素[code=javascript] ReactDOM.render( <div>test test test test</div>, document.getElementById('root') [/code] BraftEditor使用总结 发哥随手记: 你也没有边框,怎么显示边框出来呢? create-react-app创建的项目配置多入口MPA模式。报Cannot read property ‘filter’ of undefined 再次重逢的时间: create-react-app创建的项目配置多入口MPA模式。报Cannot read property ‘filter’ of undefined 一只小鸟er 昏黄烛火轻摇晃: 可能是你的order.html或者是order的入口文件index写的有问题,具体可以参考我的一个多入口的小项目:https://github.com/liusiasi/train-ticket create-react-app创建的项目配置多入口MPA模式。报Cannot read property ‘filter’ of undefined 昏黄烛火轻摇晃: 大佬 配置后只有index.html页面有内容,其他的页面都是没有内容的,但是js却是正常加载,我访问order.html 下面加载的js是order.***.js