vue3.0 開發一個「簡單「插件

vue3.0開發一個「簡單「插件

前言

隨着vue3.0beta的發佈,而且其核心插件vuex,vue-router也都出了beta和alpha版進行適配。感受差很少已經能夠用3.0擼個項目來提早感覺一下Composition API的魅力。結果發現,尚未一個ui框架出了什麼beta仍是alpha來適配3.0。
因而,那就本身試着擼一個簡單ui插件,看看和2.0有什麼不一樣。因而這個項目就是,重構element-ui。目標,適配3.0。
固然本文主要是講解插件開發的不一樣點前端

install函數

插件的入口文件index.jsvue

const install = function (app, opts = {}) {
  // locale.use(opts.locale);
  // locale.i18n(opts.i18n);

  // register components
  components.forEach(component => {
    // debugger
    app.component(component.name, component)
  })

  // Vue.use(InfiniteScroll);
  // Vue.use(Loading.directive);

  /***
   *   Vue.prototype.$ELEMENT = {
        size: opts.size || '',
        zIndex: opts.zIndex || 2000
       };
   */
  app.provide(ELEMENTSymbol, {
    size: opts.size || '',
    zIndex: opts.zIndex || 2000
  })


  // Vue.prototype.$loading = Loading.service;
  // Vue.prototype.$msgbox = MessageBox;
  // Vue.prototype.$alert = MessageBox.alert;
  // Vue.prototype.$confirm = MessageBox.confirm;
  // Vue.prototype.$prompt = MessageBox.prompt;
  // Vue.prototype.$notify = Notification;
  // Vue.prototype.$message = Message;

}

這是對element-ui源碼的install函數的改造。其中最大的區別是,install函數的參數由本來的Vue變成app了。
不單純是名稱的改變。原來的Vue是原型,能夠理解是類。而如今的app是Vue的實例。這樣一來,用法就徹底不一樣了。
不能再像之前同樣使用原型的prototype屬性來實現全局變量,函數啥的了。而且新api已經不推薦採用this.$xxx的方式來訪問全局對象。而是採用provide,inject函數來封裝。
所以以上代碼中,全部的prototype就必須全都註釋掉。也就是由於這個緣由,有些開發者在vue3.0中直接導入如今element-ui版本,運行就直接報錯。其實就是卡在了prototype屬性缺失這裏。react

button.vue

雖然重構整個element-ui是個漫長的踩坑之旅,但柿子先撿軟的捏,先弄個比較簡單的button組件來捏。git

export default {
    name: 'ElButton',
    props: {
      type: {
        type: String,
        default: 'default'
      },
      size: String,
      icon: {
        type: String,
        default: ''
      },
      nativeType: {
        type: String,
        default: 'button'
      },
      loading: Boolean,
      disabled: Boolean,
      plain: Boolean,
      autofocus: Boolean,
      round: Boolean,
      circle: Boolean
    },
    setup(props,ctx) {
      // inject
      const elForm = inject('elForm', '')
      const elFormItem = inject('elFormItem', '')
      const ELEMENT = useELEMENT()

      // computed
      const _elFormItemSize = computed(() => {
        return (elFormItem || {}).elFormItemSize
      })
      const buttonSize = computed(() => {
        return props.size || _elFormItemSize.value || (ELEMENT || {}).size
      })
      const buttonDisabled = computed(() => {
        return props.disabled || (elForm || {}).disabled
      })

      console.log(buttonSize.value)

      //methods
      const handleClick = (evt) => {
        ctx.emit('click', evt)
      }

      return {
        buttonSize,
        buttonDisabled,
        handleClick
      }
    },
    /*inject: {
      elForm: {
        default: ''
      },
      elFormItem: {
        default: ''
      }
    },*/

    /*computed: {
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      buttonSize() {
        return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
      },
      buttonDisabled() {
        return this.disabled || (this.elForm || {}).disabled;
      }
    },*/

    /*  methods: {
      handleClick(evt) {
        this.$emit('click', evt);
      }
    }*/
  };

