搜尋
首頁web前端js教程seajs1.3.0源碼解析之module依賴有序載入_javascript技巧

這裡是seajs loader的核心部分,有些IE相容的部分還不是很明白,主要是理解各個模組如何依賴有序加載,以及CMD規範。

程式碼有點長,要耐心看:

複製程式碼 程式碼

/**
* loader的核心
*/
;(function(seajs, util, config) {
// 模組快取
var cachedModules = {}
// 介面修改快取
var cachedModifiers = {}
// 編譯佇列
var compileStack = []
// 模組狀態
var STATUS = {
'FETCHING': 1, // The // The module file is fetching now. 模組正在下載
'FETCHED': 2, // The module file has been fetched. 模組已下載
'SAVED': 3, // Themodule info has been saved. 模組資訊已儲存
'READY': 4, // All dependencies and self are ready to compile. 模組的依賴項已下載,等待編譯
'COMPILING': 5, // The module is in compiling now .模組正在編譯中
'COMPILED': 6 // The module is compiled and module.exports is available. 模組已編譯
}


function Module(uri(
this.uri = uri
this.status = status || 0

// this.id is set when saving
// this.dependencies is set when saving
// this.dependencies is set when saving /ving
/ving / this.factory is set when saving
// this.exports is set when compiling
// this.parent is set when compiling
// this.require is set when compiling
>

Module.prototype._use = function(ids, callback) {
//轉換為數組,統一運算
util.isString(ids) && (ids = [ids])
util.isString(ids) && (ids = [ids])
util.isString(ids) && (ids = [ids])
// 使用模組系統內部的路徑解析機制來解析並返回模組路徑
var uris = resolve(ids, this.uri)

this._load(uris, function() {
// Loads preload files introduced in modules before compiling.
// 在編譯之前,再呼叫preload預載模組
// 因為在程式碼執行期間,隨時可以呼叫seajs.config配置預先載入模組
preload(function() {
// 編譯每個模組,並將各個模組的exports作為參數傳遞給回呼函數
var args = util.map(uris, function(uri) {
return uri ?cachedModules[uri]._compile() : null
})

if (callback) {
// null使回呼函數中this指標為window
callback.apply(null, args)
}
})
})
}

// 主模組載入相依模組(稱為子模組),執行回呼函數
Module. prototype._load = function(uris, callback) {
// 過濾uris數組
// 情況一:緩存中不存在該模組,返回其uri
// 情況二:緩存中存在該模組,但是其status var unLoadedUris = util.filter(uris, function(uri) {
return uri && (!cachedModules[uri] ||
|| cachedModules[uri].status })

var length = unLoadedUris.length
// 如果length為0,表示依賴項為0或都已下載完成,那麼執行回調編譯操作
if (length === 0) {
callback()
return
}

var remain = length

for (var i = 0; i // 閉包,為onFetched函數提供上下文環境
(function(uri) {
// 建立模組物件
var module = cachedModules[uri ] ||
(cachedModules[uri] = new Module(uri, STATUS.FETCHING))
//如果模組已下載,那麼執行onFetched,否則執行fetch操作(請求模組)
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)

function onFetched() {
// cachedModules[uri] is changed in un-correspondence case uri]
// 如果模組狀態為SAVED,表示模組的依賴項已經確定,那麼下載依賴模組
if (module.status >= STATUS.SAVED) {
// 從模組資訊取得依賴模組列表,並進行循環依賴的處理
var deps = getPureDependencies(module)
// 如果存在依賴項,繼續下載
if (deps.length) {
Module.prototype._load (deps, function() {
cb(module)
})
}
// 否則直接執行cb
else {
cb(module)
}
else {
cb(module)
}
else {
cb(module)
}
else {
cb(module)
}
else {
cb(module)
}
// Maybe failed to fetch successfully, such as 404 or non-module.
// In these cases, just call cb function directly.
// 如果下載模組不成功,例如404或模組不規範(程式碼出錯),導致此時模組狀態可能為fetching,或fetched
// 此時直接執行回呼函數,在編譯模組時,模組只會傳回null
else {
cb()
}
}

})(unLoadedUris[i])
}

function cb(module) {
// 更改模組狀態為READY,當remain為0時表示模組依賴都已經下完,那麼執行callback (module || {}).status -- remain === 0 && callback() } } Module.prototype._compile = function() { var module = this // 如果該模組已經編譯過,直接回傳module.exports if (module.status === STATUS.COMPILED) { return module.exports }

// Just return null when:
// 1. the module file is 404.
// 2. the module file is not written with valid module format.
// 33 . other error cases.
// 這裡是處理一些異常情況,此時直接返回null
if (module.status return null
}
// 更改模組狀態為COMPILING,表示模組正在編譯
module.status = STATUS.COMPILING

// 模組內部使用,是一種方法,用來獲取其他模組提供(稱之為子模組)的接口,同步操作
function require(id) {
// 根據id解析模組的路徑
var uri = resolve(id, module.uri)
// 從模組快取中取得模組(注意,其實這裡子模組作為主模組的依賴項是已經被下載下來的)
var child = cachedModules[uri]

// Just return null when uri is invalid .
// 如果child為空,只能表示參數填入錯誤導致uri不正確,那麼直接回傳null
if (!child) {
return null
}

// Avoids circular calls.
// 如果子模組的狀態為STATUS.COMPILING,直接回傳child.exports,避免因為循環依賴反覆編譯模組
if (child.status === STATUS.COMPILING) {
return child.exports
}
// 指向初始化時呼叫目前模組的模組。根據此屬性,可以得到模組初始化時的Call Stack.
child.parent = module
// 傳回編譯過的child的module.exports
return child._compile()
}
}
}
// 模組內部使用,用來非同步載入模組,並在載入完成後執行指定回呼。
require.async = function(ids, callback) {
module._use(ids, callback)
}
// 使用模組系統內部的路徑解析機制來解析並傳回模組路徑。函數不會載入模組,只傳回解析後的絕對路徑。
require.resolve = function(id) {
return resolve(id, module.uri)
}
// 透過此屬性,可以查看所有模組系統載入過的模組。
// 在某些情況下,如果需要重新載入某個模組,可以得到該模組的 uri, 然後透過 delete require.cache[uri] 來將其資訊刪除掉。這樣下次使用時,就會重新取得。
require.cache = cachedModules

// require是一種方法,用來取得其他模組提供的介面。
module.require = require
// exports是一個對象,用來向外提供模組介面。
module.exports = {}
var factory = module.factory

// factory 為函數時,表示模組的建構方法。執行該方法,可以得到模組向外提供的介面。
if (util.isFunction(factory)) {
compileStack.push(module)
runInModuleContext(factory, module)
compileStack.pop() } } compileStack.pop() } // factory 為物件、字串等非函數類型時,表示模組的介面就是該物件、字串等值。
// 如:define({ "foo": "bar" });
// 如:define('I am a template. My name is {{name}}.');
else if (factory !== undefined) {
module.exports = factory
}

// 更改模組狀態為COMPILED,表示模組已編譯
module.status = STATUS.COMPI
// 執行模組介面修改,透過seajs.modify()
execModifiers(module)
return module.exports
}


