ホームページ  >  記事  >  ウェブフロントエンド  >  Vue の JSX とは何ですか?いつ使用しますか?使い方?

Vue の JSX とは何ですか?いつ使用しますか?使い方?

青灯夜游
青灯夜游転載
2023-01-16 20:23:092966ブラウズ

Vue の JSX とは何ですか?次の記事では、Vue での JSX の概要、JSX を使用するタイミング、Vue2 での基本的な使い方について説明します。

Vue の JSX とは何ですか?いつ使用しますか?使い方?

JSX の概要

JSX は Javascript の構文拡張であり、Javascript## のすべての機能を備えています。 # 関数でありながら、html のセマンティクスと直観性も備えています。これにより、JS でテンプレート構文を記述することができます:

const el = <div>Vue 2</div>;

上記のコードは HTML でも文字列でもありません。これは、JavaScript の拡張構文である JSX と呼ばれます。 JSX というとテンプレート構文を思い出すかもしれませんが、JavaScript のプログラミング能力をすべて備えています。 [関連する推奨事項:

vuejs ビデオ チュートリアル Web フロントエンド開発 ]

JSX を使用する場合

いつ使用するか見出しコンポーネントが

level プロパティを通じてのみ動的に生成できる場合、すぐに次のように実装することを考えるかもしれません:

<script type="text/x-template" id="anchored-heading-template">
<h1 v-if="level === 1"> 
    <slot></slot> 
</h1> 
<h2 v-else-if="level === 2"> 
    <slot></slot> 
</h2> 
<h3 v-else-if="level === 3"> 
    <slot></slot> 
</h3> 
</script>

ここでテンプレートを使用することは最良の選択ではありません。コードの一部がレベル タイトルで繰り返されていますが、これは十分に簡潔でエレガントではありません。 JSX で記述しようとすると、コードははるかに単純になります:

const App = {
  render() {
    const tag = `h${this.level}`
    return <tag>{this.$slots.default}</tag>
  }
}

または、多数の

render 関数を記述すると、次のコードを書くのが非常に面倒に感じるかもしれません。

createElement(  
    &#39;anchored-heading&#39;, {  
        props: {  
            level: 1  
        }  
    }, [  
    createElement(&#39;span&#39;, &#39;Hello&#39;),  
        &#39; world!&#39;  
    ]  
)

特に、対応するテンプレートが非常に単純な場合:

<anchored-heading :level="1">  
    <span>Hello</span> world!  
</anchored-heading>

現時点では、Vue で JSX 構文を使用できるため、テンプレートに近い構文に戻ることができます:

import AnchoredHeading from &#39;./AnchoredHeading.vue&#39;  
  
new Vue({  
    el: &#39;#demo&#39;,  
    render: function (h) {  
        return (  
            <AnchoredHeading level={1}>  
                <span>Hello</span> world!  
            </AnchoredHeading>  
        )  
    }  
})

開発プロセスでは、メッセージ プロンプト コンポーネント Message がよく使用されます。これを記述する方法の 1 つは次のとおりです:

Message.alert({
  messge: &#39;确定要删除?&#39;,
  type: &#39;warning&#39;
})

ただし、

message でいくつかのスタイルをカスタマイズできることを願っています、現時点では、Message.alertJSX をサポートする必要があるかもしれません (もちろん、slot/html や他のメソッドを使用して解決することもできます)

Message.alert({
  messge: <div>确定要删除<span style="color:red">xxx</span>的笔记?</div>,
  type: &#39;warning&#39;
})

さらに、

.vue ファイルに記述できるコンポーネントは 1 つだけです。これは、シナリオによっては不便な場合があります。多くの場合、ページを記述するときに、実際には次のことが必要になることがあります。いくつかの小さなノードのフラグメントを分割し、それらを小さなコンポーネントで再利用します。これらの小さなコンポーネントは、実際には単純な関数コンポーネントを書くことで解決できます。通常、SFC の制限により、すべてを 1 つのファイルに記述することに慣れているかもしれませんが、この方法を試してみてもよいと言わざるを得ません。

