[Vue警告]:渲染错误:“ TypeError:将圆形结构转换为JSON

时间:2019-06-08 18:10:57

标签: javascript typescript vue.js phosphorjs

我已经在Vue中成功创建了单个文件组件;没有编译错误,但是,当我尝试查看组件(通过导航到其路由链接)时,未显示预期的页面,而是在(Chrome)浏览器中打印了堆栈跟踪-使用Vue Devtools插件。

这是Vue Devtools插件控制台中的stacktrace输出:

vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: 
log.js?1afd:24 [HMR] Waiting for update signal from WDS...
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: "TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'DockPanel'
    |     property '_layout' -> object with constructor 'DockLayout'
    --- property '_parent' closes the circle"

found in

---> <Demo> at src/components/Demo.vue
       <App> at src/App.vue
         <Root>
warn @ vue.runtime.esm.js?2b0e:619
logError @ vue.runtime.esm.js?2b0e:1884
globalHandleError @ vue.runtime.esm.js?2b0e:1879
handleError @ vue.runtime.esm.js?2b0e:1839
Vue._render @ vue.runtime.esm.js?2b0e:3544
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
init @ vue.runtime.esm.js?2b0e:3118
merged @ vue.runtime.esm.js?2b0e:3301
createComponent @ vue.runtime.esm.js?2b0e:5972
createElm @ vue.runtime.esm.js?2b0e:5919
createChildren @ vue.runtime.esm.js?2b0e:6047
createElm @ vue.runtime.esm.js?2b0e:5948
patch @ vue.runtime.esm.js?2b0e:6471
Vue._update @ vue.runtime.esm.js?2b0e:3939
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
init @ vue.runtime.esm.js?2b0e:3118
createComponent @ vue.runtime.esm.js?2b0e:5972
createElm @ vue.runtime.esm.js?2b0e:5919
patch @ vue.runtime.esm.js?2b0e:6510
Vue._update @ vue.runtime.esm.js?2b0e:3939
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
(anonymous) @ main.ts?bc82:8
./src/main.ts @ app.js:5941
__webpack_require__ @ app.js:767
fn @ app.js:130
1 @ app.js:6015
__webpack_require__ @ app.js:767
(anonymous) @ app.js:902
(anonymous) @ app.js:905
Show 11 more frames
vue.runtime.esm.js?2b0e:1888 TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'DockPanel'
    |     property '_layout' -> object with constructor 'DockLayout'
    --- property '_parent' closes the circle
    at JSON.stringify (<anonymous>)
    at Proxy.toString (vue.runtime.esm.js?2b0e:94)
    at Proxy.render (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"4105b414-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/Demo.vue?vue&type=template&id=09260093&scoped=true& (app.js:2401), <anonymous>:10:25)
    at VueComponent.Vue._render (vue.runtime.esm.js?2b0e:3542)
    at VueComponent.updateComponent (vue.runtime.esm.js?2b0e:4060)
    at Watcher.get (vue.runtime.esm.js?2b0e:4473)
    at new Watcher (vue.runtime.esm.js?2b0e:4462)
    at mountComponent (vue.runtime.esm.js?2b0e:4067)
    at VueComponent.Vue.$mount (vue.runtime.esm.js?2b0e:8409)
    at init (vue.runtime.esm.js?2b0e:3118)
logError @ vue.runtime.esm.js?2b0e:1888
globalHandleError @ vue.runtime.esm.js?2b0e:1879
handleError @ vue.runtime.esm.js?2b0e:1839
Vue._render @ vue.runtime.esm.js?2b0e:3544
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
init @ vue.runtime.esm.js?2b0e:3118
merged @ vue.runtime.esm.js?2b0e:3301
createComponent @ vue.runtime.esm.js?2b0e:5972
createElm @ vue.runtime.esm.js?2b0e:5919
createChildren @ vue.runtime.esm.js?2b0e:6047
createElm @ vue.runtime.esm.js?2b0e:5948
patch @ vue.runtime.esm.js?2b0e:6471
Vue._update @ vue.runtime.esm.js?2b0e:3939
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
init @ vue.runtime.esm.js?2b0e:3118
createComponent @ vue.runtime.esm.js?2b0e:5972
createElm @ vue.runtime.esm.js?2b0e:5919
patch @ vue.runtime.esm.js?2b0e:6510
Vue._update @ vue.runtime.esm.js?2b0e:3939
updateComponent @ vue.runtime.esm.js?2b0e:4060
get @ vue.runtime.esm.js?2b0e:4473
Watcher @ vue.runtime.esm.js?2b0e:4462
mountComponent @ vue.runtime.esm.js?2b0e:4067
Vue.$mount @ vue.runtime.esm.js?2b0e:8409
(anonymous) @ main.ts?bc82:8
./src/main.ts @ app.js:5941
__webpack_require__ @ app.js:767
fn @ app.js:130
1 @ app.js:6015
__webpack_require__ @ app.js:767
(anonymous) @ app.js:902
(anonymous) @ app.js:905
Show 10 more frames
Demo.vue?96da:47 *** this actually ran

package.json

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "test:unit": "vue-cli-service test:unit"
  },
  "dependencies": {
    "@phosphor/widgets": "^1.7.0",
    "axios": "^0.19.0",
    "core-js": "^2.6.5",
    "vue": "^2.6.10",
    "vue-class-component": "^7.0.2",
    "vue-property-decorator": "^8.1.0",
    "vue-router": "^3.0.3",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@types/chai": "^4.1.0",
    "@types/mocha": "^5.2.4",
    "@vue/cli-plugin-babel": "^3.8.0",
    "@vue/cli-plugin-eslint": "^3.8.0",
    "@vue/cli-plugin-typescript": "^3.8.0",
    "@vue/cli-plugin-unit-mocha": "^3.8.0",
    "@vue/cli-service": "^3.8.0",
    "@vue/eslint-config-airbnb": "^4.0.0",
    "@vue/eslint-config-typescript": "^4.0.0",
    "@vue/test-utils": "1.0.0-beta.29",
    "babel-eslint": "^10.0.1",
    "chai": "^4.1.2",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "typescript": "^3.4.3",
    "vue-template-compiler": "^2.6.10"
  }
}

