vue组件内应该新建vue对象吗

1.问题

.vue文件的export default能替换为新建的vue对象吗?怎样转换?或者,新建vue对象的代码,怎样转换为export default形式?

vue-cliwebpack建立的项目,自带了根组件App.vuecomponents/Hello.vue子组件,以及入口文件main.jsindex.html。作为组件存在的.vue文件的<script></script>标签内,通常会这样写:

export default {
    data () {
        return {
            some_data: 'this is some data',
            another_items: ['this', 'is', 'another', 'data']
        }
    },
    methods: {
        actionOne () {
            // do sth
        },
        actionTwo () {
            // do another thing
        }
    },
    ...
}

但有时候一些代码实例往往是会新建一个Vue对象的,甚至多个Vue对象,比如我想在vuejs中使用CodeMirror代码高亮库,找到的参考代码(这里只贴了vue里面的js代码,html和css见http://codepen.io/royportas/pen/XKXXzv/


var model = {
  text: '',
  stats: {
    words: 0,
    chars: 0
  }
};

var TallyComponent = Vue.extend({
  template: "#tally",
  data: function() {
    return {
      model: model
    }
  },
  
  computed: {
    words: function() {
      return this.model.text.split(' ').length - 1;
    },
      
    chars: function() {
      return this.model.text.length;
    },
    
    lastChar: function() {
      return this.model.text[this.model.text.length - 1];
    }
  }
});

Vue.component('tally', TallyComponent);

var app = new Vue({
  el: '#app',
  data: function() {
    return {
      model: model,
      editor: null
    }
  },
  
  created: function() {
    var scope = this;
    
    this.editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
      lineNumbers: true
    });
    
    this.editor.on('change', function(cm) {
      scope.model.text = cm.getValue();
    });
  }
});

这个时候有几个疑问:

  1. .vue组件文件内定义vue对象合适吗?export default出来一个对象,似乎是官方倾向使用的方式?

  2. 我怎样把上面的代码,转换为export default的形式?

======更新======
现在问题还是没有解决,vue-cli的webpack模版建立项目,目录结构和用到的文件(红框标出):

clipboard.png

index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 新 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
    
    <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
    <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>

    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  <title>rsite</title>
</head>

<body>
  <app></app>
  <!-- built files will be auto injected -->
  </div>

</html>

src/main.js

import Vue from 'vue'
import App from './App'

/* eslint-disable no-new */
new Vue({
  el: 'body',
  components: { App }
})

src/App.vue

<template>
  <div id="app">
    <div class="container" id="app">
    <h1>CodeMirror Example</h1>
    
    <div class="row">
      
      <div class="col-sm-4">
        <tally :model="model"></tally>
        
        <div class="card card-block">
          <p>{{model.text}}</p>
        </div>
      </div>
      
      <div class="col-sm-8">
        <div class="card card-block">
          <h3 class="card-title">Editor Pane</h3>
          <textarea id="editor" v-model="model.text">
          </textarea>
        </div>
      </div>

    </div>

  </div>
</template>


<script>
import tally from './components/tally.vue'
import CodeMirror from 'codemirror'
var model = {
  text: '',
  stats: {
    words: 0,
    chars: 0
  }
}
export default {
  data () {
    return {
      model: model
    }
  },
  components: {
    tally
  },
  created () {
    var scope = this
    this.editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
      lineNumbers: true
    })
    this.editor.on('change', function (cm) {
      scope.model.text = cm.getValue()
    })
  }
}
</script>

src/components/tally.vue

<template>
  <div id="tally">
    <div class="card card-block">
      <p>Total words: <b>{{words}}</b></p>
      <p>Total chars: <b>{{chars}}</b></p>
      <p>Last character: <b>{{lastChar}}</b></p>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    words () {
      return this.model.text.split(' ').length - 1
    },
    chars () {
      return this.model.text.length
    },
    lastChar () {
      return this.model.text[this.model.text.length - 1]
    }
  },
  props: {
    model: {
      required: true
    }
  }
}
</script>

现在的问题:页面是空白,报错提示:

clipboard.png

如果去掉App.vue里面的这两句:

import CodeMirror from 'codemirror'
,
  created () {
    var scope = this
    this.editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
      lineNumbers: true
    })
    this.editor.on('change', function (cm) {
      scope.model.text = cm.getValue()
    })
  }

那么页面就正常显示了,但是这明显没有使用到codemirror

====再次更新====

我觉得codepen.io上那个例子过于麻烦了,我其实最需要的就是代码实时高亮,也就是在<textarea>里面输入后立即看到高亮过的代码。所以现在写成这样了:

index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8"/>
  <script src="static/lib/codemirror.js"></script>
  <link rel="stylesheet" href="static/lib/codemirror.css">
  <script src="static/mode/javascript/javascript.js"></script>
<!-- 以上三个引入的外部文件,是把下载后的codemirror库中的lib和mode目录拷贝到当前项目的static目录中做到的 -->
</head>

<body>
  <app></app> <!-- 用来对比 -->
  
  <div>
    <h1>index.html中定义</h1>
    <textarea id="myTextArea" cols="10" rows="10"></textarea>
  </div>
  <script type="text/javascript">
    var editor = CodeMirror.fromTextArea(document.getElementById('myTextArea'), {
      lineNumbers: true,
      mode: 'javascript'
    })
  </script>
  <!-- built files will be auto injected -->

