やっとvueのrender関数(二)がわかった(笑・̀ㅂ•́)و✧

29280 ワード

文書ディレクトリ
  • まずvm.$を理解してください.scopedSlots
  • データオブジェクト
  • に深く入り込む
  • JSX

  • 注意:このコードは、単一ファイルコンポーネントで作成されます.コードアドレス
    まずvm.$を調べてみましょうscopedSlots vm.$scopedSlots
    役割ドメインスロットにアクセスできます. slotを含む各スロットについて、オブジェクトは対応するVNodeを返す関数を含む.
    注:2.6.0から、このpropertyには2つの変化があります.
  • 役割ドメインスロット関数は、戻り値が無効な場合にundefinedを返さない限り、VNode配列を返すことを保証します.
  • のすべての$slotsは、$scopedSlotsに関数として露出するようになった.レンダー関数を使用している場合は、現在のスロットにスコープがあるかどうかにかかわらず、$scopedSlotsから常にアクセスすることをお勧めします.これにより、将来の役割ドメインの追加が簡単になるだけでなく、すべてのスロットが関数であるVue 3に最終的に簡単に移行することができます.

  • つまり、$scopedSlotsですべてのスロットにアクセスできるが、$slotsは直接VNodeに戻り、$scopedSlotsはVNode関数(関数の戻り値はVNode)を返す.
    ⚠️ コードを修正する前にvueを2.6.0にアップグレードしてから、vue-template-compilerバージョンもvueバージョンと一致しなければなりません.もしあなたのプロジェクトがこの間違いError: [vue-loader] vue-template-compiler must be installed as a peer dependを報告したら、それはその2つのバージョンが一致していないためです.アップグレードコマンド:
    npm update vue vue-template-compiler
    

    前章のはheaderdefaultをそれぞれに変更するだけです.
    - let _header = _this.$slots.header
    + let _header = _this.$scopedSlots.header()
    
    - _this.$slots.default
    + _this.$scopedSlots.default()
    

    データ・オブジェクトの詳細
    表では、操作ボタンをレンダリングするときに使用されることが多いので、直接見てみましょう.
    //      BaseButton.vue
    <template>
      <div class="button-card">
        <div>
          <slot name="title"></slot>
        </div>
        <button :class="[`${type}`, `${size}`]" @click="handleClick">
          <slot name="button" v-bind:children="contentObj">{{ contentObj.text }}</slot>
        </button>
        <p>
          <slot>help</slot>
        </p>
      </div>
    </template>
    
    <script>
    export default {
      name: 'BaseButton',
      props: {
        type: {
          type: String,
          default: ''
        },
        size: {
          type: String,
          default: ''
        },
        contentObj: {
          type: Object,
          default() {
            return {}
          }
        }
      },
      methods: {
        handleClick() {
          this.$emit('click')
        }
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    .button-card {
      background: #eee;
      border-radius: 8px;
      padding: 24px;
    }
    button {
      border-radius: 4px;
    }
    .success {
      background-color: rgb(149, 204, 149);
      border: 1px solid rgb(149, 204, 149);
      color: #fff;
    }
    .warning {
      background-color: orange;
      border: 1px solid orange;
      color: #fff;
    }
    .danger {
      background-color: red;
      border: 1px solid red;
      color: #fff;
    }
    .small {
      height: 20px;
      padding: 0 8px;
    }
    .middle {
      height: 32px;
      padding: 0 12px;
    }
    .large {
      height: 40px;
      padding: 0 20px;
    }
    </style>
    
    //    
    
    // 1.     
    import BaseButton from './BaseButton'
    // 2. render      
    renderButton: {
      render: function(createElement) {
        const _this = this['$options'].parent
        return createElement(BaseButton, {
          //   `v-bind:class`   API   ,       、              
          class: {
            'base-button': true,
            'ui-button': false
          },
          //   `v-bind:style`   API   ,       、  ,        
          style: {
            // color: 'red',
            fontSize: '14px'
          },
          //     HTML attribute,    BaseButton        
          attrs: {
            id: 'base-button'
          },
          //    prop,                props
          props: {
            type: 'warning',
            size: 'large',
            contentObj: {
              text: 'Confirm',
              icon: '❓'
            }
          },
          // DOM property,             ,        `innerHTML`     
          // domProps: {
          //   innerHTML: 'button   ,      '
          // },
          //        `on`  ,
    	  //        `v-on:keyup.enter`       。
    	  //              keyCode。
    	  //     `$emit`         ,             
          on: {
            click: _this.clickHandler,
            dblclick: _this.dblclickHandler
          },
          //      ,        ,          `vm.$emit`      。
          nativeOn: {
            dblclick: _this.nativeClickHandler
          },
          //      。 
          // directives: [],
          //          :{ name: props => VNode | Array }
          scopedSlots: {
            default: props => {
              if (props.children) {
                return createElement('span', props.children.text + ' ?')
              }
              return createElement('span', 'parent slot defult')
            }
          },
          //              ,        。         
          slot: 'name-of-slot'
          //        property。 
          // key: '',
          // ref: '',
          // refInFor: true
        })
      }
    },
    // 3.      `renderButton`
    <renderButton />
    

    JSX
    render関数は私たちの問題を解決しましたが、本当に面倒です.
    これは、VueでJSX構文を使用するためのBabelプラグインがあり、テンプレートに近い構文に戻ることができます.
    JSXの使用:
  • インストールプラグイン
    npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
    
  • .babelrcファイルを新規作成し(プロジェクトにない場合)、次の構成
    {
      "presets": ["@vue/babel-preset-jsx"]
    }
    
  • を追加します.
    ここで、上記のコンポーネントをJSXの書き方に変更します.
    renderButtonWidthJSX: {
      render(h) {
        const _this = this['$options'].parent
        const contentObj = { text: 'jsx' }
    
        return (
          <BaseButton
            type="danger"
            size="small"
            contentObj={contentObj}
            style={{ fontSize: '12px' }}
            class="jsx-button"
            onclick={_this.clickHandler}
            scopedSlots={{
              title: () => <h1>jsx title</h1>,
              // button: () => 'Delete',
              default: () => <span>default</span>
            }}
          />
        )
      }
    }
    

    次のように書くこともできます.
    renderButtonWidthJSX: {
      render(h) {
        const _this = this['$options'].parent
        const contentObj = { text: 'jsx' }
        return (
          <BaseButton
            {...{
              props: {
                type: 'danger',
                size: 'small',
                contentObj,
                style: { fontSize: '12px' },
                class: 'jsx-button'
              },
              on: {
                click: _this.clickHandler,
                dblclick: _this.dblclickHandler
              },
              nativeOn: {
                dblclick: _this.nativeClickHandler
              },
              scopedSlots: {
                title: () => <h1>jsx title</h1>,
                // button: () => 'Delete',
                default: () => <span>default</span>
              }
            }}
          />
        )
      }
    }
    

    ⚠️ render関数はhパラメータを使用していませんが、送信する必要があります.そうしないと、エラーが発生します.hcreateElementの別名とすることはVue生態系における一般的な慣例であり,実際にはJSXが要求している.VueのBabelプラグインの3.4.0バージョンから、ES 2015構文で宣言されたJSXを含む任意の方法とgetter(関数や矢印関数ではない)にconst h = this.$createElementを自動的に注入し、(h)パラメータを削除することができます.以前のバージョンのプラグインでは、hが現在の役割ドメインで使用できない場合、アプリケーションは間違っています.