Load流程是整个资源加载管线的最后一棒,由Loader这个pipe负责(loader.js)。通过Download流程拿到内容之后,需要对内容做一些“加载”处理。使得这些内容可以在游戏中使用。这里并不是所有的资源都需要进行一个加载处理,目前只有图片、Json、Plist、Uuid(Prefab、场景)等资源才会执行加载的流程,其他的资源在Download流程之后就可以在游戏中使用了。

  • Loader处理

Loader的handle接收一个item和callback,根据item的type在this.extMap中获取对应的loadFunc。

Loader.prototype.addHandlers = function (extMap) {
this.extMap = JS.mixin(this.extMap, extMap);
}; Loader.prototype.handle = function (item, callback) {
var loadFunc = this.extMap[item.type] || this.extMap['default'];
return loadFunc.call(this, item, callback);
};
  • 资源的加载方式

Loader的this.extMap记录了各种资源类型的下载方式,所有的类型最终都对应这5个加载方法,loadNothing、loadJSON、loadImage、loadPlist、loadUuid,它们对应实现了各种类型资源的加载,通过Loader.addHandlers可以添加或修改任意资源的加载方式。加载结束后将可用的内容返回。

// 无需加载,即经过前面的下载已经可用了,例如font、script等资源
function loadNothing (item, callback) {
return null;
} // 使用JSON.parse进行解析并返回
function loadJSON (item, callback) {
if (typeof item.content !== 'string') {
return new Error('JSON Loader: Input item doesn\'t contain string content');
} try {
var result = JSON.parse(item.content);
return result;
}
catch (e) {
return new Error('JSON Loader: Parse json [' + item.id + '] failed : ' + e);
}
} // 创建Texture2D,并根据图片的内容初始化Texture2D,最后添加到cc.textureCache中
function loadImage (item, callback) {
if (sys.platform !== sys.WECHAT_GAME && !(item.content instanceof Image)) {
return new Error('Image Loader: Input item doesn\'t contain Image content');
}
var rawUrl = item.rawUrl;
var tex = cc.textureCache.getTextureForKey(rawUrl) || new Texture2D();
tex.url = rawUrl;
tex.initWithElement(item.content);
tex.handleLoadedTexture();
cc.textureCache.cacheImage(rawUrl, tex);
return tex;
} // 使用cc.plistParser进行解析并返回
function loadPlist (item, callback) {
if (typeof item.content !== 'string') {
return new Error('Plist Loader: Input item doesn\'t contain string content');
}
var result = cc.plistParser.parse(item.content);
if (result) {
return result;
}
else {
return new Error('Plist Loader: Parse [' + item.id + '] failed');
}
}
  • loadUuid

loadUuid用于加载creator内部统一规划的资源,每个uuid都会对应一个json对象,可能是prefab、spriteFrame,等等。在loadUuid这个方法中,最关键的操作就是cc.deserialize反序列化把资源对象创建了出来,其次就是加载依赖资源。

uuid的解析首先需要一个json对象,如果item的content是string类型,则进行解析,如果是object类型,则直接使用item.content,如果既不是string也不是object则直接报错。

