详解Angular 4.x Injector


Posted in Javascript onMay 04, 2017

在介绍 Angular Injector (注入器) 之前,我们先要了解 Dependency Injection,即依赖注入的概念。

依赖注入允许程序设计遵从依赖倒置原则 (简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户端与实现模块间的耦合) 调用者只需知道服务的接口,具体服务的查找和创建由注入器 (Injector) 负责处理并提供给调用者,这样就分离了服务和调用者的依赖,符合低耦合的程序设计原则。

从上述的内容可知,依赖注入中包含三种角色:调用者、服务和注入器 (Injector)。现在我们开始介绍 Injector,在 Angular 中 Injector (注入器) 用来管理服务对象的创建和获取。接下来我们先来看一下 Injector 抽象类:

Injector 抽象类

// angular2\packages\core\src\di\injector.ts
export abstract class Injector {
 static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
 static NULL: Injector = new _NullInjector();

 /**
 * 用于根据给定的Token从注入器中获取相应的对象。
 * 如果没有找到相应的对象,将返回notFoundValue设置的值。若notFoundValue的值与
 * _THROW_IF_NOT_FOUND相等,则会抛出异常。
 */
 abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
}

const _THROW_IF_NOT_FOUND = new Object();

Injector 抽象类中定义了一个 get() 抽象方法,该方法用于根据给定的 Token 从注入器中获取相应的对象,每个Injector 抽象类的子类都必须实现该方法。在 Angular 中常见的 Injector 抽象类子类有:

  1. _NullInjector
  2. ReflectiveInjector

下面我们来依次介绍它们:

_NullInjector 类

_NullInjector 类的实例用于表示空的注入器。

// angular2\packages\core\src\di\injector.ts
class _NullInjector implements Injector {
 get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
 if (notFoundValue === _THROW_IF_NOT_FOUND) {
  throw new Error(`No provider for ${stringify(token)}!`);
 }
 return notFoundValue;
 }
}

ReflectiveInjector 抽象类

ReflectiveInjector 表示一个依赖注入容器,用于实例化对象和解析依赖。

ReflectiveInjector 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
var car = injector.get(Car);
expect(car instanceof Car).toBe(true);
expect(car.engine instanceof Engine).toBe(true);

上面示例中,我们通过调用 ReflectiveInjector 抽象类的 resolveAndCreate() 方法,创建注入器。然后通过调用注入器的 get() 方法,获取 Token 对应的对象。该抽象类除了 resolveAndCreate() 静态方法外,还含有以下静态方法:

  1. resolve() - 解析 Provider 列表为 ResolvedReflectiveProvider 列表
  2. fromResolvedProviders() - 基于 ResolvedReflectiveProvider 列表创建 ReflectiveInjector 对象

接下来我们来分析上述的静态方法:

resolveAndCreate()

static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
 const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
 return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
}

从上面代码中,我们可以看出 resolveAndCreate() 方法内部是通过调用 ReflectiveInjector.resolve() 方法和 ReflectiveInjector.fromResolvedProviders() 方法来创建 ReflectiveInjector 对象。

resolve()

该方法用于把 Provider 数组解析为 ResolvedReflectiveProvider 数组。

static resolve(providers: Provider[]): ResolvedReflectiveProvider[] {
 return resolveReflectiveProviders(providers);
}

resolve() 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
expect(providers.length).toEqual(2);

expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
expect(providers[0].key.displayName).toBe("Car");
expect(providers[1].key.displayName).toBe("Engine");

resolve() 解析图示

详解Angular 4.x Injector 

Provider 类型

export type Provider =
 TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];

// ApiService
export interface TypeProvider extends Type<any> {}

// { provide: ApiService, useClass: ApiService } 
export interface ClassProvider {
 // 用于设置与依赖对象关联的Token值,Token值可能是Type、InjectionToken、OpaqueToken的实例或字符串
 provide: any; 
 useClass: Type<any>;
 // 用于标识是否multiple providers,若是multiple类型,则返回与Token关联的依赖对象列表
 multi?: boolean; 
}
 
// { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
export interface ValueProvider {
 provide: any;
 useValue: any;
 multi?: boolean;
}
 
// { provide: 'ApiServiceAlias', useExisting: ApiService } 
export interface ExistingProvider {
 provide: any;
 useExisting: any;
 multi?: boolean;
}
 
// { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], multi: true }
export interface FactoryProvider {
 provide: any;
 useFactory: Function;
 deps?: any[]; // 用于设置工厂函数的依赖对象
 multi?: boolean;
}

ResolvedReflectiveProvider 接口

