New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Support pre-compiled component `view`s #4492

Open
justinbmeyer opened this Issue Sep 27, 2018 · 7 comments

Comments

Projects
None yet
5 participants
@justinbmeyer
Copy link
Contributor

justinbmeyer commented Sep 27, 2018

I find .component files a bit awkward to use. These sorts of things are apparently very popular in VueJS though. It's my understanding that the advantage is that these files can be built without needing eval in production.

While stache doesn't ever need eval, there's still performance benefits for building stache templates. Thus, I'm proposing a new form of .component file that looks just like how we write all of our current components across our guides. For example, a file like the following:

import {Component} from "can";

Component.extend({
  view: `<h1> some stache </h1>`
  ViewModel: { ... }
})

Could be transpiled to something like:

import {Component, stache} from "can";

Component.extend({
  view: stache([ ... processed ast ... ])
  ViewModel: { ... }
})

Ideally, we could do this to any file that either:

  • Imports Component from "can"
  • imports something from "can-component".

Secondly, it's also desirable to support CSS and/or LESS (maybe SASS). We could also support the following:

import {Component, stache} from "can";

Component.extend({
  view: `<h1> some stache </h1>`
  css: `
    h1: {color: "red"}
  `,
  ViewModel: { ... }
})
@thomaswilburn

This comment has been minimized.

Copy link
Contributor

thomaswilburn commented Sep 27, 2018

As I'm understanding it from this bug, this doesn't really look like Vue's single file components, and I don't think it carries the same benefits. As used in Vue, they're convenient because they provide an easy way to package up a component with its styles and view template, and they also eliminate a bunch of the boilerplate. I personally like that they tend to work well with editors for syntax highlighting without adding special plugins the way that JSX or CSS-in-JS approaches normally do.

The equivalent, I think, for CanJS, might look something more like this:

SingleFileComponent.canjs

<template type="stache">
  Hello {{name}}
</template>
<script>
export default {
  ViewModel: {
    name: {
      get() { return "World" }
    }
  }
}
</script>
<style type="less" scoped>
single-file-component {
  background: papayawhip;

  &::before, &::after {
    content: " *** ";
  }
}
</style>

Would compile down to something like:

import { Component, stache } from 'canjs';
// load CSS somehow, compiling LESS in the process

