二、 在Sails中使用Typescript

2023-11-03

Typescript 基础

Typescript 安装

Sails推荐的编程语言是javascript,但是Typescript的强类型的确会让编程过程变得更加愉快一点。

  1. 要使用Typescript,首先需要安装三个库
npm install typescript ts-node --save
npm install @types/node --save
npm install @types/express --save

npm 安装后面加–save参数,可以把安装的依赖库保存到package.json里面

  1. 安装完ts之后,需要执行初始化,帮助生成ts配置文件
npx tsc --init

执行之后,在项目根目录下面会多出一个tsconfig.json文件
3. 配置tsconfig.json,更多的配置可以查看TS官网文档

"compilerOptions": {   
    "target": "es2016",           
    "module": "commonjs",     
    "paths": {
      "@/*": ["./api/*"],      
      "@/typing/*": ["./typing/*"],
    },
    "allowJs": true,  
    "esModuleInterop": true,     
    "forceConsistentCasingInFileNames": true,   
    "strict": true,    
    "suppressImplicitAnyIndexErrors": true,      
    "skipLibCheck": true         
  },
  "include": [
    "api/**/*",
    "api/*",
    "typing/**/*",
    "typing/*",    
  ],
  "exclude": ["node_modules", "build", "dist", "scripts", ".tmp/*"]
  1. 最后,在app.js里面添加如下语句
require('ts-node/register');
  1. 使用Ts之后,不再使用sails lift,需要改成node app.js

TypeScript 问题

1、tsconfig.json
有时候,tsconfig.json文件在VsCode编译器里面会出现标红(不影响使用),提示错误如下:
报错内容:JSON schema for the TypeScript compiler’s configuration file,无法写入文件“xxxx/xxx.js”,因为它会覆盖输入文件。这种情况虽然不影响使用,但是会害死强迫症,可以用如下方式解决:
在tsconfig.json文件的配置中添加配置,保存配置文件后重启vscode就可以了

"compilerOptions": {
   "outDir":"dist"
}
  1. tsc 编译并测试(需要依赖tsconfig.json)
    如果有需要,需要手动编译对.ts文件为js文件,可以使用tsc进行编译
tsc -p .  //-p 指明tsconfig.json的文件路径 "."表示配置文件在当前目录
node dist/test/test.js //dist是tsconfig.json里面指定outdir的目录,test/test.js是要测试的js文件

3、typescript 用索引访问对象问题
有时候需要用object[key]的方式访问未知对象key的属性,类似下面的代码

for (const key in obejct) {
	// 处理...
	obejct[key]
	....
}

在ts里面,如果是any类型的时候,会报错如下:
元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型 “Object“。 在类型 “Object“ 上找不到具有类型为 “string“ 的参数的索引签名
解决方法是:
在tsconfig.json中compilerOptions里面新增忽略的代码,如下所示,添加后则不会报错

"suppressImplicitAnyIndexErrors": true

最简单的改造

前面的准备工作都做好了之后,我们已经可以用ts写一个控制器了,类似代码如下:

export function hello(req:any, res:any, next: Function):any {
  res.status(200).send('Hello from Typescript!');
}

大家可以和我们原来的控制器做个比较,我们发现区别不是很大,module.exports 改成export,其它都是any类型。any类型是一种最懒惰的类型,如果只是这样的“强”类型,对我们后面的工作其实是没有什么意义的。我们希望的是能够正在在我们使用变量的时候能够得到类型提醒和受到类型约束。目前的这种改造显然是不够的。

Sails重定义

要达到真正对后面的编程有强类型约束,我们需要做两个事情,一个是把sails里面的waterline(ORM)这部分消化掉,并且写成相关联的typing(.d.ts)。一个是要把api的请求(request)和响应(response)消化掉,写出相应的interface

为了规划整个App的文件结构,我们在根目录下面新建一个typing文件夹,用来存放这个App所需要的类型定义

Waterline(Orm)