export interface ResolvedReflectiveProvider {
 // 唯一的对象用来从ReflectiveInjector中获取对象
 key: ReflectiveKey;
 // 工厂函数用于创建key相关的依赖对象 
 resolvedFactories: ResolvedReflectiveFactory[];
 // 标识当前的provider是否为multi-provider
 multiProvider: boolean;
}

ResolvedReflectiveFactory 类

export class ResolvedReflectiveFactory {
 constructor(
  public factory: Function,
  public dependencies: ReflectiveDependency[]) {}
}

ReflectiveDependency 类

export class ReflectiveDependency {
 constructor(
  public key: ReflectiveKey, 
  public optional: boolean, 
  public visibility: Self|SkipSelf|null) {}

 static fromKey(key: ReflectiveKey): ReflectiveDependency {
 return new ReflectiveDependency(key, false, null);
 }
}

ReflectiveKey 类

ReflectiveKey 对象中包含两个属性:系统范围内唯一的id 和 token。系统范围内唯一的id,允许注入器以更高效的方式存储已创建的对象。另外我们不能手动的创建 ReflectiveKey,当 ReflectiveInjector 对象解析 providers 的时候会自动创建 ReflectiveKey 对象。

export class ReflectiveKey {
 constructor(public token: Object, public id: number) {
 if (!token) {
  throw new Error('Token must be defined!');
 }
 }
 
 // 返回序列化的token
 get displayName(): string { return stringify(this.token); }

 // 获取token对应的ReflectiveKey
 static get(token: Object): ReflectiveKey {
 return _globalKeyRegistry.get(resolveForwardRef(token));
 }

 // 获取系统中已注册ReflectiveKey的个数
 static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
}

const _globalKeyRegistry = new KeyRegistry(); // 创建Key仓库

export class KeyRegistry {
 private _allKeys = new Map<Object, ReflectiveKey>();

 /**
 * 若token是ReflectiveKey类的实例,则直接返回。若_allKeys对象中包含token属性
 * 则返回token对应的ReflectiveKey对象。否则创建一个新的ReflectiveKey对象,并
 * 保存到_allKeys对象中
 */
 get(token: Object): ReflectiveKey {
 if (token instanceof ReflectiveKey) return token;

 if (this._allKeys.has(token)) {
  return this._allKeys.get(token) !;
 }

 const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
 this._allKeys.set(token, newKey);
 return newKey;
 }

 // 获取已保存ReflectiveKey的个数
 get numberOfKeys(): number { return this._allKeys.size; }
}

分析完 resolve() 方法的输入参数和返回类型,我们来看一下该方法内部的具体实现:

export function resolveReflectiveProviders(providers: Provider[])
 : ResolvedReflectiveProvider[] {
  const normalized = _normalizeProviders(providers, []); // 步骤一
  const resolved = normalized.map(resolveReflectiveProvider); // 步骤二
  const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map()); // 步骤三
  return Array.from(resolvedProviderMap.values()); // 步骤四
}

步骤一 —— 规范化Provider

const normalized = _normalizeProviders(providers, []);

// 规范化Providers
function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
 providers.forEach(b => {
  // providers: [Type] => providers: [{provide: Type, useClass: Type }]
  if (b instanceof Type) { 
   res.push({provide: b, useClass: b});
  } else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
   res.push(b as NormalizedProvider);
  } else if (b instanceof Array) { // 若b是数组,则递归调用_normalizeProviders()方法
   _normalizeProviders(b, res);
  } else {
   throw invalidProviderError(b);
  }
 });
 return res;
}

interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, 
  ExistingProvider, FactoryProvider {}

步骤二 —— 转化NormalizedProvider为ResolvedReflectiveProvider

const resolved = normalized.map(resolveReflectiveProvider);

// 解析NormalizedProvider为ResolvedReflectiveProvider
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
 return new ResolvedReflectiveProvider_(
   ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)],
   provider.multi || false);
}

// 用于创建已解析的Provider实例
export class ResolvedReflectiveProvider_ implements ResolvedReflectiveProvider {
 constructor(
   public key: ReflectiveKey, 
   public resolvedFactories: ResolvedReflectiveFactory[],
   public multiProvider: boolean) {}

 get resolvedFactory(): ResolvedReflectiveFactory { return this.resolvedFactories[0]; }
}