function loadUuid (item, callback) {
if (CC_EDITOR) {
MissingClass = MissingClass || Editor.require('app://editor/page/scene-utils/missing-class-reporter').MissingClass;
} // 获取json对象,如果是string则进行解析,如果是object则直接使用,报错则返回Error对象
var json;
if (typeof item.content === 'string') {
try {
json = JSON.parse(item.content);
} catch (e) {
return new Error('Uuid Loader: Parse asset [' + item.id + '] failed : ' + e.stack);
}
} else if (typeof item.content === 'object') {
json = item.content;
} else {
return new Error('JSON Loader: Input item doesn\'t contain string content');
} // 根据是否场景对象、编辑器环境来决定classFinder的实现。
var classFinder;
var isScene = isSceneObj(json);
if (isScene) {
if (CC_EDITOR) {
// 编辑器 + 场景的模式下,使用MissingClass.classFinder作为包裹函数
MissingClass.hasMissingClass = false;
classFinder = function (type, data, owner, propName) {
var res = MissingClass.classFinder(type, data, owner, propName);
if (res) {
return res;
}
return cc._MissingScript.getMissingWrapper(type, data);
};
classFinder.onDereferenced = MissingClass.classFinder.onDereferenced;
} else {
// 非编辑器下,使用cc._MissingScript.safeFindClass,也是调用了JS._getClassById
// 区别是在解析失败后会调用cc.deserialize.reportMissingClass(id);
classFinder = cc._MissingScript.safeFindClass;
}
} else {
classFinder = function (id) {
// JS为引擎的platform\js.js,而_getClassById方法从_idToClass[classId]中返回Class
// _idToClass为id到类的一个注册map,key为id,value为class
// 使用CCClass定义继承自cc.Component的类会被自动注册到_idToClass中
// platform\CCClass.js中的var cls = define(name, base, mixins, options);
// 最终调用了JS.setClassName,Creator的类的实现细节是另外一个大话题
// 这里只需要了解,所有可拖拽到prefab上的类都会被注册到JS._idToClass中,这里的id就是类名
var cls = JS._getClassById(id);
if (cls) {
return cls;
}
cc.warnID(4903, id);
return Object;
};
} // 进行反序列化,反序列化出asset
var tdInfo = cc.deserialize.Details.pool.get();
var asset;
try {
// deserialize的实现位于platform\deserialize.js
// 具体的实现非常复杂,大致可以理解为new出对应的类,并从json对象中反序列化该类的所有属性
// 所以返回的asset是这个json最顶层object对应的类,比如cc.SpriteFrame或者自定义的组件
// 该资源所依赖的所有资源会被反序列化到tdInfo中,在tdInfo.uuidList中。
asset = cc.deserialize(json, tdInfo, {
classFinder: classFinder,
target: item.existingAsset,
customEnv: item
});
} catch (e) {
cc.deserialize.Details.pool.put(tdInfo);
var err = CC_JSB ? (e + '\n' + e.stack) : e.stack;
return new Error('Uuid Loader: Deserialize asset [' + item.id + '] failed : ' + err);
} // 如果是在编辑器下的场景存在类丢失,进行报告(应该是报红)
asset._uuid = item.uuid;
if (CC_EDITOR && isScene && MissingClass.hasMissingClass) {
MissingClass.reportMissingClass(asset);
} // 判断是否可延迟加载,并调用loadDepends
var deferredLoad = canDeferredLoad(asset, item, isScene);
loadDepends(this.pipeline, item, asset, tdInfo, deferredLoad, callback);
}

canDeferredLoad方法会根据资源类型监测是否可以延迟加载,当item的deferredLoadRaw为true且该资源支持延迟加载(在代码中搜索preventDeferredLoadDependents可以发现除了TileMap、DragonBones、Spine等资源外,都不支持延迟加载),或是设置了延迟加载的场景才可以延迟加载。

// can deferred load raw assets in runtime
// 检查是否延迟加载Raw Assets
function canDeferredLoad (asset, item, isScene) {
if (CC_EDITOR || CC_JSB) {
return false;
}
var res = item.deferredLoadRaw;
if (res) {
// check if asset support deferred
// 检查该资源是否支持延迟加载
if (asset instanceof cc.Asset && asset.constructor.preventDeferredLoadDependents) {
res = false;
}
} else if (isScene) {
// 如果是prefab或scene,取其asyncLoadAssets属性
if (asset instanceof cc.SceneAsset || asset instanceof cc.Prefab) {
res = asset.asyncLoadAssets;
}
}
return res;
}

loadDepends方法会加载依赖,主要做了2个事情,延迟加载和依赖加载

延迟加载指的是资源A依赖了B、C、D,其中资源D延迟加载了,那么BC加载完成即算这个资源加载完成,并执行回调,D也会进行加载,但什么时候加载完这里并不关心。在实际应用中的表现就是加载一个场景,基础部分的内容加载完成了,进入了该场景之后再陆续看到其他内容加载完成。

根据deferredLoadRawAssetsInRuntime,对raw类型资源进行延迟加载,延迟加载的内容会进入dependKeys数组,而不延迟加载的内容进入depends数组。

depends数组是该资源所依赖的资源数组,loadDepends会调用pipeline.flowInDeps进行加载,如果该数组为空则不加载依赖,执行完成回调。dependKeys数组是item的属性,记录了该资源依赖的所有资源,在做资源释放的时候会用到。预加载的内容会直接进入dependKeys,而正常加载的资源在加载完成后才会被添加到dependKeys中。

最后调用pipeline.flowInDeps加载depends数组,flowInDeps的完成回调中,如果item加载完成且没有报错,调用loadCallback,如果未加载完成,插入到item的queue的 _callbackTable[dependSrc]中或添加queue的监听(这两个操作的意义都是在item加载完成后执行loadCallback),loadCallback将依赖对象的依赖属性进行赋值,并添加该资源的id到dependKeys中。