// 一个文件写多个组件
const Input = (props) => <input {...props} />
export const Textarea = (props) => <input {...props} />
export const Password = (props) => <input type="password" {...props} />

export default Input

たとえば、ここでは入力コンポーネントがカプセル化されています。ユーザーが実際のニーズに応じて使用できるように、パスワード コンポーネントとテキストエリア コンポーネントを同時にエクスポートしたいと考えています。これら 2 つのコンポーネント自体は入力コンポーネントを使用します内部的には異なりますが、一部の小道具はカスタマイズされています。 JSXでは非常に便利で、基本的には単純な関数コンポーネントを記述し、インターフェースを通じてpropsを宣言するだけで十分です。ただし、テンプレートを使用して記述されている場合は、3 つのファイルに分割される可能性があり、おそらく

index.js のエントリ ファイルが追加されて 3 つのコンポーネントをエクスポートすることになります。

JSX の本質は JavaScript であるため、JavaScript の完全なプログラミング機能を備えています。別の例として、一連の DOM ノードを反転するロジックを使用する必要がある場合、これをテンプレートに記述する場合は、おそらく 2 つのコードを記述する必要があります。

この例は一般的ではないかもしれませんが、シナリオによっては、テンプレートよりも JSX の方が記述が簡単であることを認めなければなりません。

Vue 2 以降、テンプレートは実行前に JavaScript の

render 関数にコンパイルされます。

Vue の JSX とは何ですか?いつ使用しますか?使い方?

#Vue では、ほとんどの場合、テンプレートを使用して HTML を作成することをお勧めします。ただし、シナリオによっては、テンプレートよりも柔軟なレンダリング関数を使用する必要があります。これらの

レンダリング関数は、実行時段階の伝説的な 仮想 DOM です。

Vue の JSX とは何ですか?いつ使用しますか?使い方?

Vue2 での JSX の基本的な使用法

設定

Vue 2 では、 JSX コンパイルは、2 つのパッケージ

@vue/babel-preset-jsx@vue/babel-helper-vue-jsx-merge-props に依存する必要があります。前者のパッケージは JSX の構文のコンパイルを担当し、後者のパッケージはランタイム mergeProps 関数を導入するために使用されます。

npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props

さらに、babel.config.js に構成を追加します。

