在使用EasyUI的时候,有时我们要改动某个组件的源码,因为jquery.easyui.min.js体积庞大,且格式混乱,所以我们往往选择修改位于plugins目录下的单个组件源码,然后在使用的时候先引入jquery.easyui.min.js文件再引入plugins/jquery.validatebox.js(这里拿validatebox组件说事)文件,这样就起到了覆盖定义的效果。
这样做到底有没有什么问题呢?在没有遇到问题的时候,我也是觉得应该没啥问题了。上周群内有兄弟问了一个validatebox扩展的rules无效的问题,我分析过后,禁不住拍了下脑门,原来这种覆盖定义的方式还是存在一定隐患的。我们拿依赖validatebox的combobox来还原”凶案现场“。
凶案现场:
文件引入顺序:
- <script type="text/javascript" src="../../jquery-1.8.0.min.js"></script>
- <script type="text/javascript" src="../../jquery.easyui.min.js"></script>
- <script type="text/javascript" src="../../plugins/jquery.validatebox.js"></script>
- <script type="text/javascript">
- //alert($.fn.validatebox.defaults.rules == $.fn.combo.defaults.rules);
- $(document).ready(function () {
- $.extend($.fn.validatebox.defaults.rules, {
- IsExist: {
- validator: function (value) {
- alert(11);
- }
- }
- });
- });
- </script>
使用扩展的IsExist规则:
- <select id="abc" class="easyui-combobox" data-options="validType:'IsExist'" name="state" style="width:200px;">
- <option value="AL">ssadasdasdas</option>
- <option value="AK">Alaska</option>
- <option value="AZ">Arizona</option>
- <option value="AR">Arkansas</option>
- <option value="CA">California</option>
- </select>
结果让人出乎意料呀,我们扩展的IsExist规则并未被触发,这是怎么回事呢?
问题分析:
要弄清楚这个问题,我们必须把关键步骤梳理出来,也就是要把关键代码的执行顺序给梳理出来,这一点对于分析问题很重要。经过简单分析,可以得到关键代码的执行顺序如下:
1.定义validatebox组件
jquery.easyui.min.js内部首先引入的是validatebox组件,组件的定义方式都是自动运行的匿名函数,匿名函数内部定义组件的构造函数,默认值,方法,属性转换器等,对于本文要分析的问题,我们把重点放在默认值和属性转换器的定义上:
1-1.定义默认值:
- $.fn.validatebox.defaults = {
- required: false,
- //...省略若干属性
- rules: {
- url: {
- validator: function(value) {
- return false;//这里就不写那串正则表达式了
- },
- message: "Please enter a valid URL."
- }
- //...省略若干规则
- }
- };
我们看到定义了$.fn.validatebox.defaults属性,而这个属性指向一个内存堆,我们姑且将这个堆叫做:A;defaults属性内部还有一个rules属性,rules也是一个对象,指向另一内存堆:B.
2.定义combo组件
combo组件依赖validatebox组件,所以后于validatebox组件引入,引入combo的时候自然也会定义combo的构造函数,默认值,方法,属性转换器等。
2-1.定义默认值:
- $.fn.combo.defaults = $.extend({}, $.fn.validatebox.defaults, {
- width: "auto",
- //省略若干属性,注意的是combo的默认值并没有显式地申明rules属性
- //组件间属性的继承是通过extend函数实现的,extend函数的第二个实参就是validatebox的默认属性
- });
3.定义combobox组件
combobox组件依赖combo组件,所以后于combo组件引入,引入combobox的时候自然也会定义combobox的构造函数,默认值,方法等,属性转换器。
3-1.定义默认值:
- $.fn.combobox.defaults = $.extend({}, $.fn.combo.defaults, {
- valueField: "value"
- //此处省略属性属性若干
- //combobox同样适用extend函数实现部分属性继承自combo
- });
到这步为止,我们的validatebox,combo,combobox组件都具有rules属性,因其实通过extend实现的,所以他们的rules属性指向同一个内存堆:B
4.重新定义validatebox组件
因为我们在jquery.easyui.min.js之后又引入了jquery.validatebox.js文件,等于重新定义了一遍validatebox组件,期间必要重新定义默认值和属性转换器。
4-1.重新定义默认值:
- $.fn.validatebox.defaults = {
- required: false,
- //...省略若干属性
- rules: {
- url: {
- validator: function(value) {
- return false;//这里就不写那串正则表达式了
- },
- message: "Please enter a valid URL."
- }
- //...省略若干规则
- }
- };
重新定义了$.fn.validatebox.defaults属性,而这个属性指向新的内存堆,我们将这个堆叫做:C;defaults属性内部的rules属性也指向另一新的内存堆:D.
而这时候combo和combobox的rules属性却依旧指向内存堆B,这就为后面扩展rules属性无效问题埋下了凶案现场的定时炸弹。
5.注册一个document.ready事件
注册document.ready事件是在parser组件内完成的,document.ready事件内部负责解析渲染页面内发现的EasyUI组件。
6.构造combobox组件
parser组件发现页面存在combobox关键class,开始调用combobox构造函数初始化combobox组件。
7.构造combo组件
combobox组件依赖combo组件,故在combobox初始化的时候会同时构造combo组件的实例。
8.构造validatebox组件
combo组件依赖validatebox组件,故在combo初始化的时候会同时构造validatebox组件的实例。
9.执行开发者扩展rules的代码
最后才是运行开发者注册的document.ready事件里面的代码,也就是扩展validatebox组件rules属性的代码:
- $(document).ready(function() {
- $.extend($.fn.validatebox.defaults.rules, {
- IsExist: {
- validator: function(value) {
- alert(11);
- }
- }
- });
- });
问题很明确了,这时候我们扩展的rules属性指向的是内存堆D,而前面也分析了combo和combobox的rules属性依旧指向内存堆B,理所当然,扩展的"IsExist"规则只能在validatebox内部使用了,在combo和combobox组件上无法使用,因为combo和combobox的rules属性压根就没被扩展过!
解决问题:
既然已经分析出原因,那么对这个问题我们也就有了偷懒的解决方式,那就是将扩展rules的代码放到引入jquery.validatebox.js代码的前面,同时不能将扩展rules的代码放到document.ready里面,即:
- <script type="text/javascript" src="../../jquery-1.8.0.min.js"></script>
- <script type="text/javascript" src="../../jquery.easyui.min.js"></script>
- <script type="text/javascript">
- $.extend($.fn.validatebox.defaults.rules, {
- IsExist: {
- validator: function(value) {
- alert(11);
- }
- }
- });
- </script>
- <script type="text/javascript" src="../../plugins/jquery.validatebox.js"></script>