GithubHelp home page GithubHelp logo

rfcs's Introduction

Vue RFCs

What is an RFC?

The "RFC" (request for comments) process is intended to provide a consistent and controlled path for new features to enter the framework.

Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow.

Some changes though are "substantial", and we ask that these be put through a bit of a design process and produce a consensus among the Vue core team and the community.

The RFC life-cycle

An RFC goes through the following stages:

  • Pending: when the RFC is submitted as a discussion thread. We use discussions instead of Pull Requests as the former provides better discussion threading.
  • Active: when an RFC is acknowledged and undergoing implementation. The feature may be shipped as experimental during this phase.
  • Landed: when an RFC's proposed changes are shipped as stable in a release.
  • Rejected: when an RFC is officially rejected or dropped.

When to follow this process

You need to follow this process if you intend to make "substantial" changes to Vue core.

We are limiting the RFC process to core to keep the workflow manageable. If you wish to suggest changes to those other projects, please use their respective issue lists.

What constitutes a "substantial" change is evolving based on community norms, but may include the following:

  • A new feature that creates new API surface area
  • Changing the semantics or behavior of an existing API
  • The removal of features that are already shipped as part of the release channel.
  • The introduction of new idiomatic usage or conventions, even if they do not include code changes to Vue itself.

Some changes do not require an RFC:

  • Additions that strictly improve objective, numerical quality criteria (speedup, better browser support)
  • Fixing objectively incorrect behavior
  • Rephrasing, reorganizing or refactoring
  • Addition or removal of warnings
  • Additions only likely to be noticed by other implementors-of-Vue, invisible to users-of-Vue.

If you submit a pull request to implement a new feature without going through the RFC process, it may be closed with a polite request to submit an RFC first.

Why do you need to do this

It is great that you are considering suggesting new features or changes to Vue - we appreciate your willingness to contribute! However, as Vue becomes more widely used, we need to take stability more seriously, and thus have to carefully consider the impact of every change we make that may affect end users. On the other hand, we also feel that Vue has reached a stage where we want to start consciously preventing further complexity from new API surfaces.

These constraints and tradeoffs may not be immediately obvious to users who are proposing a change just to solve a specific problem they just ran into. The RFC process serves as a way to guide you through our thought process when making changes to Vue, so that we can be on the same page when discussing why or why not these changes should be made.

Gathering feedback before submitting

It's often helpful to get feedback on your concept before diving into the level of API design detail required for an RFC. You may open an issue on this repo to start a high-level discussion, with the goal of eventually formulating an RFC pull request with the specific implementation design.

What the process is