// 解析NormalizedProvider对象,创建ResolvedReflectiveFactory对象
function resolveReflectiveFactory(provider: NormalizedProvider): ResolvedReflectiveFactory {
 let factoryFn: Function;
 let resolvedDeps: ReflectiveDependency[];
 if (provider.useClass) {
  // { provide: ApiService, useClass: ApiService } 
  const useClass = resolveForwardRef(provider.useClass);
  factoryFn = reflector.factory(useClass);
  resolvedDeps = _dependenciesFor(useClass);
 } else if (provider.useExisting) {
  // { provide: 'ApiServiceAlias', useExisting: ApiService } 
  factoryFn = (aliasInstance: any) => aliasInstance;
  resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
 } else if (provider.useFactory) {
  // { provide: APP_INITIALIZER, useFactory: configFactory, deps: [AppConfig], 
  //   multi: true }
  factoryFn = provider.useFactory;
  resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
 } else {
  // { provide: 'API_URL', useValue: 'http://my.api.com/v1' }
  factoryFn = () => provider.useValue;
  // const _EMPTY_LIST: any[] = [];
  resolvedDeps = _EMPTY_LIST;
 }
 return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
}

步骤三 —— 合并已解析的Provider

const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());

export function mergeResolvedReflectiveProviders(
  providers: ResolvedReflectiveProvider[],
  normalizedProvidersMap: Map<number, ResolvedReflectiveProvider>):
  Map<number, ResolvedReflectiveProvider> {
  for (let i = 0; i < providers.length; i++) {
  const provider = providers[i];
   // 从normalizedProvidersMap对象中获取key.id对应的ResolvedReflectiveProvider对象
  const existing = normalizedProvidersMap.get(provider.key.id);
  if (existing) {
    // 如果当前的provider不是multi provider,则抛出异常
   if (provider.multiProvider !== existing.multiProvider) {
    throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
   }
   // 如果当前的provider是multi provider,则把当前provider的resolvedFactories
   // 列表中的每一项添加到已存在的provider对象的resolvedFactories列表中。
   if (provider.multiProvider) {
    for (let j = 0; j < provider.resolvedFactories.length; j++) {
     existing.resolvedFactories.push(provider.resolvedFactories[j]);
    }
   } else { 
    // 如果当前的provider不是multi provider,则覆盖已存在的provider
    normalizedProvidersMap.set(provider.key.id, provider);
   }
  } else {
   let resolvedProvider: ResolvedReflectiveProvider;
   // 如果当前的provider是multi provider,则创建一个新的ResolvedReflectiveProvider对象
   if (provider.multiProvider) {
    resolvedProvider = new ResolvedReflectiveProvider_(
      provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
   } else {
    resolvedProvider = provider;
   }
   // 在normalizedProvidersMap中保存已解析的ResolvedReflectiveProvider对象
   normalizedProvidersMap.set(provider.key.id, resolvedProvider);
  }
 }
 return normalizedProvidersMap;
}

步骤四 —— 生成ResolvedReflectiveProvider[]

// resolvedProviderMap的values,创建ResolvedReflectiveProvider[]
Array.from(resolvedProviderMap.values());

/**
 * 基于一个类似数组或可迭代对象创建一个新的数组实例
 * 
 * arrayLike:转换成真实数组的类数组对象或可遍历对象。
 * mapFn(可选):如果指定了该参数,则最后生成的数组会经过该函数的加工处理后再返回。
 * thisArg(可选):执行mapFn函数时this的值。
 */
Array.from(arrayLike[, mapFn[, thisArg]])

fromResolvedProviders()

该方法用于基于已解析的 providers 创建注入器。

static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent?: Injector):
 ReflectiveInjector {
  return new ReflectiveInjector_(providers, parent);
}

fromResolvedProviders() 使用示例

@Injectable()
class Engine {}

@Injectable()
class Car {
 constructor(public engine:Engine) {}
}

var providers = ReflectiveInjector.resolve([Car, Engine]);
var injector = ReflectiveInjector.fromResolvedProviders(providers);
expect(injector.get(Car) instanceof Car).toBe(true);

了解完 fromResolvedProviders() 方法的使用方式,接下来我们来重点分析一下 ReflectiveInjector_ 类。

ReflectiveInjector_ 类

ReflectiveInjector_ 类的属性

// 构造次数
_constructionCounter: number = 0;

// ResolvedReflectiveProvider列表
 public _providers: ResolvedReflectiveProvider[];

// 父级注入器
 public _parent: Injector|null;

// ReflectiveKey id列表
 keyIds: number[];

// 依赖对象列表
 objs: any[];

ReflectiveInjector_ 构造函数

export class ReflectiveInjector_ implements ReflectiveInjector {
 constructor(_providers: ResolvedReflectiveProvider[], _parent?: Injector) {
   this._providers = _providers;
   // 设置父级注入器
   this._parent = _parent || null;

   const len = _providers.length;

   this.keyIds = new Array(len);
   this.objs = new Array(len);

   // 初始化keyIds列表和objs对象列表
   for (let i = 0; i < len; i++) {
    this.keyIds[i] = _providers[i].key.id;
    this.objs[i] = UNDEFINED;
   }
 }
}

