搜尋
首頁後端開發php教程yii2源码学习笔记(十四),yii2源码学习笔记_PHP教程

yii2源码学习笔记(十四),yii2源码学习笔记_PHP教程

Jul 12, 2016 am 08:51 AM
moduleyii2學習應用模組原始碼筆記類別

yii2源码学习笔记(十四),yii2源码学习笔记

Module类是模块和应用类的基类。  yiisoft\yii2\base\Module.php

<span>  1</span> <?<span>php
</span><span>  2</span> <span>/*</span><span>*
</span><span>  3</span> <span> * @link </span><span>http://www.yiiframework.com/</span>
<span>  4</span> <span> * @copyright Copyright (c) 2008 Yii Software LLC
</span><span>  5</span> <span> * @license </span><span>http://www.yiiframework.com/license/</span>
<span>  6</span>  <span>*/</span>
<span>  7</span> 
<span>  8</span> <span>namespace</span> yii\<span>base</span><span>;
</span><span>  9</span> 
<span> 10</span> <span>use Yii;
</span><span> 11</span> <span>use yii\di\ServiceLocator;
</span><span> 12</span> 
<span> 13</span> <span>/*</span><span>*
</span><span> 14</span> <span> * Module is the base class for module and application classes.
</span><span> 15</span> <span> *  Module是模块和应用类的基类
</span><span> 16</span> <span> * A module represents a sub-application which contains MVC elements by itself, such as
</span><span> 17</span> <span> * models, views, controllers, etc.
</span><span> 18</span> <span> * 模块是一个由模型、视图、控制器等组成的子应用
</span><span> 19</span> <span> * A module may consist of [[modules|sub-modules]].
</span><span> 20</span> <span> * 模块内也可以包含模块或子模块
</span><span> 21</span> <span> * [[components|Components]] may be registered with the module so that they are globally
</span><span> 22</span> <span> * accessible within the module.
</span><span> 23</span> <span> * 组件可以注册到模块,以便在模块内全局访问
</span><span> 24</span> <span> * @property array $aliases List of path aliases to be defined. The array keys are alias names (must start
</span><span> 25</span> <span> * with '@') and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example.
</span><span> 26</span> <span> * This property is write-only. 要定义的别名路径数组    只写
</span><span> 27</span> <span> * @property string $basePath The root directory of the module. 模块的根路径
</span><span> 28</span> <span> * @property string $controllerPath The directory that contains the controller classes. This property is
</span><span> 29</span> <span> * read-only.   控制器类的路径 只读
</span><span> 30</span> <span> * @property string $layoutPath The root directory of layout files. Defaults to "[[viewPath]]/layouts".
</span><span> 31</span> <span> * 模板路径数组 只读
</span><span> 32</span> <span> * @property array $modules The modules (indexed by their IDs). 模块数组
</span><span> 33</span> <span> * @property string $uniqueId The unique ID of the module. This property is read-only.模块的唯一标识 只读
</span><span> 34</span> <span> * @property string $viewPath The root directory of view files. Defaults to "[[basePath]]/views".
</span><span> 35</span> <span> * 模块下视图文件路径
</span><span> 36</span> <span> * @author Qiang Xue <qiang.xue@gmail.com>
</span><span> 37</span> <span> * @since 2.0
</span><span> 38</span>  <span>*/</span>
<span> 39</span> <span>class</span><span> Module extends ServiceLocator
</span><span> 40</span> <span>{
</span><span> 41</span>     <span>/*</span><span>*
</span><span> 42</span> <span>     * @event ActionEvent an event raised before executing a controller action. 在执行控制的的action方法前触发
</span><span> 43</span> <span>     * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
</span><span> 44</span> <span>     * 可以设置[[ActionEvent::isValid]]为false取消行动的执行。
</span><span> 45</span>      <span>*/</span>
<span> 46</span>     <span>const</span> EVENT_BEFORE_ACTION = <span>'</span><span>beforeAction</span><span>'</span><span>;
</span><span> 47</span>     <span>/*</span><span>*
</span><span> 48</span> <span>     * @event ActionEvent an event raised after executing a controller action.
</span><span> 49</span> <span>     * 在执行控制的的action方法后触发
</span><span> 50</span> <span>     * 
</span><span> 51</span>      <span>*/</span>
<span> 52</span>     <span>const</span> EVENT_AFTER_ACTION = <span>'</span><span>afterAction</span><span>'</span><span>;
</span><span> 53</span> 
<span> 54</span>     <span>/*</span><span>*
</span><span> 55</span> <span>     * @var array custom module parameters (name => value). 自定义模块参数
</span><span> 56</span>      <span>*/</span>
<span> 57</span>     <span>public</span> $<span>params</span> =<span> [];
</span><span> 58</span>     <span>/*</span><span>*
</span><span> 59</span> <span>     * @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].
</span><span> 60</span> <span>     * 模块的唯一标识,用于区分同一父模块下的模块
</span><span> 61</span>      <span>*/</span>
<span> 62</span>     <span>public</span><span> $id;
</span><span> 63</span>     <span>/*</span><span>*
</span><span> 64</span> <span>     * @var Module the parent module of this module. Null if this module does not have a parent.
</span><span> 65</span> <span>     *  当前模块的父模块
</span><span> 66</span>      <span>*/</span>
<span> 67</span>     <span>public</span><span> $module;
</span><span> 68</span>     <span>/*</span><span>*
</span><span> 69</span> <span>     * @var string|boolean the layout that should be applied for views within this module. This refers to a view name
</span><span> 70</span> <span>     * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]
</span><span> 71</span> <span>     * will be taken. If this is false, layout will be disabled within this module.
</span><span> 72</span> <span>     * 布局文件 如果没有设置,调用 [[module|parent module]]的值。如果是false,在模块中布局将被禁用。
</span><span> 73</span>      <span>*/</span>
<span> 74</span>     <span>public</span><span> $layout;
</span><span> 75</span>     <span>/*</span><span>*
</span><span> 76</span> <span>     * @var array mapping from controller ID to controller configurations. 控制器ID到控制器配置的映射
</span><span> 77</span> <span>     * Each name-value pair specifies the configuration of a single controller.
</span><span> 78</span> <span>     * A controller configuration can be either a string or an array.
</span><span> 79</span> <span>     * If the former, the string should be the fully qualified class name of the controller.
</span><span> 80</span> <span>     * If the latter, the array must contain a 'class' element which specifies
</span><span> 81</span> <span>     * the controller's fully qualified class name, and the rest of the name-value pairs
</span><span> 82</span> <span>     * in the array are used to initialize the corresponding controller properties. For example,
</span><span> 83</span> <span>     * 每个键值对指定单独的控制器,控制器配置可以是字符串或者数组,如果是前者,该字符串是指定控制的的全路径
</span><span> 84</span> <span> 95  * 如果是后者,则包含一个&lsquo;class&rsquo;元素指定控制器的全路径,其余的参数用于初始化对应的属性
</span><span> 85</span> <span>     * ~~~
</span><span> 86</span> <span>     * [
</span><span> 87</span> <span>     *   'account' => 'app\controllers\UserController',
</span><span> 88</span> <span>     *   'article' => [
</span><span> 89</span> <span>     *      'class' => 'app\controllers\PostController',
</span><span> 90</span> <span>     *      'pageTitle' => 'something new',
</span><span> 91</span> <span>     *   ],
</span><span> 92</span> <span>     * ]
</span><span> 93</span> <span>     * ~~~
</span><span> 94</span>      <span>*/</span>
<span> 95</span>     <span>public</span> $controllerMap =<span> [];
</span><span> 96</span>     <span>/*</span><span>*
</span><span> 97</span> <span>     * @var string the namespace that controller classes are in.    控制器的命名空间
</span><span> 98</span> <span>     * This namespace will be used to load controller classes by prepending it to the controller
</span><span> 99</span> <span>     * class name.
</span><span>100</span> <span>     * 命名空间 在控制器类的前面加载控制器类
</span><span>101</span> <span>     * If not set, it will use the `controllers` sub-namespace under the namespace of this module.
</span><span>102</span> <span>     * For example, if the namespace of this module is "foo\bar", then the default
</span><span>103</span> <span>     * controller namespace would be "foo\bar\controllers".
</span><span>104</span> <span>     * 如果没有设置,默认为当前模块的命名空间加上 `controllers`构成的命名空间
</span><span>105</span> <span>119  * 如当前模块的命名空间为"foo\bar",控制器的默认命名空间为"foo\bar\controllers"
</span><span>106</span> <span>     * See also the [guide section on autoloading](guide:concept-autoloading) to learn more about
</span><span>107</span> <span>     * defining namespaces and how classes are loaded.
</span><span>108</span>      <span>*/</span>
<span>109</span>     <span>public</span><span> $controllerNamespace;
</span><span>110</span>     <span>/*</span><span>*
</span><span>111</span> <span>     * @var string the default route of this module. Defaults to 'default'. 当前前模块的默认路由
</span><span>112</span> <span>     * The route may consist of child module ID, controller ID, and/or action ID.
</span><span>113</span> <span>     * For example, `help`, `post/create`, `admin/post/create`.
</span><span>114</span> <span>     * If action ID is not given, it will take the default value as specified in
</span><span>115</span> <span>     * [[Controller::defaultAction]].
</span><span>116</span> <span>     * route 可能包含子模块ID,控制器ID,操作ID,如果action ID未给定,会调用[Controller::defaultAction]指定的action
</span><span>117</span>      <span>*/</span>
<span>118</span>     <span>public</span> $defaultRoute = <span>'</span><span>default</span><span>'</span><span>;
</span><span>119</span> 
<span>120</span>     <span>/*</span><span>*
</span><span>121</span> <span>     * @var string the root directory of the module.    当前模块的根路径
</span><span>122</span>      <span>*/</span>
<span>123</span>     <span>private</span><span> $_basePath;
</span><span>124</span>     <span>/*</span><span>*
</span><span>125</span> <span>     * @var string the root directory that contains view files for this module 当前模块下视图文件的路径
</span><span>126</span>      <span>*/</span>
<span>127</span>     <span>private</span><span> $_viewPath;
</span><span>128</span>     <span>/*</span><span>*
</span><span>129</span> <span>     * @var string the root directory that contains layout view files for this module.
</span><span>130</span> <span>     * 当前模块下的布局文件路径
</span><span>131</span>      <span>*/</span>
<span>132</span>     <span>private</span><span> $_layoutPath;
</span><span>133</span>     <span>/*</span><span>*
</span><span>134</span> <span>     * @var array child modules of this module  当前模块的子模块数组
</span><span>135</span>      <span>*/</span>
<span>136</span>     <span>private</span> $_modules =<span> [];
</span><span>137</span> 
<span>138</span> 
<span>139</span>     <span>/*</span><span>*
</span><span>140</span> <span>     * Constructor. 构造函数
</span><span>141</span> <span>     * @param string $id the ID of this module 当前模块的标识
</span><span>142</span> <span>     * @param Module $parent the parent module (if any) 当前模块的父模块
</span><span>143</span> <span>     * @param array $config name-value pairs that will be used to initialize the object properties
</span><span>144</span> <span>     * 配置文件 用于初始化对象属性
</span><span>145</span>      <span>*/</span>
<span>146</span>     <span>public</span> function __construct($id, $parent = <span>null</span>, $config =<span> [])
</span><span>147</span> <span>    {
</span><span>148</span>         $<span>this</span>->id = $id; <span>//</span><span>给当前模块唯一标识</span>
<span>149</span>         $<span>this</span>->module = $parent;    <span>//</span><span>当前模块的父模块</span>
<span>150</span>         parent::__construct($config);   <span>//</span><span>调用父类的配置</span>
<span>151</span> <span>    }
</span><span>152</span> 
<span>153</span>     <span>/*</span><span>*
</span><span>154</span> <span>     * Returns the currently requested instance of this module class.   取得当前类的实例
</span><span>155</span> <span>     * If the module class is not currently requested, null will be returned.
</span><span>156</span> <span>     * 没有当前请求的模块类,将返回null。
</span><span>157</span> <span>     * This method is provided so that you access the module instance from anywhere within the module.
</span><span>158</span> <span>     * 可以在模块内的任何地方访问类的实例
</span><span>159</span> <span>     * @return static|null the currently requested instance of this module class, or null if the module class is not requested.
</span><span>160</span>      <span>*/</span>
<span>161</span>     <span>public</span> <span>static</span><span> function getInstance()
</span><span>162</span> <span>    {
</span><span>163</span>         $<span>class</span> =<span> get_called_class();
</span><span>164</span>         <span>return</span> isset(Yii::$app->loadedModules[$<span>class</span>]) ? Yii::$app->loadedModules[$<span>class</span>] : <span>null</span><span>;
</span><span>165</span> <span>    }
</span><span>166</span> 
<span>167</span>     <span>/*</span><span>*
</span><span>168</span> <span>     * Sets the currently requested instance of this module class.  设置模块类的当前请求实例。
</span><span>169</span> <span>     * @param Module|null $instance the currently requested instance of this module class.
</span><span>170</span> <span>     * If it is null, the instance of the calling class will be removed, if any.
</span><span>171</span> <span>     * 当前模块类的实例。如果为null,调用类的实例将被删除
</span><span>172</span>      <span>*/</span>
<span>173</span>     <span>public</span> <span>static</span><span> function setInstance($instance)
</span><span>174</span> <span>    {
</span><span>175</span>         <span>if</span> ($instance === <span>null</span>) {<span>//</span><span>如果没有传入参数,直接unset</span>
<span>176</span>             unset(Yii::$app-><span>loadedModules[get_called_class()]);
</span><span>177</span>         } <span>else</span> {<span>//</span><span>将该类和类的实例存入loadedModules数组中</span>
<span>178</span>             Yii::$app->loadedModules[get_class($instance)] =<span> $instance;
</span><span>179</span> <span>        }
</span><span>180</span> <span>    }
</span><span>181</span> 
<span>182</span>     <span>/*</span><span>*
</span><span>183</span> <span>     * Initializes the module.
</span><span>184</span> <span>     * 初始化模块
</span><span>185</span> <span>     * This method is called after the module is created and initialized with property values
</span><span>186</span> <span>     * given in configuration. The default implementation will initialize [[controllerNamespace]]
</span><span>187</span> <span>     * if it is not set.
</span><span>188</span> <span>     * 该模块创建和初始化给出的配置  如果没有设置,默认初始化[[controllerNamespace]]
</span><span>189</span> <span>     * If you override this method, please make sure you call the parent implementation.
</span><span>190</span> <span>     * 重写确保父类调用
</span><span>191</span>      <span>*/</span>
<span>192</span>     <span>public</span><span> function init()
</span><span>193</span> <span>    {
</span><span>194</span>         <span>if</span> ($<span>this</span>->controllerNamespace === <span>null</span>) {<span>//</span><span>判断是否为空</span>
<span>195</span>             $<span>class</span> = get_class($<span>this</span>); <span>//</span><span>获取类名</span>
<span>196</span>             <span>if</span> (($pos = strrpos($<span>class</span>, <span>'</span><span>\\</span><span>'</span>)) !== <span>false</span><span>) {
</span><span>197</span>                 $<span>this</span>->controllerNamespace = substr($<span>class</span>, <span>0</span>, $pos) . <span>'</span><span>\\controllers</span><span>'</span>; <span>//</span><span>取得命名空间</span>
<span>198</span> <span>            }
</span><span>199</span> <span>        }
</span><span>200</span> <span>    }
</span><span>201</span> 
<span>202</span>     <span>/*</span><span>*
</span><span>203</span> <span>     * Returns an ID that uniquely identifies this module among all modules within the current application.
</span><span>204</span> <span>     * Note that if the module is an application, an empty string will be returned.
</span><span>205</span> <span>     * 当前应用程序中模块的唯一标识,如果该模块是应用程序返回空字符串
</span><span>206</span> <span>     * @return string the unique ID of the module.模块的唯一标识
</span><span>207</span>      <span>*/</span>
<span>208</span>     <span>public</span><span> function getUniqueId()
</span><span>209</span>     {     <span>//</span><span>如果当前模块有父模块,则返回拼接的标识作为唯一ID,否则只返回当前模块ID</span>
<span>210</span>         <span>return</span> $<span>this</span>->module ? ltrim($<span>this</span>->module->getUniqueId() . <span>'</span><span>/</span><span>'</span> . $<span>this</span>->id, <span>'</span><span>/</span><span>'</span>) : $<span>this</span>-><span>id;
</span><span>211</span> <span>    }
</span><span>212</span> 
<span>213</span>     <span>/*</span><span>*
</span><span>214</span> <span>     * Returns the root directory of the module.    返回当前模块的根路径
</span><span>215</span> <span>     * It defaults to the directory containing the module class file.   默认为包含模块类文件的路径。
</span><span>216</span> <span>     * @return string the root directory of the module. 当前模块的根路径
</span><span>217</span>      <span>*/</span>
<span>218</span>     <span>public</span><span> function getBasePath()
</span><span>219</span> <span>    {
</span><span>220</span>         <span>if</span> ($<span>this</span>->_basePath === <span>null</span><span>) {
</span><span>221</span>             $<span>class</span> = <span>new</span> \ReflectionClass($<span>this</span>);   <span>//</span><span>生成当前类的反射对象</span>
<span>222</span>             $<span>this</span>->_basePath = dirname($<span>class</span>->getFileName());<span>//</span><span>取得类定义的路径</span>
<span>223</span> <span>        }
</span><span>224</span> 
<span>225</span>         <span>return</span> $<span>this</span>-><span>_basePath;
</span><span>226</span> <span>    }
</span><span>227</span> 
<span>228</span>     <span>/*</span><span>*
</span><span>229</span> <span>     * Sets the root directory of the module.   设置当前模块的根路径
</span><span>230</span> <span>     * This method can only be invoked at the beginning of the constructor. 只在构造函数开始时调用。
</span><span>231</span> <span>     * @param string $path the root directory of the module. This can be either a directory name or a path alias.
</span><span>232</span> <span>     * 模块的根目录。可以是一个目录名或路径别名
</span><span>233</span> <span>     * @throws InvalidParamException if the directory does not exist. 如果路径不存在。抛出异常
</span><span>234</span>      <span>*/</span>
<span>235</span>     <span>public</span><span> function setBasePath($path)
</span><span>236</span> <span>    {
</span><span>237</span>         $path = Yii::getAlias($path);<span>//</span><span>将路径别名转换为实际路径。</span>
<span>238</span>         $p = realpath($path);   <span>//</span><span>返回绝对路径名</span>
<span>239</span>         <span>if</span> ($p !== <span>false</span> &&<span> is_dir($p)) {
</span><span>240</span>             $<span>this</span>->_basePath = $p;<span>//</span><span>是目录名且不为false,返回目录名,否则抛出异常</span>
<span>241</span>         } <span>else</span><span> {
</span><span>242</span>             <span>throw</span> <span>new</span> InvalidParamException(<span>"</span><span>The directory does not exist: $path</span><span>"</span><span>);
</span><span>243</span> <span>        }
</span><span>244</span>     }

 

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1132373.htmlTechArticleyii2源码学习笔记(十四),yii2源码学习笔记 Module类是模块和应用类的基类。 yiisoft\yii2\base\Module.php 1 ? php 2 /* * 3 * @link http://www.yiiframework.c...
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
簡單地說明PHP會話的概念。簡單地說明PHP會話的概念。Apr 26, 2025 am 12:09 AM

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInAcookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionWwithSession_start()和stordoredAtain $ _session.2)

您如何循環中存儲在PHP會話中的所有值?您如何循環中存儲在PHP會話中的所有值?Apr 26, 2025 am 12:06 AM

在PHP中,遍歷會話數據可以通過以下步驟實現:1.使用session_start()啟動會話。 2.通過foreach循環遍歷$_SESSION數組中的所有鍵值對。 3.處理複雜數據結構時,使用is_array()或is_object()函數,並用print_r()輸出詳細信息。 4.優化遍歷時,可採用分頁處理,避免一次性處理大量數據。這將幫助你在實際項目中更有效地管理和使用PHP會話數據。

說明如何使用會話進行用戶身份驗證。說明如何使用會話進行用戶身份驗證。Apr 26, 2025 am 12:04 AM

會話通過服務器端的狀態管理機制實現用戶認證。 1)會話創建並生成唯一ID,2)ID通過cookies傳遞,3)服務器存儲並通過ID訪問會話數據,4)實現用戶認證和狀態管理,提升應用安全性和用戶體驗。

舉一個如何在PHP會話中存儲用戶名的示例。舉一個如何在PHP會話中存儲用戶名的示例。Apr 26, 2025 am 12:03 AM

Tostoreauser'snameinaPHPsession,startthesessionwithsession_start(),thenassignthenameto$_SESSION['username'].1)Usesession_start()toinitializethesession.2)Assigntheuser'snameto$_SESSION['username'].Thisallowsyoutoaccessthenameacrossmultiplepages,enhanc

哪些常見問題會導致PHP會話失敗?哪些常見問題會導致PHP會話失敗?Apr 25, 2025 am 12:16 AM

PHPSession失效的原因包括配置錯誤、Cookie問題和Session過期。 1.配置錯誤:檢查並設置正確的session.save_path。 2.Cookie問題:確保Cookie設置正確。 3.Session過期:調整session.gc_maxlifetime值以延長會話時間。

您如何在PHP中調試與會話相關的問題?您如何在PHP中調試與會話相關的問題?Apr 25, 2025 am 12:12 AM

在PHP中調試會話問題的方法包括:1.檢查會話是否正確啟動;2.驗證會話ID的傳遞;3.檢查會話數據的存儲和讀取;4.查看服務器配置。通過輸出會話ID和數據、查看會話文件內容等方法,可以有效診斷和解決會話相關的問題。

如果session_start()被多次調用會發生什麼?如果session_start()被多次調用會發生什麼?Apr 25, 2025 am 12:06 AM

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

您如何在PHP中配置會話壽命?您如何在PHP中配置會話壽命?Apr 25, 2025 am 12:05 AM

在PHP中配置會話生命週期可以通過設置session.gc_maxlifetime和session.cookie_lifetime來實現。 1)session.gc_maxlifetime控制服務器端會話數據的存活時間,2)session.cookie_lifetime控制客戶端cookie的生命週期,設置為0時cookie在瀏覽器關閉時過期。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具