How does Vue dynamically render components through JSX? The following article will introduce to you how Vue can efficiently render components dynamically through JSX. I hope it will be helpful to you!
How to render dynamic components? [Related recommendations: vuejs video tutorial]
There is a set of array structures as follows:
const arr = [ { tag: 'van-field' }, // 输入框 { tag: 'van-cell' }, // 弹出层 { tag: 'van-stepper' } // 步进器 ]
I want to get the tag
rendering corresponding by looping arr components.
Let’s analyze how to write the best way.
We can write av-for
Loop through the arr array, and then use v-if
to determine the tag and render the corresponding component type.
It is not impossible to write like this, but the scalability is not good. Every time a tag is added, a v-if must be added to the template.
I believe many people wrote like this at first.
But this is not the code that we elegant people should write.
Can we render real tags based on the tags of tag
.
The key is how to render the real component based on the traversed tags within the loop.
<van-cell v-for="(cell, cellKey) in arr" :key="cellKey" > <!-- 动态渲染 --> </van-cell>
I would like to invite today’s protagonist JSX to come on stage.
Parent component
const arr = [ { tag: 'van-field' }, // 输入框 { tag: 'van-cell' }, // 弹出层 { tag: 'van-stepper' } // 步进器 ]
Child componentRendTag.vue
<script> const AssemblyRend = { name: "assembly-rend", props: ["cell"], data() { return { input: "", }; }, methods: { onClick() { this.$emit("changeTag", this.input); }, }, computed:{ itemVal:{ get(){ return this.cell.value }, set(v){ this.cell.value = v } } }, render() { const { cell } = this; // 解构 const assembly = cell.tag; // 这里就是我们动态渲染组件的核心 return ( <assembly v-model={this.itemVal} placeholder={cell.placeholder} min={cell.min} onClick={this.onClick} > </assembly> ); }, }; export default { name: "RendTag", props: { cell: { type: Object, default:()=>{ return { { "title": "能否输入", placeholder: '请输入姓名', "value": "name", "tag": "van-switch", } } } }, }, methods: { changeTag(val) {}, }, render() { const { cell } = this; // 解构 return ( <div class="rendTag-content"> <AssemblyRend cell={cell} onChangeTag={this.changeTag} ></AssemblyRend> </div> ); }, }; </script>
We can use JSX's render
to write JavaScript return components to achieve our dynamic rendering of tags.
render
is equivalent to the template in our vue.
So the rendering effect is as follows: render into a real component according to the tag
We use ordinary components, which cannot be rendered into what we want like JSX s component.
Herev-model
I recommend reading the pitfalls of using calculated attributes:How to bind multi-loop expressions in actual v-model (including principles)
In fact, these two articles are related to a certain extent, but I have separated the requirements here.
Mainly to facilitate friends’ reading and understanding.
Based on this requirement, we will make up for JSX.
JSX is a syntax extension of Javascript, JSX = Javascript XML
, which means writing XML in Javascript. Because of this feature of JSX, it has the flexibility of Javascript sex, and at the same time has the semantics and intuitiveness of html.
Some components with strong activity can be replaced by JSX (such as the above requirements);
It is not necessary to use JSX for the entire project.
We can also embed in components ButtonCounter component.
const ButtonCounter = { name: "button-counter", props: ["count"], methods: { onClick() { this.$emit("changeNum", this.count + 1); } }, render() { return <button onClick={this.onClick}>数量:{this.count}</button>; } }; export default { name: "HelloWorld", props: { msg: String }, data() { return { count: 0 }; }, methods: { // 改变button按钮数量 changeNum(val) { this.count = val; } }, render() { const { count } = this; // 解构 return ( <div class="hello-world-content"> <ButtonCounter style={{ marginTop: "20px" }} count={count} onChangeNum={this.changeNum}></ButtonCounter> </div> ); } };
As you can see, it is basically the same as the template writing method of vue, but you need to pay attention to it. It is curly braces;
requires two pairs of curly braces in the vue template, while in JSX
only needs to write and a pair of .
export default { name: "HelloWorld", props: { msg: String }, data() { return { count: 0, text: "Hello World!", msgClass: "msg-class", isGreen: true }; }, render() { const { count, text } = this; // 解构 return ( <div class="hello-world-content"> <p class={this.msg ? this.msgClass : ""}>动态绑定class</p> <p style={this.isGreen ? "color: green" : ""}>动态绑定style</p> </div> ); } };
Common instructions for v-html, v-if, v-for, v-model It cannot be used in JSX and needs to be implemented in other ways.
v-html
In JSX, if you want to set the innerHTML
of the DOM, you need to use domProps
.
Component usage:
<HelloWorld msg="<div class='custom-div'>这是自定义的DOM</div>"> </HelloWorld>
Component code:
export default { name: "HelloWorld", props: { msg: String }, data() { return {}; }, methods: {}, render() { return <div domPropsInnerHTML={this.msg}></div>; } };
Rendering DOM result:
##v- for
Usemap to implement:
render() { const list = [1,2,3] return( <div> { list.map(item => <button>按钮{item}</button>) } </div> ) }
v-if
Simple example: use ternaryrender() { const bool = false; return <div>{bool ? <button>按钮1</button> : <button>按钮2</button>}</div>; }
render() { let num = 3 if(num === 1){ return( <button>按钮1</button> ) } if(num === 2){ return( <button>按钮2</button> ) } if(num === 3){ return( <button>按钮3</button> ) } }
v-model
Use directly:export default { name: "HelloWorld", props: { msg: String }, data() { return { value: "abc" }; }, watch: { value(val) { console.log("this.model内容:" + val); } }, methods: {}, render() { return ( <div> <input v-model={this.value} placeholder="普通文本" /> </div> ); } };
Listening events
Listening Events like onChange, onClick, etc. can be used.需要注意的是,传参数不能使用 onClick={this.handleClick(params)}
,这样子会每次 render
的时候都会自动执行一次方法。
应该使用bind
,或者箭头函数
来传参。
组件示例代码:
export default { name: "HelloWorld", props: { msg: String }, data() { return {}; }, methods: { handleClick(val) { alert(val); } }, render() { return ( <div> <button type="button" onClick={this.handleClick.bind(this, 11)}> 方式一 </button> <button type="button" onClick={() => this.handleClick(22)}> 方式二 </button> </div> ); } };
用监听事件来实现v-model
:
methods: { input(e) { this.value = e.target.value; } }, render() { return ( <div> <input type="text" value={this.value} onInput={this.input} /> </div> ); }
也可以调整为:
<input type="text" value={this.value} onInput={(e) => (this.vaue = e.target.value)} />
还可以使用对象的方式去监听事件:解构事件
export default { name: "HelloWorld", props: { msg: String }, data() { return { value: "" }; }, watch: { value(val) { console.log("this.model的内容:" + val); } }, methods: { handleInput(e) { this.value = e.target.value; }, handleFocus(e) { console.log(e.target); } }, render() { return ( <div> <input type="text" value={this.value} {...{ on: { input: this.handleInput, focus: this.handleFocus } }} /> </div> ); } };
nativeOn
仅对于组件,用于监听原生事件,也可以使用对象的方式去监听事件:
{...{nativeOn:{click: this.handleClick}}}
事件修饰符
和指令一样,除了个别的之外,大部分的事件修饰符都无法在JSX中使用。
event.stopPropagation()
来代替event.preventDefault()
来代替if (event.target !== event.currentTarget){ return }
.enter与keyCode
: 在特定键触发时才触发回调
if(event.keyCode === 13) { // 执行逻辑 }
除了上面这些修饰符之外,尤大大对于.once,.capture,.passive,.capture.once做了优化,简化代码:
export default { name: "HelloWorld", props: { msg: String }, methods: { handleClick(e) { console.log("click事件:" + e.target); }, handleInput(e) { console.log("input事件:" + e.target); }, handleMouseDown(e) { console.log("mousedown事件:" + e.target); }, handleMouseUp(e) { console.log("mouseup事件" + e.target); } }, render() { return ( <div {...{ on: { // 相当于 :click.capture "!click": this.handleClick, // 相当于 :input.once "~input": this.handleInput, // 相当于 :mousedown.passive "&mousedown": this.handleMouseDown, // 相当于 :mouseup.capture.once "~!mouseup": this.handleMouseUp } }} > 点击模块 </div> ); } };
父传子。
示例:
<HelloWorld> <template slot="default">默认内容</template> <template slot="footer"> <el-button type="primary">确定</el-button> <el-button>取消</el-button> </template> </HelloWorld>
HelloWorld组件代码:this.$slots
export default { name: "HelloWorld", render() { return ( <div> <div class="default">{this.$slots.default}</div> <div class="footer">{this.$slots.footer}</div> </div> ); } };
子传父。
示例:
<HelloWorld> <template v-slot:content="{ name, age }"> <div>姓名:{{ name }}</div> <div>年龄:{{ age }}</div> </template> </HelloWorld>
HelloWorld组件代码:this.$scopedSlots
export default { name: "HelloWorld", render() { return ( <div> <div class="content">{this.$scopedSlots.content({ name: "张三", age: 20 })}</div> </div> ); } };
子组件通过{this.$scopedSlots.content({ name: "张三", age: 20 })}
指定插槽的名称为content
,并将含有name,age属性的对象数据传递给父组件,父组件就可以在插槽内容中使用子组件传递来的数据。
看到v-html用innerHTML;v-for用map;.stop用
event.stopPropagation()
。
你有什么感想?
这不就是我们JavaScript方法的操作吗。
所以JSX就是Javascript + XML。
我以前一直觉得Vue中没必要用JSX吧,用模板Template足以了。
但经过这个需求,我想JSX在处理动态渲染组件还是蛮占有优势的?。
日后面试官问我JSX在Vue的有什么应用场景,我想我可以把这个需求说一说。
The above is the detailed content of Let's talk about how Vue dynamically renders components through JSX. For more information, please follow other related articles on the PHP Chinese website!