1.抽丝剥茧:Electron与Node.js的源码奇葩Bug
2.jsç¨ä»ä¹è½¯ä»¶ç¼è¾å¨(ç¼è¾jsç软件)
3.MUD游戏源码下载地址
4.reactadmin?
5.umi3源码解析之核心Service类初始化
6.图灵访谈 | Vue.js官方团队成员霍春阳:跨专业做程序员,是源码什么感受?
抽丝剥茧:Electron与Node.js的奇葩Bug
起因是最近在用Electron开发一个桌面端项目,有个需求是源码需要遍历某个文件夹下的所有JavaScript文件,对它进行AST词法语法分析,源码解析出元数据并写入到某个文件里。源码需求整体不复杂,源码小白源码资源站只是源码细节有些麻烦,当我以为开发的源码差不多时,注意到一个匪夷所思的源码问题,查的源码我快怀疑人生。
缘起
什么问题呢?
原来这个需求一开始仅是源码遍历当前文件夹下的文件,我的源码获取所有JS文件的代码是这样的:
后来需求改为要包含文件夹的子文件夹,那就需要进行递归遍历。源码按照我以前的源码做法,当然是源码手撸一个递归,代码并不复杂,缺点是递归可能会导致堆栈溢出:
但做为一个紧跟时代浪潮的开发者,我知道Node.js的fs.readdir API中加了一个参数recursive,表示可以进行递归,人家代码的鲁棒性肯定比我的好多了:
只改了一行代码,美滋滋~
兼容性怎么样呢?我们到Node.js的API文档里看下:
是从v..0添加的,而我本地使用的Node.js版本正是这个(好巧),我们Electron中的Node.js版本是多少呢?先看到electron的版本是.0.4:
在Electron的 发布页上能找到这个版本对应的是..1,比我本地的还要多一个小版本号:
这里需要说明一下,Electron之所以优于WebView方案,是因为它内置了Chrome浏览器和Node.js,锁定了前端与后端的版本号,这样只要Chrome和Node.js本身的跨平台没有问题,理论上Electron在各个平台上都能获得统一的UI与功能体验。 而以Tauri为代表的WebView方案,则是不内置浏览器,应用程序使用宿主机的浏览器内核,开发包的体积大大减小,比如我做过的同样功能的一个项目,Electron版本有M+,而Tauri的只有4M左右。虽然体积可以这么小,又有Rust这个性能大杀器,但在实际工作中的技术选型上,想想各种浏览器与不同版本的兼容性,换我也不敢头铁地用啊! 所以,尽管Electron有这样那样的缺点,仍是我们开发客户端的不二之选。 之所以提这个,是因为读者朋友需要明白实际项目运行的是Electron内部的Node.js,而我们本机的Node.js只负责启动Electron这个工程。
以上只是为了说明,我这里使用fs.readdir这个API新特性是没有问题的。
排查
为方便排查,我将代码再度简化,提取一个单独的文件中,被Electron的Node.js端引用:
能看到控制台打印的 Node.js 版本与我们刚才查到的是一样的,文件数量为2:
同样的代码使用本机的Node.js执行:
难道是这个小版本的锅?按理说不应该。但为了排除干扰,我将本机的Node.js也升级为..1:
这下就有些懵逼了!
追踪
目前来看,锅应该是Electron的。那么第一思路是什么?是不是人家已经解决了啊?我要不要先升个级?
没毛病。
升级Electron
将Electron的版本号换成最新版本v.1.0:
再看效果:
我去,正常了!
不过,这个包的升级不能太草率,尤其正值发版前夕,所以还是先改代码吧,除了我上面手撸的代码外,还有个包readdirp也可以做同样的python源码问题事情:
这两种方式,在原版本的Electron下都没有问题。
GitHub上搜索
下来接着排查。
Electron是不是有人发现了这个Bug,才进行的修复呢?
去 GitHub issue里瞅一瞅:
没有,已经关闭的问题都是年提的问题,而我们使用的Electron的版本是年月日发布的。 那么就去 代码提交记录里查下(GitHub这个功能还是很好用的):
符合条件的就一条,打开看下:
修复的这个瞅着跟我们的递归没有什么关系嘛。
等等,这个文件是什么鬼?
心血来潮的收获
我们找到这个文件,目录在lib下:
从命名上看,这个文件是对Node.js的fs模块的一个包装。如果你对Electron有了解的话,仔细一思索,就能理解为什么会有这么个文件了。我们开发时,项目里会有许多的资源,Electron的Node.js端读取内置的文件,与正常Node.js无异,但事实上,当我们的项目打包为APP后,文件的路径与开发状态下完全不一样了。所以Electron针对打包后的文件处理,重写了fs的各个方法。
这段代码中重写readdir的部分如下:
上面的判断isAsar就是判断是否打包后的归档文件,来判断是否要经Electron特殊处理。如果要处理的话,会调用Electron内部的C++代码方法进行处理。
我发现,这里Electron并没有对打包后的归档文件处理递归参数recursive,岂不是又一个Bug?应该提个issue提醒下。
不过,我们目前的问题,并不是它造成的,因为现在是开发状态下,上面代码可以简化为:
对Promise了如指掌的我怎么看这代码也不会有问题,只是心血来潮执行了一下:
我去,差点儿脑溢血!
好的一点是,曙光似乎就在眼前了!事实证明,心血来潮是有用的!
Node.js的源码
这时不能慌,本地切换Node.js版本为v,同样的代码再执行下:
这说明Electron是被冤枉的,锅还是Node.js的!
Node.js你这个浓眉大眼的,居然也有Bug!呃,还偷偷修复了!
上面的情况,其实是说原生的fs.readdir有问题:
也就是说,fs.promises.readdir并不是用util.promisify(fs.readdir)实现的!
换成同步的代码readdirSync,效果也是一样:
我们来到Node.js的GitHub地址,进行 搜索:
打开这两个文件,能发现,二者确实是不同的实现,不是简单地使用util.promisify进行转换。
fs.js
我们先看 lib/fs.js。
当recursive为true时,调用了一个readdirSyncRecursive函数,从这个命名上似乎可以看出有性能上的隐患。正常来说,这个函数是异步的,不应该调用同步的乾坤量源码方法,如果文件数量过多,会把主进程占用,影响性能。
如果没有recursive,则是原来的代码,我们看到binding readdir这个函数,凡是binding的代码,应该就是调用了C++的底层库,进行了一次『过桥』交互。
我们接着看readdirSyncRecursive,它的命名果然没有毛病,binding readdir没有第4个参数,是完全同步的,这个风险是显而易见的:
fs/promises.js
在lib/internal/fs/promises.js中,我们看到binding readdir的第4个参数是kUsePromises,表明是个异步的处理。
当传递了recursive参数时,将调用readdirRecursive,而readdirRecursive的代码与readdirSyncRecursive的大同小异,有兴趣的可以读一读:
fs.js的提交记录
我们搜索readdir的提交记录,能发现这两篇都与深度遍历有关:
其中 下面的这个,正是我们这次问题的罪魁祸首。
刚才看到的fs.js中的readdirSyncRecursive里这段长长的注释,正是这次提交里添加的:
从代码对比上,我们就能看出为什么我们的代码遍历的程序为2了,因为readdirResult是个二维数组,它的长度就是2啊。取它的第一个元素的长度才是正解。坑爹!
也就是说,如果不使用withFileTypes这个参数,得到的结果是没有问题的:
发版记录
我们在Node.js的发版记录中,找到这条提交记录,也就是说,v..0才修复这个问题。
而Electron只有Node.js更新到v后,这个功能才修复。
而从Electron与Node.js的版本对应上来看,得更新到v了。
只是需要注意的是,像前文提过的,如果是遍历的是当前项目的内置文件,Electron并没有支持这个新的参数,在打包后会出现Bug。
fs的同步阻塞
其实有人提过一个 issue:
确实是个风险点。所以,建议Node.js开发者老老实实使用fs/promises代替fs,而Electron用户由于坑爹的fs包裹,还是不要用这个特性了。
总结
本次问题的起因是我的一个Electron项目,使用了一个Node.js fs API的一个新参数recursive递归遍历文件夹,偶然间发现返回的文件数量不对,就开始排查问题。
首先,我选择了升级Electron的包版本,发现从v.0.4升级到最新版本v.1.0后,问题解决了。但由于发版在即,不能冒然升级这么大件的东西,所以先使用readdirp这个第三方包处理。
接着排查问题出现的原因。我到Electron的GitHub上搜索issue,只找到一条近期的提交,但看提交信息,不像是我们的目标。我注意到这条提交的飞鹰指标源码修改文件(asar-fs-wrapper.ts),是Electron针对Node.js的fs API的包装,意识到这是Electron为了解决打包前后内置文件路径的问题,心血来潮之下,将其中核心代码简化后,测试发现问题出在fs.promises readdir的重写上,继而锁定了Node.js版本v..1的fs.readdir有Bug。
下一步,继续看Node.js的源码,确定了fs.promises与fs是两套不同的实现,不是简单地使用util.promisify进行转换。并在fs的代码找到关于recursive递归的核心代码readdirSyncRecursive。又在提交记录里,找到这段代码的修复记录。仔细阅读代码对比后,找到了返回数量为2的真正原因。
我们在Node.js的发版记录中,找到了这条记录的信息,确定了v..0才修复这个问题。而内嵌Node.js的Electron,只有v版本起才修复。
不过需要注意的是,如果是遍历的是当前项目的内置文件,由于Electron并没有支持这个新的参数,在打包后会出现Bug。而且由于fs.readdir使用recursive时是同步的,Electron重写的fs.promises readdir最终调用的是它,可能会有隐性的性能风险。
本次定位问题走了些弯路,一开始将目标锁定到Electron上,主要是它重写fs的锅,如果我在代码中用的fs.readdirSync,那么可能会更早在Node.js上查起。谁能想到我调用的fs.promises readdir不是真正的fs.promises readdir呢?
最后,针对此次问题,我们有以下启示:
PS:我给Electron提了个 issue,一是让他们给fs.readdir添加recursive参数的实现,二是让他们注意下重写时fs.promises readdir的性能风险。
jsç¨ä»ä¹è½¯ä»¶ç¼è¾å¨(ç¼è¾jsç软件)
å代ç ç¨ä»ä¹è½¯ä»¶
1ãVscodeVscodeå ¨ç§°VisualStudioCodeï¼æ¯ä¸æ¬¾é对äºç¼åç°ä»£webåäºåºç¨ç跨平å°æºä»£ç ç¼è¾å¨ã
2ã常ç¨çå代ç 软件æ以ä¸å 个ï¼WebStormãWebStormæ¯jetbrainså ¬å¸æä¸ä¸æ¬¾JavaScriptå¼åå·¥å ·ãç®åå·²ç»è¢«å¹¿å¤§ä¸å½JSå¼åè èªä¸ºâWebå端å¼åç¥å¨âãâæ强大çHTML5ç¼è¾å¨âãâææºè½çJavaScriptIDEâçãSublimeTextã
3ãIntellijIDEAIntellijIDEAæ¯å½åJavaå¼å人åå¼å£°å¾é«çä¸æ¬¾IDEï¼å ·æç¾è§ï¼é«æçä¼å¤ç¹ç¹ï¼å°ç¼è¿æ¯é常å欢çï¼æ以以å¾æé½æ¯å¨èªå·±çµèå®è£ è¿æ¬¾è½¯ä»¶çã
4ãé®é¢ä¸ï¼çµèç¼ç¨ç¨ä»ä¹è½¯ä»¶å¥½Cè¯è¨ç¼ç¨è½¯ä»¶æåªäºã
5ãå¨å¹³æ¿ä¸åCè¯è¨ä»£ç ï¼å¯ä»¥ä½¿ç¨ä»¥ä¸å ç§è½¯ä»¶ï¼TurboEditor-ä¸æ¬¾Androidå¹³å°çææ¬ç¼è¾å¨ï¼æ¯æå¤ç§ç¼ç¨è¯è¨ï¼å æ¬Cè¯è¨ãå®æ代ç é«äº®ãèªå¨ç¼©è¿çåè½ï¼å¯ä»¥è®©ä½ æ´å æ¹ä¾¿å°ç¼å代ç ã
6ãç®ç§°TCæ件管çå¨ï¼ä¸æ¬¾åè½å¼ºå¤§çå ¨è½æ件管ç软件ï¼è½å¤æ大å°æé«æ件å¤çå·¥ä½çæçï¼ä»¥å ¶ä½¿ç¨é«ææ¹ä¾¿ã稳å®å¯é ãæ©å±æ§ä¸å¯é ç½®æ§å¼ºå¾æäºæ æ°çµèèé¸ã
æåªäºå¥½ç¨ç代ç ç¼è¾å¨å¼å¾æ¨è?
1ãVisualStudioCodeæ¯ç®åæå欢è¿ç代ç ç¼è¾å¨ãVisualStudioCodeæ¯ä¸ä¸ªç±å¾®è½¯å ¬å¸åå¸çå è´¹å¼æºä¸è·¨å¹³å°ç代ç ç¼è¾å¨ï¼åä¸é®ä¸ï¼å®å°±ä»¥å¼ºå¤§çåè½ãç¾è§ççé¢ãé«åº¦çå¯å®å¶æ§ï¼è¢«èªä¸ºå¾®è½¯çè¯å¿ä¹ä½ã
2ãSublimeTextãSublimeTextæ¯ä¸ä¸ªä»£ç ç¼è¾å¨ä¹æ¯HTMLåæ£æå è¿çææ¬ç¼è¾å¨ãHBuilderã
3ãé¢ä¸»æ说çç«é ·ç代ç ç¼è¾å¨ï¼ä¹è®¸æ´å¤çåªæ¯ä¸»é¢çç»ä¸½å§ï¼ä¸é¢å°ä¸äºä¸ªäººå¸¸ç¨ç代ç ç¼è¾å¨åä¸æ»ç»ãé¢ä¸»å·²ç»è¯´äºç¼è¾å¨ï¼å¤§å®¶å°±ä¸ç¨å¨æ¨èVisualStudioãEclipseä¹ç±»çç¼è¯å¨äºã
å代ç ç软件æåªäº1ãVscodeVscodeå ¨ç§°VisualStudioCodeï¼æ¯ä¸æ¬¾é对äºç¼åç°ä»£webåäºåºç¨ç跨平å°æºä»£ç ç¼è¾å¨ã
2ãIntellijIDEAIntellijIDEAæ¯å½åJavaå¼å人åå¼å£°å¾é«çä¸æ¬¾IDEï¼å ·æç¾è§ï¼é«æçä¼å¤ç¹ç¹ï¼å°ç¼è¿æ¯é常å欢çï¼æ以以å¾æé½æ¯å¨èªå·±çµèå®è£ è¿æ¬¾è½¯ä»¶çã
3ã常ç¨çå代ç 软件æ以ä¸å 个ï¼WebStormãWebStormæ¯jetbrainså ¬å¸æä¸ä¸æ¬¾JavaScriptå¼åå·¥å ·ãç®åå·²ç»è¢«å¹¿å¤§ä¸å½JSå¼åè èªä¸ºâWebå端å¼åç¥å¨âãâæ强大çHTML5ç¼è¾å¨âãâææºè½çJavaScriptIDEâçãSublimeTextã
4ãç¼ç¨æ¯ç¼å®ç¨åºçä¸æç®ç§°ï¼å°±æ¯è®©è®¡ç®æºä»£ç 解å³æ个é®é¢ï¼å¯¹æ个计ç®ä½ç³»è§å®ä¸å®çè¿ç®æ¹å¼ï¼ä½¿è®¡ç®ä½ç³»æç §è¯¥è®¡ç®æ¹å¼è¿è¡ï¼å¹¶æç»å¾å°ç¸åºç»æçè¿ç¨ãç¼ç¨è½¯ä»¶æï¼BASICï¼PASCALï¼COBOLï¼FORTRANï¼LOGOï¼VCï¼VBjavaã
jsæ件å¯ä»¥ç¨dreamweaverç¼åå?JSæ件ç¨ä»ä¹ç¼è¾å¨é½æ¯å¯ä»¥åçï¼ä¸è¦è¯´dreamweaveräºï¼å°±æ¯è®°äºæ¬é½å¯ä»¥ï¼é«æä¸ä¼æ»æ¯ç¨ä»ä¹å·¥å ·çï¼é½æ¯æå代ç ã
å¯ä»¥åï¼ä»»å¡ç¼è¾å¨é½å¯ä»¥ç¼åï¼è®°äºæ¬é½å¯ä»¥ãåªæ¯æ²¡æè¯æ³åå½æ°æ©å±æéã
.jsæ件æ¯JavaScriptè¯è¨æ件çæ©å±åãJavaScriptä¸ç§ç´è¯å¼èæ¬è¯è¨ï¼æ¯ä¸ç§å¨æç±»åã弱类åãåºäºååçè¯è¨ï¼å ç½®æ¯æç±»åã
å½ç¶æ¯ç´æ¥å»ºjavascriptï¼åºå«æ¯htmlæ¯ç½é¡µåï¼javascriptæ¯jsèæ¬æ件ï¼æjs代ç é½åjavascriptæ件éï¼ç¶åå¯ä»¥å¨htmléè°ç¨ä½¿ç¨è¿äºä»£ç ã
å¨ä»»ä½IDEåç¼è¾å¨ä¸é½å¯ä»¥ç¼åJSå端ç¨åºï¼èæ们åªéè¦æ³¨æ注æJSè¯è¨çè§èå°±è¡äºãç®åå¨HTMLæ件ä¸ï¼è¿æ§JSæ两ç§æ¹å¼ï¼ä¸ç§æ¯å åµä»£ç ï¼å¦ä¸ç§æ¯ç¼ååç¬çJSæ件ï¼ç¶åå¼ç¨ã
以js为æ©å±åçæ件ï¼æ¯ç¨javascriptèæ¬è¯è¨ç¼åç.jsæ件常è§çæ两ç§ç¨æ³ã
MUD游戏源码下载地址
下载地址:/azyx/jsby/mudanmudyouxiv.html
类型:安卓游戏-角色扮演
版本:牡丹mud游戏v1..
大小:.M
语言:中文
平台:安卓APK
推荐星级(评分):★★★★★
游戏标签: 魔幻手游 rpg手游 牡丹mud 牡丹mud手机版为玩家朋友带来酣畅淋漓的战斗竞技挑战,在这里大家可以实时匹配对手来对战,玩法操作极其畅爽,搭配上华丽的套装和高级装备,瞬间让英雄看起来更拉风,除了单人副本之外还有多人团战模式可以体验哦!
牡丹mud游戏介绍:在这个充斥着黑暗的世界里可以体验到无比狂野的对决,每个玩家都可以通过在游戏里进行厮杀挑战获得强大的力量,一次次的完成游戏之中所设定的各种考验,成为真正的强者。
牡丹mud手游玩法:开放地图,最激情四射的战斗场景,角色体验很独特。
玩家们需要在这里不断地实时作战,获取更多的资源。
超高清画质,各种职业搭配,在这个世界尽情的对决。
享受最真实的战斗玩法,超大的世界地图等你来探索。
海量的副本任务随便刷,隐藏的地图和神器等你发现。
游戏特色:开启新的征程,在全服激战中来实现你们的霸业。
多个不同阵营的英雄,简单操作,轻松享受游戏。
超多任务挑战源源不断,锻造收集稀有武器装备。答题源码django
每名角色都有独一无二的特性,英雄战斗畅爽刺激。
手游亮点:3D引擎精心制作的传奇征战游戏,副本任务很多。
每一个玩家都可以选择喜欢的职业开启精彩对战。
与不同的对手比拼,你就能掌握更多的作战技巧。
选择搭配上合适的武器和法宝,畅享激战的快感。
小编测评:获得史诗装备和传奇英雄,各种招式极为华丽刺激。
即使离线也一样获得丰厚的奖励,其乐无穷策略性。
大幅增强自身战斗力你需要完美的装备搭配策略。
幻境之塔竞技场公会,无尽精彩内容等你来体验。
综上所述,墨鱼下载站是您寻找安卓游戏和角色扮演解决方案的理想之选。无论是安卓游戏角色扮演爱好者还是专业人士,我们推荐安卓游戏角色扮演的相关内容都能满足您的需求。立即下载或体验牡丹mud游戏,享受安卓游戏角色扮演带来的无尽乐趣!此外,我们还提供牡丹mud游戏的详细信息,包括功能介绍、用户评价以及官方下载链接/azyx/jsby/mudanmudyouxiv.html 。让您的下载过程更加轻松快捷!
reactadmin?
开箱即用的React前端框架——ReactAdmin
ReactAdmin是一个Github上免费开源的前端框架(不是组件库,也不是模板,它是一个框架),采用es6、React和MaterialDesign构建基于Rest/GraphQlAPI的Web应用程序。在React上star数超过8k。
ReactAdmin不是个UI组件库,它是一个前端框架,因此你基本上基本上只要按照官网的文档进行一些配置等然后在其基础上开发自己的应用程序即可,可谓开箱即用,意识就是都给你集成好了。
你可以直接使用以下命令进行安装(这是安装react-admin及所有的依赖)
下面我们看一下官网提供的一个最简单的示例,你可以在它的主仓库中获取
我们进入到simple中,大致看一下代码和目录结构
我们安装一些依赖然后启动
成功后打开浏览器,这是使用react-admin最简单的一个例子
一图了解
由于ReactAdmin是一个非常复杂的框架,你可以参考提供的文档,我这里就不详细介绍了,感兴趣的可以直接看文档,文档是英文的,所有的说明都在文档中。
ReactAdmin它是一个集合,它将一些前端开发所需要的东西都集成了进来,然后做好,我们直接使用即可,不仅仅适合个人学习,也适合通过它来构建企业级的应用。我们不仅仅是拿过来直接用,我们可看一看别人是怎么实现这样的一个框架的,从源码去学习会更快的提升自己的水平,希望对你有所帮助!
react-admin一款基于reactjs后台解决方案
使用ES6,React和MaterialDesign在REST/GraphQLAPI之上构建在浏览器中运行的管理应用程序的一款前端框架。Githubstar8.7K+,MIT协议。由marmelab开源和维护。marmelab还有一款非常热门的angularjs后台解决方案ng-admin基于angularjs感兴趣的同学可以去了解下。react-admin官网示例截图如下:
该Resource组件是一个配置组件,它允许以限定子组件对于每个管理视图的:list,edit,和create。这些组件使用MaterialUI和react-admin中的自定义组件:
antdesign表格标题下面还有子标题一、如图展示表格如何展示下方嵌套的deptName
在这里插入描述
官网在这里插入描述
二、解决方案
Columnalign="center"title="部门"dataIndex={ ["dept","deptName"]}/
1
1
技术交流分享/面试总结
微信名片
打开CSDNAPP,看更多技术内容
最新发布保姆级教程:AntDesignVue中a-table嵌套子表格
AntDesignVue中a-table嵌套子表格及只打开一个嵌套表格的方法
继续访问
Antd(Ant-design),嵌套子表格(expandedRowRender)的异步获取数据
使用阿里的ant-design开源框架,要在表格里面嵌套子表格,需要在用户点击父表格的一行数据后,获取该行的key,然后去异步请求后台的数据用来填充子表格的内容。如果这样写(省略无关代码):expandedRowRender=(record)={ dispatch({ type:'flow/getPlanList',payload:{ contractId:record.contract_id,//该参数...
继续访问
react-antdesigntable表格多级可编辑表格
antd-react3X版本多级可编辑table
继续访问
react.jsant-design中table树结构三级嵌套时逻辑问题
实现三级嵌套树结构时,勾选三级里的子节点时候父节点也会自动勾选,当子节点大于一项时候取消勾选某一子结构时父节点不变;当子节点只有一项时父节点也会自动取消勾选importStandardTablePagefrom'@/components/StandardTablePage';//用户选中某一行userSelect(record,selected,selectedRows,nativeEvent){ let{ selectedRowKeys}=this.st.
继续访问
Reactantd的table表格之嵌套表格
Reactantd的table表格之嵌套表格最近做了几个pc端的后台管理需求,涉及了table中的嵌套表格,嵌套的子表格大体分为两种效果:效果1-----点击展开新的子表格,旧的子表格关闭效果2-----可同时展开多个子表格效果1:嵌套表格,实现点击展开按钮,展开子表格请求接口数据,点击新的子表格收起原来的子表格效果//设置一个State用来储存展开的行,控制属性const[expandedRowKeys,setExpandedRowKeys]=useStateanygt
继续访问
vue嵌套表格组件_支持嵌套对象、多级数组的Vue动态多级表单组件——vue-dynamic-form-component...
方便不想看完全篇文章的童鞋,简单总结一下,这是篇软广,主要是推广自己在业务中沉淀的一个开源组件vue-dynamic-form-component。基于element-ui实现的vue组件,只需编写类似async-validator的规则,自动生成对应的表单,支持常见输入类型的同时,支持嵌套对象、hashmap、多维数组等复杂类型。有需要的童鞋欢迎使用和贡献代码,顺便给个star(我...
继续访问
antdtable嵌套子表格后端动态获取数据rudex写法示例
有一个需求是可以使主表格里每一栏数据展开,在子table里显示与其相关的子数据项,展开的时候去向后台请求数据显示。用的组件库是Antd。首先我们看Antd官方文档的Table有嵌套子表格的功能,可见我们需要使用expandedRowRender参数,但是尝试在expandedRowRender函数中进行请求,会发现发出了连续的请求,所以我们把请求写在onExpand中,只在点击展开图表的时候发出一次。之后我们写onExpand函数,注意这里的参数要写上expanded,代表是展开还是合并,我之前
继续访问
React-Antd-表格-嵌套子表格
文档地址:链接.import{ Table,Badge,Menu,Dropdown,Space}from"antd";import{ DownOutlined}from"@ant-design/icons";import{ useEffect,useState}from"react";exportdefaultfunctionApp(){ constcolumns=[//定义外层表格头数据{ title:"姓名",dataInde
继续访问
ReactantdTable实现单元格点击表头斜线分组等功能
reactantd单元格添加点击事件自定义单元格
继续访问
进阶Ant-Design-Vue你知道table多级表头嵌套展开写法吗?
前言:在Ant-Design-Vue的前端项目中,我们会经常处理表格,表单这些组件元素,熟练运用并知道它们在使用过程中的联系与区别,这是一个前端必不可少的哟。本文我旨在解决两个问题:(1)如何便于更好的嵌套多级表头(2)如何通过a-checkbox控制全选,单选显示a-table对应的列元素类似于ElementUI,Ant-Design-vue中有很多相似点,但又不完全苟同,有很多自己独有的写法和思想。相信很多人都是先入手ElementUI,再入手Ant,这其实是对开发者比较友好的方式,如果
继续访问
前端面试题(react)
性能优化分为2个方面setState是修改其中的部分状态,相当于Object.assign,只是覆盖,不会减少原来的状态;replaceState是完全替换原来的状态,相当于赋值,将原来的state替换为另一个对象,如果新状态属性减少,那么state中就没有这个状态了接收旧的state和action,返回新的state受控组件就是可以被react状态控制的组件在react中,Inputtextarea等组件默认是非受控组件(输入框内部的值是用户控制,和React无关)
继续访问
Antd表格设置表头分组实现可编辑行
主要通过onCell方法修改children中cloumn的属性。
继续访问
antd能自适应吗_admin-antd-react是一个后台前端解决方案,它基于React、AntDesign和UmiJs实现。...
admin-antd-react是一个后台前端解决方案,它基于React、AntDesign和UmiJs实现。--::?阅读次#介绍[admin-antd-react]()是一个后台前端解决方案,它基于[React](...
继续访问
react-antd-Table相似表格不同字段处理
1、当两个表格字段相似时,但有一两个字段不同,我们可以将不同的字段单独以对象的形式抽出,根据情况push进去即可。2、代码参考constchange={ title:'操作',dataIndex:'operate',key:'operate',width:'%',align:'center',render:(text,record)={
继续访问
React中控制AntDesignTable列的显示与隐藏
React中控制AntDesignTable列的显示与隐藏
继续访问
热门推荐使用antd中Table组件某一列有多个变量值需要写入
当某一列需要有多个变量值写入时,我们就不可以再用dataIndex来定义一个ID,具体解决方案将在文中给出。
继续访问
reactant-designtable显示数据以及上传数据显示到另一列中
需求是:用table展示数据,并在每一行的最后一列中给一个上传附件的按钮,可上传多个附件,上传之后在另一列去显示数据,可以删除附件因为有别的组件用到columns,他不需要上传附件功能,我就把columns的公共部分提出去了,在这个组件需要的时候在push到column中。columns如下:/***处理上传附件列*/handleColumn=()={ letcArr=this.state.columns;
继续访问
antd表单一个label下多个内容
1.如何实现一个label旁并排多项内容比如这样,在other的label下,既有输入文本域,又有toggle开关。同时两者有序地并排在右侧,同一行。方法是再用一层Form.Item分别包裹右侧内容,并且设置noStyle属性示例代码:Form.Itemlabel="DateofBirth:"Form.Itemname="birthDate"noStyle
继续访问
React针对antDesignselect组件进行二次封装
React针对antDesign库select组件进行二次封装由于业务需要对select进行样式上的修改,部分select还需要使用原样式。这种情况可以通过两种方式来实现:1通过className进行样式覆盖2通过二次封装组件,相对于仅修改css样式来说更加的灵活本次介绍第二种方式对组件进行二次封装/**@Date/5/8*@Authorzuolinya*@Descriptionantdselect组件二次封装*1设置为圆角*/import
继续访问
React基于AntdDesign的RadioGroup按钮组控件封装
开源Vue后端UI开箱即用解决方案——vuestic-admin这是一个Vue的后端开箱即用UI项目框架,和之前的ReactAdmin类似,它是一个框架,也就意味着它帮你完成了很多公用的部分,你只需要在其基础上进行自己的项目扩展即可。大体上这是由Vue和bootstrap4构建的,其中还集成了很多其他的东西。
如果你使用yarn你也可以使用它安装
在安装好vuestic后,你就可以使用它进行项目创建了
创建好后大致看一下目录结构(可能不清晰,你可以直接搭建体验)
成功后打开localhost:
如果你想详细的学习,你可以查看官网文档
浏览器兼容性,很遗憾只支持到IE+和主流的Chrome、FireFox、Safari、Edge等
目前有很多开箱即用的解决方案,还有一些仅仅是UI模板,每个解决方案都有各自的优势,我们尽可能的减少不必要的开发时间浪费,在通用的基础上在扩展,最主要的还是学习看源码,希望对大家有所帮助,谢谢!
react中实现登录鉴权vue中会使用导航守卫判断token,
react中使用redirect方式
使用高阶组件定义PrivateRoute导出
在router中用
PrivateRoute?path="/admin/roles"?exact?component={ Roles}/PrivateRoute的方式判断需要token的路由
import?React,?{ ?Component?}?from?'react'
import?{ Redirect,?Route}?from?'react-router-dom'
function?PrivateRoute({ ?component:Children,?...rest?})?{
return?(
Route{ ...rest}
render={ ()?={
let?token?=?localStorage.getItem('token')if(token){
return?Children/
}else{
returnRedirect
to={
'/login'
}/
}
}
}
/);
}export?default?PrivateRouteumi3源码解析之核心Service类初始化
前言
umi是一个插件化的企业级前端应用框架,在开发中后台项目中应用颇广,确实带来了许多便利。借着这个契机,便有了我们接下来的“umi3源码解析”系列的分享,初衷很简单就是从源码层面上帮助大家深入认知umi这个框架,能够更得心应手的使用它,学习源码中的设计思想提升自身。该系列的大纲如下:
开辟鸿蒙,今天要解析的就是第一part,内容包括以下两个部分:
邂逅umi命令,看看umidev时都做了什么?
初遇插件化,了解源码中核心的Service类初始化的过程。
本次使用源码版本为?3.5.,地址放在这里了,接下来的每一块代码笔者都贴心的为大家注释了在源码中的位置,先clone再食用更香哟!
邂逅umi命令该部分在源码中的路径为:packages/umi
首先是第一部分umi命令,umi脚手架为我们提供了umi这个命令,当我们创建完一个umi项目并安装完相关依赖之后,通过yarnstart启动该项目时,执行的命令就是umidev
那么在umi命令运行期间都发生了什么呢,先让我们来看一下完整的流程,如下图:
接下来我们对其几个重点的步骤进行解析,首先就是对于我们在命令行输入的umi命令进行处理。
处理命令行参数//packages/umi/src/cli.tsconstargs=yParser(process.argv.slice(2),{ alias:{ version:['v'],help:['h'],},boolean:['version'],});if(args.version&&!args._[0]){ args._[0]='version';constlocal=existsSync(join(__dirname,'../.local'))?chalk.cyan('@local'):'';console.log(`umi@${ require('../package.json').version}${ local}`);}elseif(!args._[0]){ args._[0]='help';}解析命令行参数所使用的yParser方法是基于yargs-parser封装,该方法的两个入参分别是进程的可执行文件的绝对路径和正在执行的JS文件的路径。解析结果如下:
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}在解析命令行参数后,对version和help参数进行了特殊处理:
如果args中有version字段,并且args._中没有值,将执行version命令,并从package.json中获得version的值并打印
如果没有version字段,args._中也没有值,将执行help命令
总的来说就是,如果只输入umi实际会执行umihelp展示umi命令的使用指南,如果输入umi--version会输出依赖的版本,如果执行umidev那就是接下来的步骤了。
提问:您知道输入umi--versiondev会发什么吗?
运行umidev
//packages/umi/src/cli.tsconstchild=fork({ scriptPath:require.resolve('./forkedDev'),});process.on('SIGINT',()=>{ child.kill('SIGINT');process.exit(0);});//packages/umi/src/utils/fork.tsif(CURRENT_PORT){ process.env.PORT=CURRENT_PORT;}constchild=fork(scriptPath,process.argv.slice(2),{ execArgv});child.on('message',(data:any)=>{ consttype=(data&&data.type)||null;if(type==='RESTART'){ child.kill();start({ scriptPath});}elseif(type==='UPDATE_PORT'){ //setcurrentusedportCURRENT_PORT=data.portasnumber;}process.send?.(data);});本地开发时,大部分脚手架都会采用开启一个新的线程来启动项目,umi脚手架也是如此。这里的fork方法是基于node中child_process.fork()方法的封装,主要做了以下三件事:
确定端口号,使用命令行指定的端口号或默认的,如果该端口号已被占用则prot+=1
开启子进程,该子进程独立于父进程,两者之间建立IPC通信通道进行消息传递
处理通信,主要监听了RESTART重启和UPDATE_PORT更新端口号事件
接下来看一下在子进程中运行的forkedDev.ts都做了什么。
//packages/umi/src/forkedDev.ts(async()=>{ try{ //1、设置NODE_ENV为developmentprocess.env.NODE_ENV='development';//2、InitwebpackversiondeterminationandrequirehookinitWebpack();//3、实例化Service类,执行run方法constservice=newService({ cwd:getCwd(),//umi项目的根路径pkg:getPkg(process.cwd()),//项目的package.json文件的路径});awaitservice.run({ name:'dev',args,});//4、父子进程通信letclosed=false;process.once('SIGINT',()=>onSignal('SIGINT'));process.once('SIGQUIT',()=>onSignal('SIGQUIT'));process.once('SIGTERM',()=>onSignal('SIGTERM'));functiononSignal(signal:string){ if(closed)return;closed=true;//退出时触发插件中的onExit事件service.applyPlugins({ key:'onExit',type:service.ApplyPluginsType.event,args:{ signal,},});process.exit(0);}}catch(e:any){ process.exit(1);}})();设置process.env.NODE_ENV的值
initWebpack(接下来解析)
实例化Service并run(第二part的内容)
处理父子进程通信,当父进程监听到SIGINT、SIGTERM等终止进程的信号,也通知到子进程进行终止;子进程退出时触发插件中的onExit事件
initWebpack
//packages/umi/src/initWebpack.tsconsthaveWebpack5=(configContent.includes('webpack5:')&&!configContent.includes('//webpack5:')&&!configContent.includes('//webpack5:'))||(configContent.includes('mfsu:')&&!configContent.includes('//mfsu:')&&!configContent.includes('//mfsu:'));if(haveWebpack5||process.env.USE_WEBPACK_5){ process.env.USE_WEBPACK_5='1';init(true);}else{ init();}initRequreHook();这一步功能是检查用户配置确定初始化webpack的版本。读取默认配置文件.umirc和config/config中的配置,如果其中有webpack5或?mfsu等相关配置,umi就会使用webpack5进行初始化,否则就使用webpack4进行初始化。这里的mfsu是webpack5的模块联邦相关配置,umi在3.5版本时已经进行了支持。
初遇插件化该部分在源码中的路径为:packages/core/src/Service
说起umi框架,最先让人想到的就是插件化,这也是框架的核心,该部分实现的核心源码就是Service类,接下来我们就来看看Service类的实例化和init()的过程中发生了什么,可以称之为插件化实现的开端,该部分的大致流程如下
该流程图中前四步,都是在Service类实例化的过程中完成的,接下来让我们走进Service类。
Service类的实例化//packages/core/src/Service/Service.tsexportdefaultclassServiceextendsEventEmitter{ constructor(opts:IServiceOpts){ super();this.cwd=opts.cwd||process.cwd();//当前工作目录//repoDirshouldbetherootdirofrepothis.pkg=opts.pkg||this.resolvePackage();//package.jsonthis.env=opts.env||process.env.NODE_ENV;//环境变量//在解析config之前注册babelthis.babelRegister=newBabelRegister();//通过dotenv将环境变量中的变量从.env或.env.local文件加载到process.env中this.loadEnv();//1、getuserconfigconstconfigFiles=opts.configFiles;this.configInstance=newConfig({ cwd:this.cwd,service:this,localConfig:this.env==='development',configFiles});this.userConfig=this.configInstance.getUserConfig();//2、getpathsthis.paths=getPaths({ cwd:this.cwd,config:this.userConfig!,env:this.env,});//3、getpresetsandpluginsthis.initialPresets=resolvePresets({ ...baseOpts,presets:opts.presets||[],userConfigPresets:this.userConfig.presets||[],});this.initialPlugins=resolvePlugins({ ...baseOpts,plugins:opts.plugins||[],userConfigPlugins:this.userConfig.plugins||[],});}}Service类继承自EventEmitter用于实现自定义事件。在Service类实例化的过程中除了初始化成员变量外主要做了以下三件事:
1、解析配置文件
//packages/core/src/Config/Config.tsconstDEFAULT_CONFIG_FILES=[//默认配置文件'.umirc.ts','.umirc.js','config/config.ts','config/config.js',];//...if(Array.isArray(opts.configFiles)){ //配置的优先读取this.configFiles=lodash.uniq(opts.configFiles.concat(this.configFiles));}//...getUserConfig(){ //1、找到configFiles中的第一个文件constconfigFile=this.getConfigFile();this.configFile=configFile;//潜在问题:.local和.env的配置必须有configFile才有效if(configFile){ letenvConfigFile;if(process.env.UMI_ENV){ //1.根据UMI_ENV添加后缀eg:.umirc.ts-->.umirc.cloud.tsconstenvConfigFileName=this.addAffix(configFile,process.env.UMI_ENV,);//2.去掉后缀eg:.umirc.cloud.ts-->.umirc.cloudconstfileNameWithoutExt=envConfigFileName.replace(extname(envConfigFileName),'',);//3.找到该环境下对应的配置文件eg:.umirc.cloud.[ts|tsx|js|jsx]envConfigFile=getFile({ base:this.cwd,fileNameWithoutExt,type:'javascript',})?.filename;}constfiles=[configFile,//eg:.umirc.tsenvConfigFile,//eg:.umirc.cloud.tsthis.localConfig&&this.addAffix(configFile,'local'),//eg:.umirc.local.ts].filter((f):fisstring=>!!f).map((f)=>join(this.cwd,f))//转为绝对路径.filter((f)=>existsSync(f));//clearrequirecacheandsetbabelregisterconstrequireDeps=files.reduce((memo:string[],file)=>{ memo=memo.concat(parseRequireDeps(file));//递归解析依赖returnmemo;},[]);//删除对象中的键值require.cache[cachePath],下一次require将重新加载模块requireDeps.forEach(cleanRequireCache);this.service.babelRegister.setOnlyMap({ key:'config',value:requireDeps,});//requireconfigandmergereturnthis.mergeConfig(...this.requireConfigs(files));}else{ return{ };}}细品源码,可以看出umi读取配置文件的优先级:自定义配置文件?>.umirc>config/config,后续根据UMI_ENV尝试获取对应的配置文件,development模式下还会使用local配置,不同环境下的配置文件也是有优先级的
例如:.umirc.local.ts>.umirc.cloud.ts>.umirc.ts
由于配置文件中可能require其他配置,这里通过parseRequireDeps方法进行递归处理。在解析出所有的配置文件后,会通过cleanRequireCache方法清除requeire缓存,这样可以保证在接下来合并配置时的引入是实时的。
2、获取相关绝对路径
//packages/core/src/Service/getPaths.tsexportdefaultfunctiongetServicePaths({ cwd,config,env,}:{ cwd:string;config:any;env?:string;}):IServicePaths{ letabsSrcPath=cwd;if(isDirectoryAndExist(join(cwd,'src'))){ absSrcPath=join(cwd,'src');}constabsPagesPath=config.singular?join(absSrcPath,'page'):join(absSrcPath,'pages');consttmpDir=['.umi',env!=='development'&&env].filter(Boolean).join('-');returnnormalizeWithWinPath({ cwd,absNodeModulesPath:join(cwd,'node_modules'),absOutputPath:join(cwd,config.outputPath||'./dist'),absSrcPath,//srcabsPagesPath,//pagesabsTmpPath:join(absSrcPath,tmpDir),});}这一步主要获取项目目录结构中node_modules、dist、src、pages等文件夹的绝对路径。如果用户在配置文件中配置了singular为true,那么页面文件夹路径就是src/page,默认是src/pages
3、收集preset和plugin以对象形式描述
在umi中“万物皆插件”,preset是对于插件的描述,可以理解为“插件集”,是为了方便对插件的管理。例如:@umijs/preset-react就是一个针对react应用的插件集,其中包括了plugin-access权限管理、plugin-antdantdUI组件等。
//packages/core/src/Service/Service.tsthis.initialPresets=resolvePresets({ ...baseOpts,presets:opts.presets||[],userConfigPresets:this.userConfig.presets||[],});this.initialPlugins=resolvePlugins({ ...baseOpts,plugins:opts.plugins||[],userConfigPlugins:this.userConfig.plugins||[],});在收集preset和plugin时,首先调用了resolvePresets方法,其中做了以下处理:
3.1、调用getPluginsOrPresets方法,进一步收集preset和plugin并合并
//packages/core/src/Service/utils/pluginUtils.tsgetPluginsOrPresets(type:PluginType,opts:IOpts):string[]{ constupperCaseType=type.toUpperCase();return[//opts...((opts[type===PluginType.preset?'presets':'plugins']asany)||[]),//env...(process.env[`UMI_${ upperCaseType}S`]||'').split(',').filter(Boolean),//dependencies...Object.keys(opts.pkg.devDependencies||{ }).concat(Object.keys(opts.pkg.dependencies||{ })).filter(isPluginOrPreset.bind(null,type)),//userconfig...((opts[type===PluginType.preset?'userConfigPresets':'userConfigPlugins']asany)||[]),].map((path)=>{ returnresolve.sync(path,{ basedir:opts.cwd,extensions:['.js','.ts'],});});}这里可以看出收集preset和plugin的来源主要有四个:
实例化Service时的入参
process.env中指定的UMI_PRESETS或UMI_PLUGINS
package.json中dependencies和devDependencies配置的,需要命名规则符合?/^(@umijs\/|umi-)preset-/这个正则
解析配置文件中的,即入参中的userConfigPresets或userConfigPresets
3.2、调用pathToObj方法:将收集的plugin或preset以对象的形式输出
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}0umi官网中提到过:每个插件都会对应一个id和一个key,id是路径的简写,key是进一步简化后用于配置的唯一值。便是在这一步进行的处理
形式如下:
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}1思考:为什么要将插件以对象的形式进行描述?有什么好处?
执行run方法,初始化插件在Service类实例化完毕后,会立马调用run方法,run()执行的第一步就是执行init方法,init()方法的功能就是完成插件的初始化,主要操作如下:
遍历initialPresets并init
合并initpresets过程中得到的plugin和initialPlugins
遍历合并后的plugins并init
这里的initialPresets和initialPlugins就是上一步收集preset和plugin得到的结果,在这一步要对其逐一的init,接下来我们看一下init的过程中做了什么。
Initplugin
//输入umidev经yargs-parser解析后为://args={ //_:["dev"],//}2这段代码主要做了以下几件事情:
getPluginAPI方法:newPluginAPI时传入了Service实例,通过pluginAPI实例中的registerMethod方法将register方法添加到Service实例的pluginMethods中,后续返回pluginAPI的代理,以动态获取最新的register方法,以实现边注册边使用。
//输入umidev经yargs-parser解析后为:/图灵访谈 | Vue.js官方团队成员霍春阳:跨专业做程序员,是什么感受?
霍春阳,Vue.js官方团队成员,专注Web研发领域,Vue.js3核心贡献者之一,Vuese文档生成工具作者,技术社区活跃者,撰写大量好评技术博客。
大学一年级,霍春阳就读于材料成型及控制工程专业,开始与同学创业并获得万元投资。大三时,他毅然决定退学,独自前往北京,开启职业程序员生涯。
现为Vue.js官方团队成员,出版畅销书《Vue.js设计与实现》。他为何跨专业做程序员?为何决定大学退学?又是如何加入Vue.js官方团队?本期图灵访谈带你了解他的传奇经历。
非科班程序员,机械专业因创业爱上编程
非计算机专业出身的霍春阳,接触编程源于大一计算机社团招新。对创业的兴趣促使他加入计算机创业协会,随后与会长成为好朋友,共同开发大学生交友软件聘爱网。从PHP语言学习到创业项目上线,他开始喜欢编程。创业期间,他与团队成员一起创业,后因项目发展不景气,他选择退学并前往北京工作。
考虑家庭条件,他决定退学并选择北京作为工作地点。创业失败后,他开始写PHP做后端,后来转向前端开发。他认为程序员求职时,学历和专业背景并非决定性因素。
退学原因包括创业休学、希望尽早帮助家庭减轻负担和专业与计算机无关。尽管如此,他认为学历仍然重要,应努力提高自身技能。
给程序员的学习建议
学习计算机基础知识,为未来成长奠定基础。英语水平对程序员来说非常重要,直接关系到成长和发展。阅读源码以了解他人解决问题的思路和代码风格,通过实践提升技能。
与Vue.js结缘
在第二家公司工作时,团队选择了Vue.js,霍春阳也因此接触Vue.js。他认为Vue.js易于学习,适合新手。他加入Vue.js官方团队是因为对Vue的贡献,这种被认可的感觉让他非常开心。
写书,为Vue.js做贡献
写作《Vue.js设计与实现》是一场考验,需要大量时间和精力。为了讲好故事,他需要对Vue.js源码有深入了解。写作过程中,他反复修改案例以确保内容准确无误。通过这本书,他建立了自信。
如何使用《Vue.js设计与实现》
跟随书中的代码实践,亲自动手编写,有助于更好地理解内容。关注书中勘误链接,解决影响理解的代码错误。
购买并阅读《Vue.js设计与实现》,这本高分佳作获得豆瓣评分9.6分。感谢阅读,更多好书信息请参见图灵社区。欢迎关注@图灵教育,获取更多好书资讯。
javascriptå±äºä»ä¹è¯è¨ï¼
javascriptæ¯ä»ä¹ææ
JavaScriptæ¯Webå¼åé¢åä¸çä¸ç§åè½å¼ºå¤§çç¼ç¨è¯è¨ï¼ä¸»è¦ç¨äºå¼å交äºå¼çWeb页é¢ãå¨è®¡ç®æºãææºç设å¤ä¸æµè§çç½é¡µï¼å ¶å¤§å¤æ°ç交äºé»è¾å ä¹é½æ¯ç±JavaScriptå®ç°çã对äºå¶ä½ä¸ä¸ªç½é¡µèè¨ï¼HTMLãCSSåJavaScriptåå«ä»£è¡¨äºç»æãæ ·å¼åè¡ä¸ºï¼ç»ææ¯ç½é¡µç骨æ¶ï¼æ ·å¼æ¯ç½é¡µçå¤è§ï¼è¡ä¸ºæ¯ç½é¡µç交äºé»è¾ï¼æ¯è¾HTMLãCSSåJavaScriptï¼åéå®ç½æ¯æ¥æ´æ°ææ°è½¯ä»¶å¼ååºç¡ç¥è¯å 容ï¼å·©åºæ¥å¸¸å¦ä¹ ä¸çåºç¡æè½ãæ´æå è´¹ç软件å¼åè§é¢æç¨å¸®å©å¦åå¿«éå¦ä¹ ãåéæè²å°±æ线ä¸å è´¹ç软件å¼åå ¬å¼è¯¾ï¼ã
JavaScriptå åµäºHTMLç½é¡µä¸ï¼éè¿æµè§å¨å ç½®çJavaScriptå¼æè¿è¡è§£éæ§è¡ï¼æä¸ä¸ªåæ¬åªç¨æ¥æ¾ç¤ºç页é¢è½¬åææ¯æç¨æ·äº¤äºç页é¢ç¨åºãæµè§å¨æ¯è®¿é®äºèç½ä¸åç§ç½ç«æå¿ å¤çå·¥å ·ï¼JavaScript主è¦å°±æ¯è¿è¡å¨æµè§å¨ä¸çï¼ä»¥ä¸å举äºå ç§å¸¸è§çæµè§å¨åå ¶ç¹ç¹ãåéæè²éå¢ç®åå·²ä¸å½å å¤å®¶ä¼ä¸å»ºç«äººæè¾éåä½ï¼ä¸å¤æ大å¦å»ºç«å®è®å°±ä¸åä½ï¼æ¯å¹´ä¸ºå大ä¼ä¸è¾éä¸ä¸å移å¨å¼åå·¥ç¨å¸ï¼æ¯å¹´ææ°åä¸åå¦ååçäºåéæè²ç»ç»çææ¯ç 讨ä¼ãææ¯å¹è®è¯¾ãç½ç»å ¬å¼è¯¾åå è´¹æå¦è§é¢ã
jsæ¯çµèçä»ä¹è¯è¨ï¼
ç¼ç¨è¯è¨ã
jså³JavaScriptï¼å为å端åå端ç¼ç¨ã
å端js主è¦å¨æµè§å¨ä¸éè¿api对htmlåcsså¨æä¿®æ¹ï¼ä»èè¾¾å°å¨ä¸å·æ°é¡µé¢çæ åµä¸å®ç°å¨ç»ææææ°æ®å¨æç»å®çã
å端js主è¦æânode.jsâï¼ä¸å ¶ä»phpãjavaçå端è¯è¨ä¸æ ·ï¼å¯ä»¥æä½æ°æ®åºãæ件åå¾åå¤ççã
javascriptæ¯ä»ä¹è¯è¨Javascriptæ¦è¿°
1ãä»ä¹æ¯Javascript
Javascriptï¼ç®ç§°ä¸ºJS
æ¯ä¸ç§è¿è¡ä¸JS解éå¨/å¼æä¸ç解éåèæ¬è¯è¨
è¿è¡ç¯å¢ï¼
1ãç¬ç«å®è£ çJS解éå¨(NodeJS)
2ãåµå ¥å¨[æµè§å¨]å æ ¸ä¸JS解éå¨
解éåï¼
è¿è¡ä¹åæ¯ä¸éè¦ç¼è¯çï¼è¿è¡ä¹åä¸ä¼æ£æ¥é误ï¼ç´å°ç¢°å°é误为æ¢ã
ç¼è¯åï¼
对æºç è¿è¡ç¼è¯ï¼è¿è½æ£æ¥è¯æ³é误
JS使ç¨åºåï¼
PCæºï¼ææºï¼å¹³æ¿ï¼æºé¡¶ç
2ãJSåå±å²
1ãå¹´ï¼Nombas为èªå·±ç软件å¼åäºä¸æ¬¾èæ¬è¯è¨ScriptEaseï¼å¯ä»¥åµå ¥å¨ç½é¡µä¸çã
2ãå¹´ï¼Netscape(ç½æ¯)å¼åäºLiveScript,æ´å为Javascriptï¼ä¸Java没æä»»ä½å ³ç³»çã
3ãå¹´ï¼Microsoftï¼IE3.0åå¸äºJSå éç--JScript
4ãå¹´ï¼JS1.1çæ¬è¢«ä½ä¸ºèæ¡æ交ç»äºECMA(欧洲计ç®æºå¶é ååä¼)ï¼ECMAScript(ES)æ å
3ãJSç»æ
å®æ´çJSæ¯ç±ä¸é¨åç»æï¼
1ãæ ¸å¿(ECMAScript)
2ãæ档对象模å(DOM,DocumentObjectModel)
让JSæè½åä¸ç½é¡µè¿è¡å¯¹è¯
3ãæµè§å¨å¯¹è±¡æ¨¡å(BOM,BrowserObjectModel)
让JSæè½åä¸æµè§å¨è¿è¡å¯¹è¯
4ãJSç¹ç¹
1ãå¼åå·¥å ·ç®åï¼è®°äºæ¬å³å¯
2ãæ éç¼è¯ï¼ç´æ¥ç±JSå¼æè´è´£æ§è¡
3ã弱类åè¯è¨
ç±æ°æ®æ¥å³å®æ°æ®ç±»å
4ãé¢å对象
ç¨éï¼
1ã客æ·ç«¯(æµè§å¨)
1ã客æ·ç«¯æ°æ®è®¡ç®
2ã表åæ§ä»¶è¾å ¥éªè¯
3ãæµè§å¨çäºä»¶è§¦ååå¤ç
4ãç½é¡µç¹æ
5ãæå¡å¨å¼æ¥æ°æ®æ交
2ãæå¡å¨ç«¯
1ãåå¸å¼è¿ç®
2ãå®æ¶æå¡å¨
3ãçªå£åºç¨
4ãç½ç»åºç¨
javascriptæ¯ä¸ç§ä»ä¹è¯è¨JavaScriptå°±æ¯è§£éæ§è¯è¨ã
JavaScriptæ¯Netscapeå ¬å¸ç产å,å ¶ç®çæ¯ä¸ºäºæ©å±NetscapeNavigatoråè½,èå¼åçä¸ç§å¯ä»¥åµå ¥Web页é¢ä¸çåºäºå¯¹è±¡åäºä»¶é©±å¨ç解éæ§è¯è¨ã
JavaScriptæ¯ä»ä¹è¯è¨ï¼ä½ 好ï¼JavaScriptæ¯ä¸ç§è½è®©ä½ çç½é¡µæ´å çå¨æ´»æ³¼çç¨å¼è¯è¨ï¼ä¹æ¯ç®åç½é¡µä¸è®¾è®¡ä¸æ容æå¦åææ¹ä¾¿çè¯è¨ãä½ å¯ä»¥å©ç¨JavaScriptè½»æçååºäº²åç欢è¿è®¯æ¯ãæ¼äº®çæ°åéãæ广åææçè·é©¬ç¯åç®æçé举ï¼è¿å¯ä»¥æ¾ç¤ºæµè§å¨åççæ¶é´ã让è¿äºç¹æ®æææé«ç½é¡µçå¯è§æ§ã
javascriptï¼æ¯javaåï¼ä¸æ¯çï¼ä¸¤è æå¾å¤§çå·®å«ãè¯æ³æ¹é¢JavaScriptçåºæ¬è¯æ³å对象ä½ç³»ï¼æ¯æ¨¡ä»¿Javaè设计çãä½æ¯ï¼JavaScript没æéç¨Javaçéæç±»åãæ£æ¯å 为JavaScriptä¸Javaæå¾å¤§çç¸ä¼¼æ§ï¼æ以è¿é¨è¯è¨æä»ä¸å¼å§çLiveScriptæ¹å为JavaScriptãåºæ¬ä¸ï¼JavaScriptè¿ä¸ªååçåææ¯âå¾åJavaçèæ¬è¯è¨âãåéå®ç½æ¯æ¥æ´æ°ææ°è½¯ä»¶å¼ååºç¡ç¥è¯å 容ï¼å·©åºæ¥å¸¸å¦ä¹ ä¸çåºç¡æè½ãæ´æå è´¹ç软件å¼åè§é¢æç¨å¸®å©å¦åå¿«éå¦ä¹ ãåéæè²å°±æ线ä¸å è´¹ç软件å¼åå ¬å¼è¯¾ï¼ã
å¨JavaScriptè¯è¨ä¸ï¼å½æ°æ¯ä¸ç§ç¬ç«çæ°æ®ç±»åï¼ä»¥åéç¨åºäºåå对象ï¼prototypeï¼ç继æ¿é¾ãè¿æ¯å®ä¸Javaè¯æ³æ大ç两ç¹åºå«ãJavaScriptè¯æ³è¦æ¯Javaèªç±å¾å¤ãæ°æ®ç»æé¨åååé´Javaè¯è¨ï¼å æ¬å°å¼åæåå§å¼å对象两大类ãå¦å¤ï¼Javaè¯è¨éè¦ç¼è¯ï¼èJavaScriptè¯è¨åæ¯è¿è¡æ¶ç±è§£éå¨ç´æ¥æ§è¡ãæ»ä¹ï¼JavaScriptçåå§è®¾è®¡ç®æ æ¯ä¸ç§å°åçãç®åçå¨æè¯è¨ï¼ä¸Javaæ足å¤çç¸ä¼¼æ§ï¼ä½¿å¾ä½¿ç¨è ï¼å°¤å ¶æ¯Javaç¨åºåï¼å¯ä»¥å¿«éä¸æåéæè²éå¢ç®åå·²ä¸å½å å¤å®¶ä¼ä¸å»ºç«äººæè¾éåä½ï¼ä¸å¤æ大å¦å»ºç«å®è®å°±ä¸åä½ï¼æ¯å¹´ä¸ºå大ä¼ä¸è¾éä¸ä¸å移å¨å¼åå·¥ç¨å¸ï¼æ¯å¹´ææ°åä¸åå¦ååçäºåéæè²ç»ç»çææ¯ç 讨ä¼ãææ¯å¹è®è¯¾ãç½ç»å ¬å¼è¯¾åå è´¹æå¦è§é¢ã