var SingleFileComponent = Component.extend({
  tag: 'single-file-component',
  view: stache(`
  Hello {{name}}
`),
  ViewModel: {
    name: { get() { return "World" } }
  }
}

export default SingleFileComponent;

Essentially, this isn't too different from the existing idea of modlets, it just means we're working in one file per Component instead of three. It also gives us an easy way to build presentation-only components, like a flexbox container:

<template type="stache">
  <div class="flexbox" data-direction="{{direction}}">
    <can-slot />
  </div>
</template>
<style type="less">
.flexbox {
  display: flex;

  & > * {
    flex: 1;
  }

  &[data-direction="column"] {
    flex-direction: column;
  }
}
</style>

Assuming that it generates unsealed (or Proxy-based ViewModels), I could now write a three-panel nested flex layout with:

<flex-box>
  <flex-box direction="column">
    <div style="background: papayawhip"></div>
    <div style="background: salmon"></div>
  </flex-box>
  <div style="wheat"></div>
</flex-box>

Is this better than just writing well-constructed markup with reasonable CSS classes? I would argue... probably not? But it seems to be common in React and Vue to have logic-less components like this just for layout or presentation, which then wrap components with more complicated functionality.

@justinbmeyer

This comment has been minimized.

Copy link
Contributor

justinbmeyer commented Sep 27, 2018

@thomaswilburn

Interpreting:

. As used in Vue, they're convenient because they provide an easy way to package up a component with its styles and view template, and they also eliminate a bunch of the boilerplate. I personally like that they tend to work well with editors for syntax highlighting without adding special plugins the way that JSX or CSS-in-JS approaches normally do.

And applying it to what I proposed:

  • package up a component with its styles and view template
  • eliminate a bunch of the boilerplate
  • editors for syntax highlighting without adding special plugins the way that JSX or CSS-in-JS approaches normally do

It's really the 3rd point that is the difference. And I think only the CSS/LESS highlighting would be lost in what I proposed (when compared to yours) because handlebars / mustache does not come for free in either case.

=======

Also regarding:

Assuming that it generates unsealed

If a component gets values upon instantiation, those properties do not need to be predefined.

@justinbmeyer

This comment has been minimized.

Copy link
Contributor

justinbmeyer commented Sep 27, 2018

Also, the existing .component files look like:

<can-component tag="pmo-order-history">
  <style type="less">
    display: block;

    p { font-weight: bold; }
  </style>
  <view>
    <div class="order-history">
      <div class="order header">
        <address>Name / Address / Phone</address>
        <div class="items">Order</div>
        <div class="total">Total</div>
        <div class="actions">Action</div>
      </div>
    </div>
  </view>
  <script type="view-model">
    export default {
      message: {
        default: 'This is the pmo-order-history component'
      }
    };
  </script>
</can-component>
@justinbmeyer

This comment has been minimized.

Copy link
Contributor

justinbmeyer commented Sep 27, 2018

Putting this all together ... is CSS highlighting working by default worth deviating from the look of a "standard" component?

I think there's a lot of benefit of having these things look the same across all sorts of projects.

@thomaswilburn

This comment has been minimized.

Copy link
Contributor

thomaswilburn commented Sep 27, 2018

I couldn't find a component file that looks like the example from your follow-up (which is probably why my initial reply was mostly just re-hashing stuff you already knew). Is that documented in the guides anywhere?

I don't have a solid reason for this, but for some reason I'm less comfortable with transpilation that involves changing something written as a single JS file with template literals, as compared to the more explicit split that .vue files have. Part of it may be that I think it might be easier to abuse your all-JS component--say, by tagging the template literal or performing string interpolation on it--and end up with something that I didn't expect but which is "technically" still valid JS. Although .vue files are all in one, they enforce separation of concerns by making sure that your templates and styles are expressed as static inputs in separate languages.

I'm sure you could guard components against this, or de-optimize and warn during compilation, but sometimes the simpler approach might easier for people to understand, and for build tools to process. I guess it depends on who you're targeting.

@BigAB

This comment has been minimized.

Copy link
Contributor

BigAB commented Sep 27, 2018

So the real gain of this proposal would be a performance boost for initial parsing of the template, right?

That's why you would pre-compile?
Would it be significant enough to warrant a pre-compile step?

Right now the benefit of the .component files seems to be, to me at least, a convenience so you don't have to split anything out to separate files, which has become pretty redundant now with the fact that you can make "single-file" components just like normal ones:

import {Component, stache} from "can";

Component.extend({
  view: `<h1> some stache </h1>`
  css: `
    h1: {color: "red"}
  `,
  ViewModel: { ... }
})

...and those "single-file" components don't need any special loaders to import as they are just .js files.

Honestly, I'd rather see work done on an editor extension/plugin to add stache syntax highlighting to the template literals (and .stache files btw), and the css property (or maybe styles to include less/sass) added to can component, more than making a step to pre-compile an already-working js file to load a little faster.

...of course if the perf makes a huge noticeable difference it may be worth it, but I don't feel like I am having stache loading perf problems (maybe I am and just don't know it, I'd have to see the difference I guess)

RE: An editor extension/plugin for adding stache syntax highlighting to template literals
Here is maybe something we could for to start out: https://marketplace.visualstudio.com/items?itemName=webreflection.literally-html

@Mattchewone

This comment has been minimized.

Copy link
Contributor

Mattchewone commented Sep 27, 2018

One of the things I raised in the training was about highlighting in .component files and .stache files which I have since found a way thanks to @mikemitchel's help.

Having seen a .component file I can see how similar it is to a .vue file although maybe it's just personal preference or just because I knew Vue before Canjs but I do still like the separation of the template / js / css in specific blocks within the file rather than all within the can-component tag. Although I'm sure I could get used to it.

I have seen on projects that there are mixes between the split modlet components and sporadic .component files I think having so many ways to create the same thing maybe more the issue. The guides show using the following format:

import {Component, stache} from "can";

Component.extend({
  view: `<h1> some stache </h1>`
  css: `
    h1: {color: "red"}
  `,
  ViewModel: { ... }
})

But as far as I can tell .component files or everything like this are not recommended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment