In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "the advanced application of TypeScript in Model". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "the advanced application of TypeScript in Model".
Preface
In the classic front-end development models such as MVC and MVVC, V and C are often important, which may be the main focus of the front-end business. Combined with the actual business, the author prefers routing patterns and plug-in design, which can benefit developers in terms of iteration and maintenance (but you need to find PM to coordinate this matter, after all, they understand how to simplify the user experience, mostly how to make the user operation simple). But let's take a look at Model today and see what M can expand.
Background
Before reading this article, you may use the following strategy to request server data in your actual project (such as React+Redux):
Send redux action request data in componentDidMount
Initiate an asynchronous network request in action. Of course, you have encapsulated the network request.
Handle certain exceptions and marginal logic within the network request, and then return the requested data
Get the data this.setState to refresh the page, and maybe save a copy to the global redux.
Normally, an interface corresponds to at least one interface corresponding to the Model, what if you also define the Model requested by the interface and there are 5 interfaces on a page?
If the project has introduced TypeScript, combined with writing Model, your writing experience will certainly be like a stream of water! However, in the actual development, you also need to do some extra work on the data returned by the server, the parameters passed between pages, and other places related to data transmission:
Exception handling for null values such as null, undefined, etc. (in the latest ES solution and TS support, add: chain call? And operator?, please check the user's manual.)
The transliteration of sex=0, 1,2 timepieces 1591509066, etc.
Is there anything else? Welcome to leave a message to add)
As an excellent and mature developer, you must have done the above additional work, wrote dozens or even hundreds of tool class functions under the utils file, and even classified according to the purpose of the function: time class, age and gender class, number class,., and then you import where you need it, and then you begin to pass parameters to call. Yes, everything looks perfect!
The above process is said by the author himself:).
Current situation
With the iteration of the project and business, and the boss is still under pressure, the worst-case scenario is that you meet colleagues who do not follow the above "development specifications", and the result can only be ah. Let's get to the point.
Although the above process has a certain design, it does not achieve the principle of high cohesion and low coupling. I think it is not conducive to the late iteration and local reconstruction of the project.
Recommend another design principle: object-oriented five principles SOLID [3]
Here's an example:
When the fields in the interface are changed, such as the gender is changed from Sex to Gender
The front-end internal refactoring, when the data model does not match, page C supports jumping in from page An additional parameter a, or page B additional parameter b, and page B1 additional parameter b1 also jumps to C. From the design point of view, it must be best to let B1 adapt to the previous B as far as possible, otherwise C will become heavier and heavier.
As mentioned above, whether it is page interaction or business interaction, the most fundamental thing is the exchange and transmission of data, thus affecting the page and the business. Data is the core of concatenated pages and business, and Model is the form of data.
For example, in the current development mode of separation of front and rear ends, after the requirements are confirmed, the first thing the development needs to do is database design and interface design, which is simply the field agreement, and then page development is carried out, and finally interface debugging and system debugging are carried out until delivery testing. During this period, the back end needs to perform interface unit tests, and the front end needs Mock data development pages.
How to solve interface management
At present, the notes are managed in the form of JSON. During the initialization of the project, the configured interface list is registered in Redux Action with the help of dva [4], and then the interface call is directly sent to Action. Finally get the Data of the server response.
Interface configuration (corresponding to the second version below):
List: [{alias: 'getCode', apiPath:' / user/v1/getCode', auth: false,}, {alias: 'userLogin', apiPath:' / user/v1/userLogin', auth: false, nextGeneral: 'saveUserInfo',}, {alias:' loginTokenByJVerify', apiPath:'/ user/v1/jgLoginApi', auth: false, nextGeneral: 'saveUserInfo',},]
First edition:
Import {apiComm, apiMy} from 'services'; export default {namespace:' bill', state: {}, reducers: {updateState (state, {payload}) {return {... state,... payload};},}, effects: {* findBydoctorIdBill ({payload, callback}, {call}) {const res = yield call (apiMy.findBydoctorIdBill, payload) ! apiComm.IsSuccess (res) & & callback (res.data);}, * findByDoctorIdDetail ({payload, callback}, {call}) {const res = yield call (apiMy.findByDoctorIdDetail, payload);! apiComm.IsSuccess (res) & & callback (res.data);}, * findStatementDetails ({payload, callback}, {call}) {const res = yield call (apiMy.findStatementDetails, payload) ! apiComm.IsSuccess (res) & & callback (res.data);},},}
The second edition uses higher-order functions and supports server address switching to reduce redundant code:
Export const connectModelService = (cfg: any = {}) = > {const {apiBase ='', list = []} = cfg; const listEffect = {}; list.forEach (kAlias = > {const {alias, apiPath, nextGeneral, cbError = false,... options} = kAlias; const effectAlias = function* da ({payload = {}, nextPage, callback}, {call, put}) {let apiBaseNew = apiBase; / / apiBaseNew = urlApi If (global.apiServer) {apiBaseNew = global.apiServer.indexOf ('xxx.com')! =-1? Global.apiServer: apiBase;} else if (! isDebug) {apiBaseNew = urlApi;} const urlpath = apiPath.indexOf ('http://') =-1 & & apiPath.indexOf (' https://') =-1? `${apiBaseNew} ${apiPath}`: apiPath; const res = yield call (hxRequest, urlpath, payload, options); const next = nextPage | | nextGeneral / / console.log ('= = hxRequest res', next, res); if (next) {yield put ({type: next, payload, res, callback,});} else if (cbError) {callback & & callback (res);} else {hasNoError (res) & & callback & callback (res.data) }}; listEffect [alias] = effectAlias;}); return listEffect;}
Another problem is that the interface and the corresponding request do not correspond to the corresponding data Model. If you look at the code again, it will take some time to sort out the business logic.
Please think about the above questions, and then move on.
Model management
An interface must correspond to a unique request Model and a unique response Model. Yes, that's right! This mechanism is used for further discussion below.
Therefore, by initiating an interface request in response to Model, you can also use the request Model to determine whether the participation is unreasonable when the function is called, thus switching the protagonist from the interface to Model. Here, I think it is more appropriate to give priority to responding to Model, so that I can more directly understand the data format obtained after this request.
Let's take a look at the code that initiates the request through Model:
SimpleModel.get ({id:'1'}, {auth: false, onlyData: false},) .then ((data: ResponseData) = > setTimeout () = > console.log ('set to return all data, return ResponseData or ResponseData', typeof data, data,), 2000,))
Where SimpleModel is the defined response Model, the first parameter is the request, the second parameter is the request configuration item, and the interface address is hidden inside the SimpleModel.
Import {Record} from 'immutable'; import {ApiOptons} from'. / Common'; import {ServiceManager} from'. / Service'; / * simple type * / const SimpleModelDefault = {a: 'test string', sex: 0,}; interface SimpleModelParams {id: string } export class SimpleModel extends Record (SimpleModelDefault) {static async get (params: SimpleModelParams, options?: ApiOptons) {return await ServiceManager.get (SimpleModel, 'http://localhost:3000/test', / / hidden interface address params, options,);} static sexMap = {0:' confidential', 1: 'male', 2: 'female',} SexText () {return SimpleModel.sexMap [this.sex]? 'Secret';}}
With the help of Record [5] in immutable, the purpose is to deserialize JSON Object into Class Object, and to improve the cohesion of Model related functions in the project. For more information, see my other article: JavaScript's path to strong language-alternative JSON serialization and deserialization [6].
/ / utils/tool.tsx export const sexMap = {0: 'confidential', 1: 'male', 2: 'female',}; export const sexText = (sex: number) = > {return sexMap [sex]? 'Confidential';}
Accessing specific data directly in SimpleModel with this is more cohesive and easier to maintain than passing in external parameters when calling the utils/tool function. Through this way of thinking, I believe you can create more "dark magic" grammar candy!
Next, let's look at the contents of the Common file:
/ * * API response, outermost uniform format * / export class ResponseData {code? = 0; message? = 'successful operation'; toastId? =-1; data?: T;} / * * api configuration information * / export class ApiOptons {headers?: any = {}; / / additional request header loading?: boolean = true; / / whether to display loading loadingTime?: number = 2 / / display loading time auth?: boolean = true; / / whether authorization onlyData?: boolean = true is required / / only return data} / * enumerate the types that can be returned by the API *-T, T [] take effect when ApiOptons.onlyData is true *-ResponseData, ResponseData is effective when ApiOptons.onlyData is false *-ResponseData generally takes effect when an exception occurs inside the interface * / export type ResultDataType = | T | T [] | ResponseData | ResponseData | ResponseData
The axios is encapsulated inside the Service file:
Import axios, {AxiosRequestConfig, AxiosResponse} from 'axios'; import {ApiOptons, ResponseData, ResultDataType} from'. / Common'; / * simulate UI loading * / class Toast {static loading (txt: string, time: number = 3) {console.log (txt, time); return 1;} static info (txt: string, time: number = 3) {console.log (txt, time); return 1 } static remove (toastId: number) {console.log (toastId);}} / * * unknown (default) error code * / const codeUnknownTask =-999; / * * API request encapsulation base class * / export class InterfaceService {/ * todo * / private static userProfile: {sysToken?:''} = {}; public static setUser (_ user: any) {InterfaceService.userProfile = _ user } constructor (props: ApiOptons) {this.options = props;} / * default configuration * / public options = new ApiOptons (); / * * todo * / public get sysToken (): string {return InterfaceService.userProfile?.sysToken?'' } / * build header * / public get headers (): Object {return {Accept: 'application/json',' Content-Type': 'application/json; charset=utf-8',' app-info-key': 'xxx', / / Custom Field};} / * request precondition. You can reconstruct this function * / preCheck () {if (this.options.loading & & this.options.loadingTime > 0) {return Toast.loading ('loading...', this.options?.loadingTime? 3);} return-1 according to your situation. Download json and return the object * / public static async getJSON (url: string) {try {const res = await fetch (url); return await res.json ();} catch (e) {console.log (e); return {} } / * API request encapsulation (axios version, other versions of requests can also be encapsulated) * / export class InterfaceAxios extends InterfaceService {constructor (props: ApiOptons) {super (props) } / * Encapsulation axios * / private request = (requestCfg: AxiosRequestConfig): Promise = > {return axios (requestCfg) .then (this.checkStatus) .catch ((err: any) = > {/ / background interface exception, such as interface failure, http status code non-200and data non-json format, it is determined to be fatal error console.log (requestCfg, err) Return {code: 408, message: 'network exception',};});}; / * check network response status code * / private checkStatus (response: AxiosResponse) {if (response.status > = 200 & & response.status)
< 300) { return response.data; } return { code: 408, message: '网络数据异常', }; } /** * 发送POST请求 */ public async post(url: string, data?: any) { const toastId = this.preCheck(); const ret = await this.request({ url, headers: this.headers, method: 'POST', data: Object.assign({ sysToken: this.sysToken }, data), }); ret.toastId = toastId; return ret; } /** * 发送GET请求 */ public async get(url: string, params?: any) { const toastId = this.preCheck(); const ret = await this.request({ url, headers: this.headers, method: 'GET', params: Object.assign({ sysToken: this.sysToken }, params), }); ret.toastId = toastId; return ret; } } export class ServiceManager { /** * 检查接口数据 */ public hasNoError(res: ResponseData) { if (res.toastId >0) {Toast.remove (res.toastId);} if (res?.code! = = 0 & & res.code! = = codeUnknownTask) {Toast.info (res?.message?? Return false;} return true;} / * * parse response * / public static parse (modal: {new (x: any): t}, response: any, options: ApiOptons,): ResultDataType {if (! response | |! response.data) {response.data = new modal ({}) } else {if (response.data instanceof Array) {response.data = response.data.map ((item: t) = > new modal (item));} else if (response.data instanceof Object) {response.data = new modal (response.data);} return options.onlyData? Response.data: response;}} / * post API request * / public static async post (modal: {new (x: any): t}, url: string, body?: any, options: ApiOptons = new ApiOptons (),): Promise {/ / use merging to reduce external incoming configuration options = Object.assign (new ApiOptons (), options); const request = new InterfaceAxios (options) If (options.auth & &! request.sysToken) {return {code: 403, message: 'unauthorized',};} try {const response = await request.post (url, body); return ServiceManager.parse (modal, response, options);} catch (err) {/ / record error log console.log (url, body, options, err) Return {code: codeUnknownTask, message: 'internal error, please try again later',} }} / * get API request * / public static async get (modal: {new (x: any): t}, url: string, params?: any, options: ApiOptons = new ApiOptons (),): Promise {/ / use merge to reduce external incoming configuration options = Object.assign (new ApiOptons (), options); const a = new InterfaceAxios (options) Const request = new InterfaceAxios (options); if (options.auth & &! request.sysToken) {return {code: 403, message: 'unauthorized',} try {const response = await a.get (url, params); return ServiceManager.parse (modal, response, options) } catch (err) {/ / record error log console.log (url, params, options, err); return {code: codeUnknownTask, message: 'internal error, please try again later',};}
The content in the Service file is a bit long, with the following categories:
Toast: simulates the loading when an API is requested, which can be configured when the API is called
InterfaceService: the base class of the interface request, which internally records the Token of the current user, multi-environment server address switching (not implemented in the code), interface configuration for a single request, custom Header, logic check before the request, and direct request for remote JSON configuration files.
InterfaceAxios: inherits from InterfaceService, which is the axios version of the API request, and initiates the actual request internally. You can encapsulate the fetch version.
ServiceManager: the request class provided to Model. After passing the response Model and the corresponding server address, the Data of the corresponding data is parsed into the corresponding Model after the asynchronous request gets the data.
Import {ResponseData, ApiOptons, SimpleModel} from'. / model'; / / three different request SimpleModel.get ({id:'1'}) .then ((data: ResponseData) = > setTimeout () = > console.log ('internal exception due to authorization, return ResponseData:', typeof data, data,), 1000,) SimpleModel.get ({id:'1'}, {auth: false, onlyData: false},) .then ((data: ResponseData) = > setTimeout () = > console.log ('set to return all data, return ResponseData or ResponseData', typeof data, data,), 2000,)) SimpleModel.get ({id:'1'}, {auth: false, onlyData: true},) .then ((data: SimpleModel) = > setTimeout (()) = > console.log ('return only critical data data, return T or T []:', typeof data, data, data.sexText (), 3000,))
The console prints the results.
Internal exception due to authorization required. ResponseData:object {code: 403, message: 'unauthorized'} setting returns all data Return ResponseData or ResponseData object {code: 0, message: '1percent, data: SimpleModel {_ _ ownerID: undefined, _ values: List {size: 2, _ origin: 0, _ capacity: 2, _ level: 5, _ root: null, _ tail: [VNode], _ _ ownerID: undefined, _ _ hash: undefined Return T or T []: object SimpleModel {_ _ ownerID: undefined, _ values: List {size: 2, _ origin: 0, _ capacity: 2, _ level: 5, _ root: null, _ tail: VNode {array: [Array], ownerID: OwnerID {}}, _ _ ownerID: undefined, _ _ hash: undefined _ _ altered: false}} Men finally add a common compound type Model example: / * complex type * / const ComplexChildOneDefault = {name: 'lyc', sex: 0, age: 18,} Const ComplexChildTwoDefault = {count: 10, lastId: '20200607,}; const ComplexChildThirdDefault = {count: 10, lastId:' 20200607,}; / / const ComplexItemDefault = {/ / userNo: 'us1212', / / userProfile: ComplexChildOneDefault, / / extraFirst: ComplexChildTwoDefault, / / extraTwo: ComplexChildThirdDefault, / / compound type is recommended to use class instead of object above. Class ComplexItemDefault {userNo = 'us1212'; userProfile = ComplexChildOneDefault; extraFirst? = ComplexChildTwoDefault; extraSecond? = ComplexChildThirdDefault;} / / const ComplexListDefault = {/ / list: [], / / pageNo: 1, / / pageSize: 10, / / pageTotal: 0, / /}; / / there is a compound type of array elements. If you want to specify the Model of array elements, you must use class class ComplexListDefault {list: ComplexItemDefault [] = []; pageNo = 1; pageSize = 10 PageTotal = 0;} interface ComplexModelParams {id: string;} / / because of the class used, new is required to initialize Record export class ComplexModel extends Record (new ComplexListDefault ()) {static async get (params: ComplexModelParams, options?: ApiOptons) {return await ServiceManager.get (ComplexModel, 'http://localhost:3000/test2', params, options,);}}
Here is the calling code:
ComplexModel.get ({id:'2'}) .then ((data: ResponseData) = > setTimeout () = > console.log ('internal exception due to authorization, return ResponseData:', typeof data, data,), 1000,) ComplexModel.get ({id:'2'}, {auth: false, onlyData: false},) .then ((data: ResponseData) = > setTimeout () = > console.log ('set to return all data, return ResponseData or ResponseData', typeof data, data.data.toJSON (),), 2000,)) ComplexModel.get ({id:'2'}, {auth: false, onlyData: true},) .then ((data: ComplexModel) = > setTimeout (()) = > console.log ('return only critical data data, return T or T []:', typeof data, data.toJSON (),), 3000,)
Then there is the print result.
ResponseData:object {code: 403, message: 'unauthorized'} setting returns all data, ResponseData or ResponseData object {list: [{userNo: '1permission, userProfile: [Object]}], pageNo: 1, pageSize: 10, pageTotal: 0} returns only critical data data Return T or T []: object {list: [{userNo: '1customers, userProfile: [Object]}], pageNo: 1, pageSize: 10, pageTotal: 0} Thank you for your reading. This is the content of "the Advanced Application of TypeScript in Model". After the study of this article, I believe you have a deeper understanding of the advanced application of TypeScript in Model, and the specific use needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.