AngularJS学习笔记--002--Angular JS路由插件ui.router源码解析
2023-09-11 14:17:33 时间
首先,无论是使用哪种路由,作为框架额外的附加功能,它们都将以模块依赖的形式被引入,简而言之就是:在引入路由源文件之后,你的代码应该这样写(以ui.router为例):
这样一看,其实ui.router和ngRoute大体的设计思路,对应的模块划分都是一致的(毕竟是同一个团队开发),不同的地方在于功能点的实现和增强。 那么问题来了:ngRoute弱在哪些方面,ui.router怎么弥补了这些方面? 这里,列举两个最重要的方面来说(其他细节,后面再说):
比如:页面一个区块用来显示页面状态,另一个区块用来显示页面主内容,当路由切换时,页面状态跟着变化,对应的页面主内容也跟着变化。 首先,我们尝试着用ngRoute来做: html
这次,结果是我们想要的,两个区块,分别显示了不同的内容,原因在于,在ui.router中:
比如:页面一个主区块显示主内容,主内容中的部分内容要求根据路由变化而变化,这时就需要另一个动态变化的区块嵌套在主区块中。 其实,嵌套视图,在html中的最终表现就像这样:
对于前端MVC(VM)而言,就是将hash值(#xxx)与一系列的路由规则进行查找匹配,匹配出一个符合条件的规则,然后根据这个规则,进行数据的获取,以及页面的渲染。 所以,接下来:
上面,我们通过调用$stateProvider.state(...)方法,创建了一个简单路由规则,通过参数,可以容易理解到:
意思就是说:当我们访问http://xxxx#/abc的时候,这个路由规则被匹配到,对应的模板会被填到某个div[ui-view]中。 看上去似乎很简单,那是因为我们还没有深究具体的一些路由配置参数(我们后面再说)。 这里需要深入的是:$stateProvider.state(...)方法,它做了些什么工作?
然后,调用$urlRouterProvider.when(...)方法,进行路由的注册(之前是路由的创建),代码里是这样写的:
当程序匹配到某一个子路由时,我们就认为这个子路由state被激活,同时,它对应的父路由state也将被激活。 我们还可以手动的激活某一个state,就像上面写的那样,$state.transitionTo(state, ...);,这样的话,它的父state会被激活(如果还没有激活的话),它的子state会被销毁(如果已经激活的话)。
它创建了一个rule,并存储在rules集合里面,之后的,每次hash值变化,路由重新查找匹配都是通过遍历这个rules集合进行的。
angular 在刚开始的digest时,‘rootScope会触发locationChangeSuccess‘事件(angular在每次浏览器hashchange的时候也会触发‘locationChangeSuccess`事件) ui.router 监听了$locationChangeSuccess事件,于是开始通过遍历一系列rules,进行路由查找匹配 当匹配到路由后,就通过$state.transitionTo(state,...),跳转激活对应的state 最后,完成数据请求和模板的渲染 可以从下面这段源代码看到,看到查找匹配的起始和过程:
根据以上两点,于是ui.router提供了另一个指令叫做:ui-sref指令,来解决这个问题,比如这样:
不过,在这之前,需要一个demo,ui.router的官网demo无非就是最好的学习例子,里面涉及了大部分的知识点,所以接下来的代码讲解大部分都会是这里面的(建议下载到本地进行代码学习)。 为了更好的学习这个demo,我画了一张图来描述这个demo的contacts部分各个视图模块,如下:
之前就说到,在ui.router中,路由就有父与子的关系(多个父与子凑起来就有了,祖先和子孙的关系),从javascript的角度来说,其实就是路由对应的state对象之间存在着某种引用的关系。 用一张数据结构的表示下contacts部分,大概是这样(原图):
上面的图看着有点乱,不过没关系,起码能看出各个state对象之间通过parent字段维系了这样一个父与子的关系(粉红色的线)。
ok,接下来就看下是如何定义路由的父子关系的?
假设有一个父路由,如下:
当路由成功跳转到指定的state时,ui.router会触发$stateChangeSuccess事件通知所有的ui-view进行模板重新渲染。 代码是这样的:
上述我们介绍了设置单视图和多视图模板的方式,其实最终它们在ui.router内部都会被统一格式化成的views的形式,且它们的key值会做特殊变化: 上述的单视图会变成这样:
指的是ui-view="status"中的status 也可以是(空字符串),因为会有匿名的ui-view或者ui-view=""
默认情况下是父路由的state.name,因为子路由模板一般都安插在父路由的ui-view中 也可以是(空字符串),表示最顶层rootState 还可以是任意的祖先state.name
这样原型的意思是,表示该模板将会被安插在名为stateName路由对应模板的viewName视图下(可以看看上面代码中的注释理解下)。 其实这也解释了之前我说的:“为什么state.name里面不能存在@符号”?因为@在这里被用于特殊含义了。 所以,到这里,我们就知道在ui-view重新进行模板渲染时,是根据viewName + @ + stateName来获取对应的视图模板内容(其实还有controller等)的。 其实,由于路由有了父与子的关系,某种程度上就有了override(覆盖或者重写)可能。 父路由和子路由之间就存在着视图的override,像下面这段代码:
有了模板之后,必然不可缺少controller向模板对应的作用域(scope)中填写数据,这样才可以渲染出动态数据。 我们可以为每一个视图添加不同的controller,就像下面这样:
resolve在state配置参数中,是一个对象(key-value),每一个value都是一个可以依赖注入的函数,并且返回的是一个promise(当然也可以是值,resloved defer)。 我们通常会在resolve中,进行数据获取的操作,然后返回一个promise,就像这样:
简化了controller的操作,将数据的获取放在resolve中进行,这在多个视图多个controller需要相同数据时,有一定的作用。 只有当reslove中的promise全部resolved(即数据获取成功)后,才会触发$stateChangeSuccess切换路由,进而实例化controller,然后更新模板。
另外,子路由的resolve或者controller都是可以依赖注入父路由的resolve提供的数据服务,就像这样:
路由可以有单独的state.resolve之外,还可以在views视图中单独配置resolve,视图resolve是可以依赖注入自身state.resolve甚至是父路由的state.resolve
angular.module("myApp", ["ui.router"]); // myApp为自定义模块,依赖第三方路由模块ui.router这样做的目的是:在程序启动(bootstrap)的时候,加载依赖模块(如:ui.router),将所有挂载在该模块的服务(provider),指令(directive),过滤器(filter)等都进行注册,那么在后面的程序中便可以调用了。 说到这里,就得看看ngRoute模块和ui.router模块各自都提供了哪些服务,哪些指令?
这样一看,其实ui.router和ngRoute大体的设计思路,对应的模块划分都是一致的(毕竟是同一个团队开发),不同的地方在于功能点的实现和增强。 那么问题来了:ngRoute弱在哪些方面,ui.router怎么弥补了这些方面? 这里,列举两个最重要的方面来说(其他细节,后面再说):
比如:页面一个区块用来显示页面状态,另一个区块用来显示页面主内容,当路由切换时,页面状态跟着变化,对应的页面主内容也跟着变化。 首先,我们尝试着用ngRoute来做: html
div ng-view 区块1 /div div ng-view 区块2 /divjs
$routeProvider .when(/, { template: hello world });我们在html中利用ng-view指令定义了两个区块,于是两个div中显示了相同的内容,这很合乎情理,但却不是我们想要的,但是又不能为力,因为,在ngRoute中:
这次,结果是我们想要的,两个区块,分别显示了不同的内容,原因在于,在ui.router中:
比如:页面一个主区块显示主内容,主内容中的部分内容要求根据路由变化而变化,这时就需要另一个动态变化的区块嵌套在主区块中。 其实,嵌套视图,在html中的最终表现就像这样:
div ng-view I am parent div ng-view I am child /div /div转成JavaScript,我们会在程序里这样写:
$routeProvider .when(/, { template: I am parent div ng-view I am child /div });倘若,你真的用ngRoute这样写,你会发现浏览器崩溃了,因为在ng-view指令link的过程中,代码会无限递归下去。 那么造成这种现象的最根本原因:路由没有明确的父子层级关系! 看看ui.router是如何解决这一问题的?
$stateProvider .state(parent, { abstract: true, url: /, template: I am parent div ui-view /div .state(parent.child, { url: , template: I am child });
对于前端MVC(VM)而言,就是将hash值(#xxx)与一系列的路由规则进行查找匹配,匹配出一个符合条件的规则,然后根据这个规则,进行数据的获取,以及页面的渲染。 所以,接下来:
上面,我们通过调用$stateProvider.state(...)方法,创建了一个简单路由规则,通过参数,可以容易理解到:
意思就是说:当我们访问http://xxxx#/abc的时候,这个路由规则被匹配到,对应的模板会被填到某个div[ui-view]中。 看上去似乎很简单,那是因为我们还没有深究具体的一些路由配置参数(我们后面再说)。 这里需要深入的是:$stateProvider.state(...)方法,它做了些什么工作?
然后,调用$urlRouterProvider.when(...)方法,进行路由的注册(之前是路由的创建),代码里是这样写的:
$urlRouterProvider.when(state.url, [$match, $stateParams, function ($match, $stateParams) { // 判断是否是同一个state || 当前匹配参数是否相同 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) { $state.transitionTo(state, $match, { inherit: true, location: false }); }]);上述代码的意思是:当hash值与state.url相匹配时,就执行后面那段回调,回调函数里面进行了两个条件判断之后,决定是否需要跳转到该state? 这里就插入了一个话题:为什么说 “跳转到该state,而不是该url”? 其实这个问题跟大家一直说的:“ui.router是基于state(状态)的,而不是url”是同一个问题。 我的理解是这样的:之前就说过,路由存在着明确的父子关系,每一个路由可以理解为一个state,
当程序匹配到某一个子路由时,我们就认为这个子路由state被激活,同时,它对应的父路由state也将被激活。 我们还可以手动的激活某一个state,就像上面写的那样,$state.transitionTo(state, ...);,这样的话,它的父state会被激活(如果还没有激活的话),它的子state会被销毁(如果已经激活的话)。
它创建了一个rule,并存储在rules集合里面,之后的,每次hash值变化,路由重新查找匹配都是通过遍历这个rules集合进行的。
angular 在刚开始的digest时,‘rootScope会触发locationChangeSuccess‘事件(angular在每次浏览器hashchange的时候也会触发‘locationChangeSuccess`事件) ui.router 监听了$locationChangeSuccess事件,于是开始通过遍历一系列rules,进行路由查找匹配 当匹配到路由后,就通过$state.transitionTo(state,...),跳转激活对应的state 最后,完成数据请求和模板的渲染 可以从下面这段源代码看到,看到查找匹配的起始和过程:
function update(evt) { // ...省略 function check(rule) { var handled = rule($injector, $location); // handled可以是返回: // 1. 新的的url,用于重定向 // 2. false,不匹配 // 3. true,匹配 if (!handled) return false; if (isString(handled)) $location.replace().url(handled); return true; var n = rules.length, i; // 渲染遍历rules,匹配到路由,就停止循环 for (i = 0; i i++) { if (check(rules[i])) return; // 如果都匹配不到路由,使用otherwise路由(如果设置了的话) if (otherwise) check(otherwise); function listen() { // 监听$locationChangeSuccess,开始路由的查找匹配 listener = listener || $rootScope.$on($locationChangeSuccess, update); return listener; if (!interceptDeferred) listen();那么,问题来了:难道每次路由变化(hash变化),由于监听了’$locationChangeSuccess事件,都要进行rules的遍历来查找匹配路由,然后跳转到对应的state吗? 答案是:肯定的,一般的路由器都是这么做的,包括ngRoute。 那么ui.router对于这样的问题,会怎么进行优化呢? 回归到问题:我们之所以要循环遍历rules,是因为要查找匹配到对应的路由(state),然后跳转过去,倘若不循环,能直接找到对应的state吗? 答案是:可以的。 还记得前面说过,在用ui.router在创建路由时:
根据以上两点,于是ui.router提供了另一个指令叫做:ui-sref指令,来解决这个问题,比如这样:
a ui-sref="home" 通过ui-sref跳转到home state /a当点击这个a标签时,会直接跳转到home state,而并不需要循环遍历rules,ui.router是这样做到的(这里简单说一下): 首先,ui-sref="home"指令会给对应的dom添加click事件,然后根据state.name,直接跳转到对应的state,代码像这样:
element.bind("click", function(e) { // ..省略若干代码 var transition = $timeout(function() { // 手动跳转到指定的state $state.go(ref.state, params, options); });跳转到对应的state之后,ui.router会做一个善后处理,就是改变hash,所以理所当然,会触发’$locationChangeSuccess事件,然后执行回调,但是在回调中可以通过一个判断代码规避循环rules,像这样:
function update(evt) { var ignoreUpdate = lastPushedUrl $location.url() === lastPushedUrl; // 手动调用$state.go(...)时,直接return避免下面的循环 if (ignoreUpdate) return true; // 省略下面的循环ruls代码 }说了那么多,其实就是想说,我们不建议直接使用href="#/xxx"来改变hash,然后跳转到对应state(虽然也是可以的),因为这样做会多了一步rules循环遍历,浪费性能,就像下面这样:
a href="#/abc" 通过href跳转到home state /a
不过,在这之前,需要一个demo,ui.router的官网demo无非就是最好的学习例子,里面涉及了大部分的知识点,所以接下来的代码讲解大部分都会是这里面的(建议下载到本地进行代码学习)。 为了更好的学习这个demo,我画了一张图来描述这个demo的contacts部分各个视图模块,如下:
![视图模块](http://gtms02.alicdn.com/tps/i2/TB1rRR1HFXXXXcXXpXXH4A8PVXX-722-492.png)
之前就说到,在ui.router中,路由就有父与子的关系(多个父与子凑起来就有了,祖先和子孙的关系),从javascript的角度来说,其实就是路由对应的state对象之间存在着某种引用的关系。 用一张数据结构的表示下contacts部分,大概是这样(原图):
![state](http://gtms01.alicdn.com/tps/i1/TB1je9bHFXXXXXEXXXXysygGpXX-2481-1686.jpg)
$stateProvider .state(contacts, {});ui.router提供了几种方法来定义它的子路由: 1.点标记法(推荐)
$stateProvider .state(contacts.list, {});通过状态名简单明了地来确定父子路由关系,如:状态名为a.b.c的路由,对应的父路由就是状态名为a.b路由。 2.parent属性
$stateProvider .state({ name: list, // 状态名也可以直接在配置里指定 parent: contacts // 父路由的状态名 });或者:
$stateProvider .state({ name: list, // 状态名也可以直接在配置里指定 parent: { // parent也可以是一个父路由配置对象(指定路由的状态名即可) name: contacts });通过parent直接指定父路由,可以是父路由的状态名(字符串),也可以是一个包含状态名的父路由配置(对象)。 竟然路由有了父与子的关系,那么它们的注册顺序有要求嘛? 答案是:没有要求,我们可以在父路由存在之前,创建子路由(不过,不是很推荐),因为ui.router在遇到这种情况时,在内部会帮我们先缓存子路由的信息,等待它的父路由注册完毕后,再进行子路由的注册。
当路由成功跳转到指定的state时,ui.router会触发$stateChangeSuccess事件通知所有的ui-view进行模板重新渲染。 代码是这样的:
if (options.notify) { $rootScope.$broadcast($stateChangeSuccess, to.self, toParams, from.self, fromParams); }而ui-view指令在进行link的时候,在其内部就已经监听了这一事件(消息),来随时更新视图:
scope.$on($stateChangeSuccess, function() { updateView(false); });大体的模板渲染过程就是这样的,这里遇到一个问题,就是:每一个 div[ui-view]在重新渲染的时候如何获取到对应视图模板的呢? 要想知道这个答案, 首先,我们得先看一下模板如何设置? 一般在设置单视图的时候,我们会这样做:
$stateProvider .state(contacts, { abstract: true, url: /contacts, templateUrl: app/contacts/contacts.html });在配置对象里面,我们用templateUrl指定模板路径即可。 如果我们需要设置多视图,就需要用到views字段,像这样:
$stateProvider .state(contacts.detail, { url: /{contactId:[0-9]{1,4}}, views: { : { templateUrl: app/contacts/contacts.detail.html, hint@: { template: This is contacts.detail populating the "hint" ui-view menuTip: { templateProvider: [$stateParams, function($stateParams) { return hr small Contact ID: + $stateParams.contactId + /small ; });这里我们使用了另外两种方式设置模板:
上述我们介绍了设置单视图和多视图模板的方式,其实最终它们在ui.router内部都会被统一格式化成的views的形式,且它们的key值会做特殊变化: 上述的单视图会变成这样:
views: { // 模板内容会被安插在根路由模板(index.html)的匿名视图下 @: { abstract: true, url: /contacts, templateUrl: app/contacts/contacts.html }多视图会变成这样:
views: { // 模板内容会被安插在父路由(contacts)模板的匿名视图下 @contacts: { templateUrl: app/contacts/contacts.detail.html, // 模板内容会被安插在根路由(index.html)模板的名为hint视图下 hint@: { template: This is contacts.detail populating the "hint" ui-view // 模板内容会被安插在父路由(contacts)模板的名为menuTip视图下 menuTip@contacts: { templateProvider: [$stateParams, function($stateParams) { return hr small Contact ID: + $stateParams.contactId + /small ;我们会发现views对象里面的key变化了,最明显的是出现了一个@符号,其实这样的key值是ui.router的一个设计,它的原型是:viewName + @ + stateName,解释下:
指的是ui-view="status"中的status 也可以是(空字符串),因为会有匿名的ui-view或者ui-view=""
默认情况下是父路由的state.name,因为子路由模板一般都安插在父路由的ui-view中 也可以是(空字符串),表示最顶层rootState 还可以是任意的祖先state.name
这样原型的意思是,表示该模板将会被安插在名为stateName路由对应模板的viewName视图下(可以看看上面代码中的注释理解下)。 其实这也解释了之前我说的:“为什么state.name里面不能存在@符号”?因为@在这里被用于特殊含义了。 所以,到这里,我们就知道在ui-view重新进行模板渲染时,是根据viewName + @ + stateName来获取对应的视图模板内容(其实还有controller等)的。 其实,由于路由有了父与子的关系,某种程度上就有了override(覆盖或者重写)可能。 父路由和子路由之间就存在着视图的override,像下面这段代码:
$stateProvider .state(contacts.detail, { url: /{contactId:[0-9]{1,4}}, views: { hint@: { template: This is contacts.detail populating the "hint" ui-view $stateProvider .state(contacts.detail.item, { url: /item/:itemId, views: { hint@: { template: This is contacts.detail.item overriding the "hint" ui-view });上面两个路由(state)存在着父与子的关系,且他们都对@hint定义了视图,那么当子路由被激活时(它的父路由也会被激活),我们应该选择哪个视图配置呢? 答案是:子路由的配置。 具体的,ui.router是如何实现这样的视图override的呢? 简单地回答就是:通过javascript原型链实现的,你可以在每次路由切换成功后,尝试着打印出$state.current.locals这个变量一看究竟。 还有一个很重要的问题,关乎性能:当我们子路由变化时,页面中所有的ui-view都会重新进行渲染吗? 答案是:不会,只会从子路由对应的视图开始局部重新渲染。 在每次路由变化时,ui.router会记录变化的子路由,并对子路由进行重新的预处理(包括controller,reslove等),最后局部更新对应的ui-view,父路由部分是不会有任何变化的。
有了模板之后,必然不可缺少controller向模板对应的作用域(scope)中填写数据,这样才可以渲染出动态数据。 我们可以为每一个视图添加不同的controller,就像下面这样:
$stateProvider .state(contacts, { abstract: true, url: /contacts, templateUrl: app/contacts/contacts.html, resolve: { contacts: [contacts, function( contacts){ return contacts.all(); controller: [$scope, $state, contacts, utils, function ($scope, $state, contacts, utils) { // 向作用域写数据 $scope.contacts = contacts; });注意:controller是可以进行依赖注入的,它注入的对象有两种:
resolve在state配置参数中,是一个对象(key-value),每一个value都是一个可以依赖注入的函数,并且返回的是一个promise(当然也可以是值,resloved defer)。 我们通常会在resolve中,进行数据获取的操作,然后返回一个promise,就像这样:
resolve: { contacts: [contacts, function( contacts){ return contacts.all(); }上面有好多contacts,为了不混淆,我改一下代码:
resolve: { myResolve: [contacts, function(contacts){ return contacts.all(); }这样就看清了,我们定义了resolve,包含了一个myResolve的key,它对应的value是一个函数,依赖注入了一个服务contacts,调用了contacts.all()方法并返回了一个promise。 于是我们便可以在controller中引用myResolve,像这样:
controller: [$scope, $state, myResolve, utils, function ($scope, $state, contacts, utils) { // 向作用域写数据 $scope.contacts = contacts; }]这样做的目的:
简化了controller的操作,将数据的获取放在resolve中进行,这在多个视图多个controller需要相同数据时,有一定的作用。 只有当reslove中的promise全部resolved(即数据获取成功)后,才会触发$stateChangeSuccess切换路由,进而实例化controller,然后更新模板。
另外,子路由的resolve或者controller都是可以依赖注入父路由的resolve提供的数据服务,就像这样:
$stateProvider .state(parent, { url: , resolve: { parent: [$q, $timeout, function ($q, $timeout) { var defer = $q.defer(); $timeout(function () { defer.resolve(parent); }, 1000); return defer.promise; template: I am parent div ui-view /div .state(parent.child, { url: /child, resolve: { child: [parent, function (parent) { // 调用父路由的解决项 return parent + and child; controller: [child, parent, function (child, parent) { // 调用自身的解决项,以及父路由的解决项 console.log(child, parent); template: I am child });另外每一个视图也可以单独定义自己的resolve和controller,它们也是可以依赖注入自身的state.resolve,或者view下的resolve,或者父路由的reslove,就像这样: html
div ui-view /div div ui-view="status" /divjavascript:
$stateProvider .state(home, { url: /home, resolve: { common: [$q, $timeout, function ($q, $timeout) { // 公共的resolve var defer = $q.defer(); $timeout(function () { defer.resolve(common data); }, 1000); return defer.promise; views: { : { resolve: { special: [common, function (common) { // 访问state.resolve console.log(common); status: { resolve: { common: function () { // 重写state.resolve return override common data controller: [common, function (common) { // 访问视图自身的resolve console.log(common); });总结一下:
路由可以有单独的state.resolve之外,还可以在views视图中单独配置resolve,视图resolve是可以依赖注入自身state.resolve甚至是父路由的state.resolve
相关文章
- Html Table用JS导出excel格式问题 导出EXCEL后单元格里的000412341234会变成412341234 7-14 会变成 2018-7-14(7月14) 自定义格式 web利用table表格生成excel格式问题 js导出excel增加表头、mso-number-format定义数据格式 数字输出格式转换 mso-number-format:"@"
- html table表格导出excel的方法 html5 table导出Excel HTML用JS导出Excel的五种方法 html中table导出Excel 前端开发 将table内容导出到excel HTML table导出到Excel中的解决办法 js实现table导出Excel,保留table样式
- h5 录音 自动生成proto Js语句 UglifyJS-- 对你的js做了什么 【原码笔记】-- protobuf.js 与 Long.js 【微信开发】-- 发送模板消息 能编程与会编程 vue2入坑随记(二) -- 自定义动态组件 微信上传图片
- js_html_input中autocomplete="off"在chrom中失效的解决办法 使用JS模拟锚点跳转 js如何获取url参数 C#模拟httpwebrequest请求_向服务器模拟cookie发送 实习期学到的技术(一) LinqPad的变量比较功能 ASP.NET EF 使用LinqPad 快速学习Linq
- 【Js】前端使用xlsx.full.min.js读取和导出excel表格数据
- js,jq滚动监听,切换等常用JS代码
- JS监听不到被操作后dom的事件,js动态生成的DOM绑定事件失效,解决方案
- 在angular中利用分页插件进行分页
- 【Vue/js】Js中执行变量中的命令语句,也就是所谓的宏替换(很实用的例子)
- Vue - 实现用 JS 调用自定义组件 / 类似 ElementUI 弹框组件(在 js 文件中通过 this.xxx 方式调用并显示自定义弹框模态框组件)
- Angular从零到一1.2 环境配置要求
- Angular从零到一1.3 第一个小应用 Hello Angular
- Angular从零到一1.6 引导过程
- angular入门教程
- Angular学习之ControlValueAccessor接口详解
- (1)Angular的开发
- 【HarmonyOS】【JS】鸿蒙Js camera怎么拍照并使用image显示出来
- Angular为什么选择TypeScript?
- Angular require(抄别的)
- Angular 简单的Get
- angular实例教程(用来熟悉指令和过滤器的编写)
- angular-scope.assign
- js 保留两位小数,Js四舍五入,JavaScript Math四舍五入
- JS教程之使用 P5.js 构建一个贪吃蛇游戏(教程含源码)
- 前后端加密解密 【JS加密模块(md5 、 crypto 、 crypto-js、jsencrypt) python RSA加密解密(pycryptodome )模块安装与使用】
- js 给json添加新的字段,或者添加一组数据,在JS数组指定位置删除、插入、替换元素
- JS 将数字字符串数组转为 数字数组 (互换),js获取数组对象中 某一个key的值,js判断一个数组是否包含另一个数组(一维数组)
- 推荐4款高星星JS库:canvas库-Fabric.js、JavaScript客户端文件上传库-FilePond、客户端保存文件解决方案-FileSaver、JavaScript在线解压 ZIP 文件-JSZip
- 我从Angular 2转向Vue.js, 也没有选择React
- 【HarmonyOS】【JS】 鸿蒙js开发使用div自带的scroll,滑动条拉不到最下面?
- 来自 Thoughtram 的 Angular 2 系列资料
- JQuery/JS插件 linq.js 入门