昨天介绍了我的web插件化构想.在实现中为了底层能和用户编写的代码进行方便的交互.我把面向对象里的类融合进来了.
最初,面向开发者的编程,打算直接使用js.后来发现底层不能很方便的与开发者写的代码进行交互.
思考2天后,我使用xml描述类结构.底层通过类的描述生成动态的js对象.这样就可以很方便的与底层C#交互了.
最后我把这种编程方式命名为 "jrxml"
<
jrxml:classes
xmlns:jrxml
="http://tmp"
>
<
jrxml:method
access
="private"
name
="loadTpl"
modifier
="readonly"
/>
<
jrxml:method
access
="internal"
name
="render"
modifier
="fixed"
>
//
<![CDATA[
return '<div id="root">render</div>';
//
]]>
</
jrxml:method
>
<
jrxml:method
access
="internal"
name
="domReady"
params
="htmlDoc"
modifier
="fixed"
>
//
<![CDATA[
htmlDoc.getElementById('root').innerHTML='domReady<br/>--------------<br/>---------- by jaws';
this.onCreate.fire();
//
]]>
</
jrxml:method
>
<
jrxml:method
access
="internal"
name
="ctor"
modifier
="fixed"
><!--构造函数-->
this.width=100;
</
jrxml:method
>
<
jrxml:method
name
="update"
modifier
="readonly"
/>
<
jrxml:event
name
="onCreate"
/>
<
jrxml:prop
name
="width"
/>
<
jrxml:prop
name
="config"
>
<
jrxml:setter
>
return value+100;
</
jrxml:setter
>
<
jrxml:getter
>
return value;
</
jrxml:getter
>
</
jrxml:prop
>
</
jrxml:classes
>
上面是一个完整的类结构
xml元素:
<classes 根节点
<method 方法定义
<prop 属性定义 属性可以在method节点里,this.xxxx,可以set和get
<event 事件 事件可以在method节点力,this.xxxx.subscribe(function(){});进行订阅,和调用this.xxxx.fire();
xml节点属性:
name="" 类成员名
access="public|private|internal" 访问控制, 公共|私有|内部
modifier="none|fixed|readonly" 修饰符, 无|固定|只读
因为本身只是简单的类结构模拟,所以没有考虑结构,抽象类,继承. 所以加入fixed和readonly特性,对于标示2个特性的成员,开发者不可对此成员进行修改.
上面是对类结构的描述,接下来需要有一个编译过程.
在C#代码中要初始化一个类的顺序为:
验证类结构->
js运行时->
编译
->创建js object
验证类结构:
其实就是验证xml,对于classes的xml结构定义了一个xsd,验证的话直接C#用xsd验证xml结构即可.
下面是classes_schema.xsd
View Code
<
xs:schema
xmlns:xs
="http://www.w3.org/2001/XMLSchema"
xmlns:xs2
="http://tmp"
targetNamespace
="http://tmp"
>
<
xs:attributeGroup
name
="attr_typeb"
>
<
xs:attribute
name
="access"
default
="public"
>
<
xs:simpleType
>
<
xs:restriction
base
="xs:string"
>
<
xs:enumeration
value
="public"
/>
<
xs:enumeration
value
="private"
/>
<
xs:enumeration
value
="internal"
/>
</
xs:restriction
>
</
xs:simpleType
>
</
xs:attribute
>
<
xs:attribute
name
="modifier"
default
="none"
>
<
xs:simpleType
>
<
xs:restriction
base
="xs:string"
>
<
xs:enumeration
value
="none"
/>
<
xs:enumeration
value
="fixed"
/>
<
xs:enumeration
value
="readonly"
/>
</
xs:restriction
>
</
xs:simpleType
>
</
xs:attribute
>
</
xs:attributeGroup
>
<
xs:attributeGroup
name
="attr_name"
>
<
xs:attribute
name
="name"
use
="required"
>
<
xs:simpleType
>
<
xs:restriction
base
="xs:string"
>
<
xs:pattern
value
="[a-zA-Z_][a-zA-Z0-9_]{0,49}"
/>
</
xs:restriction
>
</
xs:simpleType
>
</
xs:attribute
>
</
xs:attributeGroup
>
<
xs:complexType
name
="setget"
>
<
xs:simpleContent
>
<
xs:extension
base
="xs:string"
>
<
xs:attributeGroup
ref
="xs2:attr_typeb"
/>
</
xs:extension
>
</
xs:simpleContent
>
</
xs:complexType
>
<
xs:element
name
="classes"
>
<
xs:complexType
>
<
xs:choice
minOccurs
="0"
maxOccurs
="unbounded"
>
<
xs:element
name
="prop"
minOccurs
="0"
maxOccurs
="unbounded"
>
<
xs:complexType
>
<
xs:all
>
<
xs:element
name
="setter"
type
="xs2:setget"
minOccurs
="0"
maxOccurs
="1"
/>
<
xs:element
name
="getter"
type
="xs2:setget"
minOccurs
="0"
maxOccurs
="1"
/>
</
xs:all
>
<
xs:attributeGroup
ref
="xs2:attr_name"
/>
<
xs:attributeGroup
ref
="xs2:attr_typeb"
/>
</
xs:complexType
>
</
xs:element
>
<
xs:element
name
="event"
minOccurs
="0"
maxOccurs
="unbounded"
>
<
xs:complexType
>
<
xs:attributeGroup
ref
="xs2:attr_name"
/>
<
xs:attributeGroup
ref
="xs2:attr_typeb"
/>
</
xs:complexType
>
</
xs:element
>
<
xs:element
name
="method"
minOccurs
="0"
maxOccurs
="unbounded"
>
<
xs:complexType
>
<
xs:simpleContent
>
<
xs:extension
base
="xs:string"
>
<
xs:attributeGroup
ref
="xs2:attr_name"
/>
<
xs:attribute
name
="params"
>
<
xs:simpleType
>
<
xs:restriction
base
="xs:string"
>
<
xs:pattern
value
="([a-zA-Z_][a-zA-Z0-9_]{0,45}|[a-zA-Z_][a-zA-Z0-9_]{0,45}, )+[^, ]"
/>
</
xs:restriction
>
</
xs:simpleType
>
</
xs:attribute
>
<
xs:attributeGroup
ref
="xs2:attr_typeb"
/>
</
xs:extension
>
</
xs:simpleContent
>
</
xs:complexType
>
</
xs:element
>
</
xs:choice
>
</
xs:complexType
>
</
xs:element
>
</
xs:schema
>
js运行时: 服务端js引擎使用开源的Jurassic
这里挺曲折的,最早我用的IronJS引擎.据说效率很好,用了之后发现效率真的很烂.开始怀疑是我使用方式错了.
最后时用官方的例子来跑,结果一样慢.我把.net几个开源的js引擎做了个简单对比就改用Jurassic了.
var jsEngine= new js.ScriptEngine();
编译:
读取xml类结构,根据不同的节点,生成相应的对象. 下面贴出相应的代码片段
View Code
pe.result_rt CompileMethod(pe.jtype type)
{
var ret =
new pe.result_rt { succ =
true };
var method = (pe.jmethod)type;
try
{
var ls =
new List<
string>();
foreach (
var item
in method.param_list)
{
ls.Add(item);
}
ls.Add(method.body);
var fun = Engine.Function.Construct(ls.ToArray());
if (_methodDic.ContainsKey(method.name))
{
DefineProperty(method.name,
new PropertyDescriptor(_methodDic[method.name], PropertyAttributes.Enumerable),
true);
}
else
if (method.access_type != pe.jaccess_type.jinternal)
{
DefineProperty(method.name,
new PropertyDescriptor(fun, PropertyAttributes.Enumerable),
true);
}
else
{
_methodDic.Add(method.name, fun);
}
}
catch (Exception ex)
{
ret.succ =
false;
#region code mark
ret.markline_list.Add(
new pe.result_rt_markline
{
line = method.line -
1,
type =
"
err
",
msg = ex.Message
});
#endregion
}
return ret;
}
pe.result_rt CompileEvent(pe.jtype type)
{
var ret =
new pe.result_rt { succ =
true };
var evt = (pe.jevent)type;
DefineProperty(evt.name,
new PropertyDescriptor(
new ClassesEvent(
this, evt.name, Engine), PropertyAttributes.Enumerable),
true);
return ret;
}
pe.result_rt CompileSetterGetter(
string name, pe.jsgtter sgtter)
{
var ret =
new pe.result_rt { succ =
true };
if (sgtter !=
null && sgtter.body !=
"")
{
try
{
var fun = Engine.Function.Construct(
"
value
", sgtter.body);
_propSGDic.Add(name, fun);
}
catch (Exception ex)
{
ret.succ =
false;
#region code mark
ret.markline_list.Add(
new pe.result_rt_markline
{
line = sgtter.line -
1,
type =
"
err
",
msg = ex.Message
});
#endregion
}
}
return ret;
}
pe.result_rt CompileProp(pe.jtype type)
{
var ret =
new pe.result_rt { succ =
true };
var prop = (pe.jprop)type;
var pName = prop.name;
ret = CompileSetterGetter(
"
set
" + pName, prop.setter);
if (!ret.succ)
{
return ret;
}
ret = CompileSetterGetter(
"
get
" + pName, prop.getter);
if (!ret.succ)
{
return ret;
}
var c = prop.access_type == pe.jaccess_type.jpublic ?
1 :
0;
c += prop.setter ==
null || prop.setter.access_type == pe.jaccess_type.jpublic ?
1 :
0;
c += prop.getter ==
null || prop.getter.access_type == pe.jaccess_type.jpublic ?
1 :
0;
if (c ==
3)
{
_publicPropNameIdDic.Add(pName, type.id);
}
_propValueDic.Add(pName, Null.Value);
var setter =
new ClrFunction(Engine.Function.InstancePrototype,
new Action<
object>(value =>
{
SetPropValue(pName, value);
}));
var getter =
new ClrFunction(Engine.Function.InstancePrototype,
new Func<
object>(() =>
{
return GetPropValue(pName);
}));
DefineProperty(prop.name,
new PropertyDescriptor(getter, setter, js.lib.PropertyAttributes.Enumerable),
true);
return ret;
}
思路很简单,只是一个简单的实现.希望能给一些朋友带来帮助.