根据Sails文档,我们所有数据模型Model都是以Waterline捆绑在一起的,当我们写了一个user的model的时候,实际上我们继承了waterline已经多好的许多功能,比如create函数,attribute属性等等。
创建一个model的时候,我们需要提供这个model的attributes(相当于数据库中的字段信息),我们还需要继承waterline已经做好的功能函数比如create,find等。然后我们使用一个model的时候,还需要提供它作为对象本身的可以在js里面直接操作的属性(object的key)

  1. 关于attribute的定义
interface AttributeObject {
    /**
     * 类型
     */
    type: 'string' | 'number' | 'boolean' | 'json' | 'ref',
    /**
     * 默认值
     */
    defaultsTo?: any,
    /**
     * 是否自动增长
     */
    autoIncrement?: boolean,
    /**
     * 唯一性
     */
    unique?: boolean,
    /**
     * 是否允许空值
     */
    allowNull?: booleab,
    /**
     * 是否加密字段,如果设置true,改字段自动加密
     */
    encrypt?: boolean,
    /**
     * 索引
     */
    index?: boolean,
    /**
     * 主键
     */
    primaryKey?: boolean,
    enum?: any[],
    size?: number,
    /**
     * 字段名称
     */
    columnName?: string,
    /**
     * 字段类型,如果没有指定,自动根据type类型转换成数据库类型
     */
    columnType?: string,
    special?: boolean,
    model?: string;
    collection?: string;
    via?: string;
    dominant?: boolean;
  }
  1. 常用函数定义
 /**
   * 模型函数
   */
  interface ModelMethods<instance extends ModelInstance> {
    destroy(): Promise<this>;
    toJSON(): any;
    save(): WaterlinePromise<this>;
    create(params: instance): WaterlinePromise<instance>;
    create(params: any): WaterlinePromise<instance>;
    create(params: any[]): WaterlinePromise<instance>;

    find(): QueryBuilder<instance[]>;
    find(criteria: any): QueryBuilder<instance[]>;

    findOne(criteria: any): QueryBuilder<instance>;
    findOne(criteria: string): QueryBuilder<instance>;
    findOne(criteria: number): QueryBuilder<instance>;
    findOne(): QueryBuilder<instance>;


    count(criteria: any): WaterlinePromise<number>;
    count(criteria: any[]): WaterlinePromise<number>;
    count(criteria: string): WaterlinePromise<number>;
    count(criteria: number): WaterlinePromise<number>;

    destroy(criteria: any): WaterlinePromise<instance[]>;
    destroy(criteria: any[]): WaterlinePromise<instance[]>;
    destroy(criteria: string): WaterlinePromise<instance[]>;
    destroy(criteria: number): WaterlinePromise<instance[]>;

    update(criteria: any, changes: any): WaterlinePromise<instance[]>;
    update(criteria: any, changes: any[]): WaterlinePromise<instance[]>;
    update(criteria: any[], changes: any): WaterlinePromise<instance[]>;
    update(criteria: any[], changes: any[]): WaterlinePromise<instance[]>;
    update(criteria: string, changes: any): WaterlinePromise<instance[]>;
    update(criteria: string, changes: any[]): WaterlinePromise<instance[]>;
    update(criteria: number, changes: any): WaterlinePromise<instance[]>;
    update(criteria: number, changes: any[]): WaterlinePromise<instance[]>;


    query(sqlQuery: string, cb: (err: Error, results: any[]) => void);

    native(cb: (err: Error, collection: any) => void);

    stream(criteria: any, writeEnd: any): NodeJS.WritableStream;
    stream(criteria: any[], writeEnd: any): NodeJS.WritableStream;
    stream(criteria: string, writeEnd: any): NodeJS.WritableStream;
    stream(criteria: number, writeEnd: any): NodeJS.WritableStream;

    stream(criteria: any, writeEnd: any): Error;
    stream(criteria: any[], writeEnd: any): Error;
    stream(criteria: string, writeEnd: any): Error;
    stream(criteria: number, writeEnd: any): Error;
  }