当反序列化出来的asset._preloadRawFiles有值时,会将callback进行包裹,在异步加载完RawFiles才执行最终的callback。实际并没有什么作用。

function loadDepends (pipeline, item, asset, tdInfo, deferredLoadRawAssetsInRuntime, callback) {
// tdInfo.uuidList为这个prefab或场景所依赖的uuid类型的资源
var uuidList = tdInfo.uuidList;
var objList, propList, depends;
var i, dependUuid;
// cache dependencies for auto release
// dependKeys用于缓存该资源的依赖,在资源释放的时候会用到
var dependKeys = item.dependKeys = []; /******************************* 过滤决定哪些资源要加载,哪些要延迟,得出depends数组 **********************************/
// 如果支持延迟加载
if (deferredLoadRawAssetsInRuntime) {
objList = [];
propList = [];
depends = [];
// parse depends assets
for (i = 0; i < uuidList.length; i++) {
dependUuid = uuidList[i];
var obj = tdInfo.uuidObjList[i];
var prop = tdInfo.uuidPropList[i];
var info = cc.AssetLibrary._getAssetInfoInRuntime(dependUuid);
if (info.raw) {
// skip preloading raw assets
// 对于raw类型的资源不进行加载,tdInfo.uuidObjList[i][prop] = url
var url = info.url;
obj[prop] = url;
dependKeys.push(url);
} else {
objList.push(obj);
propList.push(prop);
// declare depends assets
// 对于非raw类型的资源,进入depends进行加载,但带上deferredLoadRaw标记
// 意为该uuid引用的其他raw类型的资源进行延迟加载
depends.push({
type: 'uuid',
uuid: dependUuid,
deferredLoadRaw: true,
});
}
}
} else {
objList = tdInfo.uuidObjList;
propList = tdInfo.uuidPropList;
depends = new Array(uuidList.length);
// declare depends assets
// 不支持延迟加载则直接进入depends数组,这里没有deferredLoadRaw标记
for (i = 0; i < uuidList.length; i++) {
dependUuid = uuidList[i];
depends[i] = {
type: 'uuid',
uuid: dependUuid
};
}
} /******************************* tdInfo.rawProp和asset._preloadRawFiles的处理 **********************************/
// declare raw
// 有些json文件包含了一些raw属性,以$_$rawType结尾,这里会直接加载item.url,但目前还未碰到过这样类型的资源。
// 下面2个说法是错误的。
// 如果这个uuid资源本身就是一个raw资源,加载自己?
// 如果这个uuid资源存在raw属性,例如一个脚本拖拽了一个Texture2D类型的资源作为它的成员变量?
if (tdInfo.rawProp) {
objList.push(asset);
propList.push(tdInfo.rawProp);
depends.push(item.url);
}
// preload raw files
// 预加载它的raw文件,这里是asset的属性,但从引擎代码没有看到哪里对这个属性赋值过
// 不过prefab等文件倒是有一个_rawFiles的属性,但从代码上看也与这个方法无关,看上去倒像是预留的一个接口
// 提供给开发者做某种资源类型的完成回调包装。
if (asset._preloadRawFiles) {
var finalCallback = callback;
callback = function () {
asset._preloadRawFiles(function (err) {
finalCallback(err || null, asset);
});
};
}
// fast path
// 如果没有资源要加载就直接返回
if (depends.length === 0) {
cc.deserialize.Details.pool.put(tdInfo);
return callback(null, asset);
} /******************************* 调用pipeline.flowInDeps进行依赖加载,资源加载完成后调用loadCallback **********************************/
// Predefine content for dependencies usage
// 加载depends,加载完成后注册到item.dependKeys中,并赋值给this.obj[this.prop]
item.content = asset;
pipeline.flowInDeps(item, depends, function (errors, items) {
// 这个回调在所有的item都加载完成后执行,所以item都是有的,但有可能有报错
var item, missingAssetReporter;
for (var src in items.map) {
item = items.map[src];
if (item.uuid && item.content) {
item.content._uuid = item.uuid;
}
}
for (var i = 0; i < depends.length; i++) {
var dependSrc = depends[i].uuid;
var dependUrl = depends[i].url;
var dependObj = objList[i];
var dependProp = propList[i];
item = items.map[dependUrl];
if (item) {
var thisOfLoadCallback = {
obj: dependObj,
prop: dependProp
}; // 资源加载完成的回调,关联依赖对象obj的prop为item的value
function loadCallback (item) {
var value = item.isRawAsset ? item.rawUrl : item.content;
this.obj[this.prop] = value;
if (item.uuid !== asset._uuid && dependKeys.indexOf(item.id) < 0) {
dependKeys.push(item.id);
}
} // 如果资源已经加载完了,且没有报错,则执行loadCallback回调
if (item.complete || item.content) {
if (item.error) {
if (CC_EDITOR && item.error.errorCode === 'db.NOTFOUND') {
if (!missingAssetReporter) {
var MissingObjectReporter = Editor.require('app://editor/page/scene-utils/missing-object-reporter');
missingAssetReporter = new MissingObjectReporter(asset);
}
missingAssetReporter.stashByOwner(dependObj, dependProp, Editor.serialize.asAsset(dependSrc));
} else {
cc._throw(item.error);
}
} else {
loadCallback.call(thisOfLoadCallback, item);
}
} else {
// item was removed from cache, but ready in pipeline actually
// 该item从cache中移除了?但在pipeline中?
// 这里监听了该item的加载完成事件,在加载完成时调用loadCallback
var queue = LoadingItems.getQueue(item);
// Hack to get a better behavior
// 这个behavior非常的bad,_callbackTable是CallbacksHandler的成员变量
// 两个操作都是添加监听,但前者是直接拿到监听该事件的回调数组,强行插入
var list = queue._callbackTable[dependSrc];
if (list) {
list.unshift(loadCallback, thisOfLoadCallback);
} else {
queue.addListener(dependSrc, loadCallback, thisOfLoadCallback);
}
}
}
}
if (CC_EDITOR && missingAssetReporter) {
missingAssetReporter.reportByOwner();
}
cc.deserialize.Details.pool.put(tdInfo);
callback(null, asset);
});
}

