赞
踩
在上一篇文章中介绍了样式绑定的实现方式,可查看博客----,事件的绑定在低代码开发工具中是必不可少的,一个良好的设计方案可以提高事件绑定的灵活性,提高组件之间的交互通能力,在这篇文章中讲解一下事件绑定的实现原理。
项目地址要是觉着有意思不妨给颗星,目前项目开源欢迎大家参与项目的开发
首先将编辑器中编写的object 文本通过eval()的方式转换为js可使用的object,再将object传入到对应的函数(EcVue())中进行处理,将处理过后的函数绑定到对应的页面中,便可在各组件中使用。
单个页面的数据格式如下:
{type:"page",
pageName:'',
label:'',
children:[],
status:{active:false},
data:{},
ecVueInfo:'',// 编辑器的object文本
EcVue:null, // 用于存放页面的object
css:"",
id:""}
将如下格式的文本传入**createEcVue()**中便可返回EcVue函数对象,将EcVue函数对象放入到对应的页面中,便可在组件上通过对应的方法名、属性名称来进行调用,其中mounted、data、methods与vue中的是一样的作用,可以通过this的方式进行互通调用
export default{
mounted(){
},
data(){
return
}},
methods:{
}}
// 将页面object文本生成函数并返回
export function createEcVue(ecVueInfo) {
ecVueInfo = ecVueInfo.replace("export default", '')
let ecVue = null
try {
if (ecVueInfo && ecVueInfo !== '') {
let info = `()=>{return ${ecVueInfo}}`
let a = eval(info)
ecVue = new ECVue(a())
}
} catch (e) {
console.log(e)
}
return ecVue
}
EcVue 的实现,可以通过这种方式在methods中可以通过this.属性名的方式调用data()中定义的变量
import {registryMethods} from "./registered/registeredMethods"; var noop = function () {} var sharedPropertyDefinition = { get() { }, set() { } }; export function ECVue(options) { if (!this instanceof ECVue) { console.log('ECVue是一个构造函数,应使用“new”关键字调用'); } this._init(options) } ECVue.prototype._init = function (options) { var sc = this; sc.$options = options //代码进行了删减 initState(sc); } function initState(sc) { var opts = sc.$options; //这里是我们在创建实例的时候传的参数 //如果传了methods 则去调用 if (opts.methods) { initMethods(sc, opts.methods); } if (opts.data) { initData(sc); } if(registryMethods){ initRegistryMethods(sc,registryMethods) } // 对应vue 中mounted函数的调用 sc['mounted'] = typeof opts.mounted === 'function' ? opts.mounted : noop // 用于存放vue中的ref 可通过this.$refs['ref']来进行调用 sc['$refs'] = {} } function initRegistryMethods(sc,registryMethods){ for (var key in registryMethods) { sc[key] = typeof registryMethods[key] !== 'function' ? noop : bind(registryMethods[key], sc); } } function initMethods(sc, methods) { //循环methods对象 for (var key in methods) { //给实例增加methods中的方法 这样其实我们就已经可以用vm访问 到methods中的方法了 sc[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], sc); } } function initData(sc) { var data = sc.$options.data; data = sc._data = typeof data === 'function' ? getData(data, sc) : data || {}; var keys = Object.keys(data); var methods = sc.$options.methods; var i = keys.length; while (i--) { var key = keys[i]; //判断key值有没有跟methods中的key重名 { if (methods && hasOwn(methods, key)) { console.log(key + "与methods重名"); } } proxy(sc, `_data`, key) } } function proxy(target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter() { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter(val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) } function bind(method, sc) { return method } function hasOwn(method, key) { return method[key] ? true : false } function getData(data, sc) { return sc.$options.data() }
当有些函数需要重复使用时便可通过添加自定义的函数,在此处也可以绑定api等对应的方法,在methods中通过this.自定义方法名的形式进行调用。
import {ecRouter} from "../core";
export const registryMethods = {
"router":ecRouter,// 路由跳转方法
}
在这里用input组件来演示讲解EcVue函数的使用
**component:**用于记录组件的配置信息如样式,事件,属性等相关内容
**setter:**用于配置组件的设置相关的内容
1.attributes 用于配置组件的属性
bind: ‘value’ 标识的属性为需要进行v-model属性的绑定,当对应属性所需属性值为Function、object时可以设定bind:'value’通过数据绑定的方式来实现其效果。
bind:‘ref’ 标识的属性为需要进行ref的绑定,考虑到ref值的固定性,其设定为对应的ref名不可更改,若在添加组件时有冲突会进行名称增加数字后缀的操作以进行区分
2.events 用于配置组件中的事件
export const ScInput = { component: { component: "ScInput", label: '输入框', events: {}, attributes: {}, styles: {}, }, setter:{ component: "ScInput", setter: { attributes: [ { attributeName: "inputValue", label: "value绑定", detail:"绑定值", bind: 'value', type: "input", value: "inputValue", defaultValue: "inputValue", }, { attributeName: "inputRef",//组件配置中属性字段名 label: "绑定inputRef", bind:'ref', type: "input", value: "inputRef", defaultValue: "inputRef", }, { attributeName: "type", label: "类型", type: "select", value: "text", defaultValue: "text", detail:"类型", typeArray: [{ value: 'text', label: 'text' }, { value: 'textarea', label: 'textarea' },] }, ], styles: {}, events: [ { event: "blur", // 事件名称 enable: false,// 是否启用 detail:"当选择器的输入框失去焦点时触发", method: ''// 绑定方法名 }, { event: "focus", // 事件名称 enable: false,// 是否启用 detail:"当选择器的输入框获得焦点时触发", method: ''// 绑定方法名 }, { event: "change", // 事件名称 enable: false,// 是否启用 detail:"仅当 modelValue 改变时,当输入框失去焦点或用户按Enter时触发", method: ''// 绑定方法名 }, { event: "input", // 事件名称 enable: false,// 是否启用 detail:"在 Input 值改变时触发", method: ''// 绑定方法名 }, { event: "clear", // 事件名称 enable: false,// 是否启用 detail:"在点击由 clearable 属性生成的清空按钮时触发", method: ''// 绑定方法名 } ], } }, }
v-model的绑定 考虑到要保留其响应式的效果采用computed的方式来进行处理,通过调用getPageData()与setPageData()的方法来与EcVue中的data()中设定的变量进行值的交换。
事件绑定 在组件调用的事件中,使用**this.EcVue[‘方法名’]**的形式调用在EcVue中注册的事件,在调用对应的事件之前通过执行execMethod()方法来确认对应事件是否可以执行。
this.EcVue[this.propValue.events['focus'].method](方法参数...)
ref绑定 在组件的mounted生命周期时将组件的ref="inputRef"通过bindRefs()方法传入到EcVue中的$refs中进行使用。
<template> <el-input v-bind = "propValue.attributes" v-model="inputValue" ref="inputRef" @change="changeMethod" @blur = "blurMethod" @focus="focusMethod" @input="inputMethod" @clear="clearMethod" /> </template> <script> import { getPageData, setPageData,execMethod,bindRefs} from "@/utils/core"; export default { name: 'ScInput', props: { propValue: { type: Object, String, default: function () { } }, EcVue:{ type:Function, default:()=>{} } }, computed:{ inputValue: { get(){ // 绑定事件监听 return getPageData(this.propValue.attributes['inputValue'],this.EcVue) }, set(value){ setPageData(this.propValue.attributes['inputValue'],value,this.EcVue) } } }, mounted() { bindRefs(this.propValue.attributes,this.$refs['inputRef'],'inputRef',this.EcVue) }, methods: { changeMethod(val){ if(execMethod(this.propValue.events['change'])){ this.EcVue[this.propValue.events['change'].method](val) } }, blurMethod(val){ if(execMethod(this.propValue.events['blur'])){ this.EcVue[this.propValue.events['blur'].method](val) } }, focusMethod(val){ if(execMethod(this.propValue.events['focus'])){ this.EcVue[this.propValue.events['focus'].method](val) } }, inputMethod(val){ if(execMethod(this.propValue.events['input'])){ this.EcVue[this.propValue.events['input'].method](val) } }, clearMethod(){ if(execMethod(this.propValue.events['clear'])){ this.EcVue[this.propValue.events['clear'].method]() } } } } </script> <style scoped> </style>
getPageData方法
若想给组件属性绑定form中的name属性,可以通过form.name的形式进行绑定,getPageData()先通过analysisData()获得一个属性的数组[‘form’,‘name’]在进行循环调用以获取name中的数值,setPageData()同理。通过这种方式可以应用到表单组件的form-item中进行v-model的绑定
// 调用示范 form.name
form:{
name:'Tom'
}
// 调用示范 formArrauy.0.name
formArrauy:[
{name:'Tom'}
]
// 返回当前页面绑定的数据 export function getPageData(attribute, EcVue) { const params = analysisData(attribute) let setData = EcVue let result = '' if (params.length > 0) { let length = params.length result = (setData !== null && setData[params[0]]) ? setData[params[0]] : '' if (setData) { for (let i = 1; i < length; i++) { result = result[params[i]] } } return result } }
setPageData方法
// 设置当前页面绑定的数据
export function setPageData(attribute, value, EcVue) {
const params = analysisData(attribute)
let setData = EcVue
if (params.length > 0) {
let length = params.length
for (let i = 0; i < length - 1; i++) {
setData = setData[params[i]]
}
setData[params[length - 1]] = value
}
}
analysisDataf方法
// 解析数据绑定的数值
function analysisData(param) {
if (!param) {
return []
}
return param.split(".")
}
bindRefs方法
export function bindRefs(attr, refs, name, EcVue) { if (!EcVue.$refs) { EcVue['$refs'] = null } if (!refs) { refs = null } if (attr[name]) { let i = 0 let value = attr[name].replace(/[0-9]+/g, "") while (EcVue.$refs[attr[name]]) { i++ attr[name] = value + i } EcVue.$refs[attr[name]] = refs } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。