template的部分並無什麼改變(可能有些改變我不知道),因此,直接照抄源碼。因此不貼出來了。
js部分,最大的不一樣就是新api推薦的新函數setup(),這幾乎是一個all in one的函數,它能夠把之前的data,computed,methods等全都寫到裏面去。寫的好,一個組件就name,props,setup三個屬性就結束了。
setup中的props參數就是用於獲取props中的屬性值用的。ctx參數是一個封裝了slots,emit等對象的proxy對象,用於替代之前的this.$slots,this.$emit等。
button組件相對簡單,三個computed的屬性用computed函數替代,
注意,computed函數返回的實際上是一個Ref對象,在setup中訪問時須要寫成xxx.value才能得到值。而在template中則不須要加value。會自動解析。
老的methods屬性中的函數則直接在setup函數中定義,經過return返回便可。github

row.js

這是一個沒有template的純js組件,挑選這個組件,主要看看他的render函數在新api中應該怎麼寫。vue-router

import {computed, h, provide, inject} from 'vue'

// 常量
const gutterSymbol = Symbol()

export function useGutter() {
  return inject(gutterSymbol)
}
export default {
  name: 'ElRow',

  componentName: 'ElRow',

  props: {
    tag: {
      type: String,
      default: 'div'
    },
    gutter: Number,
    type: String,
    justify: {
      type: String,
      default: 'start'
    },
    align: {
      type: String,
      default: 'top'
    }
  },
  setup(props, ctx) {
    const style = () => computed(() => {
      const ret = {}

      if (props.gutter) {
        ret.marginLeft = `-${props.gutter / 2}px`
        ret.marginRight = ret.marginLeft
      }

      return ret
    })
    provide(gutterSymbol, props.gutter)

    return () => h(props.tag, {
      class: [
        'el-row',
        props.justify !== 'start' ? `is-justify-${props.justify}` : '',
        props.align !== 'top' ? `is-align-${props.align}` : '',
        { 'el-row--flex': props.type === 'flex' }
      ],
      style
    }, ctx.slots);
  },
  /*computed: {
    style() {
      const ret = {};

      if (this.gutter) {
        ret.marginLeft = `-${this.gutter / 2}px`;
        ret.marginRight = ret.marginLeft;
      }

      return ret;
    }
  },*/

  /*render() {
    return h(this.tag, {
      class: [
        'el-row',
        this.justify !== 'start' ? `is-justify-${this.justify}` : '',
        this.align !== 'top' ? `is-align-${this.align}` : '',
        { 'el-row--flex': this.type === 'flex' }
      ],
      style: this.style
    }, this.$slots.default);
  }*/
}

在新api中,render函數已經被setup的return給整合了。當你用template時,return的是template中須要使用的綁定數據對象。但若是沒有template,return的就是渲染函數。一樣,這個return也支持jsx語法。好比vuex

return () => <div>jsx</div>

不過目前官方尚未開發好vue3.0的支持jsx的babel插件。因此,當下仍然只能使用h函數,也就是createElement函數。編程

總結一波

首先,插件入口函數install變了,參數由Vue原型,變成了Vue的根實例。
其次,組件已基本被setup函數all in one了。一個組件,咱們甚至能夠寫成以下形式:element-ui

const vueComponent = {
    setup(props) {
        return h('div',props.name)
    }
}

是否是很眼熟,是否是很像以下代碼:api

const reactComponent = (props) => {
  return <h1>Hello, {props.name}</h1>;
}

沒錯,很像react了。將來vue3.0中是鐵定支持jsx語法的。二者就更像了。vue3.0的官方徵求意見稿中也說了,它們借鑑了react的hook。其實,vue3.0此次的最大更新就是從對象式編程走向了函數式。react的hook就是爲了完善函數式編程而出現的。
終於,兩個主流的前端框架,仍是走到了一塊兒。不知道隔壁的angular啥時候也跟進一波。估計將來某一天就大統一了。

從擼代碼的角度,最大的不一樣就是,之前滿屏的this,看不到了。理論上,新的api中,this是能夠徹底不須要使用的。一個setup函數搞定全部。

目前本項目纔開始,但我會持續踩坑下去。

附上項目地址:https://github.com/Hades-li/element-next

相關文章
相關標籤/搜索