CCLoader的flowInDeps,实现如下,传入资源的owner,依赖列表urlList,以及urlList的回调。当一个依赖又有依赖的时候,queue的append又会走到这个新资源的loadUuid,去加载那一层所依赖的资源。而flowInDeps开头的var item = this._cache[res.url] 也确保了资源不会被重复加载。

proto.flowInDeps = function (owner, urlList, callback) {
// 准备_sharedList,已加载或正在加载的资源push item,未加载的push res
_sharedList.length = 0;
for (var i = 0; i < urlList.length; ++i) {
var res = getResWithUrl(urlList[i]);
if (!res.url && ! res.uuid)
continue;
var item = this._cache[res.url];
if (item) {
_sharedList.push(item);
} else {
_sharedList.push(res);
}
} // 创建一个新的队列,当有owner时,将子队列的进度同步到ownerQueue
var queue = LoadingItems.create(this, owner ? function (completedCount, totalCount, item) {
if (this._ownerQueue && this._ownerQueue.onProgress) {
this._ownerQueue._childOnProgress(item);
}
} : null, function (errors, items) {
callback(errors, items);
// Clear deps because it's already done
// Each item will only flowInDeps once, so it's still safe here
// 加载完成后清除owner.deps数组
owner && owner.deps && (owner.deps.length = 0);
items.destroy();
});
if (owner) {
var ownerQueue = LoadingItems.getQueue(owner);
// Set the root ownerQueue, if no ownerQueue defined in ownerQueue, it's the root
// 设置queue的ownerQueue
queue._ownerQueue = ownerQueue._ownerQueue || ownerQueue;
}
var accepted = queue.append(_sharedList, owner);
_sharedList.length = 0;
return accepted;
};
  • 延迟加载

  • 延迟加载的作用

在creator编辑器中可以设置场景和prefab的延迟加载,设置了延迟加载之后,场景或prefab所引用的一些Raw类型资源如cc.Texture2D、cc.AudioClip等会延迟加载,同时,玩家进入场景后可能会看到一些资源陆续显示出来,并且激活新界面时也可能会看到界面中的元素陆续显示出来,因此这种加载方式更适合网页游戏。

具体的实现是在loadUuid中执行canDeferredLoad时,它的asset.asyncLoadAssets为一个Object。在后面的loadDepends方法中会执行deferredLoadRawAssetsInRuntime的判断。所有Raw类型的资源会被延迟加载,而非Raw类型的资源会被添加到depends数组中进行加载。最终加载完成时我们可以得到一个不完整的资源(因为它有一部分依赖被延迟加载了)。

  • 延迟加载的资源在什么时候加载?

