
# 华为云Astro轻应用脚本开发规则及约束限制
 #### 脚本性能检查规则
在华为云Astro轻应用脚本编辑器中编写脚本代码时，请仔细阅读如下内容，了解脚本性能的检查规则。在华为云Astro轻应用标准页面和高级页面中，对页面性能进行分析时，会分析页面所引用脚本的性能。如果脚本存在性能问题，界面会通过弹框提示您，方便您直观的发现脚本中的问题。如何在标准页面和高级页面进行性能分析，请参见[查看华为云Astro轻应用标准页面性能](https://support.huaweicloud.com/usermanual-astrozero/astrozero_05_9057.html)、[查看华为云Astro轻应用高级页面性能](https://support.huaweicloud.com/usermanual-astrozero/astrozero_05_9126.html)。
华为云Astro轻应用低代码平台对脚本进行性能检查遵循的总体原则是：主要检查SELECT语句中，WHERE查询字段是否创建索引，另外对SELECT查询字段和WHERE限制条件进行检查。
- 静态检查规则
  - 规则1：SELECT语句需要带上where条件 SELECT语句中缺少查询条件，存在由于查询结果数量过大，导致脚本性能不佳的风险。请确认查询结果，如果查询结果数量过大，建议增加限制条件或采用分页查询，分页查询请见下方推荐代码示例。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select object_name from object_demo order by createddate desc")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select object_name from object_demo where id = ? order by createddate desc")
    ```
    推荐代码示例：
    ```
    import * as db from 'db';
    let se = db.sql()
    let sqlResult = []
    for(let i=0; i <1000; i++){
            let querySql = se.exec(`select object_name from object_demo limit ${5000 * i}, 5000`);
        sqlResult.push(querySql)
        if(querySql.length<5000){
            break
        }
    }
    ```
    
  
  - 规则2：SELECT语句中单次limit查询数量需要低于平台最高查询个数5000的限制 SELECT语句中单次limit查询数量大于平台限制个数，存在由于查询结果数量过大，导致脚本性能不佳的风险。如果查询结果过大，请判断是否需要增加限制条件或采用分页查询，分页查询请见下方推荐代码示例。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select object_name from object_demo  where object_type = 'Test' limit 100000")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select object_name from object_demo  where object_type = 'Test' limit 3000")
    ```
    推荐代码示例：
    ```
    import * as db from 'db';
    let se = db.sql()
    let sqlResult = []
    for(let i=0; i <1000; i++){
        let query_sql = se.exec(`select object_name from object_demo limit ${5000*i},5000`)
        sqlResult.push(query_sql)
        if(query_sql.length<5000){
            break
        }
    }
    ```
    
  
  - 规则3：SELECT语句中谨慎使用区间查询条件 SELECT语句中使用区间查询（"\<\>"、"\<"、"\>"、"\>="、"\<="），存在由于查询结果数量过大，导致脚本性能不佳的风险。如果必须采用区间查询，建议增加limit限制条件，以免影响查询效率。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select object_name from object_demo  where object_num > 50")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select object_name from object_demo  where object_num > 50 limit 3000")
    ```
    
  
  - 规则4：SELECT语句中查询字段不在表的索引库中 如果SELECT语句where条件中，查询字段并未创建索引，请判断该字段是否需要创建索引，以提高代码查询效率。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select object_name from object_demo  where object_id = ?")
    ```
    表"object_demo"中的"object_id"并没有创建索引。
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select object_name from object_demo where id = ?")
    ```
    表"object_demo"中的"id"创建了索引。
    
  
  - 规则5：SELECT语句中尽量避免同时从大于等于4张表中取数据 SELECT语句中，进行多表关联查询时，尽量不要同时从大于或等于4张表中获取数据。如果必须要查询大于或等于4张表的数据时，建议先通过关联少于4张表进行查询，然后根据查询结果再做关联查询，保证每次关联查询的表数量少于4，以提高查询效率。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select a.item as item1, b.item as item2, c.item as item3 ,d.item as item4, e.item as item5 from object_demo1 as a, object_demo2 as b, object_demo3 as c ,object_demo4 as d, object_demo5 as e where a.id=b.objectid and b.name = c.objectid and c.name = d.objectid and d.name = e.objectid and e.name ='Test'")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let sqlResult1 = db.sql().exec("select c.item as item3, d.item as item4, e.item as item5 from object_demo3 as c ,object_demo4 as d, object_demo5 as e where c.name = d.objectid and d.name = e.objectid and e.name ='Test'")
    let sqlResult = []
    let item3s = sqlResult1.map(item => item.item3).map(val => `"${val}"`).join(",");
    let sqlResult2 = db.sql().exec('select a.item as item1, b.item as item2, c.item as item3 from object_demo1 as a, object_demo2 as b, object_demo3 as c where a.id=b.objectid and b.name = c.objectid and c.item in ('+item3s+')')
    for (let i in sqlResult1) {
      const result1Item = sqlResult1[i];
      for (let j in sqlResult2) {
        const result2Item = sqlResult2[j];
        if (result1Item.item3 === result2Item.item3) {
          sqlResult.push({
            item1: result2Item.item1,
            item2: result2Item.item2,
            item3: result1Item.item3,
            item4: result1Item.item4,
            item5: result1Item.item5
          });
        }
      }
    }
    ```
    
  
  - 规则6：SELECT语句中聚集函数必须增加别名 SELECT语句中，聚合函数必须使用别名方式存储查询结果，以免因聚合函数返回的结果不一致，导致存在兼容性问题。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select count(*) from object_demo where object_name = 'Test'")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select count(*) as count from object_demo where object_name = 'Test'")
    ```
    推荐代码示例：
    ```
    **聚合函数示例**
    select count(*) as count_res,
    select max(*) as max_res,
    select min(*) as min_res,
    select avg(*) as avg_res,
    select sum(*) as sum_res
    ```
    
  
  - 规则7：SELECT语句中严禁使用"select from..."形式查询语句 严禁使用"select ..."形式查询语句，请指出select的具体字段。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select from object_demo where object_name = 'test'")
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = db.sql().exec("select id, object_type from object_demo where object_name = 'test'")
    ```
    
  
  - 规则8：SELECT语句中拼接的参数值请谨慎使用入参变量 SELECT语句中，拼接的参数值请谨慎使用入参变量，以免引起SQL注入的风险。
    错误代码示例：
    ```
    import * as db from 'db';
    let errorDemo = "select id,name from object_demo where id = ";
    errorDemo += input.parameter 
    let errorDemoResult = db.sql().exec(errorDemo)
    ```
    其中，"input.parameter"为脚本入参。
    正确代码示例：
    ```
    import * as db from 'db';
    let correctDemo = "select id,name from object_demo where id = ?";
    let correctDemoResult = db.sql().exec(correctDemo, { params: [input.parameter] })
    ```
    其中，"input.parameter"为脚本入参。
    
  
  - 规则9："for"循环中请谨慎使用SELECT语句 "for"循环中，请谨慎使用SELECT语句，以免后续进行结果赋值时，由于数据过多，导致内存溢出。
    错误代码示例：
    ```
    import * as db from 'db';
    for (let i = 0; i < input.para.length; i++) {
      db.sql().exec("select id,name from object_demo where id = 'Test'");
    }
    ```
    正确代码示例：
    ```
    import * as db from 'db';
    db.sql().exec("select id,name from object_demo where id = 'Test'");
    ```
    
  
  - 规则10：SELECT语句中谨慎使用order by SELECT语句中，请谨慎使用order by。如果需要使用order by，请为排序字段增加索引，以提高查询效率。如果无法增加索引，需要关注是否存在查询性能低下的风险。
    如果"object_demo"对象中，创建了"createdDate"索引，可使用如下语句，如果未创建索引，则不建议使用。
    ```
    import * as db from 'db';
    let errorDemo = db.sql().exec("select object_name from object_demo  where object_id = 'Test' Order by createdDate")
    ```
    
   
- 动态检查规则 脚本运行时，触发动态检查。动态检查规则以静态检查规则为基础，无法检查SQL注入以及for循环中的SQL项。
  - 当存在脚本性能问题时，通过弹框界面提示给用户，方便用户直观发现脚本中的问题。
  
  - 当不存在性能问题时，便不展示弹框页面，以免影响用户开发。
   
 
#### 脚本开发规范及限制
在华为云Astro轻应用脚本编辑器中编写脚本代码时，建议您先了解脚本编写的规范和限制，便于快速地完成脚本代码的编写。
- 命名限制 所有的名称定义要能体现其作用，避免使用缩写（专有名词除外）。
  - 脚本采用小驼峰命名，例如createDeviceInstance。
  
  - 结构体（struct）采用大驼峰命名，例如QueryPaymentResult。
  
  - 结构体内的字段采用小驼峰命名，例如customerName 。
  
  - 类、枚举值和接口采用大驼峰命名。
    ```
    class Comments {...}
    interface Console {...}
    enum Direction { Up = 1, Down, Left, Right};
    ```
    
  
  - 函数采用小驼峰命名。
    ```
    addOfferingAssociatePriceAndStock(input: Input): Output {...}
    ```
    
  
  - 属性或变量采用小驼峰命名。
    ```
    class Comments {
               userId: String;
               content: String;
           }
    let oneComments = new Comments();
    ```
    变量为单数时，命名包含对象名称，如level1Catalog、level2Catalog。变量为复数时，命名包含集合名称，如attributeRelationRuleList。
    
   
- 脚本定义规则
  - 每个字段均需要定义type、label、description、required和isCollection。
  
  - 当字段为集合类型时，需要定义成"\[\]"。
    ```
    @action.param({
        type: "Attribute",
        label: "Attribute",
        description: "attributeList",
        required: false,
        isCollection: true
    })
    attributeList: Attribute[];
    ```
    
  
  - @action.object时，需要在脚本中详细定义清楚Object，不要引用其他脚本的Object。
  
  - 如果是嵌套结构体，则从下到上粒度依次变小。
    ```
    export class ProductObject {
           //ignore
    }
    export class StockObject {
           //ignore
    }
    export class ProductStock {
        @action.param({ type: 'Struct', label: "ProductObject", isCollection: false })
        productInformation: ProductObject;
        @action.param({ type: 'Struct', label: "StockObject", isCollection: false })
        stock: StockObject;
    }
    @action.object({ type: "param" })
    export class Input {
        @action.param({ type: 'String', label: "productId", isCollection: false })
        productId: String;
    }
    ```
    
  
  - 不需要定义多个Output对象，可以直接在方法使用定义的对象出参。 【推荐】：
    ```
    @action.object({ type: "param" })
    export class Input {
        @action.param({ type: 'String', label: "productId", isCollection: false })
        productId: String;
    }
    export class GetProductStock {
    @action.method({ input: "Input", output: "ProductStock", label: 'getProductStock' })
    getProductStock(input: Input): ProductStock {}
    }
    ```
    【不推荐】：如果出参对象包含从外部引入的对象，还是要按该方式定义。
    ```
    @action.object({ type: "param" })
    export class Input {
        @action.param({ type: 'String', label: "productId", isCollection: false })
        productId: String;
    }
    @action.object({ type: "param" })
    export class Output {
        @action.param({ type: 'Object', isCollection: false })
        ProductStock: ProductStock;
    }
    @action.object({ type: "param" })
    export class getProductStock {
    @action.method({ input: "Input", output: "Output", label: 'getProductStock' })
    getProductStock(input: Input): Output {}
    ```
    
  
  - 除非业务有特殊要求，新增修改脚本不返回结果码和结果信息。
   
- 脚本注释的原则和写法
  无用的代码不能以注释形式存在，能用代码说明的尽量不要添加注释，脚本注释尽可能简洁。建议注释统一用英文，出入参不必写注释说明。
  - 在函数方法和结构体的元数据描述上，添加注释。
    ```
    /**
    * 根据产品ID查询产品详情信息
    */
    @action.method({ input: "Input", output: "Output", label: 'queryProductDetailForCart' })
    ```
    
  
  
  
  - 方法内关键业务语句前，必须添加注释。
    - 方法内的单行注释以"//"开头，应放在相关代码的上方或右方，不可放于下方。如果放于上方，注释需与前面的代码间用空行隔开。
    
    - 注释与代码的比例没有量化标准。在删掉所有的代码内容、仅保留代码层级结构和注释，如果通过注释，可以很容易理解方法内都做了哪些事情（类似于伪代码），则认为注释比例合理。
    
    
    ```
    // 校验密码是否正确
    let password = input.password;
    if (accountRecord.Password !== password) {
    error.name = "CM-001003";
    error.message = "Invalid loginId or password.";
    throw error;
    }
    ```
    
    