以上代码的完整版详见源代码里面typing文件夹中的Orm.d.ts,Api.d.ts 两文件

  1. 我们还需要重新定义好request和response,大致代码如下:(详见源代码)
import UpstreamModel from "./UpstreamModel";
/**
 * Request和Response等Api操作
 */
declare module Api {
    interface SailsRequest {
        url: string;
        baseUrl: string;
        originalUrl: string;
        method: string;
        headers: Dictionary<string>;
        body: any;
        hostname: string;
        params: any;
        writable: boolean;
        allowHalfOpen: boolean;
        method: string;
        params: any;
        query: Dictionary<string>;
        file(field: string): UpstreamModel.Upstream;
    }
    interface ResponseHandlerFn {
        (data: any, pathToView?: string): void;
    }

    interface Response {
        badRequest: ResponseHandlerFn;
        forbidden: ResponseHandlerFn;
        negotiate: (err: any) => void;
        notFound(data: any, pathToView?: string): void;
        notFound(): void;
        created(instance: any): void;
        send: ResponseHandlerFn;
        ok: ResponseHandlerFn;
        redirect: ResponseHandlerFn;
        status(statusCode: 200 | 101 | 301 | 302 | 400 | 401 | 403 | 404 | 500): Response;
        json: ResponseHandlerFn;
        location: ResponseHandlerFn;
        clearCookie: ResponseHandlerFn;
        serverError: ResponseHandlerFn;
        view(pathToView: string): void;
        view(pathToView: string, locals: any): void;
        view(locals: any): void;
        view(): void;
    }

}
export = Api;

以上代码的完整内容详见源代码typing文件夹内Api.d.ts QueryOptions.d.ts UpstreamModel.d.ts 三文件

这些都是根据Sails.js官网上面的文档设计出来的,并不完整,我们可以一边开发一边完善。
更多的内容参考https://sailsjs.com/documentation/reference

更多内容没法写在教程里面,请自行参考github上的源代码

重写Models

  1. 定义user的数据类型
    在typing 文件夹中做好waterline基础类型定义之后,我们可以在这个基础上做用户数据定义,在typing中新增UserModel.d.ts,新建UserModel模块,代码如下:
import Orm from "./Orm";
module UserModel {
  /**
   * 这个地方主要是实例参数
   */
  export interface UserInstanceProps extends Orm.ModelInstance {
    email: string;
    password?: string;
  }
  /**
   * 实例的属性
   */
  interface UserAttributes extends UserInstanceProps, Orm.AttributeCollection {
    email: Orm.Attribute;
    password: Orm.Attribute;
  }
  /**
   * 实例定义,用于控制器操作包含实例参数和相关操作函数
   */
  export interface UserInstance extends UserInstanceProps, UserMethods { }
  /**
   * 实例的所有函数
   */
  export interface UserMethods extends Orm.ModelMethods<UserInstanceProps> {
    populateCustom?(rawValues: UserInstance): Promise<UserInstance>;
    encryptPassword?(values: { password: string, hashedPassword: string }, cb: Function): void;
  }

  /**
   * 模型定义,和数据库对应,主要用于Model里面的属性定义attributes。
   */
  export interface UserDefs extends Orm.ModelDefinition<UserInstanceProps, UserAttributes> { }
}
export = UserModel;
  1. 在api/models文件夹中删除user.js,新增User.ts,并在User.ts中添加一个email和password两个属性(相当于数据库的字段),具体如下:
import UserModel from "typing/UserModel";
let User: UserModel.UserDefs = {
  attributes: {
    email: { type: 'string'},
    password: { type: 'string'},    
  }
}
export = User;

Typescript 重写控制器

  1. 手写控制器
    因为采用TS,不能再使用Sails命令创建控制器。手写控制器需要按照约定:首字母大写+Controller.ts。还是以用户操作为例,把原来添加的UserController.js删除掉,在controllers里面添加新文件并命名为UserController.ts
  2. 在UserController.ts里面,添加查询语句
import UserModel from "typing/UserModel";
import Api from "typing/Api";
declare var sails: any;
/**
 * 查询 
* @param req 
 * @param res 
 * @param next 
 */
export async function retrieve(req: Api.SailsRequest, res: Api.Response, next: Function): Promise<any> {
  let User = <UserModel.UserInstance>sails.models.user;  
  let rows = await User.find({});      
  res.status(200).send(rows);
}

export async function create(req: Api.SailsRequest, res: Api.Response, next: Function): Promise<any> {
  let User = <UserModel.UserInstance>sails.models.user;
  let Props: UserModel.UserInstanceProps = {
    email: req.body.email,
    password: req.body.password
  }
  if (!Props.email) res.serverError("email 不能空");
  else {
    try {
      let rows = await User.create(Props).fetch();
      res.status(200).send(rows);
    } catch (error) {
      res.serverError(error);
    }
  }
};

本代码里面,declare var sails:any; 正是用来获取sails库,通过sails.models可以获取定义的所有数据模型,在通过<>类型强制转换,我们就可以直接调用sails已经做好的诸如create,find等函数。

有了TS的数据定义,现在写代码有提示了,并且如果不符合要求,还会出现编译错误,这才是我们改造Typescript的目的

在这里插入图片描述

  1. 添加路由,并用Postman测试
    config/routes.js里面新增如下代码:
  'POST /api/userCreate': { action: 'User/create' },
  'POST /api/userRetrieve': { action: 'USer/Retrieve' },

用Postman测试新增用户Api结果如下:
在这里插入图片描述
用Postman测试用户查询结果如下:
在这里插入图片描述

User Model的进一步优化

如果是其它表的操作,这样也许就可以了,但是对于用户表,现在的新增和查询暴露出两个问题。一是新增的时候,我们并没有对email进行唯一性检查;二是查询的时候用户的密码直接返回给前端,并且用户密码没有加密。这样的设计显然是不合格的。

  1. 唯一性,对于email,我们需要把它设置为不可重复。这需要我们把aip/models/User.ts 中attributes里面的email设置为unique:true
  2. 明文密码的问题,需要修改attributes里面的password为encrypt:true
  3. 查询用户信息的时候,删除password,可以通过重载customToJSON函数

为此,改造User.ts如下:

import UserModel from "typing/UserModel";
declare var _: any;//获取lodash这个操作库
let User: UserModel.UserDefs = {
  attributes: {
    email: { type: 'string', unique: true },
    password: { type: 'string', encrypt: true },    
  },
  customToJSON:function():any {
    return _.omit(this, ['password']);//使用lodash库删除password属性,也可以直接用delete
  }
}
export = User;

改造之后,多次添加用户信息,第一次可以成功,第二次添加同样email的用户,就会返回唯一性错误。而这些都是sails帮我做好了,我们只是做了一个配置上的修改而已。并且因为有了对customToJSON的重载,成功添加的时候,也不会把密码返回给前端了。
在这里插入图片描述
再次测试查询api如下图,可以看到返回到前端的password已经没有了
在这里插入图片描述

前后端约定

作为前后端分离的开发模式,前端的开发比较专注用户UI方面,数据库的查询应该是后端的工作。让前端的开发人员去学习Sails的数据库查询是不合适的。但是Sails的查询,修改和更新又是有一定的学习门槛的,因此我们需要后端做一些查询改造,让前端人员可以轻松的调用Api实现大多数数据库查询,并且需要做好约定,确保大家查询格式一致。比如我们可以约定好,前端把要查询的条件用一个Object传递过来就好(这样的操作对前端是比较容易实现的),具体约定大致如下:

前端只给出key:value,后端整理成waterline的QueryOptions
约定如下:
 * 1、每一组key:value键值代表一个查询。
 * 比如 {age:18}代表要查询的条件是age=18
 * 2、后端根据key对应属性,如果是字符型,默认为contains操作,即包含给定值的模糊查询
 * 比如 {name:'cai'} 表示要查询的是name里面包含'cai'的数据name like '%cai%' 
 * 3、多组键值对默认都是与(and)的关系
 * 比如 {name:'cai',age:18} 相当于name like '%cai%' and age=18
 * 4、每一组key:value中,key用于后端判断数据类型,应该与要查询的表对应字段名称一致,如果不同,判断为string类型
 * 5、前端的value里面如果出现$,那么$前面是操作符后面是值,比如">$3",表示大于3  
 * 6、如果字符型需要精确查找,应该多一个'=='操作符
 * 比如 {name:'==$cai'} 相当于name = 'cai'
 * 7、操作符只能是如下的值  '!=' | '<' | '<=' | '>' | '>=' | 'nin' | 'in' | 'contains' | 'startsWith' | 'endsWith' |'=='; 
 * 8、多个键值对如果是or关系,需要写成数组,用"or"作为key
 * 比如 {name:'cai',or:[{age:18},{name:'li'}]} 相当于(name like '%cai%') or (age=18 and name like '%li%')
 * 9、还有不能满足要求的,调用waterline的sendNativeQuery执行原始sql语句,这个需要另行约定 

为此,我们需要做一个可以根据model属性把前端传过来的查询条件转换成Waterline的Criteria的功能,实现过程详见源代码utils/Criteria.ts

路径别名

软件开发过程中,经常会出现需要把一些常用的功能代码独立出来,并放在类似helper或utils之类的文件夹中,sails在api文件夹的结构里面有一个helper文件夹,我认为放在根目录会更合理一些,所以我再根目录创建了一个utils文件夹。比如《前后端约定》这节内容里面,涉及到一个查询条件的转换,我把它写成一个公共函数放在utils/Criteria.ts,然后我再UserController.ts控制器里面需要用到这个功能,这个时候就需要import这个公共函数,代码类似这样:

import UserAttributes from "../models/User";
import { getByAttributes } from "../../utils/Criteria";

这种import方式,使用的是相对路径,这是一种比较难受并且易错的做法。

tsconfig.json

可以在tsconfig.json配置文件里的paths设置路径别名,如下:

 "compilerOptions": {
    ...
    "paths": {
      "api/*": [
        "api/*"       
      ],     
      "utils/*": [
        "utils/*"
      ],
      "typing/*": [
        "typing/*"
      ],
      "swagger/*": [
        "swagger/*"
      ]
    },   
  },
  ....

修改后,我们的import语句可以改成这样:

import UserAttributes from "api/models/User";
import { getByAttributes } from "utils/Criteria";

鼠标移到api/models/users上面,还可以看到如下提示
在这里插入图片描述

tsconfig.json里面还有一个 “include”: []和"exclude":[] 分别是设置需要把ts编译成js的路径和不要编译的路径

module-alias

按照上的操作,我们可以解决ts文件里面的引用问题,并且vscode里面不会有错误提示了。但是如果这个时候我们运行node app.js启动服务,终端上面还是会出现找不到utils/Criteria等提示。
这是因为tsconfig.json是为vscode支持ts文件服务的,当所有ts文件转换为js文件之后,import路径是给nodejs使用的,它还是会找不到。这个如果有用到webpack,它的alisa也可以解决这个问题。后端没有webpack的情况下,可以用module-alisa这个库来解决这个问题。

安装

npm i --save module-alias

使用

安装后,在package.json里面添加"_moduleAliases" 节点就可以,package.json 大致是这样的:

....
"_moduleAliases": {
    "api":"./api",
    "utils": "./utils",
    "swagger":"./swagger"
  },
   "scripts": {
    "start": "NODE_ENV=production node app.js",
    "test": "jest",
    "lint": "./node_modules/eslint/bin/eslint.js . --max-warnings=0 --report-unused-disable-directives && echo '✔  Your .js files look good.'",
    "custom-tests": "echo \"(No other custom tests yet.)\" && echo"
  },
....

修改完package.json之后,还需要在启动的时候,引入系统。在app.js里面添加 require(‘module-alias/register’); 添加后的app.js代码片段如下:

..... 略
process.chdir(__dirname);
require('module-alias/register');//集成module-alias别名功能

var sails;
var rc;
.....