从整个Pipeline的加载流程来看,并没有任何地方去加载这些被延迟的Raw类型资源,而在底层加载图片的地方进行断点,可以发现当场景或Prefab被激活时(添加到场景中),会有一个ensureLoadTexture方法被调用,在这里会执行这些延迟资源的加载流程。所以延迟加载的资源在节点被激活时会自动加载。下图是一个延迟加载图片的调用堆栈。

ensureLoadTexture的实现如下所示,AudioClip也类似,在调用play播放声音时会执行preload,检测到声音没有被加载时会执行cc.loader.load方法加载声音。

    /**
* !#en If a loading scene (or prefab) is marked as `asyncLoadAssets`, all the textures of the SpriteFrame which
* associated by user's custom Components in the scene, will not preload automatically.
* These textures will be load when Sprite component is going to render the SpriteFrames.
* You can call this method if you want to load the texture early.
* !#zh 当加载中的场景或 Prefab 被标记为 `asyncLoadAssets` 时,用户在场景中由自定义组件关联到的所有 SpriteFrame 的贴图都不会被提前加载。
* 只有当 Sprite 组件要渲染这些 SpriteFrame 时,才会检查贴图是否加载。如果你希望加载过程提前,你可以手工调用这个方法。
*/
ensureLoadTexture: function () {
if (!this._texture) {
this._loadTexture();
}
}, _loadTexture: function () {
if (this._textureFilename) {
// 这里返回的tex可能是一个未加载完成的纹理,如纹理未加载完成,可监听其加载完成回调
var texture = cc.textureCache.addImage(this._textureFilename);
this._refreshTexture(texture);
}
}, _refreshTexture: function (texture) {
var self = this;
if (self._texture !== texture) {
var locLoaded = texture.loaded;
this._textureLoaded = locLoaded;
this._texture = texture;
function textureLoadedCallback () {
if (!self._texture) {
// clearTexture called while loading texture...
// 在加载纹理的时候调用了clearTexture方法
return;
}
self._textureLoaded = true;
var w = texture.width, h = texture.height;
// 如果在Canvas模式下,图片有旋转,需要进行旋转的特殊处理(_cutRotateImageToCanvas)
if (self._rotated && cc._renderType === cc.game.RENDER_TYPE_CANVAS) {
var tempElement = texture.getHtmlElementObj();
tempElement = _ccsg.Sprite.CanvasRenderCmd._cutRotateImageToCanvas(tempElement, self.getRect());
var tempTexture = new cc.Texture2D();
tempTexture.initWithElement(tempElement);
tempTexture.handleLoadedTexture();
self._texture = tempTexture;
self._rotated = false;
w = self._texture.width;
h = self._texture.height;
self.setRect(cc.rect(0, 0, w, h));
} if (self._rect) {
self._checkRect(self._texture);
} else {
self.setRect(cc.rect(0, 0, w, h));
} if (!self._originalSize) {
self.setOriginalSize(cc.size(w, h));
} if (!self._offset) {
self.setOffset(cc.v2(0, 0));
} // dispatch 'load' event of cc.SpriteFrame
// cc.SpriteFrame的触发load事件
self.emit("load");
} // 如果图片已加载完,则直接执行回调,否则监听texture的load方法
if (locLoaded) {
textureLoadedCallback();
} else {
texture.once("load", textureLoadedCallback);
}
}
},
  • 禁止延迟加载

在Creator的官方文档中介绍到“Spine 和 TiledMap 依赖的资源永远都不会被延迟加载”,这主要是因为它们对Raw资源是一个强依赖,也就是说节点被激活时就必须使用到它们的纹理,所以不能延迟加载。那么它们是如何实现禁止延迟加载的呢?

在canDeferredLoad方法中,如果资源的asset.constructor.preventDeferredLoadDependents为true时,会强制返回false。在引擎中进行搜索可以发现,除了Spine和TiledMap,还有DragonBones也是被禁止延迟加载的。