- 脚本间相互引用规则
  - 脚本中不要包含没用到的标准库或对象的引用。
    例如，如果实际没用到sys模块的任何方法，则不要包含如下语句：
    ```
    import * as sys from 'sys';
    ```
    如果实际上只用到了CA_PC_Offering对象，则示例语句中其他对象需要删除：
    ```
    @useObject(['CA_PC_FeeItemDef', 'CA_PC_Offering', 'CA_PC_CatalogOffering', 'CA_PC_Catalog', 'CA_PC_OfferingAttribute'])
    ```
    
  
  - 只引用使用到的对象，而不用import \*。 【推荐】：
    ```
    import { OfferingObjectQuery } from './ca_pc__getOfferingObject';
    import { setI18nError } from 'context';
    import { sql } from 'db';
    ```
    【不推荐】：
    ```
    import * as queryOfferingObjectAction from './ca_pc__getOfferingObject';
    import * as context from 'context';
    import * as db from 'db';
    ```
    
  
  - @action.method属于必须的方法注解，写在方法上面。
    ```
    @action.method({ input: "Input", output: "Output", label: 'queryClassification' })
    queryClassification(input: Input): Output {
    ...
    }
    ```
    
  
  - 为方便页面直接调用脚本，仅在脚本最后统一导出需要的对象，而不是导出所有对象。如果不需要，可以不用导出。 例如，getOfferingObject脚本中最后一行导出OfferingObjectQuery对象：
    ```
    export let theAction = new OfferingObjectQuery();
    ```
    
  
  - 可以把公共的对象Object定义，按分类放在一些公共的脚本里（例如pc_XXX.ts、cm_XXX.ts），其他需要的脚本直接引用即可。
   
- 语法规则
  - 对象判空 单个对象示例：
    ```
    if(object){
        //ignore
    }
    ```
    集合对象示例：
    ```
    if(collection&&collection.length!=0){
        //ignore
    }
    ```
    
  
  - 数字类型统一定义成"Number"、日期类型定义成"Date"。
    ```
    @action.param({
        type: "Date",
        label: "effectiveTime",
        description: "Date."
    })
    effectiveTime: Date;
    @action.param({
        type: "Number",
        label: "salePrice",
        description: "salePrice."
    })
    salePrice: Number;
    ```
    
  
  - 变量或数组定义时，要说明类型，集合需要加上泛型。
    ```
    let isDone:Boolean = false;
    let decLiteral : number =6;
    let productList = new Array<ProductObject>();
    ```
    
  
  - 遍历循环推荐用"forEach"，不推荐用"for...in"。
    ```
    testArray.forEach((value, index, array )=>{
           //ignore
    });
    ```
    
  
  - 推荐用"let"变量声明，不推荐用"var"。 【推荐】：
    ```
    let offeringId : String = "aaa";
    ```
    【不推荐】：
    ```
    var offeringId = "aaa";
    ```
    
  
  - 代码有对象声明时，推荐使用".fieldName"获取对象的字段，而不是用"\['fieldName'\]"。 【推荐】：
    ```
    offeringStruct.id = result[i].base.offeringId;
    ```
    【不推荐】：
    ```
    offeringStruct.id = result[i]['base']['offeringId'];
    ```
    
  
  - 在需要默认值的情况下，使用"\|\|"代替"if"判断。
    ```
    function(value){
           //使用||对value进行判空
           value = value || "hello"
    }
    ```
    
  
  - 创建一个已知对象时，推荐用"new"方法。 【推荐】：
    ```
    let offeringIdRequest = new OfferingIdRequest();
    offeringIdRequest.catalogList = catalogList;
    offeringIdRequest.classificationList = classificationList;
    let getOfferingIdByConditionInput = {
           "OfferingIdRequest": offeringIdRequest
    }
    ```
    【不推荐】：
    ```
    let getOfferingIdByConditionInput = {
           "OfferingIdRequest": {
                  "catalogList": catalogList,
                  "classificationList": classificationList,
                  "status": "",
                  "stockCondition": "",
                  "keyWord": "",
                  "offset": "",
                  "limit": ""
           }
    }
    ```
    
  
  - 函数定义
    ```
    let traitRec = function(xxxx, xxx) {
        //ignore
     }
    ```
    只能在函数定义后的语句中，使用该函数。
    
  
  - 没有初始化值的变量申明，使用"undefined"，不要使用"null"。
    ```
    let object = undefined;
    ```
    
  
  - 局部变量需要在"class"内定义，不要在全局命名空间内定义类型/值（即不要在"class"外定义变量），常量可以定义成全局。
    ```
    let identityIdList = [];
    ```
    
  
  - 使用"lambda"表达式代替匿名函数。 只有需要时，才把"arrow"函数的参数括起来。正确使用"arrow"的示例如下：
    ```
    x => x + x
    (x,y) => x + y
    <T>(x: T, y: T) => x === y
    ```
    
   
- 脚本SQL语句规则
  - 不推荐用拼接SQL方法，避免注入风险。
    ```
    let sql = "select id,name,ExternalCode,FeeType,FeeItemName,FeeItemDescription,Remark,Status from CA_PC_FeeItemDef where 1=1";
    if (!InputParams.id && !InputParams.name && !InputParams.feeType && !InputParams.feeItemName && !InputParams.status) {
           context.setI18nError("ca_pc__001013");
           return;
    }
    if (InputParams.id) {
           sql += "and id ='" + InputParams.id + "'"
    }
    ```
    
  
  - 多表复杂查询建议使用"sql.exec()"或"sql.execute()"方法，"execute()"方法比"exec()"多返回字段集和操作成功数。
    ```
    let result = execsql.exec("select id,name,OfferingId,ParentId,SkuCode,ChannelId,Status,ProductLabel,PriceCode,DefaultChoose,PaymentType from CA_PC_Product where id in (" + str + ")",
    {
           params: productId
    });
    ```
    多表复杂查询只能使用拼接SQL方法，但是有限制，例如示例中的"str"要求如下：
    - str如果来源于入参，则入参在拼接SQL之前需要进行校验，以免引入SQL注入攻击。如果来源于内部数据，可以不进行校验。
    
    - 不推荐直接在SQL中拼接入参，应该采用在SQL中拼接占位符，然后把入参放入参数的数组中，例如：
      ```
      attriSql = "select AttrDef from DE_DeviceDefAttri where DeviceDef=? and ExternalCode=? and AttrType='DYNAMIC' and ValueType='1'";
      attriRecords = db.sql().exec(attriSql, {
        params: [deviceDefId, defExternalCode]
       });
      ```
      
     
  
  - 对于单表查询和增删改SQL，推荐使用Orm接口方法。 Options选项可以选择返回字段、排序、聚合运算和分页等功能。当value不存在时，默认为null类型。
    ```
    let CA_PC_Stock = db.object('CA_PC_Stock');
    let amountCount = 0;
    if (UpdateStock.amount) {
           let record = { Amount: UpdateStock.amount };
           amountCount = CA_PC_Stock.updateByCondition({
                  "conjunction": "AND",
                  "conditions": [{
                         "field": "SkuCode",
                         "operator": "eq",
                         "value": UpdateStock.skuCode
                  }]
           }, record);
    }
    ```
    
  
  - 避免在循环中调用方法和操作数据库，可以用"in"来查询在集合中的结果。
    ```
    let productIdList = input.productId;
    let str = "";
    let arr = [];
    for (let i = 0; i < productIdList.length; i++) {
           arr[i] = "?";
    }
    str = arr.toString();
    let result = execsql.exec("select id,name,OfferingId,ParentId,SkuCode,ChannelId,Status,ProductLabel,PriceCode,DefaultChoose,PaymentType from CA_PC_Product where id in (" + str + ")",
    {
           params: productIdList
    });
    ```
    
  
  - 对sql进行优化时，尽量使用有索引的字段，避免使用没有索引的字段。
  
  - 批量操作数据库时，尽量使用已封装好的批量操作接口。 例如，Orm.batchInsert、Orm.batchUpdate接口和Orm.deleteByCondition批量删除接口。如果批量创建父子对象记录，且批量创建的记录在一个完整的事务中，全部成功或全部失败，建议使用Orm.compositeInsert接口。
    ```
    var s = db.object('Customer__CST');
    var records = [];
    var record = {
        "name": "hello",
        "count__CST": 123,
        "Contacts": {
            "records": [
                {
                    "name": "hello_contact1"
                },
                {
                    "name": "hello_contact2"
                }
            ]
        }
    };
    records.push(record);
    var ids = s.compositeInsert(records);
    console.log("id list = ", ids);
    count = s.count();
    console.log("record count = ", count);
    ```
    
  
  - 匹配查询推荐用like，日期比较推荐用"\<"、"\>"。 【推荐】：
    ```
    select id from t where name like ‘abc%’
    select id from t where createdate>=’2005-11-30’ and createdate<’2005-12-1’
    ```
    【不推荐】：
    ```
    select id from t where substring(name,1,3)=’abc’
    select id from t where datediff(day,createdate, ‘2005-11-30’)
    ```
    
  
  - 使用exists替代in，使用not exists替代not in。 【推荐】：
    ```
    SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB’)
    ```
    【不推荐】：
    ```
    SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC =’MELB’)
    ```
    
  
  - 避免在索引列上使用is null和is not null，会造成索引失效。 【推荐】：
    ```
    SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0
    ```
    【不推荐】：
    ```
    SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL
    ```
    
  
  - 注意事项
    - 尽量避免Select子句中，使用"\*"。
    
    - 尽量避免在where子句中，使用!=或\<\>操作符。
    
    - 尽量避免在where子句中，对字段进行函数操作。
    
    - 尽量避免在SQL中，使用 "!=" 、"\|\|"、"+"符号。
    
    - 尽量避免在where条件中，做数据筛选。
    
    - 尽量避免查询中，按字段排序。
    
    - 尽量避免多表关联查询和嵌套查询，不要使用超过2表的关联查询。
    
    - 不要在循环内重复使用同一条件查询，应该在循环外处理。例如，公共数据仅在循环外查询一次。
    
    - 不要在同一个脚本的多个方法内，使用同一条件多次查询，可以定义类的成员变量。
    
    - 避免关联查询 关联的条件很多情况下都是唯一的，可以提前做单独查询。例如，使用公共数据作为后续条件，避免关联查询。
      
    
    - 避免频繁的数据库交互 例如，查询5000条数据，查询补充数据的时候，不要在循环内多次交互数据库，把可以合并的条件在循环外拼接并进行一次性查询，在循环内只需要从结果集中获取数据，可以极大提升查询性能。
      
    
    - 尽量利用对象做临时缓存 例如查询到DeviceDef后，按照"id:Object"的方式存起来，后续查询时先判断缓存对象中是否已经存在，如果存在则直接获取不再查询。
      
     
   
- 脚本代码风格限制
  - 每句话后面加分号，脚本写完右键选择"Format Document"统一格式。
  
  - string类型赋值统一使用双引号，获取字段统一使用单引号。
  
  - 相关代码写在一起，不相关逻辑最好以空行隔开。
  
  - 总是使用"{}"把循环体和条件语句括起来。
  
  - 开始的"{"总是在同一行。
  
  - 小括号里开始不要有空白逗号，冒号、分号后要有一个空格。
    ```
    for (var i = 0, n = str.length; i < 10; i++) { }
    if (x < 10) { }
    function f(x: number, y: string): void { }
    ```
    
  
  - 每个变量声明语句只声明一个变量。 例如，使用如下方式
    ```
    var x = 1; var y = 2;
    ```
    而不是下面的方式
    ```
    var x = 1, y = 2;
    ```
    
  
  - else要在结束的"}"后，另起一行。
  
  - 一个函数仅完成一件功能，即使是简单功能也应该编写单独的方法实现。
  
  - 单个方法的方法体不要太长，建议控制在150行以内，保证代码可读性，也方便维护、测试。
   
- 脚本扩展名限制 因为脚本实际上是存在数据库中的，所以脚本没有路径的概念，扩展名也没有特别的意义。
  导入模块时，尽量采用不带扩展的方式。如果必须要带扩展名，只允许".ts"扩展名文件。
  ```
  import * as circle from './circle';
  ```
  
- 脚本循环依赖限制 当循环调用模块时，一个模块可能在未完成执行时被返回。因此，需要仔细地规划模块间调用，以允许循环模块依赖在应用程序内正常工作。
  例如，脚本a：
  ```
  console.log('a 开始');
  exports.done = false;
  import * as b from 'b';
  console.log('在 a 中，b.done = ', b.done);
  exports.done = true;
  console.log('a 结束');
  ```
  脚本b：
  ```
  console.log('b 开始');
  exports.done = false;
  import * as a from 'a';
  console.log('在 b 中，a.done = ', a.done);
  exports.done = true;
  console.log('b 结束');
  ```
  脚本main：
  ```
  console.log('main 开始');
  import * as a from 'a';
  import * as b from 'b';
  console.log('在 main 中，a.done = ', a.done, '，b.done = ', b.done);
  ```
  当main加载a时，a又加载b。 此时，b又会尝试去加载a。 为了防止无限的循环，会返回一个a的exports对象的未完成的副本给b模块。然后b完成加载，并将exports对象提供给a模块。当main加载这两个模块时，都已完成加载，因此该程序的输出会是：
  ```
  main 开始
  a 开始
  b 开始
  在 b 中，a.done = false
  b 结束
  在 a 中，b.done = true
  a 结束
  在 main 中，a.done = true，b.done = true
  ```
  
 