Module.defidine = func , deps, factory) {
var argsLength = arguments.length
// 根據傳入的參數個數,進行參數符合

// define(factory)
// 一個參數的情況:
// id : undefined
// deps : undefined(後面會根據正則取出依賴模組列表)
// factory : function
if (argsLength === 1) {
factory = id
id = undefined
}
// define(id || deps, factory)
// 兩個參數的情況:

else if (argsLength === 2) {
// 預設:define(id, factory)
// id : '...'
// deps : undefined
// factory : function
factory = deps
deps = undefined

// define(deps, factory)
// 如果第一個參數為陣列:define(deps, factory)
// id : undefined
// deps : [...]
// factory : function
if (util.isArray(id)) {
deps = id
id = undefined
}
}

// Parses dependencies.
// 如果deps不是陣列(即deps未指定值),那麼透過正規表示式解析依賴
if (!util.isArray( deps) && util.isFunction(factory)) {
deps = util.parseDependencies(factory.toString())
}

// 元資訊,之後會將訊息傳遞給對應的module物件中
var meta = { id: id, dependencies: deps, factory: factory }
var derivedUri

// Try to derive uri in IE6-9 for anonymous modules.
/ 對於IE6-9,嘗試透過interactive script取得模組的uri
if (document.attachEvent) {
// Try to get the current script.
// 取得目前的script
var script = util.getCurrentScript()
if (script) {
// 將目前script的url進行unpareseMap操作,與模組快取中key保持一致
derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script) )
}