const UNDEFINED = new Object();

ReflectiveInjector_ 类的方法

ReflectiveInjector_ 类中的方法较多,我们只分析其中比较重要的方法,首先先根据方法的实现的功能进行分类:

  1. 用于创建ReflectiveInjector注入器
  2. 用于获取对象
  3. 用于创建对象
  4. 用于获取工厂函数依赖对象

用于创建ReflectiveInjector注入器

// 基于Provider列表并创建子注入器
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
  const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
  return this.createChildFromResolved(ResolvedReflectiveProviders);
}

// 基于已解析的ResolvedReflectiveProvider列表,创建子注入器
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
  const inj = new ReflectiveInjector_(providers);
  inj._parent = this;
  return inj;
}

用于获取对象

// 获取当前注入器的父级注入器
get parent(): Injector|null { return this._parent; }

// 获取token对应的依赖对象
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
  return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
}

// 根据ReflectiveKey及visibility可见性,获取对应的依赖对象
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any {
  // const INJECTOR_KEY = ReflectiveKey.get(Injector); 
  if (key === INJECTOR_KEY) {
   return this;
  }

  // 判断该依赖对象是否使用@Self装饰器定义,表示从本级注入器获取依赖对象
  if (visibility instanceof Self) {
   return this._getByKeySelf(key, notFoundValue);

  } else {
   // 使用默认的方式获取依赖对象
   return this._getByKeyDefault(key, notFoundValue, visibility);
  }
}

// 从本级注入器获取依赖对象
 _getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
  const obj = this._getObjByKeyId(key.id);
  return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
}

// 使用默认的方式获取依赖对象
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, 
  visibility: Self|SkipSelf|null): any {
  let inj: Injector|null;

  // 判断该依赖对象是否使用@SkipSelf装饰器定义,表示不从本级注入器获取依赖对象
  if (visibility instanceof SkipSelf) {
   inj = this._parent;
  } else {
   inj = this;
  }

  // 从本级注入器获取依赖对象,若本级获取不到,则从父级注入器中查找
  while (inj instanceof ReflectiveInjector_) {
   const inj_ = <ReflectiveInjector_>inj;
   const obj = inj_._getObjByKeyId(key.id);
   if (obj !== UNDEFINED) return obj;
   inj = inj_._parent;
  }
  if (inj !== null) {
   return inj.get(key.token, notFoundValue);
  } else {
   return this._throwOrNull(key, notFoundValue);
  }
}

// 获取keyId对应的对象,如依赖对象未创建,则调用_new()方法创建一个,然后保存到
// this.objs对象列表中
private _getObjByKeyId(keyId: number): any {
  for (let i = 0; i < this.keyIds.length; i++) {
   if (this.keyIds[i] === keyId) {
    // const UNDEFINED = new Object();
    if (this.objs[i] === UNDEFINED) {
     this.objs[i] = this._new(this._providers[i]);
    }
    return this.objs[i];
   }
  }
  return UNDEFINED;
}

用于创建对象

// 创建依赖对象
_new(provider: ResolvedReflectiveProvider): any {
  // 判断是否存在循环依赖
  if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
   throw cyclicDependencyError(this, provider.key);
  }
  return this._instantiateProvider(provider);
}

// 获取最大的对象个数
private _getMaxNumberOfObjects(): number { return this.objs.length; }

// 根据已解析的provider创建依赖对象。若是multi provider则,循环创建multi provider对象。 
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
  if (provider.multiProvider) {
   const res = new Array(provider.resolvedFactories.length);
   for (let i = 0; i < provider.resolvedFactories.length; ++i) {
    res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
   }
   return res;
  } else {
   return this._instantiate(provider, provider.resolvedFactories[0]);
  }
}

// 根据已解析的provider和已解析的工厂创建依赖对象
private _instantiate(
   provider: ResolvedReflectiveProvider,
   ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
  // 获取对象工厂函数
  const factory = ResolvedReflectiveFactory.factory;

  // 获取工厂函数所依赖的对象列表
  let deps: any[];
  try {
   deps = ResolvedReflectiveFactory.dependencies
       .map(dep => this._getByReflectiveDependency(dep));
  } catch (e) {
   if (e.addKey) {
    e.addKey(this, provider.key);
   }
   throw e;
  }

  // 调用对象工厂函数创建依赖对象
  let obj: any;
  try {
   obj = factory(...deps);
  } catch (e) {
   throw instantiationError(this, e, e.stack, provider.key);
  }
  return obj;
 }

