diff --git a/README.md b/README.md index cf1f606..d2f1e52 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,11 @@ Note: 2. Computed properties can be declared as class property accessors. -3. `data`, `render` and all Vue lifecycle hooks can be directly declared as class member methods as well, but you cannot invoke them on the instance itself. When declaring custom methods, you should avoid these reserved names. +3. Initial `data` can be declared as class properties ([babel-plugin-transform-class-properties](https://babeljs.io/docs/plugins/transform-class-properties/) is required if you use Babel). -4. For all other options, pass them to the decorator function. +4. `data`, `render` and all Vue lifecycle hooks can be directly declared as class member methods as well, but you cannot invoke them on the instance itself. When declaring custom methods, you should avoid these reserved names. + +5. For all other options, pass them to the decorator function. ``` js import Component from 'vue-class-component' @@ -30,18 +32,18 @@ import Component from 'vue-class-component'

prop: {{propMessage}}

msg: {{msg}}

+

helloMsg: {{helloMsg}}

computed msg: {{computedMsg}}

` }) class App { - // return initial data - data () { - return { - msg: 123 - } - } + // initial data + msg = 123 + + // use prop values for initial data + helloMsg = 'Hello, ' + this.propMessage // lifecycle hook mounted () { diff --git a/example/example.ts b/example/example.ts index 99bb431..5affdb4 100644 --- a/example/example.ts +++ b/example/example.ts @@ -8,14 +8,8 @@ import Component from '../lib/index' }) class App extends Vue { propMessage: string - msg: number - - // return initial data - data () { - return { - msg: 123 - } - } + msg: number = 123 + helloMsg: string = 'Hello, ' + this.propMessage // lifecycle hook mounted () { @@ -45,6 +39,7 @@ class App extends Vue { }), h('p', ['prop: ', this.propMessage]), h('p', ['msg: ', this.msg]), + h('p', ['helloMsg: ', this.helloMsg]), h('p', ['computed msg: ', this.computedMsg]), h('button', { on: { click: this.greet }}, ['Greet']) ]) @@ -55,5 +50,5 @@ class App extends Vue { // mount new App({ el: '#el', - render: h => h(App, { props: { propMessage: 'Hello!' }}) + render: h => h(App, { props: { propMessage: 'World!' }}) }) diff --git a/src/component.ts b/src/component.ts index 22e737b..1861d79 100644 --- a/src/component.ts +++ b/src/component.ts @@ -1,5 +1,6 @@ import * as Vue from 'vue' import { VueClass } from './declarations' +import { collectDataFromConstructor } from './data' const internalHooks = [ 'data', @@ -44,6 +45,14 @@ export function componentFactory ( } } }) + + // add data hook to collect class properties as Vue instance's data + ;(options.mixins || (options.mixins = [])).push({ + data (this: Vue) { + return collectDataFromConstructor(this, Component) + } + }) + // find super const superProto = Object.getPrototypeOf(Component.prototype) const Super: VueClass = superProto instanceof Vue diff --git a/src/data.ts b/src/data.ts new file mode 100644 index 0000000..b7fda12 --- /dev/null +++ b/src/data.ts @@ -0,0 +1,28 @@ +import * as Vue from 'vue' +import { VueClass } from './declarations' + +const noop = () => {} + +export function collectDataFromConstructor (vm: Vue, Component: VueClass) { + // Create dummy Vue instance to collect + // initial class properties from the component constructor. + // To prevent to print warning, + // the data object should inherit Vue.prototype. + const data = Object.create(vm, { + _init: { + get: () => noop + } + }) + + // call constructor with passing dummy instance + // `data` object will have initial data + Component.call(data) + + // create plain data object + const plainData = {} + Object.keys(data).forEach(key => { + plainData[key] = data[key] + }) + + return plainData +} \ No newline at end of file diff --git a/test/test.ts b/test/test.ts index fe26222..d778a6b 100644 --- a/test/test.ts +++ b/test/test.ts @@ -25,6 +25,44 @@ describe('vue-class-component', () => { expect(destroyed).to.be.true }) + it('data: should collect from class properties', () => { + @Component({ + props: ['foo'] + }) + class MyComp extends Vue { + foo: number + a: string = 'hello' + b: number = this.foo + 1 + } + + const c = new MyComp({ + propsData: { + foo: 1 + } + }) + expect(c.a).to.equal('hello') + expect(c.b).to.equal(2) + }) + + it('data: should collect custom property defined on beforeCreate', () => { + @Component + class MyComp extends Vue { + $store: any + foo: string = 'Hello, ' + this.$store.state.msg + + beforeCreate () { + this.$store = { + state: { + msg: 'world' + } + } + } + } + + const c = new MyComp() + expect(c.foo).to.equal('Hello, world') + }) + it('methods', () => { let msg: string = ''