~Sorry, Chinease only. I'll try to translate later.~
有时我希望在组件某区域渲染数据,它们都来自于data上比较深层的某一个属性,当数据量比较多时,直接使用文本插值的写法会比较繁琐,比如:
(以下为了精简代码,采用了pug、es6语法,并忽略Vue组件data必须为函数的要求)
The mustache interpolations of data in some deep props make the code cumbersome:
data.js
export default {
some: {
deep: {
props: {
a: 1,
b: 2,
},
},
},
};
my-page.vue
<template lang="pug">
div
p {{some.deep.props.a}}
p {{some.deep.props.b}}
</template>
<script>
import data from './data.js'
export default {
data,
}
</script>
我想到几种变通的做法:
I tried some solutions:
my-page.vue
<template lang="pug">
div
p {{props.a}}
p {{props.b}}
</template>
<script>
import data from './data.js'
export default {
data,
computed: {
props() {
return this.some.deep.props;
},
},
}
</script>
这样做相对简单,但是总要为此去定义很多计算属性似乎也有些繁琐。
It is easy to use, but the computed properties become cumbersome.
v-for
指令来缩小作用域:Use v-for
directive to define a scoped variable:my-page.vue
<template lang="pug">
div
template(v-for="props in [some.deep.props]")
p(ref="a") {{props.a}}
p(ref="b") {{props.b}}
</template>
<script>
import data from './data.js'
export default {
data,
}
</script>
这是一个取巧的做法,但也有副作用:用$refs
访问v-for
范围内的ref
引用会变成数组。
It almost works well, but have side-effect: $refs
in the v-for
scope will become an array.
data-view.vue
<template lang="pug">
div
p {{props.a}}
p {{props.b}}
</template>
<script>
export default {
props: ['props'],
}
</script>
my-page.vue
<template lang="pug">
div
data-view(:props="some.deep.props")
</template>
<script>
import data from './data.js'
import DataView from './data-view.vue'
export default {
components: {DataView},
data,
}
</script>
子组件内部的写法能简化许多,但是整体花销仍然不小。一个额外的问题是,子组件多引入了一个可能原本并不必要的包装根元素。
Interpolations in child component become simple, but more children is also cumbersome. In addition, the child need a wrapper element as the component root, which was not needed before.
sub-scope.vue
<template lang="pug">
div
slot(:sub="data")
</template>
<script>
export default {
props: ['data'],
}
</script>
my-page.vue
<template lang="pug">
div
sub-scope(:data="some.deep.props")
template(slot-scope="scope")
p {{scope.sub.a}}
p {{scope.sub.b}}
// or
sub-scope(:data="some.deep.props")
template(slot-scope="{sub}")
p {{sub.a}}
p {{sub.b}}
</template>
<script>
import data from './data.js'
import SubScope from './sub-scope.vue'
export default {
components: {SubScope},
data,
}
</script>
这也是取巧利用了子组件来改变访问作用域。可以复用看上去好像是一个优点,但实际使用还是比较繁琐,甚至代码看上去有些更乱了。与上一个方法同样存在的一个问题是,子组件引入了可能原本并不必要的包装根元素。
Only the interpolations look like simpler, but other codes become more complex.
我感觉利用v-for
指令的办法很巧妙,就是多了一些副作用,虽然也不是非常严重的影响。
因此我希望能有一个指令,能够如同v-for
一样定义一个局部作用域变量,但又不会影响$refs
引用的表现。
I hope there is a directive can define scoped variables like v-for
, but will not affect the performance of $refs
.
div(v-scope="props of some.deep") {{props.a}}
// or
div(v-scope="(a, b) of some.deep.props") {{a}}, {{b}}
或者可以提供两种ref
元素访问接口:
Or the ref
API should be optimized:
$refs
仅用于访问指定引用名的第一项,类似于querySelector()
。$refs
should only be used for reference the first of elements with the same ref
value, like querySelector()
.$groupedRefs
(我想也许还有更好一点的名字)则用于访问包含相同引用名的全部元素数组,类似于querySelectorAll()
。$groupedRefs
is used for reference the array of the elements with the same ref
value, like querySelectorAll()
.其实我很希望是这样的API:
div(v-scope="some.deep.props") {{a}}, {{b}}
如果不指定要导出的变量,那么就把v-scope
指定的表达式值作为优先级最高的作用域变量,相当于导出了全部属性变量。
If no exporting variable is specified, all properties should be exported.
You should really translate this one. I think, but I'm not sure, that this is exactly what I want in Vue as well. A way to declare a scope on a container.
Translation updated. @Solander
What about changing
<script>
import data from './data.js'
export default {
data,
}
</script>
into
<script>
import data from './data.js'
export default {
data.some.deep.props,
}
</script>
~@Atinux~ Thanks for your comments
I'm also cautious about this feature, I did not provide a shortcut ( such as $v="" ) and export all props in the implementation
This feature is sometimes useful:
<div v-for="item in items" v-local:result="doSomeComplicated(item)">
<span>{{result.a}}</span>
<span>{{result.b}}</span>
<input :value="item.some">
</div>
hope to get more feedback about this feature :XD
@Atinux Thanks for your comments
@Austio, just help to @ the right person. 😁
I also would like to be able to use the mentioned syntax:
<div v-for="item in items" v-local:result="doSomeComplicated(item)">
<span>{{result.a}}</span>
<span>{{result.b}}</span>
<input :value="item.some">
</div>
To be able to access deep properties without having to use long names like:
object.child.property
How about some sort of v-let
style notation to be more like JavaScript? My thoughts:
<template>
<div>
<!-- Object-style syntax, similar to `v-bind` -->
<template v-let="{ props: some.deep.props }">
<span>{{ props.a }}</span>
<span>{{ props.a }}</span>
</template>
</div>
</template>
<template>
<div>
<!-- Single-value syntax, similar to `v-bind:arg` -->
<template v-let:props="some.deep.props">
<span>{{ props.a }}</span>
<span>{{ props.a }}</span>
</template>
</div>
</template>
JavaScript's let
keyword is block-scoped, v-let
should be consistent with that, so the let
ed values should only be accessible in the child nodes.
If you declare a v-let
, then shouldn't there be a v-const
?
@AlexandreBonneau No, that doesn't make sense.
My thoughts are that you would still need a computed property or method in most cases for this where the data is coming from some api b/c there will be possibility of null/undefined. Keeping a separation between the template and presenting the data will be marginally more verbose but the gains are clarity and also reduction in size of vue core.
Userland solution which works, but I highly recommend not to use it "as is".
(May be useful when you can move some logic into separate component which will make template more cleaner)
@OEvgeny This is a nice workaround, thanks.
However, a simpler v-let
directive could prevent cluttering the template with those additional tags.
I think this feature request is still valid.
note that xsl has the concept of const variable.
Splitting templates or using computed or method is not always possible without breaking logical blocks or reducing readability.
个人习惯和风格可能都不太一样,清晰明朗,简洁易用,就是好框架,感觉用法还是不错的,很符合现在的代码编程习惯
Any updates on this? Just ran into this as well...
I was looking for a solution too... While I found only this:
<template>
<div v-for="item in items" v-myvar="myvar = doSomeComplicated(item)">
<span>{{myvar.a}}</span>
<span>{{myvar.b}}</span>
<input :value="item.some">
</div>
</template>
<script>
export default {
//...
//fake
directives: {
myvar: {}
}
};
</script>
Using the directives
object is counterintuitive ; if you would want to declare those local variables, then it ought to be better to use:
export default {
//...
//fake
localVariables: {
myvar: {}
}
};
However, having to name very local var in the component seems a bit too verbose.
If you have multiple <v-for>
in your component, you'll have to name each temporary variable differently, while perhaps they would manipulate the same data.
v-let
is still my preferred way to go, and would be scoped in the template to the very html element it is declared on.
if you would want to declare those local variables, then it ought to be better to use:
export default {
localVariables: {
myvar: {}
}
};
Is it works now? Can you show a complete example with local variable in template?
No, it does not work now, as we are discussing options to implement (or not) local variables!
The point of my previous comment was to explain how I think having to declare the local variables in the <script>
part is a bad idea, and how v-let
could solve this elegantly and concisely.
I agree with you.
I wrote an example of how I temporarily solved the problem.
I was looking for a solution too... While I found only this:
<template> <div v-for="item in items" v-myvar="myvar = doSomeComplicated(item)"> <span>{{myvar.a}}</span> <span>{{myvar.b}}</span> <input :value="item.some"> </div> </template> <script> export default { //... //fake directives: { myvar: {} } }; </script>
Another way:
_be careful, values are assigned to the instance
(this.bar = my.very.deeply.nested.bar), (this.secondBar = my.very.deeply.nested.secondBar), (this.foo = my.deeply.nested.foo)_
<template>
<div
:bar="((bar = my.very.deeply.nested.bar), (secondBar = my.very.deeply.nested.secondBar), undefined)"
v-bind="((foo = my.deeply.nested.foo), {})"
>
I am static! <span v-text="foo + ' '"></span> <span v-text="bar"></span>{{ secondBar }}
</div>
</template>
<script>
import * as Vue from "vue";
export default {
data: () => ({
my: {
deeply: { nested: { foo: "hello" } },
very: { deeply: { nested: { bar: "world", secondBar: "!!!" } } }
}
})
};
</script>
A v-let or v-local or something similar would be pretty awesome. Some constellations force me to write a myriad of components with only a very small template and maybe one computed getter, introducing lots of boilerplate that could be avoided with a v-let.
Our new RFC repo https://github.com/vuejs/rfcs is dedicated to such feature request.
Most helpful comment
How about some sort of
v-let
style notation to be more like JavaScript? My thoughts:JavaScript's
let
keyword is block-scoped,v-let
should be consistent with that, so thelet
ed values should only be accessible in the child nodes.