VUE3.0을 빠르게 시작하는 방법: 입력 및 학습
Nest는 모듈 메커니즘을 제공합니다. 모듈 데코레이터에서 공급자, 가져오기, 내보내기 및 공급자 생성자를 정의하여 종속성 주입이 완료되고 전체 애플리케이션 개발이 다음을 통해 구성됩니다. 모듈 트리. 프레임워크 자체의 규칙에 따라 애플리케이션을 직접 실행하는 데에는 전혀 문제가 없습니다. 그러나 저는 프레임워크에서 선언한 종속성 주입, 제어 반전, 모듈, 공급자, 메타데이터, 관련 데코레이터 등에 대한 명확하고 체계적인 이해가 부족하다고 느낍니다.
- 통제 역전이 필요한 이유는 무엇입니까?
- 의존성 주입이란 무엇입니까?
- 데코레이터는 무슨 일을 하나요?
- 모듈(@Module)의 공급자, 가져오기 및 내보내기의 구현 원칙은 무엇입니까?
이해하고 감사할 수 있을 것 같은데, 처음부터 명확하게 설명하자면, 명확하게 설명할 수는 없습니다. 그래서 좀 조사를 해보니 이 글이 나오더군요. 이제부터 처음부터 시작하여 본문을 입력해 보겠습니다.
1.1 Express, Koa
언어와 해당 기술 커뮤니티의 개발 과정은 나무 뿌리가 천천히 가지로 성장한 다음 잎이 무성해지는 과정과 마찬가지로 바닥 기능부터 위쪽으로 점차 풍부해지고 발전해야 합니다. 이전에는 Express 및 Koa와 같은 기본 웹 서비스 프레임워크가 Nodejs에 등장했습니다. 매우 기본적인 서비스 기능을 제공할 수 있습니다. 이러한 프레임워크를 기반으로 커뮤니티에서는 수많은 미들웨어와 플러그인이 탄생하기 시작했고, 프레임워크에 대한 더욱 풍부한 서비스를 제공하게 되었습니다. 우리는 애플리케이션 종속성을 구성하고 애플리케이션 스캐폴딩을 직접 구축해야 하는데, 이는 유연하고 번거로우며 특정 작업 부하도 필요합니다.
개발 후반에는 보다 효율적인 생산과 보다 통일된 규칙을 갖춘 일부 프레임워크가 탄생하여 새로운 단계를 열었습니다.
1.2 EggJs 및 Nestjs
빠른 생산 애플리케이션에 더 잘 적응하고 표준을 통합하여 즉시 사용할 수 있도록 하기 위해 EggJs, NestJs 및 Midway와 같은 프레임워크가 개발되었습니다. 이러한 유형의 프레임워크는 기본 라이프사이클을 구현하여 애플리케이션 구현을 보편적이고 확장 가능한 프로세스로 추상화합니다. 애플리케이션을 보다 간단하게 구현하려면 프레임워크에서 제공하는 구성 방법만 따르면 됩니다. 프레임워크는 프로그램의 프로세스 제어를 구현하며, 부품을 적절한 위치에 조립하기만 하면 됩니다. 이는 조립 라인 작업과 비슷해 보입니다. 각 프로세스가 명확하게 구분되어 구현 비용이 많이 절감됩니다.
1.3 요약
위의 두 단계는 단지 예시일 뿐이며, 프레임워크 업그레이드를 달성하기 위해 일부 디자인 아이디어와 패턴이 Nest에 도입될 예정입니다. 의존성 주입과 메타프로그래밍의 개념에 대해 아래에서 이야기해 보겠습니다.
2.1 종속성 주입
애플리케이션은 실제로 서로 호출하여 애플리케이션의 모든 기능을 구현하는 수많은 추상 클래스입니다. 애플리케이션 코드와 기능의 복잡성이 증가함에 따라 클래스가 점점 더 많아지고 이들 간의 관계가 점점 더 복잡해지기 때문에 프로젝트를 유지 관리하기가 점점 더 어려워질 것입니다.
예를 들어, Koa를 사용하여 애플리케이션을 개발하는 경우 Koa 자체는 주로 기본 웹 서비스 기능 세트를 구현합니다. 애플리케이션을 구현하는 과정에서 우리는 많은 클래스, 인스턴스화 방법 및 이러한 클래스의 상호 종속성을 모두 정의합니다. 코드 로직에서 우리가 자유롭게 구성하고 제어할 수 있습니다. 각 클래스의 인스턴스화는 우리가 수동으로 새로 수행하며, 클래스가 한 번만 인스턴스화되고 공유되는지, 아니면 매번 인스턴스화되는지 여부를 제어할 수 있습니다. 다음 클래스 B는 A에 의존합니다. B가 인스턴스화될 때마다 A는 한 번 인스턴스화되므로 각 인스턴스 B에 대해 A는 공유되지 않는 인스턴스입니다.
클래스 A{} // 비 클래스 B{ 건설자(){ this.a = 새로운 A(); } }
아래 C는 획득한 외부 인스턴스이므로 여러 C 인스턴스가 app.a 인스턴스를 공유합니다.
클래스 A{} // C const 앱 = {}; app.a = 새로운 A(); 클래스 C{ 건설자(){ this.a = 앱.a; } }
다음 D는 생성자 매개변수를 통해 전달됩니다. 매번 비공유 인스턴스를 전달하거나 공유 app.a 인스턴스(D 및 F 공유 app.a)를 전달할 수 있습니다. 이제 매개변수 전달입니다. X 클래스 인스턴스를 전달할 수도 있습니다.
클래스 A{} 클래스 X{} //디 const 앱 = {}; app.a = 새로운 A(); 클래스 D{ 생성자(a){ this.a = a; } } 클래스 F{ 생성자(a){ this.a = a; } } 새로운 D(app.a) 새로운 F(app.a)
새로운
D(새로운
생성자를 통한 주입(값 전달)은 하나의 구현 방법일 뿐입니다. 또한 외부 종속성을 내부 종속성으로 전달할 수 있는 한 set 메서드 호출이나 다른 메서드를 구현하여 전달할 수도 있습니다. 정말 간단합니다.
클래스 A{} //디 클래스 D{ 세트뎁(a){ this.a = a; } } const d = 새로운 D() d.setDep(new A())
2.2 종속성 주입이 모두 포함되어 있습니까?
반복이 진행됨에 따라 B의 종속성은 다른 전제 조건에 따라 변경되는 것으로 보입니다. 예를 들어, 전제 조건 1개 this.a
A의 인스턴스를 전달해야 하고, 전제 조건 2개 this.a
X의 인스턴스를 전달해야 합니다. 이제 실제 추상화를 시작하겠습니다. 위의 D와 같은 의존성 주입 방식으로 변환하겠습니다.
초기에는 애플리케이션을 구현할 때 당시 요구 사항에 맞는 한 클래스 B와 C의 작성 방법을 구현했습니다. 이 부분은 몇 년 동안 프로젝트를 반복한 후에는 문제가 되지 않았습니다. 코드를 반드시 건드릴 필요는 없습니다. 나중에 확장을 고려한다면 개발 효율성에 영향을 미치고 유용하지 않을 수 있습니다. 그래서 대부분의 경우 추상화가 필요한 시나리오에 직면하고 코드의 일부를 추상적으로 변환합니다.
// 클래스 B{ 변환 전 건설자(){ this.a = 새로운 A(); } } 새로운 B() //클래스 D{ 변환 후 생성자(a){ this.a = a; } } 새로운 D(새로운 A())새로운 D
(
새로운구현 비용.
이 예는 어떠한 제약이나 규정도 없는 개발 모델에서 이를 설명하기 위해 여기에 제공됩니다. 다양한 클래스 간의 종속성을 제어하는 코드를 자유롭게 작성할 수 있습니다. 완전히 개방된 환경에서 매우 자유롭습니다. 화전 농업의 원시 시대입니다. 고정된 코드 개발 모델이 없고 가장 높은 수준의 실행 계획이 없기 때문에 서로 다른 개발자가 개입하거나 동일한 개발자가 서로 다른 시간에 코드를 작성하므로 코드가 커짐에 따라 종속 관계는 매우 달라집니다. 공유 인스턴스는 여러 번 인스턴스화될 수 있습니다. , 메모리 낭비. 코드에서는 완전한 종속성 구조를 확인하기 어렵고 코드 유지 관리도 매우 어려워질 수 있습니다.
그런 다음 클래스를 정의할 때마다 종속성 주입 방법에 따라 작성하고 D와 같이 작성합니다. 그러면 C와 B의 추상화 프로세스가 진행되어 나중에 확장이 더 편리해지고 변환 비용이 절감됩니다. 따라서 이것을 All in 依赖注入
이라고 합니다. 즉, 모든 종속성은 종속성 주입을 통해 구현됩니다.
그러나 초기 구현 비용이 다시 높아지며, 팀 협업의 통일성과 지속성을 확보하기 어려워 결국 추가 구현 비용이 발생하지 않아 과잉 설계로 정의할 수도 있습니다. 반드시 이익을 얻을 수 있습니다.
2.3 제어 역전
이제 종속성 주입의 통합 사용에 동의했으므로 프레임워크의 기본 캡슐화를 통해 하위 수준 컨트롤러를 구현하고 종속성 구성 규칙에 동의할 수 있습니까? 우리가 정의한 종속성 구성 및 종속성 공유는 클래스 관리를 달성하는 데 도움이 됩니다. 이 디자인 패턴을 제어 반전 이라고 합니다.
통제의 반전이란 말을 처음 들으면 이해하기 어려울 수 있습니다. 무엇이 거꾸로 되었나요?
개발자들이 처음부터 이런 프레임워크를 사용해왔고, 마지막 '익스프레스와 코아 시대'를 경험하지 못했고, 구사회의 구타도 부족했기 때문인 것으로 추측된다. 거꾸로 된 표현과 함께 프로그램은 매우 추상적이고 이해하기 어려워 보입니다.
앞서 언급했듯이 Koa 애플리케이션을 구현할 때 모든 클래스는 우리가 완전히 제어하므로 기존의 프로그램 제어 방법으로 간주할 수 있으므로 이를 정회전 제어라고 부릅니다. 하단에 컨트롤러 세트를 구현하는 Nest를 사용합니다. 실제 개발 과정에서 합의에 따라 구성 코드만 작성하면 되며 프레임워크 프로그램은 클래스의 종속성 주입을 관리하는 데 도움이 되므로 다음과 같이 부릅니다. 통제의 반전.
핵심은 통합 관리를 위해 프로그램의 구현 과정을 프레임워크 프로그램에 넘기고, 제어권을 개발자로부터 프레임워크 프로그램으로 이양하는 것이다.
정방향 회전 제어 : 개발자의 순수 수동 제어 프로그램
제어 반전: 프레임워크 프로그램 제어
실제 예를 들자면, 어떤 사람이 혼자 운전해서 출근하고, 그의 목적은 회사에 도달하는 것입니다. 스스로 운전하고 자신의 경로를 제어합니다. 그리고 운전 통제권을 넘겨주고 버스를 타면 해당 셔틀버스만 선택하면 회사에 도착할 수 있다. 통제만으로도 사람들은 어떤 버스를 타야 할지 기억하기만 하면 되며, 실수할 확률도 줄어들고 사람들은 훨씬 더 편안해집니다. 버스 시스템은 컨트롤러이고 버스 라인은 합의된 구성입니다.
위의 실제 비교를 통해 제어의 역전을 이해할 수 있어야 한다고 생각합니다.
2.4 요약
Koa에서 Nest까지, 프런트엔드 JQuery에서 Vue React까지. 실제로 이전 시대의 비효율성 문제를 해결하기 위해 모두 프레임워크 캡슐화를 통해 단계적으로 구현됩니다.
위의 Koa 애플리케이션 개발은 종속성 및 인스턴스화를 제어하는 매우 원시적인 방법을 사용하는데, 이는 프론트 엔드의 JQuery 운영 돔과 유사하며 이 매우 원시적인 방법을 제어 전달이라고 하며 Vue React는 Nest가 제공하는 프로그램 계층과 같습니다. 컨트롤러, 그것들은 모두 제어 반전이라고 부를 수 있습니다. 이는 저의 개인적인 이해이기도 합니다. 만약 문제가 있다면 하나님께서 지적해 주시기를 바랍니다.
Nest의 @Module 모듈에 대해 이야기해 보겠습니다. 종속성 주입 및 제어 반전에는 이를 미디어로 필요합니다.
Nestjs는 제어 역전을 구현하고 클래스의 종속성 주입인 공급자를 관리하기 위해 모듈(@module)의 가져오기, 내보내기 및 공급자를 구성하는 데 동의합니다.
공급자는 현재 모듈에서 클래스를 등록하고 인스턴스화하는 것으로 이해될 수 있습니다. B가 생성자에서 A를 참조하는 경우 현재 ModuleD의 A 인스턴스를 참조합니다.
'@nestjs/common'에서 { 모듈 }을 가져옵니다. import { ModuleX } from './moduleX'; import { A } from './A'; './B'에서 { B } 가져오기; @기준 치수({ 수입품: [ModuleX], 공급자: [A,B], 수출: [A] }) 내보내기 클래스 ModuleD {} // 비 클래스 B{ 생성자(a:A){ this.a = a; } }
exports
외부 모듈에서 공유할 수 있는 클래스로서 현재 모듈의 providers
에서 인스턴스화된 클래스를 참조합니다. 예를 들어 ModuleF의 C 클래스가 인스턴스화되면 ModuleD의 A 클래스 인스턴스를 직접 주입하고 싶습니다. ModuleD에서 내보내기 A를 설정하고 ModuleF에서 imports
통해 ModuleD를 가져오기만 하면 됩니다.
다음 작성 방법에 따라 제어 프로그램 반전이 자동으로 종속성을 검사합니다. 먼저 자신의 모듈 공급자에 공급자 A가 있는지 확인합니다. 그렇지 않은 경우 가져온 ModuleD에서 A의 인스턴스를 찾습니다. 발견되면 ModuleD의 A가 C 인스턴스에 삽입됩니다.
'@nestjs/common'에서 { 모듈 }을 가져옵니다. './moduleD'에서 { ModuleD}를 가져옵니다. './C'에서 { C } 가져오기; @기준 치수({ 수입품: [모듈D], 제공자: [C], }) 내보내기 클래스 ModuleF {} // 씨 클래스 C { 생성자(a:A){ this.a = a; } }
따라서 외부 모듈이 현재 모듈의 클래스 인스턴스를 사용하도록 하려면 먼저 현재 모듈의 providers
에서 인스턴스화 클래스를 정의한 다음 이 클래스를 정의하고 내보내야 합니다. 그렇지 않으면 오류가 보고됩니다.
//올바른 @Module({ 제공자: [A], 수출: [A] }) //오류 @모듈({ 제공자: [], 수출: [A] })
이후의 보충
모듈의 인스턴스를 찾는 과정을 되돌아보면 사실 약간 불분명합니다. 핵심은 공급자의 클래스가 인스턴스화되고 인스턴스화 후에 공급자가 된다는 것입니다. 모듈에 있는 공급자의 클래스만 인스턴스화되고 내보내기 및 가져오기는 단지 조직 관계 구성일 뿐입니다. 모듈은 자체 공급자를 사용하는 데 우선 순위를 부여합니다. 가져온 모듈에 해당 공급자가 있는지 확인하십시오.
내보내기
클래스 C { 생성자(비공개 a: A) { } }
TypeScript는 생성자 매개변수(private, protected, public, readonly)가 암시적으로 자동으로 클래스 속성(매개변수 속성)으로 정의되도록 지원하므로 this.a = a
사용할 필요가 없습니다. Nest에는 이렇게 쓰여 있습니다.
메타프로그래밍의 개념은 Nest 프레임워크에 반영되며 데코레이터는 메타프로그래밍의 구현입니다. 메타프로그래밍의 본질은 여전히 프로그래밍이라고 대략적으로 이해할 수 있지만 그 중간에 몇 가지 추상적인 프로그램이 있습니다. 이 추상적인 프로그램은 메타데이터(예: @Module의 객체 데이터)를 식별할 수 있는데, 이는 실제로 다른 것을 사용할 수 있는 확장 기능입니다. 처리할 데이터로서의 프로그램. 그러한 추상 프로그램을 작성할 때 우리는 메타 프로그래밍을 하고 있습니다.
4.1 메타데이터
메타데이터는 네스트 문서에서 자주 언급된다. 메타데이터라는 개념은 처음 접할 때 헷갈릴 수 있는데, 시간이 지날수록 익숙해지고 이해해야 하므로 너무 헷갈릴 필요는 없다. 빠뜨리는.
메타데이터의 정의는 데이터를 설명하는 데이터, 주로 데이터 속성을 설명하는 정보이며, 프로그램을 설명하는 데이터로도 이해될 수 있습니다.
Nest에서 @Module에 의해 구성되는 exports、providers、imports、controllers
모두 메타데이터 입니다. 이는 프로그램 관계를 설명하는 데 사용되는 데이터이기 때문입니다. 이 데이터 정보는 최종 사용자에게 표시되는 실제 데이터가 아니라 사용자가 읽고 인식합니다. 프레임워크 프로그램 .
4.2 Nest Decorator
Nest의 데코레이터 소스 코드를 살펴보면 거의 모든 데코레이터 자체가 Reflect-metadata를 통해서만 메타데이터를 정의한다는 것을 알 수 있습니다.
@Injectable 데코레이터
내보내기 함수 Injectable(options?: InjectableOptions): ClassDecorator { 반환 (대상: 객체) => { Reflect.defineMetadata(INJECTABLE_WATERMARK, true, 대상); Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, 옵션, 대상); }; }
여기에는 리플렉션이라는 개념이 있으며 리플렉션은 비교적 이해하기 쉽습니다. @Module 데코레이터를 예로 들어 메타데이터 providers
정의하면 프로그램이 실제로 실행될 때 providers
배열에 클래스 providers
전달됩니다. 인스턴스화는 공급자가 되며 개발자는 인스턴스화 및 종속성 주입을 명시적으로 수행할 필요가 없습니다. 클래스는 모듈에서 인스턴스화된 후에만 공급자가 됩니다. providers
의 클래스가 반영되어 공급자가 됩니다. 제어 반전이 사용되는 반사 기술입니다.
또 다른 예는 데이터베이스의 ORM(Object Relational Mapping) 입니다. ORM을 사용하려면 테이블 필드만 정의하면 ORM 라이브러리가 자동으로 개체 데이터를 SQL 문으로 변환합니다.
const 데이터 = TableModel.build(); 데이터.시간 = 1; data.browser = '크롬'; 데이터.저장(); // SQL: INSERT INTO tableName (time,browser) [{"time":1,"browser":"chrome"}]
ORM 라이브러리는 리플렉션 기술을 사용하므로 사용자는 필드 데이터 자체에만 주의하면 됩니다. 객체는 ORM 라이브러리 반영이 SQL 실행문이 됩니다. 개발자는 데이터 필드에만 집중하면 되며 SQL을 작성할 필요가 없습니다.
4.3 Reflect-metadata
Reflect-metadata는 Nest가 메타데이터를 관리하는 데 사용하는 리플렉션 라이브러리입니다. Reflect-metadata는 WeakMap을 사용하여 전역 단일 인스턴스를 생성하고, set 및 get 메소드를 통해 데코레이팅된 객체(클래스, 메소드 등)의 메타데이터를 설정하고 가져옵니다.
// 살펴보세요 var _WeakMap = !usePolyfill && typeof WeakMap === "function" ? WeakMap : CreateWeakMapPolyfill(); var Metadata = new _WeakMap(); 함수 정의메타데이터(){ OrdinaryDefineOwnMetadata(){ GetOrCreateMetadataMap(){ var targetMetadata = Metadata.get(O); if (IsUndefine(targetMetadata)) { if (!만들기) 정의되지 않은 반환; targetMetadata = new _Map(); Metadata.set(O, targetMetadata); } var MetadataMap = targetMetadata.get(P); if (IsUndefine(metadataMap)) { if (!만들기) 정의되지 않은 반환; 메타데이터맵 = new _Map(); targetMetadata.set(P, 메타데이터맵); } 메타데이터맵을 반환합니다. } } }
Reflect-metadata는 통합 관리를 위해 장식된 인물의 메타데이터를 글로벌 싱글톤 객체에 저장합니다. Reflect-metadata는 특정 리플렉션을 구현하지 않지만 리플렉션 구현을 지원하는 도구 라이브러리를 제공합니다.
이전 질문을 살펴보겠습니다.
통제 역전이 필요한 이유는 무엇입니까?
의존성 주입이란 무엇입니까?
데코레이터는 무슨 일을 하나요?
모듈(@Module)의 공급자, 가져오기 및 내보내기의 구현 원칙은 무엇입니까?
1, 2번은 이미 명확하게 설명드린 것 같습니다. 아직 다소 모호하다면 돌아가서 다시 읽어보시고, 다른 저자의 생각을 통해 지식을 이해하는 데 도움이 되는 다른 기사를 참조하시기 바랍니다.
5.1 문제 [3 4] 개요:
Nest는 리플렉션 기술을 사용하여 제어 반전을 구현하고 메타프로그래밍 기능을 제공합니다. 개발자는 @Module 데코레이터를 사용하여 클래스를 장식하고 메타데이터(providersimportsexports)를 정의하며 메타데이터는 전역에 저장됩니다. 객체(reflect-metadata 라이브러리 사용). 프로그램이 실행된 후 Nest 프레임워크 내부의 제어 프로그램은 모듈 트리를 읽고 등록하고, 메타데이터를 스캔하고 클래스를 인스턴스화하여 공급자가 되고, 모듈 메타데이터의 공급자가져오기내보내기 정의에 따라 모든 모듈에 이를 제공합니다. . 현재 클래스의 다른 종속 클래스의 인스턴스(공급자)를 검색하고, 찾은 후 생성자를 통해 주입합니다.
이 글은 개념이 많아 너무 자세한 분석을 제공하지 않습니다. 개념을 이해하는 데 시간이 오래 걸립니다. 지금 당장 완전히 이해하지 못하더라도 너무 걱정하지 마세요. 그렇군요. 이 글은 그래도 수고가 많이 들었습니다. 좋아하는 친구들은 한 번의 클릭으로 세 번 연결할 수 있기를 바랍니다~