router.ts

import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
import Demo from './components/Demo.vue';

Vue.use(Router);

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
    },
    {
      path: '/demo',
      name: 'demo',
      component: Demo,
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
    },
  ],
});

Demo.vue

<template>
  <div class="demo">
    <h1>{{ title }}</h1>
    {{ dpanel }}
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { Widget, DockPanel } from '@phosphor/widgets';

@Component
export default class Demo extends Vue {
  @Prop() private title!: string;

  // apparently with typescript variant of vue, instance variables
  // are the equivalent of the vue 'data' object's attributes...
  dpanel:DockPanel = new DockPanel();

  components:any

  constructor() {
    super();
    this.components = {
      DockPanel,
    };
  }

  mounted() {
    const widget1 = new Widget();
    widget1.addClass('content');
    widget1.addClass('blue');
    widget1.title.label = 'Blue';
    widget1.title.closable = true;

    const widget2 = new Widget();
    widget2.addClass('content');
    widget2.addClass('red');
    widget2.title.label = 'Red';
    widget2.title.closable = true;

    this.dpanel.id = 'main';

    this.dpanel.addWidget(widget1);
    this.dpanel.addWidget(widget2);
    this.dpanel.update();
    console.log('*** this actually ran');
  }

  static setupPanels():void {
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.content {
  border: 1px solid black;
  min-width: 50px;
  min-height: 50px;
}


.red {
  background: #E74C3C;
}


.yellow {
  background: #F1C40F;
}


.green {
  background: #27AE60;
}


.blue {
  background: #3498DB;
}


.p-DockTabPanel {
  padding-right: 2px;
  padding-bottom: 2px;
}


.p-DockTabPanel > .p-StackedPanel {
  padding: 10px;
  background: white;
  border: 1px solid #C0C0C0;
  border-top: none;
  box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
}


.p-DockPanel-overlay {
  background: rgba(255, 255, 255, 0.6);
  border: 1px dashed black;
}


.p-DockPanel-overlay.p-mod-root-top,
.p-DockPanel-overlay.p-mod-root-left,
.p-DockPanel-overlay.p-mod-root-right,
.p-DockPanel-overlay.p-mod-root-bottom,
.p-DockPanel-overlay.p-mod-root-center {
  border-width: 2px;
}


.p-TabBar {
  min-height: 24px;
  max-height: 24px;
}


.p-TabBar-header {
  display: none;
}


.p-TabBar-footer {
  flex: 0 0 1px;
  background: #C0C0C0;
}


.p-TabBar-content {
  min-width: 0;
  align-items: flex-end;
}


.p-TabBar-tab {
  flex: 0 1 125px;
  min-height: 20px;
  max-height: 20px;
  min-width: 35px;
  margin-left: -1px;
  border: 1px solid #C0C0C0;
  border-bottom: none;
  padding: 0px 10px;
  background: #E5E5E5;
  font: 12px Helvetica, Arial, sans-serif;
}


.p-TabBar-tab:first-child {
  margin-left: 0;
}


.p-TabBar-tab.p-mod-current {
  min-height: 23px;
  max-height: 23px;
  background: white;
  transform: translateY(1px);
}


.p-TabBar-tab:hover:not(.p-mod-current) {
  background: #F0F0F0;
}


.p-TabBar-tabIcon,
.p-TabBar-tabText,
.p-TabBar-tabCloseIcon {
  line-height: 20px;
}


.p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon {
  margin-left: 4px;
}


.p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:before {
  content: '\f00d';
  font-family: FontAwesome;
}

.p-TabBar-tab.p-mod-drag-image {
  min-height: 23px;
  max-height: 23px;
  min-width: 125px;
  border: none;
  box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
  transform: translateX(-40%) translateY(-58%);
}

是什么原因导致此错误,以及如何解决(例如,我可以正确呈现窗口小部件,如here所示)?

1 个答案:

答案 0 :(得分:1)

我对PhosphorJS并不熟悉,所以我只能对发生的事情进行解释。

您似乎在尝试通过执行{{ dpanel }}在模板中呈现DockPanel实例。这行不通。这种“胡子语法”用于呈现基本数据类型,例如字符串和数字,如果您以此方式呈现对象,则Vue将通过在其上调用JSON.stringify来呈现该对象的JSON表示形式;在这种情况下,dpanel对象包含循环引用,因此将失败。

我刚刚浏览了PhosphorJS docs。看起来PhosphorJS不是Vue组件库,因此您必须自己将DockPanel节点插入DOM中,可能在mounted钩中。

mounted() {
  this.$el.appendChild(this.dpanel.node)
},

destroyed() {
  this.dpanel.dispose()
}

同样,我对PhosphorJS并不熟悉,所以这可能不是最正确的建议。