if (!derivedUri) {
util.log('Failed to derive URI from interactive script for:',
factory.toString(), 'warn')

// NOTE: If the id-deriving methods above is failed, then falls back
// to use onload event to get the uri.
}
}
}
// Gets uri directly for specific module.
// 如果給定id,那麼根據id解析路徑
// 顯然如果沒指定id:
// 對於非IE瀏覽器而言,則返回undefined(derivedUri為空)
// 對於IE瀏覽器則傳回CurrentScript的src
// 若指定id:
// 則皆傳回有seajs解析(resolve)過的路徑url
var resolvedUri = id ? resolve(id) : derivedUri
// uri存在的情況,進行模組資訊儲存
if (resolvedUri) {
// For IE:
// If the first ule in a package is not the cachedModules[derivedUri]
// self, it should assign to the correct module when found.
if (resolvedUri === derivedUri) {
🎜>if (refModule && refModule.realUri &&
refModule.status === STATUS.SAVED) {
cachedModules[derivedUri] = null
}
}
var module = save(resolvedUri, meta)

// For IE:
// Assigns the first module in package to cachedModules[derivedUrl]
if (derivedUri) { / cachedModules[derivedUri] may be undefined in combo case.
if ((cachedModules[derivedUri] || {}).status === STATUS.FETCHING) {
cachedModules[duules> .realUri = derivedUri
}
}
else {
// 將第一個模組儲存到firstModuleInPackage
firstModuleInPackage ||firstModulea} 🎜>// uri不存在的情況,在onload回調中進行模組資訊存儲,那裡有個閉包
else {
// Saves information for "memoizing" work in the onload event.
/ / 因為此時的uri不知道,所以將元信息暫時存儲在anonymousModuleMeta中,在onload回調中進行模塊save操作
anonymousModuleMeta = meta
}

}

// 取得正在編譯的模組
Module._getCompilingModule = function() {
return compileStack[compileStack.length - 1]
}

// 從seajs.cache 快速查看並快速查看取得已載入的模組接口,傳回值是module.exports數組
// selector 支援字串和正規表示式
Module._find = function(selector) {
var matches = []

util.forEach(util.keys(cachedModules), function(uri) {
if (util.isString(selector) && uri.indexOf(selector) > -1 ||
util.isRegExp(selselector ) && selector.test(uri)) {
var module = cachedModules[uri]
module.exports && matches.push(module.exports)
}
})


return matches
}

// 修改模組介面
Module._modify = function(id, modifier) {
var uri = resolve(id)
var module = cachedModules[uri]
////如果模組存在,且處於COMPILED狀態,那麼執行修改介面操作
if (module && module.status === STATUS.COMPILED) {
runInModuleContext(modifier, module)
}
///否則放入修改介面快取
else {
cachedModifiers[uri] || (cachedModifiers[uri] = [])
cachedModifiers[uri].push(modifier)
}

return seajs
}


// For plugin developers
Module.STATUS = STATUS
Module._resolve = util.id2Uri Module.cache = cachedModules


// Helpers
// -------
// 正在下載的模組清單
var fetchingList = {}
// 已下載的模組清單
var fetchedList = {}
// 回呼函數清單
var callbackList = {}
// 匿名模組元資訊
var anonymousModuleMeta = nullduleMeta = null
var firstModuleInPackage = null
// 循環依賴堆疊
var circularCheckStack = []

// 批次解析模組的路徑
function resolve(ids, refUri) {> if (util.isString(ids)) {
return Module._resolve(ids, refUri)
}

return util.map(ids, function(id) {
return resolve(return resolve( id, refUri)
})
}

function fetch(uri, callback) {
// fetch時,先將uri按map規則轉換
var requestUri = util. parseMap(uri)
// 在fethedList(已下載的模組列表)中查找,有的話,直接返回,並執行回調函數
// TODO : 為什麼這一步,fetchedList可能會存在該模?
if (fetchedList[requestUri]) {
// See test/issues/debug-using-map
cachedModules[uri] = cachedModules[requestUri]
callback()
>}
// 在fetchingList(正在在下載的模組列表)中查找,有的話,只需添加回調函數到列表中去,然後直接返回
if (fetchingList[requestUri]) {
callbackList[requestUri].push(callback)
return
}
// 如果走到這一步,表示該模組是第一次被請求,
///// 在fetchingList 那麼插入該模組的訊息,表示該模組已經處於下載列表中,並初始化該模組對應的回調函數列表
fetchingList[requestUri] = true
callbackList[requestUri] = [callback]

// Fetches it
// 取得此模組,即發起請求
Module._fetch(
requestUri,

function() {
// 在fetchedList插入該模組的信息,表示該模組插入該模組的信息,表示該模組已經下載完成
fetchedList[requestUri] = true

// Updates module status
var module = cachedModules[uri]
// 此時status可能為STATUS.SAVED,此時在此時在此時_define中已經說過
if (module.status === STATUS.FETCHING) {
module.status = STATUS.FETCHED
}

// Saves anonymous module meta data data 🎜>// 因為是匿名模組(此時透過閉包取得到uri,在這裡儲存模組資訊)
// 並將anonymousModuleMeta置為空
if (anonymousModuleMeta) {
save(uri, anonymousModuleMeta)
anonymousModuleMeta = null
}

// Assigns the first module in package to cachedModules[uri]
// See: test/iues/o. firstModuleInPackage && module.status === STATUS.FETCHED) {
cachedModules[uri] = firstModuleInPackage
firstModuleInPackage.realUri = uri l. 🎜>// 在fetchingList清除模組訊息,因為已經該模組fetched並save
if (fetchingList[requestUri]) {
delete fetchingList[requestUri]
}

/Lists callback
// 依序呼叫回呼函數,並清除回呼函數清單
if (callbackList[requestUri]) {
util.forEach(callbackList[requestUri], function(fn) {
fn()
})
delete callbackList[requestUri]
}

},

config.charset
)
}
uri, meta) {
var module = cachedModules[uri] || (cachedModules[uri] = new Module(uri))

// Don't override already saved module
// 此時status可能有兩個狀態:
// STATUS.FETCHING,在define裡面調用(指定了id),儲存模組資訊
// STATUS.FETCHED,在onload的回呼函數裡調用,儲存模組訊息
if (module.status // Lets anonymous module id equal to its uri
// 匿名模組(即沒有指定id),用它的uri作為id
module.id = meta.id || uri
// 將依賴項(陣列)解析成的絕對路徑,儲存到模組資訊
module.dependencies = resolve(
util.filter(meta. dependencies || [], function(dep) {
return !!dep
}), uri)
// 儲存factory(要執行的模組程式碼,也可能是物件或字串等)
module.factory = meta.factory

// Updates module status
// 更新模組狀態為SAVED,(注意此時它只是擁有了依賴項,還未全部下載下來(即還未READY))
module.status = STATUS.SAVED
}

return module
}

// 依照模組上下文執行模組程式碼」 fn, module) {
// 傳入與模組相關的兩個參數以及模組本身
// exports用來暴露介面
// require用來取得依賴模組(同步)(編譯)
var ret = fn(module.require, module.exports, module)
// 支援回傳值暴露介面形式,如:
// return {
// fn1 : xx
xx/ / ,fn2 : xx
// ...
// }
if (ret !== undefined) {
module.exports = ret
}
}
// 判斷模組是否存在介面修改
function hasModifiers(module) {
return !!cachedModifiers[module.realUri || module.uri]
}
// 修改模組
function execModifiers(module) {
var uri = module.realUri || module.uri
var modifiers = cachedModifiers[uri]
// 內部變數cachedModifiers 就是用來儲存使用者透過透過透過將點
// 看該uri是否又被modify更改過
if (modifiers) {
// 對修改點統一執行factory,回傳修改後的module.exports
util.forEach(modifiers , function(modifier) {
runInModuleContext(modifier, module)
})
// 刪除modify 方法定義的修改點,避免再次執行
delete cachedModifiers[uri]
}
}
}