Cocos Creator 资源加载流程剖析【三】——Load部分的更多相关文章

  1. Cocos Creator 资源加载流程剖析【一】——cc.loader与加载管线

    这系列文章会对Cocos Creator的资源加载和管理进行深入的剖析.主要包含以下内容: cc.loader与加载管线 Download部分 Load部分 额外流程(MD5 Pipe) 从编辑器到运 ...

  2. Cocos Creator 资源加载流程剖析【二】——Download部分

    Download流程的处理由Downloader这个pipe负责(downloader.js),Downloader提供了各种资源的"下载"方式--即如何获取文件内容,有从网络获取 ...

  3. Cocos Creator 资源加载流程剖析【六】——场景切换流程

    这里讨论场景切换的完整流程,从我们调用了loadScene开始切换场景,到场景切换完成背后发生的事情.整个流程可以分为场景加载和场景切换两部分,另外还简单讨论了场景的预加载. 加载场景的流程 load ...

  4. Cocos Creator 资源加载流程剖析【五】——从编辑器到运行时

    我们在编辑器中看到的资源,在构建之后会进行一些转化,本章将揭开Creator对资源进行的处理. 资源处理的整体规则 首先我们将Creator的开发和运行划分为以下几个场景: 编辑器 当我们将资源放到编 ...

  5. Cocos Creator 资源加载流程剖析【四】——额外流程(MD5 PIPE)

    当我们将游戏构建发布到web平台时,勾选Md5 Cache选项可以开启MD5 Pipe,它的作用是给构建后的资源加上md5后缀,避免浏览器的缓存导致部分资源不是最新,因为使用了md5后缀后,当资源内容 ...

  6. Cocos Creator 资源加载(笔记)

    cc.loader 加载资源动态加载资源要注意两点,一是所有需要通过脚本动态加载的资源,都必须放置在 resources 文件夹或它的子文件夹下.resources 需要在 assets 文件夹中手工 ...

  7. AssetBundle使用心得【资源加载】

    0.资源加载方式 静态资源 Asset下所有资源称为静态资源 Resources资源 Resources目录下,通过实例化得到的资源 AssetBundle资源 又称为增量更新资源 1.什么是Asse ...

  8. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

  9. MyBatis 源码篇-资源加载

    本章主要描述 MyBatis 资源加载模块中的 ClassLoaderWrapper 类和 Java 加载配置文件的三种方式. ClassLoaderWrapper 上一章的案例,使用 org.apa ...

随机推荐

  1. 关于CSS选择器连续性的问题

    在html中有以下结构: --- ----- <div class="row100"> <div class="col"> <di ...

  2. SpringMvc commons-fileupload图片/文件上传

    简介 SpringMvc文件上传的实现,是由commons-fileupload这个jar包实现的. 需求 在修改商品页面,添加上传商品图片功能. Maven依赖包 pom.xml <!-- 文 ...

  3. docker实践之docker-compose部署mysql

    文章目录 docker实践之docker-compose部署mysql 1.安装部署docker 2.编写docker-compose文件 3.编写配置文件和初始化文件 4.启动数据库 5.检查初始化 ...

  4. Java修炼——容器体系框架总结

    容器有俩大接口Collection接口(无序,不唯一)和Map接口 Collection接口有俩个子接口分别是List和Set. List接口特点是有序但是不唯一,她有三个子接口分别是:ArrayLi ...

  5. 2018ACM/ICPC 焦作网络预选赛-A Magic Mirror

    Jessie has a magic mirror. Every morning she will ask the mirror: 'Mirror mirror tell me, who is the ...

  6. (重点)Python深入之Python内存管理机制你会吗?

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:醍醐三叶 请注意:如果你平时学Python遇到问题找不到人解答?或者没有 ...

  7. 这十道经典Python笔试题,全做对算我输

    经常有小伙伴学了Python不知道是否能去找工作,可以来看下这十道题检验你的成果: 1.常用的字符串格式化方法有哪些?并说明他们的区别 a. 使用%,语法糖 print("我叫%s,今年%d ...

  8. 层叠机制和继承的概念以及CSS中选择器的优先级

    层叠机制: 一个元素的某个特定的样式属性可能来自行间的style属性.内联样式表或者外部引入的样式表,以及浏览器自定义的样式,还有就是继承自父元素的样式,但是最终只会选择其中的某一个来表示,这个选择的 ...

  9. Python3 常用模块3

    目录 numpy模块 创建numpy数组 numpy数组的属性和用法 matplotlib模块 条形图 直方图 折线图 散点图 + 直线图 pandas模块 numpy模块 numpy模块可以用来做数 ...

  10. 【Babel】293- 初学 Babel 工作原理

    戳蓝字「前端技术优选」关注我们哦! 前言 babel Babel 对于前端开发者来说应该是很熟悉了,日常开发中基本上是离不开它的. 已经9102了,我们已经能够熟练地使用 es2015+ 的语法.但是 ...