</html>

main.js:

import Vue from 'vue'
import App from './App.vue'

/* eslint-disable no-new */
new Vue({
  el: 'body',
  components: {
    App
  }
})

App.vue

<template>
  
    <h1>通过app.vue定义</h1>
    <textarea id="myTextArea" cols="10" rows="10"></textarea>
  
</template>

<script>
import CodeMirror from 'codemirror'
export default {
  ready () {
    editor: CodeMirror.fromTextArea(document.getElementById('myTextArea'), {
      lineNumbers: true,
      mode: 'javascript'
    })
  }
}
</script>

结果截图:

clipboard.png

很明显,通过组件App.vue编写的代码高亮区域并没有高亮,但是两个对比的区域代码几乎是一直的,区别仅仅在于写法的问题。该如何修改?

阅读 10.6k
5 个回答
  • webpack开发形式中,完全不需要New,每个组件都会被loader自动重载为实例或组件对象;
    这一步在ng2中是以ES7装饰器形式实现的,vue中是loader实现的。

  • 我是看了你提的其他几个问题,觉得开发方式不太对,Webpack开发模式中尽量不要按照Browers端开发模式去走了

一个入口文件实例:

// app.js

// babel-polyfill
import "babel-polyfill"

// Libs
import Vue from 'vue'
import Router from 'vue-router'
import Resource from 'vue-resource'
import productRouter from './appRouter'
import VueAsyncData from 'vue-async-data'

// filters
import filters from './filters'
Object.keys(filters).forEach(i => Vue.filter(i, filters[i]))

// use
Vue.use(Router)
Vue.use(Resource)
Vue.use(VueAsyncData)

// 注册动画
Vue.transition('fadeIn', {
  type: 'animation',
  enterClass: 'fadeIn',
  leaveClass: 'fadeOut'
})

// 路由
const router = new Router({
  history: true,
  saveScrollPosition: false,
  transitionOnLoad: true
})

// productRouter
productRouter(router)

// 启动App
router.start(App, 'app')
  • 针对这个问题,研究了一下,写了个扩展

npm:https://www.npmjs.com/package...

Github:https://github.com/surmon-chi...

  • 使用方法

1:npm install vue-codemirror

2:依赖:

import Vue from 'vue'

...

import CodeMirror from 'vue-codemirror'

Vue.use(CodeMirror)

3:组件中使用

<codemirror :code.sync="code"  :options="{}"></codemirror>

具体options配置什么可以直接打开下面链接

https://www.npmjs.com/package/vue-codemirror

如:
<input v-model="code">
<pre>{{ code }}</pre>
<codemirror :code.sync="code"></codemirror>

**注!**
由于codemirror 的问题(光标会闪烁至首格),
仅支持 
父 -> 子数据流的初始化和单向绑定,
子 -> 父数据流实时响应


一般全局都应该是一个vue实例实例吧,我觉着,等等看看别人的回答

  1. 可以定义,但一般不会作为程序入口,可能只是某个事件总线或什么东西(但也会单独拿出来).

tally-component.vue

<template>
<div id="#tally">...</div>
</template>

<script>
const model = {
  text: '',
  stats: {
    words: 0,
    chars: 0
  }
}

export default {
  data () {
    return {
      model: model
    }
  },
  
  computed: {
    words () {
      return this.model.text.split(' ').length - 1;
    },
      
    chars () {
      return this.model.text.length;
    },
    
    lastChar () {
      return this.model.text[this.model.text.length - 1];
    }
  }
}
</script>

app.js

import TallyComponent from './tally-component.vue'
const app = new Vue({
  el: '#app',
  components: {
    TallyComponent
  },
  ...
})

app.html

<body>
  <div id="app">
    <tally-component></tally-component>
  </div>
</body>

update:是不是textarea的v-model影响了codeMirror,去掉试试

vue的模块化就是在参数那一层去做的,你没必要在每个组件里去实例化vue对象,只在最外层,也就是脚手架工具的main.js里做一次实例化,接下来都是这个组件的参数。

你的代码里,我觉得Vue.extend可以做一个父类,放在一个单文件组件里,script标签就是extend内部的参数对象。
app是你扩展以后Vue类的一个实例化组件,继承了Vue.extend里提供的方法。

但是我看TallyComponent里的内容好像不像是需要扩展到Vue类里的,本身可以理解为一个组件,而app看起来像是入口组件。所以你应该先搞清楚这个TallyComponent以及app组件之间的关系,如果TallyComponent只是一个组件,它就是一个单文件*.vue组件,app那个new vue就可以放在入口的main.js里。

问题很明显,应当在 ready 中初始化 cm 。你在 created 中初始化它,此时 vue 的 dom 还没有插入到页面,自然是报错了。

至于.vue 的写法,只是更好的组件书写方式,编译后生成的 js,和用 js 直接写一个组件没有什么不同。在这里没有高亮的问题,只是因为你没有引入 cm 的主题 css 和 mode 的 js。import cm 只是引入了 cm 的内核,要渲染什么语言,还要引入对应的 mode 脚本。

单页上应该只有一个 new vue 对象,用于挂载应用,其他的都应当作为组件应用

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题