//取得純粹的依賴關係,得到不存在循環依賴關係的依賴數組
function getPureDependencies(module) {
var uri = module.uri
// 對每個依賴項進行過濾,對於有可能形成循環依賴的進行剔除,並打印出警告日誌
return util.filter(module.dependencies, function(dep) {
// 首先將被檢查模組的uri放到循環依賴檢查堆疊中,之後的檢查會用到
circularCheckStack = [uri]
//接下來檢查模組uri是否和其依賴的模組存在循環依賴
var isCircular = isCircularWaiting(cachedModules[dep ])
if (isCircular) {
// 如果循環,則將uri放到循環依賴檢查堆疊中
circularCheckStack.push(uri)
// 列印出循環警告日誌
printCircularLog(circularCheckStack)
}

return !isCircular
})
}

function isCircularWaiting(module) { 返回false,因為此時也無法獲得依賴模組的依賴項,所以這裡無法做判斷
// 或如果模組的狀態值等於saved,也返回false,因為模組狀態為saved的時候代表該模組的信息已經有了,
// 所以儘管形成了循環依賴,但是require主模組時,同樣可以正常編譯,返回主模組介面(好像nodejs會回傳undefined)
if (!module || module.status !== STATUS.SAVED) {
return false
}
// 如果不是以上的情況,那麼將依賴模組的uri放到循環依賴檢查棧中,之後的檢查會用到
circularCheckStack.push(module.uri)
// 再次取依賴模組的依賴模組
var deps = module.dependencies

if (deps.length) {
// 透過循環依賴檢查棧,檢查是否有循環依賴(這裡是第一層依賴模組檢查,與主模組循環依賴的情況)
if (isOverlap(deps, circularCheckStack)) {
return true
}
// 如果不存在上述情形,那麼進一步查看,依賴模組的依賴模組,查看他們是否存在對循環依賴檢查棧中的uri的模組存在循環依賴
// 這樣的話,就遞歸了,循環依賴檢查堆疊就像形成的一條鏈,當前模組依序對主模組,主模組的主模組...直到最頂上的主模組,依序判斷是否存在依賴
for (var i = 0; i if (isCircularWaiting(cachedModules[deps[i]])) {
return true
}
}
}
////不存在循環依賴,那麼pop出之前已經push進的模組uri,並回傳false
circularCheckStack.pop()
return false
}
// 印出循環警告日誌
function printCircularLog( stack, type) {
util.log('Found circular dependencies:', stack.join(' --> '), type)
}
//判斷兩個陣列是否有重複的值
function isOverlap(arrA, arrB) {
var arrC = arrA.concat(arrB)
return arrC.length > util.unique(arrC).length
}
從配置檔案讀取是否有需要提前載入的模組
// 如果有預先載入模組,首先設定預載模組為空(保證下次不必重複載入),並載入預載模組並執行回調,如果沒有則順序執行
function preload(callback) {
var preloadMods = config.preload.slice()
config.preload = []
preloadMods.length ? globalModule._use(preloadMods, callback) : callback( )
}


// Public API
// 對外暴露的API
// ----------
// 全域模組,可以認為是頁面模組,頁面中的js,css檔案都是透過它來載入的
// 模組初始狀態就是COMPILED,uri就是頁面的uri
var globalModule = new Module(util.pageUri, STATUS.COMPILED)

// 頁面js,css檔案載入器
seajs.use = function(ids, callback) {
// Loads preload modules before all other modules. / 預先載入模組
preload(function() {
globalModule._use(ids, callback)
})

// Chain
return seajs
}


// For normal users
// 供普通使用者呼叫
seajs.define = Module._define
seajs.cache = Module.cache
seajs.find = Module._find
seajs.modify = Module._modify


// For plugin developers
// 供開發者使用
seajs.pluginSDK = {
Module: Mod >util: util,
config: config
}

})(seajs, seajs._util, seajs._config)


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

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脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SecLists

SecLists

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

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。