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

相关文章
相关标签/搜索