从Yii中的registerScript方法强制执行脚本顺序

Pet*_*rus 6 php yii

我创建了一个小部件,注册自己的脚本,如下所示

class MyWidget extends CWidget {
    public function run(){
        Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT
var a = "Hello World!";
JAVASCRIPT
        , CClientScript::POS_END);
    }
}
Run Code Online (Sandbox Code Playgroud)

在布局中,我像这样调用小部件

<?php $this->widget('MyWidget');?>
<?php echo $content;?>
Run Code Online (Sandbox Code Playgroud)

但是在视图文件中,我需要该窗口小部件声明的变量.

<?php 
Yii::app()->clientScript->registerScript('script', <<<JAVASCRIPT
    alert(a);
JAVASCRIPT
    , CClientScript::POS_END);
?>
Run Code Online (Sandbox Code Playgroud)

请注意,在两个registerScript方法中,我都使用POS_END作为脚本位置,因为我打算在<body>标记之后放置所有脚本(包括CoreScript,例如jQuery,jQueryUI等).

问题是渲染的脚本将显示视图文件中的一个,然后是小部件中的一个.

alert(a);
var a = "Hello World!";
Run Code Online (Sandbox Code Playgroud)

我们可以看到,上面的代码不起作用所以我需要将第二行放在第一行之上.

关于如何强制下订单的任何想法?只要所有脚本都呈现在最终位置并且我不必将上面的内联Javascript代码拉到新的包或文件中,我就可以扩展CClientScript(并创建一个新registerScript方法).

Pet*_*rus 8

所以我终于找到了一个黑客来做到这一点.我扩展了一个新ClientScript类并修改了registerScript方法,因此它将接受另一个参数$level.

public function registerScript($id, $script, $position = self::POS_END, $level = 1);
Run Code Online (Sandbox Code Playgroud)

想想$level就像z-index在CSS中一样,除了$levelis 的数量越多,脚本的位置就越低.

例如

Yii::app()->clientScript->registerScript('script1', '/** SCRIPT #1 **/', CClientScript::POS_END, 1);
Yii::app()->clientScript->registerScript('script2', '/** SCRIPT #2 **/', CClientScript::POS_END, 2);
Yii::app()->clientScript->registerScript('script3', '/** SCRIPT #3 **/', CClientScript::POS_END, 1);
Run Code Online (Sandbox Code Playgroud)

即使在渲染后的脚本中script3声明之后script2,它也会显示在上面,script2因为它的$levelscript2大于script3's.

这是我的解决方案的代码.虽然我不确定排列方法是否足够优化,但它的工作方式与我想要的一样.

/**
 * ClientScript manages Javascript and CSS.
 */
class ClientScript extends CClientScript {
    public $scriptLevels = array();

    /**
     * Registers a piece of javascript code.
     * @param string $id ID that uniquely identifies this piece of JavaScript code
     * @param string $script the javascript code
     * @param integer $position the position of the JavaScript code.
     * @param integer $level the rendering priority of the JavaScript code in a position.
     * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5).
     */
    public function registerScript($id, $script, $position = self::POS_END, $level = 1) {
        $this->scriptLevels[$id] = $level;
        return parent::registerScript($id, $script, $position);
    }

    /**
     * Renders the registered scripts.
     * Overriding from CClientScript.
     * @param string $output the existing output that needs to be inserted with script tags
     */
    public function render(&$output) {
        if (!$this->hasScripts)
            return;

        $this->renderCoreScripts();

        if (!empty($this->scriptMap))
            $this->remapScripts();

        $this->unifyScripts();

        //===================================
        //Arranging the priority
        $this->rearrangeLevels();
        //===================================

        $this->renderHead($output);
        if ($this->enableJavaScript) {
            $this->renderBodyBegin($output);
            $this->renderBodyEnd($output);
        }
    }


    /**
     * Rearrange the script levels.
     */
    public function rearrangeLevels() {
        $scriptLevels = $this->scriptLevels;
        foreach ($this->scripts as $position => &$scripts) {
            $newscripts = array();
            $tempscript = array();
            foreach ($scripts as $id => $script) {
                $level = isset($scriptLevels[$id]) ? $scriptLevels[$id] : 1;
                $tempscript[$level][$id] = $script;
            }
            foreach ($tempscript as $s) {
                foreach ($s as $id => $script) {
                    $newscripts[$id] = $script;
                }
            }
            $scripts = $newscripts;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我只需要将类作为clientScript组件放在config中

'components' => array(
  'clientScript' => array(
      'class' => 'ClientScript'
  )
)
Run Code Online (Sandbox Code Playgroud)