用于获取工厂函数依赖对象

// 若通过@Optional装饰器定义该依赖对象,表示该依赖对象是可选的,当获取不到时返回null。
private _getByReflectiveDependency(dep: ReflectiveDependency): any {
  return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
非常不错的一个javascript 类
Nov 07 Javascript
javascript中substr,substring,slice.splice的区别说明
Nov 25 Javascript
JS获取随机数函数可自定义最小值最大值
May 08 Javascript
Javascript获取当前时间函数和时间操作小结
Oct 01 Javascript
JS实现黑色大气的二级导航菜单效果
Sep 18 Javascript
JavaScript黑洞数字之运算路线查找算法(递归算法)实例
Jan 28 Javascript
jQuery基本筛选选择器实例代码
Feb 06 Javascript
Vue路由钩子之afterEach beforeEach的区别详解
Jul 15 Javascript
Vue实现动态添加或者删除对象和对象数组的操作方法
Sep 21 Javascript
原生javascript自定义input[type=radio]效果示例
Aug 27 Javascript
微信小程序(订阅消息)功能
Oct 25 Javascript
详解vue-router的导航钩子(导航守卫)
Nov 02 Javascript
详解Vue中使用v-for语句抛出错误的解决方案
May 04 #Javascript
详解Node.js串行化流程控制
May 04 #Javascript
纯原生js实现贪吃蛇游戏
Apr 16 #Javascript
js调用刷新界面的几种方式
May 03 #Javascript
JavaScript中双向数据绑定详解
May 03 #Javascript
Js实现中国公民身份证号码有效性验证实例代码
May 03 #Javascript
Vue原理剖析 实现双向绑定MVVM
May 03 #Javascript
You might like
PHP使用mysql_fetch_object从查询结果中获取对象集的方法
2015/03/18 PHP
php支付宝在线支付接口开发教程
2016/09/19 PHP
php多线程并发实现方法
2016/09/30 PHP
利用PHPExcel读取Excel的数据和导出数据到Excel
2017/05/12 PHP
PHP编程计算文件或数组中单词出现频率的方法
2017/05/22 PHP
php记录搜索引擎爬行记录的实现代码
2018/03/02 PHP
PHP+MySQL实现输入页码跳转到指定页面功能示例
2018/06/01 PHP
php app支付宝回调(异步通知)详解
2018/07/25 PHP
tp5递归 无限级分类详解
2019/10/18 PHP
JS前端框架关于重构的失败经验分享
2013/03/17 Javascript
Jqgrid设置全选(选择)及获取选择行的值示例代码
2013/12/28 Javascript
gridview生成时如何去掉style属性中的border-collapse
2014/09/30 Javascript
js+css实现导航效果实例
2015/02/10 Javascript
JavaScript实现的简单拖拽效果
2015/06/01 Javascript
jQuery仿360导航页图标拖动排序效果代码分享
2015/08/24 Javascript
JS+CSS实现简单滑动门(滑动菜单)效果
2015/09/19 Javascript
JavaScript定义全局对象的方法示例
2017/01/12 Javascript
jQuery插件HighCharts实现的2D堆条状图效果示例【附demo源码下载】
2017/03/14 Javascript
微信小程序 首页制作简单实例
2017/04/07 Javascript
Vue-cli 使用json server在本地模拟请求数据的示例代码
2017/11/02 Javascript
javascript实现数字配对游戏的实例讲解
2017/12/14 Javascript
vue.js使用v-model指令实现的数据双向绑定功能示例
2018/05/22 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
2018/09/13 Javascript
NodeJS加密解密及node-rsa加密解密用法详解
2018/10/12 NodeJs
Vue实现的父组件向子组件传值功能示例
2019/01/19 Javascript
Vue2.x通用条件搜索组件的封装及应用详解
2019/05/28 Javascript
微信小程序停止其他视频播放当前视频的实例代码
2019/12/25 Javascript
JavaScript实现Tab标签页切换的最简便方式(4种)
2020/06/28 Javascript
一行Python代码制作动态二维码的实现
2019/09/09 Python
Python3自定义http/https请求拦截mitmproxy脚本实例
2020/05/11 Python
pandas使用函数批量处理数据(map、apply、applymap)
2020/11/27 Python
W Hamond官网:始于1979年的钻石专家
2020/07/20 全球购物
同居协议书范本
2014/04/23 职场文书
小学生自我评价100字(15篇)
2014/09/18 职场文书
2016年寒假家长评语
2015/10/10 职场文书
2017公司年会主持人开幕词
2016/03/04 职场文书