In short, to get a major feature added to Vue, one must first get the RFC merged into the RFC repo as a markdown file. At that point the RFC is 'active' and may be implemented with the goal of eventual inclusion into Vue.

  1. Work on your proposal in a Markdown file based on the template (0000-template.md) found in this repo.

    • Put care into the details: RFCs that do not present convincing motivation, demonstrate understanding of the impact of the design, or are disingenuous about the drawbacks or alternatives tend to be poorly-received.
  2. Open a new thread in Discussions and make sure to set category to "RFC Discussions".

    • Build consensus and integrate feedback in the discussion thread. RFCs that have broad support are much more likely to make progress than those that don't receive any comments.
  3. Eventually, the core team will decide whether the RFC is a candidate for inclusion in Vue.

    • An RFC can be modified based upon feedback from the core team and community. Significant modifications may trigger a new final comment period.

    • An RFC may be rejected after public discussion has settled and comments have been made summarizing the rationale for rejection. A member of the core team should then close the RFC's associated pull request.

    • An RFC may be accepted at the close of its final comment period. A core team member will merge the RFC's associated pull request, at which point the RFC will become 'active'.

  4. If the proposal has been approved for inclusion, you can prepare a Pull Request:

    • Fork this repo.

    • Create your proposal as active-rfcs/0000-my-feature.md (where "my-feature" is descriptive. don't assign an RFC number yet).

    • Submit a pull request. Make sure to link to the discussion thread.

Details on Active RFCs

Once an RFC becomes active then authors may implement it and submit the feature as a pull request to the Vue core repo. Becoming 'active' is not a rubber stamp, and in particular still does not mean the feature will ultimately be merged; it does mean that the core team has agreed to it in principle and are amenable to merging it.

Furthermore, the fact that a given RFC has been accepted and is 'active' implies nothing about what priority is assigned to its implementation, nor whether anybody is currently working on it.

Modifications to active RFC's can be done in followup PR's. We strive to write each RFC in a manner that it will reflect the final design of the feature; but the nature of the process means that we cannot expect every merged RFC to actually reflect what the end result will be at the time of the next major release; therefore we try to keep each RFC document somewhat in sync with the language feature as planned, tracking such changes via followup pull requests to the document.

Implementing an RFC

The author of an RFC is not obligated to implement it. Of course, the RFC author (like any other developer) is welcome to post an implementation for review after the RFC has been accepted.

An active RFC should have the link to the implementation PR listed if there is one. Feedback to the actual implementation should be conducted in the implementation PR instead of the original RFC PR.

If you are interested in working on the implementation for an 'active' RFC, but cannot determine if someone else is already working on it, feel free to ask (e.g. by leaving a comment on the associated issue).

Reviewing RFC's

Members of the core team will attempt to review some set of open RFC pull requests on a regular basis. If a core team member believes an RFC PR is ready to be accepted into active status, they can approve the PR using GitHub's review feature to signal their approval of the RFC.

Vue's RFC process owes its inspiration to the React RFC process, Rust RFC process and Ember RFC process

rfcs's People

Contributors

ackzell avatar antfu avatar atilkan avatar bodograumann avatar briwa avatar chenxsan avatar chriscalo avatar chrisvfritz avatar craigharley avatar cyberap avatar dobromir-hristov avatar henriqemalheiros avatar jiangying000 avatar justineo avatar kigawas avatar linusborg avatar lmiller1990 avatar mesqueeb avatar mrienstra avatar niko278 avatar petr001 avatar pierresaid avatar posva avatar smolinari avatar underfin avatar vaaski avatar wenfangdu avatar xpaw avatar yyx990803 avatar zmtlwzy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rfcs's Issues

Inject context and some builtin InjectionKeys

i think that should exists some builtin keys like ContextKey
const ContextKey: InjectionKey<SetupContext> = Symbol('tt')
so we can get context (store, router) through injection like

import { InjectionKeys } from 'vue'

function useProduct() {
  const { root: { $store } } = inject(InjectionKeys.Context)
  // do something with $store
}

this can make more modular "use" functions

Function-based component API (extended discussion)

Opening an issue here with a copy of my original comment to #42, per @yyx990803's request:


I am the author of "Love letter to Vue": http://www.evaristesys.com/blog/love-letter-to-vue/

As a big booster of Vue who can appreciate its distinctive characteristics and the way it has stood apart from competing frameworks, I must say that I am perturbed and disappointed by the function API proposal. I have to agree with the above posters who say that no serious problem is being solved here which would warrant such a radical shift, and that in essence, this proposal amounts to chasing shiny new things. Yes, there are indeed some code decomposition and modularity problems that this change addresses. But not all theoretical problems are worth addressing.

One of the biggest virtues of Vue, which I mention in the article, is that here, halfway through 2019, it's still quite literally the same Vue I first picked up in late 2016. This is a refreshing contrast to the general pace of change and deprecation in the JS/web world. Frank Chimero's high-level, perspicacious take on this problem bears mention here: https://frankchimero.com/writing/everything-easy-is-hard-again/

From a political and a philosophical point of view, I think it's vital to understand that introducing radical API changes of any kind to any project is breaking, and a generous offer of backward compatibility with old APIs does not solve this problem. With due acknowledgment to the fact that the 2.x API is not slated for deprecation and the labourious emphasis this has received, you're still sunsetting it in an overall ecosystem sense. The 2.x API will no longer be a first-class citizen. A completely new way of doing things insinuates a preferred new way of doing things, and sample code, tutorials, books, etc. will inevitably adopt it over time, leading to a manageability and direction crisis for those with investments in huge Vue code bases. New APIs are unavoidably ideological statements, and the devaluation of old APIs -- indeed, the very ontology of "old" vs. "new" -- have an ideological valence.

While change is inevitable, people want to follow "best practices" in an effort to stave off bit rot, and feel a psychological pressure to buy into the current wave of thinking. So, offering to support the old options API is not of much help; this RFC raises the prospect that the commitment to maintaining backward compatibility with the 2.x option API is not steadfast, and will slowly de-orbit over time, like the Mir space station. In effect, you are decreeing that there's now a new way of doing things, and this is the way they should be done.

I also agree with the criticism mentioned in the Downsides section above: the concern that this way of doing things is arcane and more likely to lead to byzantine or labyrinthine "spaghetti code". One of the biggest selling points of Vue is its simplicity and approachability. It's static enough that a major organisational commitment to Vue has shelf life and durability, rather than evaporating in one's hands, as so many other JavaScript-related commitments do owing to capricious, whimsical API changes and architectural gewgaws.

Thus, technical bickering about code organisation and modularity notwithstanding, from a business and ecosystem health point of view, I think this is the light in which this RFC should be considered, and the cost against which the (from my perspective, marginal) improvements offered should be carefully and judiciously weighed. I do not see a sufficiently captivating problem here worthy of such a radical solution, even if commitment to the 2.x approach is scrupulously observed for some time. It's not worth throwing away the things that make Vue good.

I'm natively Russian, and there is an old political anecdote about this:

Lazar Kaganovich brings Stalin a scaled-down model of a new, reconstituted Moscow, the grandiose global Capital of Socialism that Stalin envisioned (not unlike the visions of Hitler & Speer for the Thousand Year Reich architecture). Most iconic historical features of pre-revolutionary Moscow are demolished, and replaced with grand plazas and enormous, titanic buildings that capture the idealism of the class-conscious revolutionary proletariat, the enormity of the Generalissimus's futuristic vision, and the zeal to shed bourgeois architecture and aesthetics and break with the past.

Stalin asks: "Lazar, where is Saint Basil's Cathedral?"

"It was removed; it will be demolished."

Stalin heaves a sigh and rolls his eyes.
"Lazar, put it back."

Necessity of `reactive` call on Component API result

This is not a proposal per se but I would like to raise some points about always calling reactive on the value returned by setup (return value that I'm gonna name scope in this issue).

Code is right here:
https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/component.ts#L355

Intro: what it achieves

Calling reactive() on the return value of setup() enables:

  • (deep) reactivity on every scope field, even those that aren't ref in setup.
  • reactivity on field addition/removal.
  • unwrapping ref values for easy consumption in template.
  • (impl. detail) turns the scope into a proxy.

1. Missed optimizations

reactive() makes every field and property deeply reactive by default.
This can be counter-intuitive and misses some optimization opportunities.

Consider:

const MyApp = {
  setup() {
    return {
      count: ref(0),
      data: [/* lots of objects /*],
    };
  }
};

I would expect count to be a reactive property, while data is a non-reactive array. Maybe some read-only objects I got from server and I don't intend on modifying.

This gives me total control over the reactivity, and only what I intend on modifying is actually reactive.

But the scope will actually be fully reactive. This means every object in data is gonna get wrapped in a proxy. Every UI binding is gonna create change listeners. All this work could have been avoided.

Of course there's readonly() that enables me to avoid that if I really want to, but the default behavior doesn't feel intuitive.

It's also slightly inconsistent. Why create reactive data in setup, if it'll be reactive anyway? Answer: you need to create reactive data if you intend to use it inside setup, e.g. in compute() or watch(). Otherwise you don't have to. It feels weird and arbitrary.

2. To value or not value?

Speaking of inconsistency, Whether you have to use value or not is also a bit inconsistent.

Some users may deviate just slightly from the recommended pattern and do this:

setup() {
   let scope = {
     x: ref(0), 
     y: ref(0),
     sum() { return this.x.value + this.y }
   };
  return scope;
}

Using this in sum kind of works, but cannot work everywhere.

Inside setup, if you create a watch for example, scope.sum works if you do this.x.value, because this is scope and x is a ref.
In the template on the other hand, sum works if you do this.y because this is reactive(scope) and the ref has been unwrapped.

Of course, the solution is to not use this but refer to scope directly. You can be sure some users will fall into this trap.

Using scope doesn't solve everything either, as we fall back into the inconsistencies of 1. If I declare something without a ref inside scope, say x: 0 then event handlers that set it on scope will escape the change tracking, despite everything I said in 1 (since they would access the target of the proxy directly).

3. Unwrap refs ourselves

Point 2 shows that .value usage can become confusing.
History (I'm referring to Knockout here) also shows that devs don't like having accessors everywhere: they're verbose and it's easy to forget or misuse them if you don't have a type system (e.g. you write plain JS).

So you may be tempted to do this:

setup() {
   let scope = reactive({ x: 0, y: 0 });
}

And now you can use scope all you want without ever writing a .value.
This works great, but at this point doing reactive on the returned value is a noop.

If we go back to 1, and you want to have easy, fine control over what property is reactive and which one is not, you may be tempted to write your own function, say hideRefs that either transform every field holding a ref into a getter/setter that unwraps them; or wrap the thing behind a proxy that does the same.

setup() {
   // count is reactive, data is not, everything is usable without .value
   let scope = hideRefs({ count: ref(0), data: []);
}

In theory this is a great solution but the automatic reactive will uselessly wrap this into a proxy. Additionally, every access (read/write) to count will be double-tracked, once by the reactive proxy, and once by the ref hidden inside.

4. Class-based components

I totally understand why Vue 3 is gonna stay away from decorators, at least until they advance to a further stage.

That said, some users may still think using decorators in their project is ok. They might like the following syntax:

@component
class MyApp {
  @ref x = 0;
  @ref y = 0;

  @computed 
  get sum() { return this.x + this.y }

  @watch
  doSomething() { }
}

It's not very complicated to write the decorators that make this work. It won't be long before someone publishes them and that's fine.

There are two issues here:

  • The result of this code pattern suffers the same problem as the hideRefs technique described in 3.
  • Should users use private fields (they're becoming a thing); and should the proxy + private fields incompatibility not get addressed by TC39; then the pattern will fail miserably.

As far as I can tell, if reactive was not called on the scope automatically, this would work perfectly.

Conclusion

I love the explicit control given by ref and the new Composition API ❤️
In fact, I've been wanting this in a modern framework since the day I stopped using Knockout.

I feel like the automatic reactive call on the scope is removing a lot of that explicit control -- or making it a lot more verbose to reclaim (readonly and co.).

First idea that comes to mind is that users should be responsible for the precise shape of their scope, i.e. they need to call reactive themselves if they want to.

  • It means you can access the same object (proxy or not) in the template and in setup.
  • Many people will often call reactive anyway, because it's more convenient than doing lots of ref and value.
  • Conceptually, it's not harder than explaining toRefs...

Also: I think the hideRefs (actual name to be defined) function I mentionned above should probably in Vue core. It's a better choice to call on the scope than reactive.

"Rendered" links from pull requests need updating

Replace `reactive` with `scope` proposal to Functional-based Component API

Having a bad insomnia right now, so what I am going to write in this proposal might or might not make sense. Please be gentle. Just throwing ideas to try make things better for everyone.

This is a proposal for replacing reactive with scope in RFC #42 and #63.

Motivation

This proposal aims to address the issue with .value in value wrapper (bindings) returned from binding() and computed().

.value is the only thing from the original RFC which I am worried about. As much as I don't like .value, there isn't any other way to provide reactivity for primitive values without wrapping them in objects.

The existence of bindings is to compensate the fact that values are no longer required to attach to any object (previously in 2.x, values are attached to this), which result in not being able to provide reactivity without wrapping the value with an object.

This proposal introduces scope() which act as a place where value bindings could attach to. The scope object is a proxy that will forward get/set to value bindings.

scope()

setup() {
  const counter = scope({
    count: binding(0),
    message: 'hello world', // short hand for binding('hello world')
    plusOne: computed(() => counter.count + 1),
    setCount: n => counter.count = n;
  });

  // do not recommend use of loose bindings/methods
  const plusTwo = computed(() => counter.count + 2);
  const plusTwoMessage = computed(() => `${plusTwo.value} is +2.`);
  const acc = () => counter.count++;

  const awesome = useAwesome(counter.count);

  return {
    ...toBindings(counter),
    ...toBindings(awesome),
    plusTwo,
    plusTwoMessage
    acc
  }
}

Spreading scopes

As suggested in #63 , scope uses toBindings before spreading.

scope() vs reactive()

While they are very similar, conceptually they are different. reactive() creates a reactive object whereas scope() provides a namespace for bindings and methods.

A reactive object implies that entries are "properties" of the object. For example, user = reactive({ ... }) suggests that all keys in user is user's (user.name, user.gender, user.age...).

Scope, on the other hand, implies that entries are just part of the namespace. For example, user = scope({...}) suggests that user is just a scope. Variables attached to it do not necessarily belong to the actual "user". (user.authenticated, user.errorMessage, user.login()...)

Suggest returning scopes from composition functions

It was previously mentioned by Evan #63 (comment):

  • only recommend using reactive directly inside setup()

  • when you are extracting logic into a composition function:

    • always name the function as useXXX
    • always return a binding, or an object of bindings.

With the introduction to scope, I would suggest all bindings to be scoped.

So:

  • avoid use of binding()/computed() outside of scope()
  • when you are extracting logic into a composition function:
    • always name the function as useXXX
    • always return a scope

Benefits

  • If binding() and computed() are scoped, then no need for .value.
  • Encourage grouping relevant things inside the same scope.

Drawbacks

  • Needing to do namespace.prop in exchange for getting rid of prop.value. (but it is probably more intuitive to do namespace.prop than prop.value.
  • Loose bindings will still need to do prop.value. (So, .value is not actually solved in this proposal.)

Follow up Issues

Could we make setup to return scope directly?

setup() returns an object with bindings and methods. This is basically a scope. So could we return scope directly which can avoid the need to toBindings()? A mergeScopes helper can be useful too.

setup() {
  const counter = scope({
    count: binding(0),
    message: 'hello world', // short hand for binding('hello world')
    plusOne: computed(() => counter.count + 1),
    setCount: n => counter.count = n;
  });

  // do not recommend use of loose bindings/methods
  const plusTwo = computed(() => counter.count + 2);
  const plusTwoMessage = computed(() => `${plusTwo.value} is +2.`);
  const acc = () => counter.count++;

  const awesome = useAwesome(counter.count);

  return mergeScopes(
    counter,
    awesome,
    scope({
      plusTwo,
      plusTwoMessage
      acc
    }),
  )
}

How would we mark properties as readonly?

There is a proposal of readonly(binding) in the original proposal (see #42 (comment)). How should this be achieved with scope?

markReadonly?

function useMouse() {
  const mouse = scope({
    x: 0,
    y: 0,
  });
  const update = e => {
    mouse.x = e.pageX
    mouse.y = e.pageY
  }
  onMounted(() => {
    window.addEventListener('mousemove', update)
  })
  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })
  return markReadonly(mouse, ['x', 'y']);
}

make the whole scope readonly?

function useMouse() {
  const mouse = scope({
    x: 0,
    y: 0,
  });
  const update = e => {
    mouse.x = e.pageX
    mouse.y = e.pageY
  }
  onMounted(() => {
    window.addEventListener('mousemove', update)
  })
  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })
  return readonly(mouse);
}

With mergeScopes() which merges scopes into one. Then we can do something like mergeScopes(readonly(doNotMutateMeScope), mutateMeScope).

Alternatives

Funky Scope

setup() {
  const counterScope = scope(counter => ({
    count: binding(0),
    message: 'hello world', // short hand for binding('hello world')
    plusOne: computed(() => counter.count + 1),
    setCount: n => counter.count = n;
  }));

  // this discourage use of `counterScope` outside
  const setCount = n => counterScope.count.value = n;

  const awesomeScope = useAwesome(counterScope.count.value);

  return {
    ...counterScope,
    ...awesomeScope,
  }
}

Benefits

  • no need toBindings() for spreading as counterScope will be object of bindings.
  • encourage counter things to be scoped inside counterScope.

Drawbacks

  • It's hard to type scope (?)
  • Maybe confusing that it is required to do .value when using counterScope. (But this encourage relevant things to be nested under same scope.)

Function-based component API (async computed)

I'd like to see async computed in this RFC. The usage example looks like this:

import { value, computed } from 'vue'

const url = value("https://icanhazip.com")
const response = computed(async () => await fetch(url.value, { headers: { 'Accept': 'text/plain' } }).then(r => r.text()))

console.log(response) // Your IP (not Promise)

count.value = "https://icanhazdadjoke.com"
console.log(countPlusOne.value) // A joke (not Promise)

Clarify RFC state names

To my understanding, RFC becomes "active" once it is accepted by core team and merged, yet in the readme there's a link to open PRs labeled "Active RFC List".

I suggest extending "The RFC life-cycle" section in README to include other states than "active", assigning them formal names to avoid confusion, and changing the label to "Pending RFC List".

It may also be worth to define what happens with RFC once it is implement.


The problem seems to track back to emberjs/rfcs, possibly introduced when the authors got inspired by rust-lang/rfcs, but skipped integration website

beforeRouteEnter guard in vue-router with composition API

Hello vuers...

This will be sort. Due to beforeRouteEnter guard nature, it is executed before the route that renders the component is confirmed. That's ok.

But as you all may know, this guard offers the possibility of executing a callback once the navigation is resolved via next(vm => ...).

With a mixture of API's, the next snippet achieves what I'm talking about:

<template>
  <div>
    <template v-if="tip">
      {{ tip.prop }}
    </template>
  </div>
</template>

<script>
import { ref, watch } from 'vue'

export default {
  setup () {
    const tip = ref(null)

    watch(tip, () => console.log(tip.value, 'Changed'))

    return { tip }
  },

  beforeRouteEnter (to, from, next) {
    setTimeout(() => {
      next(vm => vm.tip = { prop: 'value' })
    }, 2000)
  }
}
</script>

I'm asking because, in the previous iteration of the composition API, Vue team said the options API was going to be deprecated for v3 and removed in v4. Of course, I know it won't, but then I assume Vue team had plans for this guard. Am I right?

In such case... How the API of this guard will look and how I'll be able to reproduce the above snippet?

Thanks!

Breaking up templates in Vue3

This is a follow up to a discussion in @yyx990803's workshop yesterday.

Specifically I wanted to register a vote for an ability to break up and organize template "chunks" along with reorganized functions/options in Vue 3's composition API.

Use case.. here is the code for the landing page you see at fiction.com

As you can see, the code is nearly 1000 lines long. The composition API will definitely help with this. However, the template alone clocks in at 150 lines. In this there are discrete areas of functionality like figures vs grids, etc..

The initial suggestion was to break things out into multiple components, however, this comes at a cognitive cost in jumping around to different files. As well as what you might call "file bloat"...

Im not sure of suggestions for the direct implementation details, however please consider its feasibility if you haven't already.

关于取消class api后需求该如何实现问题咨询

尤大,您好,我想咨询下,如果取消了class API而使用 functiond base API 像我以下这种业务该如何实现,是否还能像之前一样通过 抽象类限制子类必须实现此接口,并且复用抽象类中的属性,谢谢。

下方为代码:

抽象父类:

import {Vue} from "vue-property-decorator"
import Talk from "@/model/Talk"

export default abstract class TalkBaseVue extends Vue {
    talkList: Talk[]
    abstract queryTalkList(): Talk[]
}

第一种业务子类:

<template>
    <div>
        <!--        不同业务-->
        <div v-for="talk in talkList">
            {{talk}}
        </div>
    </div>
</template>

<script lang="ts">
    import TalkBaseVue from "@/views/talk/TalkBaseVue"
    import Talk from "@/model/Talk"

    export default class FollowTalkList extends TalkBaseVue {
        created(){
            this.talkList = this.queryTalkList()
        }

        queryTalkList(): Talk[] {
            const talkList: Talk[] = FollowTalkList.queryFollowTalkListAPI()
            for (const talk of talkList) {
                //两个子类不同的业务逻辑
            }
            return talkList
        }

        //模拟调用后台
        static queryFollowTalkListAPI(): Talk[] {
            return []
        }
    }
</script>

第二种业务子类:

<template>
    <div>
        <!--        不同业务-->
        <div v-for="talk in talkList">
            {{talk}}
        </div>
    </div>
</template>

<script lang="ts">
    import TalkBaseVue from "@/views/talk/TalkBaseVue"
    import Talk from "@/model/Talk"

    export default class LikeTalkList extends TalkBaseVue {
        created(){
            this.talkList = this.queryTalkList()
        }

        queryTalkList(): Talk[] {
            const talkList: Talk[] = LikeTalkList.queryLikeTalkListAPI()
            for (const talk of talkList) {
                //两个子类不同的业务逻辑
            }
            return talkList
        }

        //模拟调用后台
        static queryLikeTalkListAPI(): Talk[] {
            return []
        }
    }
</script>

Why do we need props?

According to Render function signature change all listeners will be included in attrs. Why not include props into attrs too?

We could extend all props settings such as type, default or required to attrs. An imaginary example:

{
  attrs: {
    modelValue: { type: Number, default: 0 },
    name: { type: String, required: true },
  }
}

That way we would have a single point to get external data - $attr.

someMethod() {
    this.$attrs.class // work with html attribute
    this.$attrs.someProp // work with component prop
    // etc.
}

But, why we need separate props and attrs ?

Proposal to unify reactive and binding with simplified access to reactive data

This a proposal for a Functional API RFC #42 and for an amendment #63.

Motivation

Amendment #63 proposes two methods to define and work with reactive data:

  • reactive — for Vue 2.x-like reactivity, where only objects can be used as a data type
  • binding — a method where you can use primitives as reactive values

These methods work differently and produce different APIs.
For example: reactive will give you direct access to the values in data.

const state = reactive({ counter: 0 });

console.log(state.counter); // 0

At the same time, binding wraps values in an internal object structure, that lets you access reactive data only with on a special property called value.

const counter = binding(0);

console.log(counter.value); // 0

This imposes several problems:

  • Two new ways to do the same thing (we already have data property on the component instance)
  • Confusion on when to use which
  • Need for extra helpers to support spreading in objects
  • A burden of using .value to access reactive data in binding

Proposal

1. Leave only binding and rename it to reactive, make it polymorphic

Bindings should be the common reactive data structure when working with reactivity.
Since reactive is a more self-explanatory word in terms of what it does it fits better as the name of this functionality. reactive should always produce bindings, even nested bindings:

const state = reactive({ foo: { bar: 0 } });

function setBar(value) {
  state.foo.bar.value = value;
}
const counter = reactive(0);

function setCounter(value) {
  counter.value = value;
}

This solves the problem with having too many ways to do the same thing and also unifies the way we work with reactive values. You won't have to decide on whether to pick binding or reactive when decomposing your functions.

There is no need in toBindings helper as well, since we're always returning reactive objects, even if we destructure them.

function useNested() {
  const state = reactive({ nested: { a: 1, b: 2 } });
  return { ...state.nested }; // a & b are bindings
}

2. Provide an easy access to reactive data with valueOf

Instead of constantly using .value every time we want to get reactive value we could make use of valueOf property, that would always return reactive value.

const state = reactive({ foo: { bar: 0 } });

console.log(state.foo.bar); // 0
const counter = reactive(0);

console.log(counter); // 0

This solves the issue with a need for .value to get data.

But it also has some disadvantages:

  • You still have to use .value to set data;
  • Inconsistency between reading and writing.

In general it's more common to read data than write it, so I think it's an acceptable quirk.

You can always opt-out of this functionality, since .value is always there.

Alternatives

setValue helper

A setValue helper could be used to set reactive data, similarly to Vue 2.x vm.$set. That would hide the internal implementation of reactivity from us, but at the same time would be very similar to using .value in terms of ergonomics.

const counter = reactive(0);

setValue(counter, 0);

Set primitives with .value and object props directly

const state = reactive({ counter: 0 });

state.counter = 1;
const counter = reactive(0);

counter.value = 0;

That looks very confusing and I wouldn't really consider it.

Function-based component API (extended discussion)

I used Vue on almost all projects, and even migrated a React project with 100k+ of lines of code to Vue. So I am also a user who loves Vue very much.

The reasons why old Vue is so attractive to me and why new changes make me depressed are listed as follows:

  • If I want to implement a certain logic, there is usually only one way. I can start working right away without extra thinking. However, in future versions, it will make people harder to choose in the use of legacy APIs or new APIs. I don't think this should happen when using a framework.
  • Code organization is easy, even for a team of 100+ of people, the code written will not be much different. Because object-based (or options-based) component definitions constrain the location and implement method of logics of the component. But the function-based component definitions which providing freedom for developers, but also brings uncertainty and lower readability (I need to find in setup() where a value is, where a method is, and which fields are computed, whether a field named isEnabled is value or computed).
  • Based on these advantages, Vue can be learned very quickly. I don't need to be familiar with JavaScript's syntactic sugar or future features to be able to produce with Vue. However, after losing these advantages, it is hard to say that Vue will be a friendly framework. It is more like a project that has been constantly disturbed by the React community and has lost its own characteristics.

The steepness of the learning curve, the readability of the code, and the possible migration costs of the old API being deprecated... these are all terrible problems.

The introduction of function-based APIs has brought about tremendous changes. In a sense, this can be considered a new framework. After all, if the object-based API are deprecated, almost all Vue plugins have to rewrite, and the plugin author should also consider providing Vue 2.x compatible. Not only did the Vue project have a division, but its plugin ecosystem also split.

So I think the Vue team can consider learning about Express and Koa, separating a new framework from Vue to serve high-end users who really need these new features.

Even if the function-based api really becomes mainstream, I won't give up using Vue. Because SFC is the most important feature of Vue for me. But based on my short work experience on WeChat(Tencent), this change will really affect my colleagues (C++ developer) who write web pages in Vue. Because they don't have the energy and time to systematically learn JavaScript syntax and best practices. Everyone just wants a tool that can produce projects quickly.

(Please forgive me for my broken English, it is not my native language, so this is a bit difficult to read. But I am still very eager to express my thoughts.)

Function-based Component API (boilerplates)

Going straight to the point: The proposed Function-based Component API is just incredible! I used to hate it (I was excited about the dropped Class-based API). Now that I have the chance to look into it, I love it!

There is just one single thing that bothers me. It is how we have to end setup() with a return including everything we want to expose. It feels like writing boilerplate code, and it is really easy to forget to include something. That is especially true with large components - it can get a little hard to read (other users seem to agree).

Chances are I am not the only one with such concerns. Take the todomvc.vue example for instance. Computeds and methods are grouped together (probably as an attempt to improve readability), which sadly resembles a lot the old API.

How does the team see this issue? Are there any plans to improve that? (perhaps by returning everything within setup() somehow?)

SFC scoped style improvements

We are aware that the current SFC scoped style implementation has a number of issues/limitations regarding the component root node and slotted content. I'd like to use this thread to collect feedback and help us improve the scoped style implementation.

What are the pain points when using <style scoped>? What features you hope <style scoped> could support? We have some ideas, but we'd like to hear from the users to get a clearer picture. If you have ideas or suggestions, please provide feedback here (and please be as specific as possible, with code samples and clear descriptions of your scenario).

to abolish the "with(){}" syntax in the template compiler in "vue3.0" to improve performance.

The "with" syntax has been banned in strict mode and performance is not very good.

to abolish the "with(){}" syntax in the template compiler in "vue3.0" to improve performance.

https://github.com/vuejs/vue-next/blob/ed29af7bea4f4f78f50992213b56e41bb5bc9052/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap

https://github.com/vuejs/vue-next/blob/e98a85f3cbda7e2c34955295f0a353d42bd48f8c/packages/compiler-dom/__tests__/transforms/__snapshots__/vModel.spec.ts.snap

https://github.com/vuejs/vue-next/blob/1c0a2c6d41dc09706b8c932440d8d519ceb34c57/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap

return function render() {
  with (this) {
    return _createVNode(\\"div\\", {
      id: \\"foo\\",
      [prop]: bar,
      [foo + bar]: bar
    }, [
      _createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
    ], 16)
  }
}

Function-based Component API (extended discussion)

Opening a new issue with a copy of my previous comment as per @yyx990803's request:

As someone who is not normally a part of these types of conversations, this one has caught my eye (and obviously many others). First off, I've read the RFC in its entirety, I've read through every single comment on it to date, I've read the Reddit posts & (most of) the comments on those as of early this morning, and I've read the Hacker News thread as well. Point is, I've done my due diligence before adding my voice to an already loud conversation. I disagree with the oft-presented idea that the external threads are passing out misleading ideas, I left my reading of this thread with a similar mood, albeit with the luxury of seeing changes made in response to community concerns. (which is great!)

I acknowledge that this RFC solves some internal and typing problems, particularly for TS users and larger applications. From that aspect, it's awesome. Truly.

Many, MANY people have brought up how they feel the object API is more intuitive, easier to understand, and easier for them to implement. I won't continue to beat that horse because the dev team has repeated now ad nauseam that they'll keep it around, but make no mistake, from an outside perspective that feels more like a patronizing way to shut people up then a good will gesture acknowledging different use-cases of the framework. It very much comes off as "ok fine, we won't remove it....yet." Correcting a mis-representation of an idea is not a big deal, but pretending like it wasn't originally presented as a deprecation is not the same thing. I would hope you can see how that doesn't inspire confidence in those of us who are quite happy with the current API tone but not the new one?

Furthermore, the comments of "if you don't like it, don't upgrade when to v4" or "just use a plugin" are dismissive and disingenuous as well. If there's no future for me or my company in a technology moving past a certain point, then why invest any more time than I have to into using that technology at all? That's not a threat of intent, but it's a practical decision that should be made sooner rather than later when talking about a change this significant. I've seen many challenges to the actual significance of the change and all I can say is that yes, for me it is quite a significant change.... and clearly I'm not alone.

There's been plenty of comments with the basic tone of trust us, we know what's better for you. If all applications and all development teams looked the same, then maybe I could buy into that. This comment even took it further and said:

Trust me, if you have been working as a project leader/architect for more than five years maintaining frontend projects that are complex and meaningful enough, you might probably agree that, the ultimate way to improve your team's productivity and meet your deadlines is not to stick to some old and familiar development fashion that comforts you, but quite the opposite -- to go with frameworks that are type-safe, composition/reusability friendly and help them envolve to furthermore meet these standards.

That was especially distasteful and condescending to me, as if smaller apps using Vue weren't worthy to be part of the conversation any longer. I've been on both sides of that fence, both large projects and small, and I can honestly say that it was never EVER the size of the project that made it "meaningful".

Early on, I found Vue to be an inviting alternative to front end development vs. the other alternatives that were either A) not as intuitive to me or B) constantly changing to such a degree that it made standards compliance a time sink with no reasonable ROI. The object API was easy to understand and had IMO a low barrier to entry. I've evangelized for Vue with the last 3 companies I've worked for, and successfully convinced 2/3 of those teams to go with Vue for new projects for both the under the hood features (which we all know, love, and appreciate) but also for the ease of on-boarding and low cost of training someone new with the 2.x api. I disagree with the dev team's insistence that this new RFC concept contains that same spirit as it is presented here, but my biggest concern is those on the core team (not all, to be clear) who have had such dismissive and condescending attitudes towards the backlash, Evan included.

Finally, coding preference is absolutely a valid metric to use for choosing one framework over another, and is IMO just as valid here. I think most of us want to see Vue improve without sacrificing that which made us love it, both technical and code aesthetic if possible.

Allow use number as value of size property in inline style

Current:

<template>
  <div :style="{ height: size + 'px'}"></div>
</template>
<script>
export default {
  data () {
    return {
      size: 10
    }
  }
}
</script>

Expect:

<template>
  <div :style="{ height: size }"></div>
</template>
<script>
export default {
  data () {
    return {
      size: 10
    }
  }
}
</script>

Similar to the design in React, the numeric value in the style sheet object is automatically added “px” as unit.

Add `v-using` property to scoped slots to provide helper components to scoped content

There have been several times I've thought that it would be really convenient if a component with scoped slots could provide components, as well as props data to the component that's consuming it.

Disclaimer: I realize that this can be done by passing a component definition through the scoped slot and using <component :is="x" />. However, I think this often hurts the semantic elegance and ease-of-readability of the template. It also makes the api slightly more obtuse, because v-slot is providing data and components without a distinction between the two:

Example:

Suppose I'm writing a library that exposes a reusable. super robust form builder component. All the consumer has to do is provide that data to edit, and a component DynamicFormField processes the data to determine it's display format and the component best suited to edit that data (a multi-select, a radio select, a textarea, a dropdown, etc), as well as the component best suited to label the field and display errors. The user just has to give me what data they want edited and the component does most of the logic work on their behalf. But in addition to choosing the best component for those things, there are several other variables for the consumer to choose that have to do with things the DynamicFormField has no way to deduce, just based on data. The style of the label, the error, and the form control can all be tweaked independently, based on the user context, by a given set of flags.

Right now, I could do it something like this:

<dynamic-form-field
  :value="myFormFieldValue"
  v-slot="{ formattedValue, FieldComponent, LabelComponent, ErrorComponent }">
  <component :is="FormLabelComponent" [bold/italic/uppercase/large/small/inline]>This is a label</component>
  <component :is="FormFieldComponent" [disabled/readonly/inline]>
  <component :is="FormErrorComponent" priority="[high/medium/low]">This is a label</component>
</dynamic-form-field

Ok, sure, that's fine. But it's messy and not super readable. For one, I have to use component and the actual component name gets relegated to a prop in each of the provided components, which, for a consuming-side api in a library that wants to be user-friendly, seems kind of obtuse. Two, I have to add Component to the name of every component the slot is providing to make it clear this is a component being provided, rather than data.

Solution: v-using, a directive which allows slotted components to declare (and provide) components which are defined on their scope, rather than just data.

<dynamic-form-field
  :value="myFormFieldValue"
  v-using="{ FormField, FormLabel, FormError }"
  v-slot="{ formattedValue }">
  <form-label [bold/italic/uppercase/large/small/inline]>This is a label</form-label>
  <form-field :value="formattedValue" [disabled/readonly/inline] />
  <form-error priority="[high/medium/low]">This is a label</form-error>
</dynamic-form-field>

Isn't that nice and clean? 😃

I realize I could also just have dynamic-form-field be non-scoped component, and pass the label, and stylistic props to it and have it splice them out amongst the different parts but

  1. DyncamicFormField would end up having a million props, which is gross, and doesn't feel like an approachable API for someone writing a library.
  2. Even if I pass down all the different variable flags as props, I'm still prevented from using directives on the field/label/error component children. And, particularly for things like a auto-focus directive on the field component, that's actually a pretty big loss in terms of re-usability.

Amendment proposal to Function-based Component API

This is a proposal for an amendment to RFC #42. I'm posting it here separately because the original thread is too long, and I want to collect feedback before updating the original RFC with this.

Please focus on discussing this amendment only. Opposition against the original RFC is out of scope for this issue.

Motivation

This update aims to address the following issues:

  1. For beginners, value() is a concept that objectively increases the learning curve compared to 2.x API.
  2. Excessive use of value() in a single-purpose component can be somewhat verbose, and it's easy to forget .value without a linter or type system.
  3. Naming of state() makes it a bit awkward since it feels natural to write const state = ... then accessing stuff as state.xxx.

Proposed Changes

1. Rename APIs:

  • state() -> reactive() (with additional APIs like isReactive and markNonReactive)
  • value() -> binding() (with additional APIs like isBinding and toBindings)

The internal package is also renamed from @vue/observer to @vue/reactivity. The idea behind the rename is that reactive() will be used as the introductory API for creating reactive state, as it aligns more with Vue 2.x current behavior, and doesn't have the annoyances of binding() (previously value()).

With reactive() now being the introductory state API, binding() is conceptually used as a way to retain reactivity when passing state around (hence the rename). These scenarios include when:

  • returning values from computed() or inject(), since they may contain primitive values, so a binding must be used to retain reactivity.
  • returning values from composition functions.
  • exposing values to the template.

2. Conventions regarding reactive vs. binding

To ease the learning curve, introductory examples will use reactive:

setup() {
  const state = reactive({
    count: 0
  })
  
  const double = computed(() => state.count * 2)
  
  function increment() {
    state.count++
  }
  
  return {
    state,
    double,
    increment
  }
}

In the template, the user would have to access the count as {{ state.count }}. This makes the template a bit more verbose, but also a bit more explicit. More importantly, this avoids the problem discussed below.

One might be tempted to do this (I myself posted a wrong example in the comments):

return {
  ...state // loses reactivity due to spread!
}

The spread would disconnect the reactivity, and mutations made to state won't trigger re-render. We should warn very explicitly about this in the docs and provide a linter rule for it.

One may wonder why binding is even needed. It is necessary for the following reasons:

  • computed and inject may return primitive values. They must be wrapped with a binding to retain reactivity.
  • extracted composition functions directly returning a reactive object also faces the problem of "lost reactivity after destructure / spread".

It is recommended to return bindings from composition functions in most cases.

toBindings helper

The toBindings helper takes an object created from reactive(), and returns a plain object where each top-level property of the original reactive object is converted into a binding. This allows us to spread it in the returned object in setup():

setup() {
  const state = reactive({
    count: 0
  })
  
  const double = computed(() => state.count * 2)
  
  function increment() {
    state.count++
  }
  
  return {
    ...toBindings(state), // retains reactivity on mutations made to `state`
    double,
    increment
  }
}

This obviously hinders the UX, but can be useful when:

  • migrating options-based component to function-based API without rewriting the template;
  • advanced use cases where the user knows what he/she is doing.

Why remove time slicing from vue3?

I saw that the time slicing has been deleted in vue-next here, and no reason was found anywhere.

Can I find the answer here? Is it because time slicing is no longer needed or something else?

Create more than one active RFC

Hello.

I wanna create another RFC for an idea that I had for Vue 2.6 but I've found that I can't without breaking the rules of README file.

The problem is I already have one 0000-template.md file.

I just wanna ask what should I do. Should I wait until my last RFC is resolved? Or can I create, for example, a 0000-template-1.md file?

Thanks.

to provide a way to parse `vue-jsx` in the browser.For "jsx" enthusiasts

I hope that vue can provide a way to parse vue-jsx in the browser.

For novice friends, the cost of learning a pre-compiled toolchain is too high.

For "jsx" enthusiasts, using "jsx" in vue means using a precompiled toolchain

The learning cost of "jsx" is not high, compared to the "Mustache" template.

If you do not want to use the precompiled toolchain, you must use the "Mustache" template

This is an annoying problem.

I have to admit that many people don't like to use "vue" because they don't like the "Mustache" template.

Of course, using "jsx" in "vue" is not a replacement for the "Mustache" template, just giving "jsx" fans another option.

You can refer to 'Hyperscript Tagged Markup', the "jsx" parser running in the browser, the volume is less than "1kb".

https://github.com/developit/htm

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

What does the proposed API look like?

import { html } from '@vue/jsx';
const component = {
    setup() {
        const msg = 'hello world';
        function doSomething() {
        }
        return {
            doSomething,
            msg,
            activeColor: 'red',
            fontSize: 30
        };
    },
    render() {
        const {msg, doSomething, activeColor, fontSize} = this;
        return html`
<div style=${ {
            color: activeColor,
            fontSize: fontSize+  'px'
        } }></div>
<span>Message: ${ msg }</span>
<a onclick=${ doSomething }></a>
`;
    }
};
export default component;

Function-based Component API (model usage)

i am sorry i had to open this issue, but my question in the main RFC kept getting lost.

i have a question about how one would implement models or "two-way data flow" with the functional API:

function useCounter(initialCount) {
  const count = value(initialCount);
  function inc() {
    count.value++;
  };

  return { count, inc };
}

export default {
  setup(props, { emit }) {
    const { count, inc } = useCounter(props.count);
    watch(count, (nextCount) => emit('update', nextCount));

    return { count, inc };
  },
}

is this dont the intended way?

i had some discussion with @jonaskuske in the main thread, i will just quote that here, so more reference about open questions is available:

@backbone87

i dont think my example or your revision is isnt fully right on this, since it kinda breaks one way data flow? i mean the counter.count gets changed first then it gets emitted up, insteadof being emitted first and as a results the new value gets propagated down again. remember the setup method will not be called on updates.

@jonaskuske

Well, the important question here is: what do you want to achieve with the emit('update') in the first place?
The way I imagined your example to work is like this:
The component has a counter somewhere, which is why you use useCounter in its setup. The counter is controlled from within the component, the inc method is called from the template and the component re-renders to show the updated counter – that's your unidirectional data flow.
But to allow for flexible side effects, the component also emits an update after the counter changed. This allows the parent component to listen for changes and perform side effects, e.g. logging – but you're right, those must be isolated side effects and not affect the state, or the unidirectional data flow is broken.

Progress of Portal? Here are some suggestions.

In the early Vue 3.0 trailer, adding Portal component to Vue 3.0 was mentioned. But it seems that the corresponding RFC has not yet been created.

I have a project that is highly dependent on the portal component. Although the portal component is implemented by myself, but the functionality is similar to portal-vue. And encountered some similar problems. I don't know where I can share these issues.

  • Breaks provide/inject: LinusBorg/portal-vue#126 . Portal may cause all mechanisms related to parent-child communication to fail. So it is important to allow mutual access between the portal and the portal-target. And the functions related to communication between the parent and child components also need to take these issues into consideration.

  • Vue-devtools support: The actual components that are mounted cannot be displayed in the children scope of portal-target. If there has a redirect or a mirror, it would be great.

Portal is a very useful feature, especially for scenarios like multi-tab code editors. But it requires some underlying compatibility of the framework to work perfectly. I will be very happy to see it become a built-in feature of Vue.

Question about the functional API with function components

I'm curious to what functional components would look like in Vue 3. Obviously the talk of the town is adding setup() and the examples show using createElement and templates. I like jsx and currently use it in Vue and I also like React + hooks, but for the same reasons @yyx990803 and @Akryum have noted, it could be improved. I have already tried out the vue-function-api plugin which only seems to work with templates at this time.

I assume in Vue 3 a typical component with jsx would look like this

import { value, onMounted } from "vue";

const TestCounter = {
  setup() {
    const count = value({ value: 0 });

    onMounted(() => {
      console.log("mounted");
    });

    return () => (
      <div>
        {count.value}
        <div>
          <button onClick={() => (count.value = count.value + 1)}>
            increment
          </button>
        </div>
      </div>
    );
  }
};

export default TestCounter;

I also have used the vue-hooks package and I really like that api, for it's simplicity, and as you can see in the example above the object only has 1 function. This makes me think an actual functional component would just return the setup.

import { value, onMounted } from "vue";

function TestCounter() {
  const count = value(0)
  
  onMounted(() => {
    console.log("mounted")
  })
  
  return (
    <div>
      {count}
      <div>
        <button onClick={() => (count = count + 1)}>
          increment
        </button>
      </div>
    </div>
  )
}

What I have in this example would be the preferred api for me. I was wondering if this is what the Vue team had envisioned. Thank you.

Open Reactive API a little more

This issue is meant to start a discussion that -- I hope -- might lead to opening up the reactive API a little more.

Brief recap of reactive API

reactivity is an internal library of Vue 3 that provides state observation. It can be seen as having two layers.

The lowest layer are the primitives that define state observation. track and trigger define state (read detection and changes); effect and stop define actions that are called when the state changes.

reactivity itself provides higher-level building blocks for state: ref wraps a reactive single value; react creates a reactive object (or array), i.e. multiple values.
There's also computed, which is a kind of ref (I'll come back to what this means later), that wraps the (cached) result of a function rather than having its own value.
Of course, those are built on top of track and trigger.

Vue (nit: why not reactivity?) provides a higher-level building block for actions: watch, built on top of effect and stop.

Currently, none of the lower layer track, trigger, effect or stop is publicly exposed.
watch is an ok replacement of effect, so I'll focus on the state side only.

Motivations

The reactive system under Vue 3 is incredibly flexible and powerful.
Advanced users may want to create their own primitives for multiple reasons: to adopt a different programming style, to glue different apis together, to achieve more efficiency/performance or to work around limitations...

Some examples:

  • Turn promise into ref;
  • Turn RX Observable into ref;
  • Create a timer dependency as a ref;
  • Create a signal API (i.e. manual change tracking of large complex changes);
  • A shallow ref that doesn't required calling markNonReactive on every value you set;
  • You prefer a merge function rather than toRefs for composition;
  • Using a strategy not based on Proxies. E.g. use getters/setters to work with classes using private fields (incompatible with Proxies at the time of writing);
  • Classes with decorators instead of functional API;
  • You may even write a TS transformer that removes those decorators and generates getters/setters automatically at build time, achieving both best startup and runtime perf;
  • Faster mutable array, see vuejs/core#673 for an implementation;
  • You may even think that faster array is a better default and write your own react variant that automatically wraps every array with the previous bullet point instead of the default react;
  • Custom collections, e.g. views (filter, map) that update incrementally when the underlying array changes;
  • Create refs that encapsulate more features, such as transactional state or validation;
  • Integrate mobx.

These are just a few ideas, I'm sure the community will have more.

The bottom line here is that it's not reasonable to think Vue core wants or even should create all these and maintain them. Those are best created in 3rd party libraries, esp. the ones that have dependencies with other frameworks or tools.

Why ref and react are not enough

Some people are gonna say: "but it's easy enough to do that with a ref". Yes, for some examples, depending on the API shape you're content with, it is.

But the point is that there are lots of things that we may want to do; and for many of them ref or react are awkward primitives that don't map naturally and are inefficient.
If you want to use ref as a proxy to the functions track and trigger, you must do this:

// you need state, even if you could be stateless otherwise
let r = ref(0);
let i = 0;
function track() { 
  ref.value; // let's hope my minifier isn't configured with pure (side-effect free) getters
}
function trigger() {
  ref.value = ++i; // `i` avoids reading r, which would call track!
}

Plus the arguments that would appear in track and trigger when debugging would make no sense in context.
Plus the value getter/setter does some extra work we don't need.

And this is just for a single trigger call. I won't suggest code for a multi-value (react) equivalent, it's even worse.

Also note that wrapping a ref means that you are not a ref yourself, and you won't be automatically unwrapped in the component template.

Finally consider this: computed is built out of track and trigger, not a ref. Could it? yes. Is it a good idea? probably not.

Maintenance burden

Public APIs have a cost, as they must be maintained for the lifetime of the framework.

  1. I think track and trigger are pretty stable APIs that have a very low probability of changing. (I can argue why if you think otherwise);

  2. Few people are going to depend directly on them. Those are advanced APIs only. So the impact of a future breaking change is low in terms of impacted user code.

  3. Lots of people could benefit from having those APIs exposed. It's likely that at least some of the examples I gave previously will end up published as npm packages by the community. They could then be used by many projects.

Overall I think it's worth doing. I would be sad if Vue was not extensible at the reactive layer and the primitives that are provided by core were the only "blessed" ones.

Suggestions

I think track and trigger should be exposed as advanced APIs.

isRef is a bit problematic. isRef doesn't really check if it has an instance of ref, but if it as something that implements the ref contract, which can be summed up as: "I am a reactive value wrapper: I have a value property and I'm reactive". Best example: computed is not a ref but pretends to be one (i.e. isRef(computed) === true).
This matters because Vue automatically unwraps ref (in the "reactive values" sense) in several places: e.g. inside templates, or when returned from a reactive getter. Also it assigns to value when writing to a ref target.

So it would be nice to have the same behavior on reactive value wrappers other than ref and computed, but we need a public API to define what's a ref in the isRef sense. Today it is { _isRef: true, value: T } but as the underscore shows, it's not exactly a public API.

A similar issue arises with isReactive, though less so. There's less magic attached to reactive and we can prevent the wrapping of our own reactive objects by calling markNonReactive on them. But other reactive primitives don't know this information, hence the ecosystem can't compose nicely (in deeply reactive wrappers).

We probably need some kind of registerReactive api that adds an object to reactiveToRaw. This is then used by isReactive, which is already public.

Function-based Component API (extended discussion)

Not sure how I found myself here, but I looked this doc over. Overall the design looks more functional. Which appears to enable much needed TS integration improvements (no longer necessary to no-op wrap components). I'm a big fan. Congrats.

I agree RE classes, mixins, and HOCs.

Thanks for the explanation and examples. And all your work. Peace!

The better way to code。

Basic example

<template>
  <div>
    <div>
      <span>double:</span><span>{{double}}</span>
    </div>
    <button @click="inc">Clicked {{ count }} times.</button>

    <div>
      <div><input type="number" v-model="form.a"></div>
      <div><input type="number" v-model="form.b"></div>
      <div>
        <span>a + b:</span>
        <span>{{result1}}</span>
      </div>
      <div>
        <span>a + count:</span>
        <span>{{result2}}</span>
      </div>
      <div>
        <span>b + count:</span>
        <span>{{result3}}</span>
      </div>
      <div>
        <span>a + b + double:</span>
        <span>{{result4}}</span>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, reactive, computed } from 'vue'

export default {
  setup () {
    const form = reactive({
      a: 1,
      b: 2
    })
    const count = ref(0)

    const inc = () => {
      count.value++
    }

    const double = computed(() => {
      return count.value * 2
    })

    const result1 = computed(() => {
      return form.a + form.b
    })

    const result2 = computed(() => {
      return form.a + count.value
    })

    const result3 = computed(() => {
      return form.b + count.value
    })

    const result4 = computed(() => {
      return form.a + form.b + double.value
    })

    return {
      form,
      count,
      double,
      result1,
      result2,
      result3,
      result4,
      inc
    }
  }
}
</script>

The usage of .value is easy to make Bug.
So I think the better way is like this.

<template>
  <div>
    <div>
      <span>double:</span><span>{{double}}</span>
    </div>
    <button @click="inc">Clicked {{ count }} times.</button>

    <div>
      <div><input type="number" v-model="form.a"></div>
      <div><input type="number" v-model="form.b"></div>
      <div>
        <span>a + b:</span>
        <span>{{result1}}</span>
      </div>
      <div>
        <span>a + count:</span>
        <span>{{result2}}</span>
      </div>
      <div>
        <span>b + count:</span>
        <span>{{result3}}</span>
      </div>
      <div>
        <span>a + b + double:</span>
        <span>{{result4}}</span>
      </div>
    </div>
  </div>
</template>

<script>
import { toRefs, reactive, computed } from 'vue'

export default {
  setup () {
    const state = reactive({
      form: {
        a: 1,
        b: 2
      },
      count: 0
    })

    const inc = () => {
      state.count++
    }

    state.double = computed(() => {
      return state.count * 2
    })

    state.result1 = computed(() => {
      return state.form.a + state.form.b
    })

    state.result2 = computed(() => {
      return state.form.a + state.count
    })

    state.result3 = computed(() => {
      return state.form.b + state.count
    })

    state.result4 = computed(() => {
      return state.form.a + state.form.b + state.double
    })

    return {
      ...toRefs(state),
      inc
    }
  }
}
</script>

No more .value,just like state instead of this in vue2.

prop "parse" function

I think it would save a lot of boilerplate code if we could somehow transform props with a simple function defined right in the props configuration.

Eg. a function called parse could be executed before props are passed to setup(props) and before the props are registered on this

export default {
  props: {
    name: {
      type: String,
      parse: val => titleCase(val)
    },
    skills: {
      type: Object,
      // merge onto default values
      parse: val => Object.assign({power: 9000}, val)
    },
  },
}

The current implementation is:

export default {
  props: {
    name: String,
    skills: Object,
  },
  data () {
    const nameParsed = titleCase(this.name)
    const skillsParsed = Object.assign({power: 9000}, this.skills)
    return { nameParsed, skillsParsed }
  },
  watch: {
    name (val) {
      this.name = titleCase(val)
    },
    skills (val) {
      this.skills = Object.assign({power: 9000}, val)
    },
  },
}

The main problems are:

  • name clashing forces us to use a convention like nameParsed
  • we need to set up additional watchers, just for simple parsing of props
  • we need to decide if it's not better to do all this as computed property or not (i honestly don't know the trade-offs between computed and data + watch)

A simple parse function to be defined in the prop config seems like a very valuable addition. It's easy to understand and not breaking; so backwards compatible.

Function-based Component API (prop definition sharing)

Personally i use mixins alot to share common props between components, for example:

@Component
export default class FlexChildStyle extends Vue {
  @LengthProp()
  public min?: number | string;
  @LengthProp()
  public max?: number | string;
  @LengthProp()
  public basis?: number | string;
  @IntegerProp(false, 0)
  public grow?: number;
  @IntegerProp(false, 0)
  public shrink?: number;

  public get flexChildStyle() {
    return {
      flexGrow: this.grow,
      flexShrink: this.shrink,
      flexBasis: toLength(this.basis),
      minWidth: toLength(this.min),
      maxWidth: toLength(this.max),
    };
  }
}

How would this be solved with the function API?

Vuex Functional API

Do you think we could have a functional approach for Vuex, like this? I think it should focus store modules, mainly.

Basically, would love to benefit from typehinting and have a similar approach to the upcoming functional API, if possible.

// store/post.ts
import { createModule, mutations, reactive, toBindings, computed } from "vuex";

// The symbol will allow injecting the store into a component.
export default createModule(Symbol("post"), () => {
    // Defining all state in a single call allows
    // the VueDevtools to show the variable names.
    const state = reactive({
        post: null,
    });

    // Defining all mutations in a single call allows
    // using the keys as the mutation ID (setPost).
    const mtts = mutations({
        setPost(post) {
            state.post = post;
        },
    });

    // Getters / Computeds.
    const hasPost = () => state.post !== null;
    const title = computed(() => state.post ? state.post.title : "");

    // Actions.
    const fetchPost = async () => {
        const response = await fetch("/api/post");
        mtts.setPost(await response.json());
    };

    // We could use submodules this way:
    // const users = useUsers();

    // Exported context.
    return {
        ...mtts,
        ...toBindings(state),
        hasPost,
        title,
        fetchPost,
    };
});
// components/post.ts
import { createComponent } from "vue";
import usePosts from "../store/post";

export default createComponent({
    setup() {
        // The store would be lazily created the first time the usePosts() is called.
        const posts = usePosts();
        return () => (
            <div>
                <button onClick={posts.fetchPost}>Fetch Post</button>
                {posts.hasPost() && <div>Post: {posts.post.value}</div>}
            </div>
        );
    },
});

Integrated extensive documentation for single file components

I would like to propose a new top-level element for single file components to hold additional component documentation.

I've seen various solutions from different Vue frameworks and most boil down to using some external file(s) to hold advance documentation and examples.

I think it's best to keep the documentation as close as possible to the code.

My suggestion would be to add a new top-level documentation element to single file components. Alternatively one could add a new attribute for the template tag to mark one as documentation.

Functional the element should probably be very similar to the template. The documentation would be written using HTML but could also contain live-examples of the component itself.

To that extent, it would be very useful to also offer some global helper components/tags like an example component that would render its contents but also display the contents as source code. Or a way to reference the documentation of other components.

The main effort however would be to extend vue-loader and other tools to handle the new element appropriately.

There could be a generate-doc (or similar) command that would parse all .vue files and generates static documentation and/or a new run mode that would serve a dynamic docs application on a different port.

Ideally, the contents of the new element would be combined with any inline documentation (JSDoc) to generate an extensive documentation for each component.

Vue TypeScript Friendly Class API

This is not an RFC - this issue is written so we can discuss more on how to make Class API works perfectly with TypeScript. Discussion is welcome and highly appreciated!

Summary

This RFC tries to make Vue Class API TypeScript friendly by breaking some of the things that now exist in Vue but not really practical to use with TypeScript. This is TypeScript oriented, so expect a lot of things that are already in Vue to be broken so it could fit TypeScript designs.

Inspired by React TypeScript supports.

Basic example

The new Class API would be able to be used like:

interface State {
  counter: number;
}
interface Props {
  startValue: number;
}

class Counter extends Vue.Component<State, Props>{
  data(): State {
    return {
      counter: this.props.startValue
    }
  }
  render(){
    const props = this.props;
    const state = this.state;
    // Not actual JSX or template
    return (
      <button @click={this.addCounter}> Counter {{ state.counter }} </button>
    )
  }
  addCounter() {
    this.state.counter++;
  }
}

Motivation

The motivation behind this that we hope Vue will have first class TypeScript supports like React. It's been a breeze to use React with TypeScript, but couldn't say the same with Vue. Having first-class TypeScript support means we can reduce the number of bugs related to inconsistent typings and better coding assistance in IDE.

The Class API is well structured, so it is easier to read and explore, that is why we hoped that we can continue to improve the Class API.

Detailed design

The goal is to separate the main instance with user-land and type-land properties. We then won't have to worry about merging the user-land code and type-land code, we can safely annotate each part that we wanted to annotate.

So we might end breaking a lot of existing Vue code because everything would be removed from the main instance.

It might look like this:

abstract class Component<TState, TProps, TApis, TEvents> {
  props: TProps;
  abstract data(): TState;
  abstract render();
  abstract api: TApis;
  abstract events: TEvents;
}

TApi might be used to expose the API that the component exposes, such as its methods. And TEvents is used to expose events that can be fired or listened to by other components.

Drawbacks

  • It breaks a lot of things
  • It also breaks user code that doesn't even use TypeScript and won't benefit from it
  • The plugin and mixins system might need to be broken too to support TypeScript

Alternatives

The Function API is a good way of supporting TypeScript.

Adoption strategy

We can write tools that detect the user state and props, and then modify the code that accesses them so it got prefixed by this.state or this.props.

We can also make the state and props property live side by side with the user existing state and props that lives on the instance directly. But it might mean we have to support both designs on the run.

Unresolved questions

We still don't know how to utilize TypeScript and make these things better:

  • Mixins
  • Events
  • Plugins

Suggestion for Vue 3.0 : let emit return a Promise array。

What problem does this feature solve?

That can do something after emit event。

What does the proposed API look like?

const loading = ref(false)
const onClick = async () =>  {
 // doSomething
  loading.value = false
  // emit event
  await Promise.all(emit('click'))
  // doSomething
  loading.value = false
}

Discussion of Scoped Mixins API

  • Start Date: 2019-06-24
  • Target Major Version: 3.x
  • Reference Issues: (fill in existing related issues, if any)
  • Implementation PR: (leave this empty)

Note

I opened this Issue to discuss ideas of scoped mixins API and gather ideas and feedback about it. The main idea is, instead of completely change the way how components are written, update mixins semantic to make it more composable and reusable.

Summary

Unify logic-composition patterns under scoped mixins.

Basic example

export default {
  data() {
    return {
      value: 0
    };
  },
  methods: {
    increment() {
      this.value++
    }
  },
  computed: {
    plusOne: function () {
      return this.value + 1
    }
  },
  watch: {
    value: function (newVal, old) {
      console.log(`count * 2 is ${newVal * 2}`)
    }
  },
  mounted() {
    console.log(`mounted`)
  }
}
<template>
  <div>
    <span>count is {{ count.value }}</span>
    <span>plusOne is {{ count.plusOne }}</span>
    <button @click="increment">count.value++</button>
  </div>
</template>

<script>
import Count from "@/mixins/Count";

export default {
  mixins: {
    count: Count
  },
  data() {
    return {
    };
  }
}
</script>

Motivation

Logic Composition

One of the key aspects of the component API is how to encapsulate and reuse logic across multiple components. With Vue 2.x's current API, there are a number of common patterns we've seen in the past, each with its own drawbacks. These include:

  • Mixins as array of mixin objects (via the mixins option)
  • Higher-order components (HOCs)
  • Renderless components (via scoped slots)

Mixins drawbacks

Mixins in its current state have several major drawbacks making it difficult to reason about them and compose.

  • One of the main complains about mixins is it's difficult to figure out sources the properties come from. When several mixins are used it becomes problematic to understand which mixin contains property and how properties are grouped by mixins.
  • Namespace clashing. Mixins can potentially clash on property and method names.
  • Unexpected merging strategy for mixins
  • Order of mixins in array is important and mixins can overwrite each other's functionality
<template>
  <h1>Problem 1 with mixins</h1>
</template>

<script>
// Mixins are composed in unexpected way
// Why is called only secondMixin and why it is called twice?
const firstMixin = {
  created: function() {
    this.hello();
  },
  methods: {
    hello: function() {
      console.log("I'm from first mixin!");
    }
  }
};

const secondMixin = {
  created: function() {
    this.hello();
  },
  methods: {
    hello: function() {
      console.log("I'm from second mixin!");
    }
  }
};

export default {
  name: "problem1",
  mixins: [firstMixin, secondMixin]
};
</script>

The new scoped-mixins API presents a clean and flexible way to compose logic inside and between components without any of these drawbacks. This can be achieved by extracting code related to a piece of logic into what we call a "scoped mixin" which can be easily injected into component. Here is an example of using a scoped mixin to extract the logic of listening to the mouse position:

export default {
  data() {
    return {
      x: 0,
      y: 0
    };
  },
  methods: {
    update(e) {
      this.x = e.pageX
      this.y = e.pageY
    }
  },
  mounted() {
    window.addEventListener('mousemove', this.update)
  },
  unmounted() {
    window.removeEventListener('mousemove', this.update)
  }
}
// in consuming component

<template>
  <div>{{ mouse.x }} {{ mouse.y }} {{ other.z }}</div>
</template>

<script>
import mouseMixin from "./mixins/mouse"
import otherMixin from "./mixins/other"

export default {
  name: "app",
  mixins: {
    mouse: mouseMixin,
    other: otherMixin
  }
};
</script>

Note in the example above:

  • Properties exposed to the template have clear sources since they are nested object properties, where parent object is used as namespace;
  • Mixin objects can be registered at arbitrary name (used as namespace) so there is no namespace collision;
  • There are no unnecessary component instances created just for logic reuse purposes.

Type Inference

TypeScript type inference support is out of scope of this RFC. This RFC solely focuses on better logic composability / reusability and tries to be better mixins / HOC / renderless components alternative.

Function-based component API (setup)

I am really excited about the Function-based component API. I make an implementation vue-function-api which I can play with.

I got some troubles with the new setup options. How can we access the merged data or computed inherited from the parent by context? Is context an exact equivalent of this in vue2.x?

const Comp = Vue.extend({
  template: `<div>{{ b }} {{ c }}</div>`,
  setup() {
    const a = value(1);
    const b = computed(() => {
      return a.value + 1;
    });
    return {
      a,
      b,
    };
  },
});

const vm = new Comp({
  setup(props, context) {
    const c = computed(() => {
      // how can I access the `b` without `this`?  `context.a`?
      return this.b + 1;
    });

    return {
      c,
    };
  },
}).$mount();

NO!

JUST NO! NO! NOOOOO!

Allow for multiple render instances

I have some ideas about the following but I'd like to hear some comments first, before making it an official RFC:

Context

Vue3 has support for a custom renderer, which we have been trying out. This custom renderer uses WebGL to render, using Vue templates / components. This is a great feature, and it was relatively easy to accomplish.

Request

Right now, it is only possible to register 1 renderer for the entire Vue instance. This means you can't use multiple instances of the same renderer (multi canvases for WebGL) and you can't mix multiple renderers (e.g. DOM & WebGL). It would be really nice if this was possible, to for example allow for easy state sharing between the two renderers.

My proposal

I would propose the following changes to be made:

  • Create methods for creating an instance of a renderer & compiler (they are seperate things, so I think they should be registered seperately as well)
  • Add extra fields in the ComponentOptions; the renderer and the compiler the component will use
  • Type the nodeOps and all other required objects to create a renderer & compiler and provide more context about the renderer & compiler instance being used, so make it easier to create a custom renderer & compiler.

Some other remarks:

  • I suggest to only allow the DOM renderer to be a "top level" renderer when using nested components: DOM > some other renderer 1 > some other renderer 2. This is because I would have no clue DOM > some other renderer > DOM, if you have suggestions I'd like to hear them!
  • If the above restriction is put in place, it would nice if it would also be possible to only define the renderer on the root of the custom-rendered-components until some other renderer is found. Meaning, if I would have the following nested component render structure DOM > DOM > GL > GL > yet another renderer, I would only need to annotate the fact that I want to use GL as renderer on the first one of the two usages, if that makes sense.
  • If nothing is stated, DOM is the default renderer

If something is vague or if you have other remarks I'd love to hear them!

Discuss the best practices of project structure

In this discussion, I would like to raise in the community questions about best practices for organizing project structure.

Vue is beautiful not least in that it allows you to organize the project structure as you want. However, I would like to hear the views of the community on this topic. I would like people who have experience working on major projects to share their experiences and thoughts on the subject.

In Vue 2, we have several global entities: components, mixins, plugins, etc.

Components may be:

  • Independent (such as the Button component). Which can be used wherever.
  • Depend on others. For example, the Slide component, which can only be in the Slider component.
  • Some components are and depend on routes.

For me, it causes some confusion. And I have some questions to discuss:

  • How to name components and component files to distinguish independent components from those that are dependent on other components or dependent on the routes.
  • How to organize directory structure? Is it necessary to embed dependent components into one another? Is it worth keeping a flat structure? Is it necessary to separate components for routes into a separate directory?

In Vue 3 With the advent of composition api, another entity emerges - composition functions.

  • How should such functions be named so that they can easily be distinguished from normal functions?
  • Should I somehow separate the composite functions I wrote from those installed through npm?
  • How to organize directory structure?

prop "default" value based on other prop

Currently we can set default values like so:

export default {
  name: 'Knight',
  props: {
    name: {
      type: String,
    },
    isKnight: {
      type: Boolean,
      default: false,
    },
  },
}

In this example we'd have to pass if a person is a knight or not:

<Knight name="John" is-knight="false" />

However, very often I need to look at other props and calculate a props' default state based on these other props.

A nice addition to default values for props could be:

export default {
  name: 'Knight',
  props: {
    name: {
      type: String,
    },
    isKnight: {
      type: Boolean,
      default: props => props.name.includes('Sir'),
    },
  },
}

Final comment period clarification

I am not insinuating that we should debate ad nauseam, but I noticed there wasn't a "final comment" period for the first RFC (that I was aware of). What is the process and can this be clarified in the README.md?

proposal: type checked in template

Is it possible to support type infer and type check in template in vue 3.0, not only single file component, but also component written in .ts? Just like some lib like styled-components' vscode plugin vscoded-styled-components, which works for css with tagged string literal.

v-bind property value shorthand

I have a working rough proof-of-concept here:
GavinRay97/vue@524d4c4

Allow binding property keys to values of the same name, the way that ES6 shorthand for object syntax works.

const a = 1
const b = 2

// Much more concise, especially when you have many entries
const obj = { a, b }

// Not DRY at all
const obj = { a: a, b: b }
<my-component :name :whatever>
// Translates to
<my-component :name="name" :whatever="whatever">

Inspired by this Twitter comment:

vuerfc

In-template variable definition

Sometimes you will encounter such a situation:

<template>
  <div class="container">
    <p class="row">{{item && item.a && item.a.b && item.a.b.hello}}</p>
    <p class="row">{{item && item.a && item.a.b && item.a.b.world}}</p>
    <p class="row">{{item && item.a && item.a.b && item.a.b.goodbye}}</p>
  </div>
</template>

These code with the same logic need to be written many times.
I hope there is a way to define local variables in the template, then the above code can be abbreviated as:

<template>
  <div class="container">
    <var name="b" :value="item && item.a && item.a.b"></var>
    <p class="row">{{b && b.hello}}</p>
    <p class="row">{{b && b.world}}</p>
    <p class="row">{{b && b.goodbye}}</p>
  </div>
</template>

or

<template>
  <div class="container" v-var:b="item && item.a && item.a.b">
    <p class="row">{{b && b.hello}}</p>
    <p class="row">{{b && b.world}}</p>
    <p class="row">{{b && b.goodbye}}</p>
  </div>
</template>

Q: Why not computed?
A: The item in the above code may come from v-for, so computed is not a feasible solution.

Question on the general TypeScript support in Vue 3

Hi. I have a few questions regarding TypeScript support that will come along with Vue 3.0. I wanted to ask them originally in #42 but I thought my comment would quickly disappear in that thread and maybe other people will have a similar questions so I hope you don't mind me opening a new issue here.

In the section Type Inference you briefly described how type inference will work in the script tag, but all the examples presented there touches only functions composition, type checking props. What I'd like to know is if/how TS will generally work in other parts of Single File Components.

Questions I have at the moment:

  1. Will type inference work in the templates of our .vue components?
    I guess during development Vetur for VS Code will help us with that, but how will it work during compile time?
  2. Will it be possible to have things like refs, slots strongly typed?

Thank you for reply. I really appreciate it :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.