保存后重新启动服务,编译错误不再出现。

Jest测试

在开发过程中,因为sails做为后端,每个控制器实现的功能都需要通过启动sails之后,再通过Postman进行测试。然而在开发期间,有时候我们只是想要测试一下某个函数是否能够达到我们设计的目的,或是想要console.log出某个sails内置的某个全局变量,这种情况每次重启sails的效率就比较低,并且没法实现自动化测试。为此我们需要引入测试库。Jest 是一个令人愉快的 JavaScript 测试框架,专注于 简洁明快。具体可以查看:https://www.jestjs.cn/
为实现开发过程测试和后面的自动化测试,我们需要

  1. 安装Jest
npm install --save-dev jest
  1. 在package.json里面添加测试脚本
{
  "scripts": {
    "test": "jest"
  }
}
  1. 安装jest的Typescript 预编译库 ts-jest,在nodejs里面执行命令如下:
npm i ts-jest --save
  1. 执行ts-jest 初始化命令如下:
npx ts-jest config:init

执行成功后,可以在项目根目录下看到 jest.config.js

  1. 在根目录里面添加test文件夹,新增criteria.test.ts,并添加如下测试代码
import User from "../api/models/User";
import { getByAttributes } from "../utils/Criteria";
test('根据前端post的body 解析成find要的格式', () => {
    let body: any = {
        email:'zz'
    };
    let res = getByAttributes(User.attributes, body);//调用要测试的函数
    console.log(JSON.stringify(res));//输出函数返回结果
    expect(res).not.toBe(false);//判断测试是否通过
});
  1. 在Visual Studio Code里面多开一个终端,并执行
npm run test

运行如下:
在这里插入图片描述

也可以使用命令行,实现单个功能测试,具体见https://www.jestjs.cn/docs/getting-started

  1. npm test 指令会测试整个根目录下面test子目录里面的所有测试单元,有时候我们只是想要测试某个测试文件,比如sqlTypes.test.ts文件,这个时候也可以执行单个文件测试。
npm test sqltypes

npm test 后面跟的是test文件夹里面的测试文件,可以忽略大小写,可以忽略.test.ts 扩展名

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二、 在Sails中使用Typescript 的相关文章

随机推荐

  • java进阶Kafka集群实战之原理分析及优化教程全在这里

    我不去想是否能够成功 既然选择了Java 便只顾风雨兼程 我不去想能否征服Kafka集群 既然钟情于Java 就勇敢地追随千锋 我不去想Kafka集群有多么晦涩难懂 既然目标是远方 留给世界的只能是努力拼搏的背影 我不去想未来是平坦还是泥泞
  • 10.两个链表相加求和

    题目 假设链表中每一个节点的值都在 0 9 之间 那么链表整体就可以代表一个整数 给定两个这种链表 请生成代表两个整数相加值的结果链表 思路 首先是逆序相加 故这里用到了两个栈 分别存放两个链表中的值 两数相加会涉及到进位问题 所以考虑了进
  • ZeroQuant与SmoothQuant量化总结

    ref ZeroQuant Efficient and Affordable Post Training Quantization for Large Scale Transformers SmoothQuant Accurate and
  • qt程序运行时无任何错误提示信息,但是无法进入main函数

    问题 开发环境 win7 vs2013 qt 5 9 1 64位程序 qt程序运行时无任何错误提示信息 但是无法进入main函数 原因 程序中使用了visa库 使用的库的版本有问题
  • #ifndef, #define, #endif区别和使用

    文件中的 ifndef 头件的中的 ifndef 这是一个很关键的东西 比如你有两个C文件 这两个C文件都include了同一个头文件 而编译时 这两个C文件要一同编译成一个可运行文件 于是问题来了 大量的声明冲突 还是把头文件的内容都放在
  • 专访京东孙海波:大牛架构师养成记及电商供应链中区块链技术的应用

    编者按 每个人的成长曲线不同 有的人在研究生之时就已有相当知名的产品和框架 从而在接下来的工作中一路顺风顺水 有的人缺需要经历一个又一个的坑才能成长 不管是前者的聪明高效 还是后者的笨鸟先飞 他们都是在迈着脚步不断地向前 不妨 我们停下脚步
  • VS永久配置Opencv 和 Cmake配置OpenCV_contrib扩展模块 (整体思路 + 注意事项)

    1 VS永久配置Opencv 1 1 在opencv官网下载后并安装Opencv 官网地址 https opencv org 1 2 安装完成后 配置opencv的环境变量 1 3 在VS中配置opencv的属性表 步骤如下 1 在VS新建
  • 分割字符串的方法

    1 split 将一个字符串分割为子字符串 然后将结果作为字符串数组返回 2 indexOf 返回某个指定的字符串值在字符串中首次出现的位置 从左向右 没有匹配的则返回 1 否则返回首次出现位置的字符串的下标值 3 substr start
  • adb几个常见命令

    ADB Android Debug Bridge 调试桥 1 将设备连接到电脑 命令 adb connect 127 0 0 1 62001 2 检查设备连接情况 命令 adb devices 3 发送文件到设备 命令 adb push d
  • 基于Python的大数据的人才招聘数据分析与可视化平台应聘兼职-爬虫安装项目定制算法作业代做计算机毕业设计源代码

    更多项目资源 最下方联系IT实战课堂 博主拥有多年的T技术研发项目架构和教学经验 CSDN 51CTO 腾讯课堂等平台优质作者 高级讲师 培训机构联合创始人 现专注项目定制Java 小程序 前端网页 Python App NodeJs PH
  • STL之rotate

    STL对左旋转字符串针对三种不同数据结构进行了不同的实现 对单向链表采用的是同步位移 双向链表是三次翻转 都很简单 主要看看针对随机存取的数组做的循环位移实现 STL这个版本的源码如下 cpp view plain copy templat
  • UniCode 下 CString 转 char* 的方法

    1 Unicode下CString转换为char 方法一 使用API WideCharToMultiByte进行转换 CString str T D 校内项目 QQ bmp 注意 以下n和len的值大小不同 n是按字符计算的 len是按字节
  • (java功能篇)谷歌获取地址经纬度

    package com mohe map import java io BufferedReader import java io IOException import java io InputStreamReader import ja
  • vant+cropperjs 实现图片上传和图片截取

    完整代码
  • 【WEB安全】PHP靶场实战分析——DVWA

    文章目录 前言 一 实战前的准备 1 dvwa靶场安装 2 代码审计工具介绍 2 1 seay代码审计工具的介绍 2 2 rips 审计工具介绍 二 DVWA通关讲解 1 brute force 暴力破解 low Medium High i
  • day02:js基础——运算符详解

    js运算符 1 JS运算符分类 2 运算符的使用说明 2 1 数学运算符 2 2 比较运算符 2 3 赋值运算符 2 4 逻辑运算符 2 5 一元运算符 2 6 三元运算符 1 JS运算符分类 运算符类型 表示符号 数学运算符 比较运算符
  • css src属性值,css维度属性 – CSS高级教程

    上一章CSS高级教程请查看 css内容溢出处理 本章介绍用于控制元素的高度和宽度的CSS维度属性 CSS维度属性 CSS提供了几个属性 如width height max width和max height等 允许你控制一个框的尺寸 下一节将
  • leetcode:最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀 如果不存在公共前缀 返回空字符串 示例 1 输入 strs flower flow flight 输出 fl 示例 2 输入 strs dog racecar car 输出 解释 输入不存在公共
  • GROMACS在虚拟机上Linux系统的安装教程

    GROMACS在虚拟机上Linux系统的安装教程 一 安装cmake所需的gcc及c 1 直接执行此命令行 sudo apt install y g gcc sudo apt install build essential sudo apt
  • 二、 在Sails中使用Typescript

    文章目录 Typescript 基础 Typescript 安装 TypeScript 问题 最简单的改造 Sails重定义 Waterline Orm 重写Models Typescript 重写控制器 User Model的进一步优化