module.exports = {
  presets: [&#39;@vue/babel-preset-jsx&#39;],
}

テキスト補間

テンプレート コード内のテキスト補間は、デフォルトで二重中括弧になります。

<h1>{{ msg }}</h1>

JSX では、単一の中括弧を使用する必要があります:

const name = &#39;Vue&#39;
const element = <h1>Hello, { name }</h1>

テンプレート構文のテキスト補間と同様に、次のような有効な JS 式が中括弧内でサポートされます:

2 2 user.firstNameformatName(user) など。

条件与循环渲染

在模板代码里面我们通过v-for去遍历元素,通过v-if去判断是否渲染元素,在JSX中,对于v-for,可以使用for循环或者array.map来代替,对于v-if,可以使用if-else语句,三元表达式等来代替

使用if-else语句

const element = (name) => {
  if (name) {
    return <h1>Hello, { name }</h1>
  } else {
    return <h1>Hello, Stranger</h1>
  }
}

使用三元表达式

const element = icon ? <span class="icon"></span> : null;

使用数组的map方法

const list = [&#39;java&#39;, &#39;c++&#39;, &#39;javascript&#39;, &#39;c#&#39;, &#39;php&#39;]
return (
  <ul>
  {list.map(item => {
   return <li>{item}</li>
  })}
  </ul>
)

属性绑定

在模板代码中,一般通过 v-bind:prop="value":prop="value"来给组件绑定属性,在JSX里面就不能继续使用v-bind指令了,而是通过单大括号的形式进行绑定:

const href = &#39;https://xxx.com&#39;
const element = <a href={href}>xxx</a>
const properties = {a: 1, b: 2}

此外,模板代码中能通过<div v-bind="properties"></div>批量绑定标签属性。

在JSX中也有相应的替换方案:<div></div>

class绑定同样也是使用单大括号的形式

const element = <div className={`accordion-item-title ${ disabled ? &#39;disabled&#39; : &#39;&#39; }`}></div>
const element = <div class={
    [ &#39;accordion-item-title&#39;, disabled && &#39;disabled&#39; ]
  }
>Item</div>

style绑定需要使用双大括号

const width = &#39;100px&#39;
const element = <button style={{ width, fontSize: &#39;16px&#39; }}></button>

事件绑定

在模板代码中通过v-on指令监听事件,在JSX中通过on + 事件名称的大驼峰写法来监听,且绑定事件也是用大括号,比如click事件要写成onClick,mouseenter事件要写成onMouseenter

const confirm = () => {
  // 确认提交
}
<button onClick={confirm}>确定</button>

有时候我们希望可以监听一个组件根元素上面的原生事件,这时候会用到.native修饰符,但是在JSX中同样也不能使用,不过也有替代方案,监听原生事件的规则与普通事件是一样的,只需要将前面的on替换为nativeOn,如下

 render() {
    // 监听下拉框根元素的click事件
    return <CustomSelect nativeOnClick={this.handleClick}></CustomSelect>
  }

除了上面的监听事件的方式之外,我们还可以使用对象的方式去监听事件

  render() {
    return (
      <ElInput
        value={this.content}
        on={{
          focus: this.handleFocus,
          input: this.handleInput
        }}
        nativeOn={{
          click: this.handleClick
        }}
      ></ElInput>
    )
  }

对于 .passive.capture 和 .once 这些事件修饰符,Vue 提供了相应的前缀可以用于 on

Vue の JSX とは何ですか?いつ使用しますか?使い方?

例如:

on: {  
    &#39;!click&#39;: this.doThisInCapturingMode,  
    &#39;~keyup&#39;: this.doThisOnce,  
    &#39;~!mouseover&#39;: this.doThisOnceInCapturingMode  
}

对于所有其它的修饰符,私有前缀都不是必须的,因为你可以在事件处理函数中使用事件方法:Vue の JSX とは何ですか?いつ使用しますか?使い方?具体可查阅Vue规范文档。

v-show与v-model

大多数指令并不能在JSX中使用,对于原生指令,只有v-show是支持的。

v-modelVue提供的一个语法糖,它本质上是由 value属性(默认) + input事件(默认)组成的,所以,在JSX中,我们便可以回归本质,通过传递value属性并监听input事件来手动实现数据的双向绑定:

export default {
  data() {
    return {
      name: &#39;&#39;
    }
  },
  methods: {
    // 监听 onInput 事件进行赋值操作
    handleInput(e) {
      this.name = e.target.value
    }
  },
  render() {
    // 传递 value 属性 并监听 onInput事件
    return <input value={this.name} onInput={this.handleInput}></input>
  }
}

此外,在脚手架vue-cli4中,已经默认集成了对v-model的支持,可以直接使用<input v-model="{this.value}">,如果项目比较老,也可以安装插件babel-plugin-jsx-v-model来进行支持。

同样的,在JSX中,对于.sync也需要用属性+事件来实现,如下代码所示:

export default {
  methods: {
    handleChangeVisible(value) {
      this.visible = value
    }
  },
  render() {
    return (
      <ElDialog
        title="测试.sync"
        visible={this.visible}
        on={{ &#39;update:visible&#39;: this.handleChangeVisible }}
      ></ElDialog>
    )
  }
}

插槽

(1)默认插槽:

使用element-uiDialog时,弹框内容就使用了默认插槽,在JSX中使用默认插槽的用法与普通插槽的用法基本是一致的,如下

 render() {
    return (
      <ElDialog title="弹框标题" visible={this.visible}>
        {/*这里就是默认插槽*/}
        <div>这里是弹框内容</div>
      </ElDialog>
    )
  }

自定义默认插槽:

Vue的实例this上面有一个属性$slots,这个上面就挂载了一个这个组件内部的所有插槽,使用this.$slots.default就可以将默认插槽加入到组件内部

export default {
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  render() {
    return (
      <div class="custom-dialog" vShow={this.visible}>
        {/**通过this.$slots.default定义默认插槽*/}
        {this.$slots.default}
      </div>
    )
  }
}

(2)具名插槽

有时候我们一个组件需要多个插槽,这时候就需要为每一个插槽起一个名字,比如element-ui的弹框可以定义底部按钮区的内容,就是用了名字为footer的插槽

 render() {
    return (
      <ElDialog title="弹框标题" visible={this.visible}>
        <div>这里是弹框内容</div>
        {/** 具名插槽 */}
        <template slot="footer">
          <ElButton>确定</ElButton>
          <ElButton>取消</ElButton>
        </template>
      </ElDialog>
    )
  }

自定义具名插槽: 在上节自定义默认插槽时提到了$slots,对于默认插槽使用this.$slots.default,而对于具名插槽,可以使用this.$slots.footer进行自定义

render() {
    return (
      <div class="custom-dialog" vShow={this.visible}>
        {this.$slots.default}
        {/**自定义具名插槽*/}
        <div class="custom-dialog__foolter">{this.$slots.footer}</div>
      </div>
    )
  }

(3)作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的,这时候就需要用到作用域插槽,在JSX中,因为没有v-slot指令,所以作用域插槽的使用方式就与模板代码里面的方式有所不同了。比如在element-ui中,我们使用el-table的时候可以自定义表格单元格的内容,这时候就需要用到作用域插槽

data() {
    return {
      data: [
        {
          name: &#39;xxx&#39;
        }
      ]
    }
  },
  render() {
    return (
      {/**scopedSlots即作用域插槽,default为默认插槽,如果是具名插槽,将default该为对应插槽名称即可*/}
      <ElTable data={this.data}>
        <ElTableColumn
          label="姓名"
          scopedSlots={{
            default: ({ row }) => {
              return <div style="color:red;">{row.name}</div>
            }
          }}
        ></ElTableColumn>
      </ElTable>
    )
  }

自定义作用域插槽:

使用作用域插槽不同,定义作用域插槽也与模板代码里面有所不同。加入我们自定义了一个列表项组件,用户希望可以自定义列表项标题,这时候就需要将列表的数据通过作用域插槽传出来。

render() {
    const { data } = this
    // 获取标题作用域插槽
    const titleSlot = this.$scopedSlots.title
    return (
      <div class="item">
        {/** 如果有标题插槽,则使用标题插槽,否则使用默认标题 */}
        {titleSlot ? titleSlot(data) : <span>{data.title}</span>}
      </div>
    )
  }

使用自定义组件

只需要导入进来,不用再在components属性声明了,直接写在jsx中:

import MyComponent from &#39;./my-component&#39;

export default {
  render() {
    return <MyComponent>hello</MyComponent>
  },
}

在method里返回JSX

我们可以定义method,然后在method里面返回JSX,然后在render函数里面调用这个方法,不仅如此,JSX还可以直接赋值给变量,比如:

 methods: {
    renderFooter() {
      return (
        <div>
          <ElButton>确定</ElButton>
          <ElButton>取消</ElButton>
        </div>
      )
    }
  },
  render() {
    const buttons = this.renderFooter()
    return (
      <ElDialog visible={this.visible}>
        <div>内容</div>
        <template slot="footer">{buttons}</template>
      </ElDialog>
    )
  }

用JSX实现简易聊天记录

假设该消息聊天记录的消息类型只有三种:文本,图片,引用。一条消息里面可以包括任意类型的内容,引用类型消息内部可以不断嵌套引用其他任意类型消息。效果图大致如下:

Vue の JSX とは何ですか?いつ使用しますか?使い方?

消息数据结构如下:

message: [
      // 每个数组的第一个参数为消息类型:0:文本 1:图片 2:引用。第二个参数为具体内容
      [
        0,
        &#39;文本&#39;
      ],
      [
        1,
        &#39;图片链接xxx&#39;
      ],
      [
        2,
        [
          [
            0,
            &#39;引用文本文本文本&#39;
          ],
          [
            1,
            &#39;引用图片链接xxx&#39;
          ]
        ]
      ]
    ]

主要有两个思路:

1、思路一:在render里返回一段用array.map渲染的消息模板,对于三种消息类型,使用if-else进行判断分别渲染,对于引用类型的消息,可以封装一个方法进行渲染,方法里面如果还有引用类型消息就继续递归渲染。

methods: {
    // 展示引用消息
    showQuote (msg) {
      return (
        <div class="content-quote">
          <span class="quote-title">引用:</span>
          {msg.map(item => {
            if (item[0] === 0) {
              return <p class="content-text">{item[1]}</p>
            } else if (item[0] === 1) {
              return (
                <el-image
                  class="content-img"
                  src={item[1]}
                  preview-src-list={[item[1]]}>
                </el-image>
              )
            } else {
              return this.showQuote(item[1])
            }
          })}
        </div>
      )
    }
  },
  render (h) {
    return (
      <ul class="chat-record-list">
        {this.recordList.map(item => {
          return (
            <li
              class="chat-record-item"
              key={item.timeStamp}
            >
              <div class="title">
                <span class="person-info">
                  { `${item.sendUserNick}(${item.sendUserNet}) → ${item.receiverNick}(${item.receiverNet})` }
                </span>
                <span class="sendtime">
                  { this.formatTime(&#39;YYYY-mm-dd HH:MM:SS&#39;, item.timeStamp) }
                </span>
              </div>
              <div class="content">
                {item.message.map(msg => {
                  if (msg[0] === 0) {
                    return <p class="content-text">{msg[1]}</p>
                  } else if (msg[0] === 1) {
                    return (
                      <el-image
                        class="content-img"
                        src={msg[1]}
                        preview-src-list={[msg[1]]}>
                      </el-image>
                    )
                  } else {
                    // 递归渲染引用类型消息
                    return this.showQuote(msg[1])
                  }
                })}
              </div>
            </li>
          )
        })}
      </ul>
    )
  }

2、思路二:第一种思路中封装的showQuote里面的代码与render中渲染消息内容的代码基本相似,因此其实现方式不够优雅。其实可以将整个消息的渲染封装成一个组件,在该组件内引入自己,然后再渲染自己。由于具体细节代码与上述类似,这里只给出思路代码,具体细节请忽略

// 当前组件就是RecordMessage组件,自己引入自己
import RecordMessage from &#39;./RecordMessage.vue&#39;

export default {
    props: {
        message: {
            type: Array,
            default: () => []
        }
    },
    render () {
        const parseMessage = msg => {
            const type = msg[0]
            if (type === 0) {
                // 文本
            } else if (type === 2) {
                // 图片
            } else {
                // 引用类型
                return (
                    <div>
                        <div>引用:</div>
                        {
                            msg[1].map(subMsg => (
                                // 自己递归渲染自己
                                <recored-message>
                                </recored-message>
                            ))
                        }
                    </div>
                )
            }
        }
        return parseMessage(this.message)
    }

(学习视频分享:vuejs入门教程编程基础视频

以上がVue の JSX とは何ですか?いつ使用しますか?使い方?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。