-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Reduce boilerplate when importing components #1038
Comments
Oh, I like that. Can't really think of any downsides |
Personally I'm against it. IMHO using explicit import seems more natural which leading to faster and better understand of the code. Plus, what re-inverting the wheels for, why add another bytes of code to svelte, if there're already tools that can deal with import well? |
I'm a huge believer in having no magic in a codebase. However I do think that since the export object is already known to be transpiled by Svelte, it makes sense that it could handle things like automatically importing components. As for other tools, this doesn't handle the import. It actually adds the import so other tools know where to import the component. |
I've long (in ractive) wanted the same thing, but would go a step further. I see no reason to have to have Personally, I think there's a difference between 'magic' and reducing coupling: having to explicitly specify something that follows the internal logic of the overall solution (Svelte) doesn't add value and introduces a potential point of inconsistency. If I use a component: , I also have to remember to list it as something I use or it doesn't work. If I replace or rename MyButton with OurButton, I now also have to remember to replace it in the declaration (and currently the import). My feeling is any time you have to add explicit linking declarations they should serve a purpose of extending the core functionality, not simply making the core functionality work. So, this serves a purpose of saying, hey, this component is sourced outside my core app
but
doesn't tell us anything we didn't already know. |
Also, is there a way to use namespaces with Svelte. I've seen this in other frameworks for libraries and this would be very helpful with building a suite of applications with common UI / behaviors. For example if I have 4 small applications for various data-entry tasks, I'd really like to be able to use the same library of Svelte forms, login components, etc. In this scenario, it would be great to be able to do something like this:
or |
I think that the syntax: components: {
Button: './components/Button'
} might cause some confusion if someone decides to do things like: components: ['Button', 'Checkbox'].reduce((previous, current) => {
previous[current] = `./components/${current}.html`
return previous
}, {}) and it leaves us with a question if the above should be supported too or not. Personally I wouldn't mind an alternative such as: <link rel='import' name='Button' href='./components/Button.html'> so that I could keep track of my components in the template only. |
This would be nice improvement to clean the code a bit. I've always found redundant the need to have the "components" property. Either // 1 - automatic look-up from the imported moduled
<MyButton />
<script>
import MyButton from './forms/button';
export default {
data: {}
}
</script> or // 2 - add the import behind the scenes
<MyButton />
<script>
export default {
data: {},
components: {
MyButton: './forms/button'
}
}
</script> will make the code cleaner. I think the first one would be better because it would still allow to use "components" as we know it today, which could be used to override the automatic look-up from the imports. That is, if "components" is used, it must have a reference to all the components used in the template. This would still allow (?) the usage of more specialized cases like some sort some dynamic processing... |
The issue with this is that MyButton is just a random value, Svelte doesn't know if it's a component or not and cannot know in most cases if it should use it as a component.
This isn't statically analyzable, so this shouldn't be allowed in the first place.
Now this is interesting. It's a single line, easy to copy/paste, and very easy to read/analyze. I do think leaving importing/defining other components to the JavaScript is where it belongs, but I'm not too against this syntax.
I like your argument, but reducing coupling and adding magic aren't mutually exclusive. While it may be more convenient to just automatically import references components, that's a lot of lookup magic that's hard to reason with as a developer.
Now this is an interesting idea. If I used
in my code with the directory format of
There wouldn't be any magic as it's directly referencing the path to import with. It's very Java-y, but I do like the idea. |
I don't need |
@Kiho that looks like async components to me. It would be awesome to have something like that in Svelte! This could even further be expanded by streamed server side rendering. *.*
What about a "special global framework tag" á là <:Self> or <:Window>? <:Import type="component" from="components/Button" />
<Button /> The components name could be derived by the filename. Another property could be used to rename the component locally: <:Import type="component" from="components/Button" as="MyButton" />
<MyButton /> Of course you could also add helpers to the list of importable "template extensions": <:Import type="helper" from="_helpers/leftPad" as="format" />
<p>
The time is
<strong>{{hours}}:{{format(minutes, 2, '0')}}:{{format(seconds, 2, '0')}}</strong>
</p> The JavaScript way of importing components and especially helpers would of course still be preferred when you need partial application, reflection or whatever else you might need. In any other case the above syntax extension could save a few key strokes. Even async components and splittable imports like with the <:AsyncImport type="component" from="./forms/button" as ="MyButton" />
<MyButton /> |
not sure about your exact use case and if you use a bundler, but rollup creates a namespace for your component package if you define output.name; also, you need to export the svelte components in the bundle's entry point defined by the ˋinputˋoption to make them available in the namespace. |
Sorry @.all, after a good night's sleep I realized how much work my proposal above would have entailed and that it would open a can of worms: |
I do like the idea of using standards and doing something like
but this would be a big change to the current component system. |
@PaulBGD What about something like this? {{#await getButton}}
<Loader />
{{then MyButton}}
<MyButton>Click me</MyButton>
{{catch error}}
<p>Sorry, we are short on buttons today.</p>
{{/await}}
<script>
export default {
components: {
"Loader": "./base/loader",
"Toast": "./base/toast"
},
asyncComponents: {
"getButton": import('./forms/button')
}
};
</script> Seems a bit off, but I tried to reuse as much of the current syntax as possible. |
I think this issue has gotten a bit off the original path. The initial proposal was for something fairly straightforward to implement, and was essentially syntactic sugar - unlike many of the later things suggested here. Paul's suggested syntax bothered be a bit at first (I can't quite put my finger on why), but it seems quite practical. I may take a stab at implementing that later today. |
Update: see below |
Well, svelte could still support both ways of declaring the components being used: the explicit one (using "import" explicitely) and the shorter one. The spirit of this feature is similar to how nodejs finds modules (the resolution algorithm, or the fact that there's no need to explicitely write ".js"). That's also a bit magical, and yet everyone uses it. |
Update: see below |
Sorry, I meant *native to Node. (That is, expected behavior.) |
Update: see below |
Yeah I think I also like the idea of leaving the
While typing this, I just noticed that this suggestion also came up earlier in this thread. I still don't know whether I prefer @PaulBGD's original suggestion or the one of automatically hooking up components. |
@arxpoetica Well there is a big difference between "magic" and "redundancy" the whole point here is that you shouldn't have to specify the component's name 3 times in order to use it. That's plain nonsense. Also if you already have a component tag added to the html markup and you imported it down there is there any other reason the compiler shouldn't understand that this component is already used there, that's one point of having a compiler.. |
My suggestion is just import './forms/button.html'; // the name identifier will be button, the same of file name
// or
import MyButton from './forms/button.html'; // the name identifier will be MyButton I'd try to make a PR #1167 for my implementations but the tests failed on CI. I'll work more if you like that propoval. |
Update: after talking about it in Gitter, I think I'm more in favor of the original proposal in this ticket. @Rich-Harris talked me down from off the ledge. 😄 |
hope it gets implemented, yeah the original suggestion seems the most logical and clean |
I was just taking a look at this because I'm interested in having it, but also because I want to get more familiar with the more recent codebase changes. A super clunky implementation was actually quite easy. Add if (node.type === 'Literal' && typeof node.value === 'string' && disambiguator === 'components' && !this.options.format || this.options.format === 'es') {
componentDefinition.addBlock(`import ${name} from ${JSON.stringify(node.value)}`);
return;
} here. This implementation does suggest some other more specific questions:
edit: Okay it's not actually quite as simple as I made it out above, but I still do not see this being particularly challenging. |
Just pushed a moderately less clunky version to |
@Conduitry I use a lot of static properties, so I need the ability to still |
Yep you can still use the regular syntax. This just makes it so that if you specify a literal string as a value for key in the |
Add support for shorthand imports of components
Implemented in 2.1.0. |
This is just a thought while writing a lot of components, usually I'm importing a component just to pass it to the components object that I'm exporting. What if we trimmed down the boilerplate and had the compiler add the import statement for us? An example would look like:
Which would get converted to
We can still have backwards compatibility by only checking if a string key/value pairis passed and if we import files in the wrong order, the user can still manually specify the import order.
The text was updated successfully, but these errors were encountered: