1029

1029 查看完整档案

南京编辑长春理工大学  |  计算机科学与技术 编辑张杰  |  总监助理 编辑填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

1029 回答了问题 · 1月9日

vue 怎么渲染自定义组件

我也遇到这个问题,我想你的问题应该是和我一样的,字符串怎么渲染成dom

我的解决方案是先用Vue.extend(template: xx)将字符串转换成模板,然后用render函数去渲染

定义一个自定义组件,用于处理string渲染成正常页面
`
import Vue from 'vue'
export default {

name: 'preview',
props: {
    code: {
        required: true
    }
},
render (h) {
    // 这里是关键,将string字符串转换成模板
    // 难点是String中包含了自定义组件,使用v-html是解析不了的
    const result = Vue.extend({
        template: this.code
    })
    return h(result, {})
}

}
`
在主页面中直接调用这个组件

`
<Preview :code="code"></Preview>
`

希望能够帮助到你

关注 3 回答 2

1029 收藏了问题 · 2020-03-31

VS Code打开文件时有个和typescript有关的进程CPU占用过高如何解决?

环境:

  • macOS Sierra 10.12.5
  • VS Code 1.14.2 (1.14.2)

状况描述:

只打开软件没问题,一但打开了项目,就有一个code helper进程强势登顶

图片描述

所有插件都停用之后也是这个状况

ps aux | grep PID查看进程详情:

luob              8926  99.6  1.1  3211000  93372   ??  R    10:54上午   0:39.51 /Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper.app/Contents/MacOS/Code Helper /Applications/Visual Studio Code.app/Contents/Resources/app/extensions/node_modules/typescript/lib/typingsInstaller.js --globalTypingsCacheLocation /Users/luob/Library/Caches/typescript/2.4 --enableTelemetry

强制结束这个进程也没见有什么影响,不知道这是提供什么功能的,能否从哪里设置关闭?

补充,发现只要项目中某个文件引入了node_modules中的包的话,就出现这个高占用的进程,不知是否和这个有关(唯一能想到会出现typescript相关进程的地方……)

图片描述

1029 回答了问题 · 2020-03-28

一个非常奇怪的前端性能渲染问题

代码上一波啊 后端请求的network也贴一下啊 现在这样猜也解决不了问题

关注 4 回答 4

1029 回答了问题 · 2020-03-28

微信小程序 循环中 加入随机数据

如果是真实数据,直接后端取值给你不就行了

关注 3 回答 2

1029 发布了文章 · 2020-03-28

Dart基础语法

Dart基础语法

一、选择开发工具

首先需要安装Dart SDK(https://www.dart-china.org/t/...),SDK安装完成呢后,选择一款编辑工具。这里就先只推荐两款工具 VS Code ,Android Studio。工具下载完成后,安装dart插件,这个时候就可以编写dart代码了。我们先新建一个hello.dart文件,然后在文件中输入下方的代码,点击运行按钮就可以执行代码。

// 在dart中,main方法是代码运行的入口方法,一切都是从main方法开始的。。
void main (){
  // 点击IDE运行按钮 在打印台中会打印出 hello Dart。 接下来的例子都是在该方法下执行的(除特殊说明)。
  print('hello Dart');
}
二、常用的数据类型
  1. num num是数字类型的父类,包含了两个子类 int和double。
  2. String String可以用 '' 或 "" 来创建,也可以使用''' 或者"""来创建可变行的字符串。

    eg.num与String的一些常用操作

    // String -> num
    int one = int.parse('1');
    print(one + 2); // 输出 3 double类型同理
    int one = int.parse('hello');
    print(one + 2); // 报错
    
    // int -> String
    String oneStr = 1.toString();
    print(oneStr); // 输出 1
    
    // double -> String
    String oneStr = 3.1455.toStringAsFixed(2);
    print(oneStr); // 截取两位小数, 输出3.15 四舍五入
    String twoStr = 3.toStringAsFixed(2);
    print(twoStr); // 输出3.00
    
    // 可以使用${表达式}将表达式的值放入字符串中。如果表达式是标识符,{}可省略
    String oneStr = 'hello';
    String twoStr = "dart";
    print('$oneStr $twoStr'); //输出 hello dart
    print('$oneStr ${twoStr.toLowerCase()}'); //输出 hello dart
    print('$oneStr $twoStr.toLowerCase()'); //输出 hello dart.toLowerCase()
  3. bool Dart是强bool类型检查。

    • 只有两个对象具有bool类型:true和false,它们都是编译时常量
    • if(非bool类型) 报错,assert( 非bool类型)报错,也就是说需要bool类型的,如果我们用int 等非bool类型的值都会报错。

      if (1){} // 报错
  4. List 列表

    var list_1 = new List(); // 非固定长度 new可以省略
    
    // 添加元素
    list_1.add('1');
    
    // 添加多个元素
    list_1.addAll(['1', '2']);
    
    // 获取list长度
    print(list_1.length); // 输出2
    
    // 获取list最后一个元素
    print(list_1.last); //输出 2
    
    // 获取list第一个元素
    print(list_1.first); // 输出 1
    
    // 查询某位置的元素,index从0开始
    print(list_1[0]); // 输出 1
    
    // 根据某个元素获取对应的index
    print(list_1.indexOf("1")); // 输出 0
    
    // 删除元素
    list_1.removeAt(0); // 通过索引删除
    list_1.remove("1"); // 通过对象删除 如果有两个 "1" 那么会删除第一个
    list_1.removeLast(); // 删除最后一个元素
    list_1.removeRange(start,end); // 删除范围内的数据
    list_1.removeWhere((item) => item.length > 6);// 删除元素长度大于6的元素 这种写法可见下面箭头函数的详解
    list_1.clear(); // 清除所有元素
    
    /*注意*/
    List list_2 = new List(2); // 固定长度
    list_2.add('1'); // 报错 报错信息 Cannot add to a fixed-length list
    
    // 另外一种定义方式
    List list_3 = ['1','2','3'];
    print(list_3); // 输出 [1,2,3]
  5. Set 集合 Set是没有顺序且不能重复的集合,所以不能通过索引去获取值

    Set set_1 = new Set();
    Set set_2 = new Set(2); // 报错 set没有固定长度的定义
    set_1.add('1');
    set_1.add('2');
    set_1.add('1');
    print(set_1); // 输出 {1, 2}
    // 可以和list一样通过contains来判断是否包含某个元素
    print(set_1.contains("1")); // 输出 true 
    set_1.addAll(['b','c']);
    print(set_1); // 输出 {1, 2, b, c}
  6. Map 映射是无序的键值对。键和值都可以是任何类型的对象。

    // 常用的两种定义方式
    Map map_1 = Map();
    Map map_2 = {"key_1":"value_1","key_2":"value_2"};
    print(map_1); // 输出 {}
    print(map_2); // 输出 {key_1: value_1, key_2: value_2}
    
    // 赋值
    map_1["1"] = "one";
    map_1["2"] = "two";
    print(map_1); // 输出 {1: one, 2: two}
    map_1["1"] = "first";
    print(map_1); // 输出 {1: first, 2: two}
    map_1[1] = "one"; // 在map中key必须要保持唯一 value可以相同 key类型可以为任意其他类型
    print(map_1); // 输出 {1: one, 2: two, 1: one}
    
    // 常用api
    map_1.remove(1); // 删除key为1的元素
    map_1.containsKey(1); // 判断是否存在key为1的元素
三、函数/方法
  1. 内置函数/方法

    print("hello Dart");

    这个是常用的将数据打印到控制台的方法,这是系统提供给我们的内置函数,可以直接调用。

  2. 自定义方法

    自定义方法的基本格式:

    返回类型 方法名称 (参数1,参数2,...){

    ​ 方法体

    ​ return 返回值;

    }

    int getMaxCount(int a, int b){
        if (a > b){
            return a;
        }
      return b;
    }
    print(getMaxCount(2, 1)); // 输出 2
    
    // 在dart中 返回值和参数类型是可以省略的
    getMinCount(a,b){
      if (a < b){
        return a;
      }
      return b;
    }
    print(getMinCount(2, 1)); // 输出 1
  3. 方法的传参

    // 可选参数
    String printUserInfo(String username,[int age]){ // username和age表示是形参 其中age表示可选参数
      return "姓名:$username  年龄:$age";
    }
    print(printUserInfo("张三",30)); // 张三 和 30 表示实参
    print(printUserInfo("张三")); // 这里可以不用传入age的参数,如果age不是可选参数的话,那么age是必须要传的
    
    // 默认参数
    String printUserInfoDefalut(String username,[int age,String sex = "男"]){
      return "姓名:$username  年龄:$age  性别:$sex";
    }
    print(printUserInfoDefalut("李四",30)); // 输出 姓名:李四  年龄:30  性别:男
    /*注意
    对于可选参数的传递是按照参数的顺序进行的 print(printUserInfoDefalut("李四","女")); 这里会报错。虽然sex和age都是可选参数,但是当对一个形参赋值时,会默认选择第一个。这里可选参数的第一个形参是age int类型的,这里传递一个"女"是string类型的,所以会报错。
    */
    // 命名参数
    String printUserInfoName(String username,{int age,String sex = "男"}){
      return "姓名:$username  年龄:$age  性别:$sex";
    }
    print(printUserInfoDefalut("李四",age:30,sex:"男")); // 输出 姓名:李四  年龄:30  性别:男
    // 这样我们通过指定参数名称实现我们想要传递的参数。这个比较常用。
    
    // 将方法作为参数
    fun_1(){
      print("方法1");
    }
    
    fun_2 (Function fun){ // fun方法其实就是一个Function对象
      fun();
    }
    
    fun_2(fun_1); // 输出 方法1
  4. 箭头函数

    // 遍历数组
    List list = ["h","e","l","l","0"];
    // 正常写法
    list.forEach((element){
      print(element);
    });
    // 箭头函数的方式 箭头后跟的方法体只能是一行 这只是一种简写的方式
    list.forEach((element)=>print(element));
    list.forEach((element) => {
      print(element) // 这里不能写分号,也是只能执行一行代码
    });
    
    List list_1 = [1,3,5,1,3,7];
    // 这也是数组的一种遍历并处理的方式
    List list_2 = list_1.map((element){
      if(element >3){
        return element*3;
      }
      return element;
    }).toList();
    print(list_2);
    List list_3 = list_1.map((element)=>element>3?element*3:element).toList();
    print(list_3);
  5. 自执行与自调用

    //实际上自执行方法我们可以理解为一个匿名函数的自调用
    ((){})();// 格式 将一个匿名函数用()包起来 然后执行
    
    ((int n){
      print(n);
    })(12); //输出12
    
    // 方法可以自己调用自己
    int sum = 1;
    fn(n){
      sum*=n;
      if (n == 1){
        return;
      }
      fn(n-1);
    }
    fn(5); // 这样通过方法的自调用可以实现5的阶乘
  6. 闭包

    • 全局变量特点:全局变量常驻内存,会污染全局。
    • 局部变量特点:不常驻内存会被垃圾回收机制回收,不会污染全局。
    • 闭包:常驻内存,不会污染全局。写法:函数嵌套函数,并return内层函数。

      //闭包
      fun(){
        // 虽然这里定义的是一个局部变量,但是通过闭包的写法就不会被垃圾回收机制回收,且不会污染全局
        int a = 1;
        return (){
          a++;
          print(a);
        };
      }
      Function b = fun(); // 因为fun方法返回的就是一个方法,所以下面直接调用b方法。
      b();
      b();
      b();
      // 输出 2 3 4
四、语法规则
  1. Dart中一切皆对象,都是继承自Object类。所以在Dart中不存在基本数据类型,我们定义的任何一个对象,如果不将其赋值的话,那么这个对象就是一个null对象。

    int a;
    int b = 0;
    print(a); // 输出 null
    print(b); // 输出 0
  2. Dart是动态型语言,如果没有指定其类型时,则默认时dynamic类型。在运行时会自动推导出具体的类型。

    var str = 'shsh';
    str = 1; // 报错
  3. 运算符及修饰符

    • static:用于修饰类成员变量,这个变量是属于类的,通过类名直接调用,而不是通过对象调用。这个与java类似。非静态方法可以访问静态成员以及非静态成员。静态方法无法访问非静态成员,也无法调用非静态方法。
    • final:用于修饰变量,表示单赋值(single-assignment),使用final修饰的变量必须进行初始化,一旦被赋值之后,不能够再次被赋值,否则编译会报错。
    • const:与final有一点类似,即只能被赋值一次。但是其修饰的对象有一定的限制。const修饰的对象的状态是完全可以在编译期间就能确定的,并且是不可变的。

      const n = 1+2;// 可行 因为这个是在编译期间我们就知道n = 3的
      const list = new List(); // 不可行
      const list_1 = [1,2,3]; // 可行
      list_1 = [3,4]; // 不可行
    • 常用的运算符和表达式

      //加减乘除
      int a = 10;
      int b = 3;
      print(a/b); // 3.3333333333333335 不用判断除数和被除数的类型,默认返回double类型
      print(a~/b); // 3  整除取整
      print(a*b); // 30
      print(a+b); // 13
      print(a-b); // 7
      print(a%b); // 1 取余
      
      //as 定型
      Person per = new Person(); // 我们定义一个Person类,关于类的定义下文会介绍
      // 如果per是Person类型的话 那么就给Person的name属性赋值,如果不是Person类型则不赋值 并
      (per as Person).name = "张三";
      
      // is 如果对象是指定的类型 那么就返回true 否则就返回false
      if (per is Person){
        per.name = "张三";
      }
      /// 注意:以上代码不相等。如果per为空或不为Person,第二个示例(带is)什么也不做;第一个(带有as)抛出异常。
      
      // ??与三目运算符
      String str_1;
      String str_2 = "张三";
      print(str_1??str_2); // 输出 张三
      print((str_1 is! Null)?str_1:str_2); // 输出张三 在dart中 关于条件的判断必须是bool类型
      
      // 级联符号 可以避免创建临时变量的繁琐步骤
      Person() ..name ="张三";
      // 等价于
      Person per = new Person();
      per.name = "张三";
      
      // ?. 有条件的成员属性访问
      Person per; // 这里per就是null
      // 如果直接调用成员会报错
      per.name = "张三"; // 报错
      per?.name = "张三"; // 不会报错
五、类与对象
  1. 类的创建

    // 与上面的例子不同,类的定义需要与mian方法同级
    class Person{
      String name; // 属性
      void study (){
        print("学习");
        } // 方法
    }
    
    void main (){
      // 实例化,也就是创建对象
      Person per = new Person(); // per就是一个对象
      per.name = "张三";
      print("${per.name}"); // 输出 张三 调用对象的属性
      per.study(); // 输出 学习 调用方法
    }
  2. 类的构造函数

    • 默认构造函数。每个类都有一个默认的构造函数,默认是不用写的,如果需要在构造函数做操作的话,则重写一下默认构造函数。

      class Person{
        String name;
        //构造参数名与类名要相同
        Person(){ // 无参
          print("我需要在这里做一些操作");
        }
        // 初始化列表
        person():name = "张三"{
          // 在执行构造函数运行之前 初始化一些实例变量
        }
      }
      
      class Student{
        String name;
        Student(String name){ // 有参数 参数必传
          this.name = name;
        }
        void printStudentInfo (){
          print("学生的姓名:${this.name}");
        }
      }
      
      class Coder{
        String name;
        Coder(this.name); // 简写的有参构造函数 参数必传
        void printCoderInfo (){
          print("程序员的姓名:${this.name}");
        }
      }
      
      class Engineer{
        String name;
        Engineer({this.name}); // 简写的有参构造函数 参数非必传
        void printEngineerInfo (){
          print("工程师的姓名:${this.name}");
        }
      }
      
      void main (){
        // 实例化,也就是创建对象
        Person per = new Person(); // 输出  我需要在这里做一些操作。
        
        // 报错 因为我们修改了默认的构造函数,所以之前的无参构造函数就不能使用了。
        // Student stu = new Student(); 
        
        Student stu_1 = new Student('张三');
        stu_1.printStudentInfo();
        
        Coder coder = Coder("张三");
        coder.printCoderInfo();
        
        Engineer eng = new Engineer();
        eng.printEngineerInfo(); // 输出 工程师的姓名:null
        
          Engineer eng_1 = new Engineer(name: "张三");
        eng_1.printEngineerInfo(); // 输出 工程师的姓名:张三
      }
    • 命名构造函数。类是可以有多个构造函数的,通过不同的命名来实现不同功能的构造函数

      class Engineer{
        String name;
        String hairStyle; //发型 
        Engineer({this.name,this.hairStyle = "茂密的头发"}); // 简写的有参构造函数 参数非必传
        Engineer.seniorEngineer(String name){ //命名构造函数
          this.name = name;
          this.hairStyle = "秃了";
        }
        void printEngineerInfo (){
          print("工程师的姓名:${this.name} 发型:${this.hairStyle}");
        }
      }
      
      void main (){
        Engineer eng_1 = Engineer(name:"张三");
        eng_1.printEngineerInfo();
        
        Engineer eng_2 = Engineer.seniorEngineer("李四");
        eng_2.printEngineerInfo();
      }
    • 类中的setter和getter方法

      class Rect{
        num height;
        num width;
        Rect(this.height,this.width);
        // getter方法
        get area{
          return this.height*this.width;
        }
        // setter方法
        set areaHeight(num height){
          this.height = height;
        }
      }
      
      void main(){
        Rect re = Rect(10,10);
        print("面积:${re.area}"); // 输出 面积:100
        re.areaHeight = 5;
        print("面积:${re.area}"); // 输出 面积:50
      }
  3. 类的继承

    • 子类使用 extends 关键字来继承父类
    • 子类会继承父类中可见你的属性和方法,但是不会继承构造函数
    • 子类能复写父类的方法 getter和setter

      class Person{
        String name;
        num age;
        Person(this.name,this.age);
        void printPersonInfo(){
          print("姓名:${this.name} 年龄:${this.age}");
          }
        
        void work(){
          print("我正在工作");
        }
      }
      
      class Coder extends Person{
        // 因为Person是重写了构造函数,所以这里也需要写一下与父类关联的构造函数
        // 如果父类有多个构造函数 则需要选一个自己需要的
        Coder(String name,num age,String grade):super(name,age){
          // 通过代码可以看出,这么写的目的是当我们在实例化Coder时也会将值传递给父类
          // grade 是子类的属性,所以不用将参数传递给父类
          this.grade = grade;
          }
        String grade;
        void printCoderInfo(){
          super.work(); // 调用父类的方法
          print("姓名:${this.name} 年龄:${this.age} 等级:${this.grade}");
          }
        
        @override // 重写父类方法
        void work() {
          print("我正在写代码");
        }
      }
      
      void main(){
        Coder code = Coder("zhangsan",20,"初级码农");
        code.printCoderInfo(); // 输出 姓名:zhangsan 年龄:20 等级:初级码农
        code.work(); // 输出 我正在写代码
      }
  4. 抽象类。Dart中的抽象类主要是用于定义标准,子类可以继承抽象类,然后实现抽象类的接口。

    • 抽象类通过abstract关键字来定义。
    • 抽象方法不能用abstract声明,我们将没有方法体的方法称为抽象方法。
    • 如果子类继承抽象类必须实现里面的抽象方法。
    • 如果把抽象类当作接口实现的话必须得实现抽象类里定义的所有属性和方法。
    • 抽象类不能被实例化,子类可以。
    • 如果要实现抽象方法约束子类的话我们需要用extends继承抽象类。
    • 如果只是把抽象类作为一个标准的话我们需要用implements实现抽象类。

      abstract class Person{
        String name;
        int age;
        Person(this.name,this.age);
        work(); // 没有实现方法体 这是一个抽象方法 抽象方法是必须要在子类中实现的
        eat(){
          print('吃饭');
        }
      }
      
      class Coder extends Person{
        Coder(String name, int age) : super(name, age);
        @override
        work(){
          print("${this.name}在搬运代码,他今年已经${this.age}了");
        }
      }
      /*
      在dart中普通类和抽象类都可以作为接口被实现 使用关键字 implements 
      dart的接口会将普通类或者是抽象类中的属性或者方法都重写一遍,所以我们一般采用抽象类实现接口。
      因为只有抽象类才可以创建抽象方法。
      */
      class Engineer implements Person{
        // 这里就不用像继承一样需要实现父类的构造方法。
        @override
        int age;
      
        @override
        String name;
      
        @override
        eat() {
          print("工程师在吃饭");
        }
      
        @override
        work() {
          print("${this.name}在搞算法,他今年才${this.age}");
        }
      }
      
      void main (){
        Coder code = Coder("张三", 30);
        code.work(); // 输出 张三在搬运代码,他今年已经30了
        
        Engineer eng = Engineer()
          ..name = "李四"
          ..age = 20
          ..work(); // 输出 李四在搞算法,他今年才20
      }
  5. 多接口和mixins

    • 通过implements的方式,可以实现多接口。

      abstract class A{
        String a;
        printA();
      }
      abstract class B{
        String b;
        printB();
      }
      class C implements A,B{
        @override
        String a;
      
        @override
        String b;
      
        @override
        printA() {
        // TODO: implement printA
        return null;
        }
      
        @override
        printB() {
        // TODO: implement printB
        return null;
        }
      }
      // 这样C类的对象就可以同时实现 A B的的抽象方法
    • 通过mixins实现多继承,Dart是不支持多继承,是无法实现类似C++的多继承的功能。

      class A{
      // mixins 不能有显示的构造方法 其实这个也好理解 因为使用mixins继承多个类的话,它也不知道用什么构造方法 只能使用默认的了
      //  A.withName(){
      //
      //  }
        a(){
          print('A.a()');
        }
      }
      
      class B{
        a(){
          print('B.a()');
        }
        b(){
          print('B.b()');
        }
      }
      
      class C{
        a(){
          print('C.a()');
        }
        b(){
          print('C.b()');
        }
        c(){
          print('C.c()');
        }
      }
      
      class D extends A with B,C{
        int count; // 可以定义自己的属性
      }
      void main (){
        var d = new D();
        d.a(); // 输出 C.a() 默认调用最后一个方法
      }
      /*mixins的方式组合的类 是必须要继承object的 A B C 就是不能继承与任何的类 除了默认的object 如果想实现类似继承的效果 我们可以使用接口的方式*/
六、泛型
  1. 泛型方法

    T getData<T>(value){
      return value;
    }
    print(getData("xingming")); // 输出 xingming
    print(getData(20)); // 输出 20
    print(getData<String>(20)); // 报错 因为指定的是string类型 但是传入的是int类型
    
    // 通过泛型方法我们就可以不用区分传入的参数类型和输出的参数类型。
    var names = List<String>();
    names.addAll(['1', '2', '3']);
    names.add(42); // 报错
  2. 泛型类

    通过指定类的泛型类型,来确定输入的数据类型是否正确

    void main (){
      LogList loglist = LogList<int>(); // 确定泛型类型为int类型,后续类中的类型判断就是int类型
      loglist.addData(1);
      loglist.addData("1"); // 报错
      loglist.printList();
      
      LogList loglist_1 = LogList(); // 没有确定泛型类型
      loglist_1.addData(1);
      loglist_1.addData("1"); // 没有报错
      loglist_1.printList();
    }
    
    class LogList<T>{
      List list = List<T>();
      void addData(T value){
        list.add(value);
      }
    
      void printList(){
        for (int i = 0;i<list.length;i++){
          print(list[i]);
        }
      }
    }
  3. 泛型接口

    void main (){
      B b = B<int>();
      b.pirntA(1);
      //b.pirntA("12"); // 报错
    }
    
    abstract class A<T>{
      pirntA(T value);
    }
    
    class B<T> implements A<T>{
      @override
      pirntA(T value) {
        print(value);
      }
    }
七、结尾

​ 上面简单的介绍了一些Dart语言的基础语法知识,关于Dart中更深层次的内容,我会在以后的文章中再慢慢的剖析。关注公众号,阅读更多精彩技术文章。

查看原文

赞 0 收藏 0 评论 0

1029 发布了文章 · 2020-03-27

Mybatis流式查询避免OOM

前言

当指定查询数据过大时,我们一般使用分页查询的方式,一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据,如果一下子将数据全部加载出来到内存中,很可能会发生OOM。这时我们可以使用流式查询解决问题。

非流式查询

为了更好的复现问题,将jvm参数,最大堆设置成212M。使用mysql数据库,表大小为730MB。

img

非流式查询表里所有数据代码

 List<InfoPO> infoPOs = infoMapper.selectList(new EntityWrapper<>());

通过查看idea控制台,很快出现了内存溢出。

img

通过jconsole工具,查看内存使用情况

img

在14.26,发现内存直接被释放了。

流式查询

流式查询表里所有数据代码

@Select("select * from t_iot")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@ResultType(InfoPO.class)
void selectAutoList(ResultHandler<InfoPO> handler);
infoMapper.selectAutoList(resultContext -> {
    resultContext.getResultObject();
});

通过查看idea控制台,程序运行正常

img

在通过jconsole工具,查看内存使用情况

img

发现内存消耗处于平稳状态。

流式查询原理

查看源码可知,我们使用流式查询时,必须要满足以下3个条件

/**
 * We only stream result sets when they are forward-only, read-only, and the
 * fetch size has been set to Integer.MIN_VALUE
 * 
 * @return true if this result set should be streamed row at-a-time, rather
 *         than read all at once.
 */
protected boolean createStreamingResultSet() {
    return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
            && (this.query.getResultFetchSize() == Integer.MIN_VALUE));
}
  1. resultSetConcurrency=ResultSet.CONCUR_READ_ONLY 设置只读结果集
  2. resultSetType = ResultSetType.FORWARD_ONLY 设置结果集的游标只能向下滚动
  3. fetchSize = Integer.MIN_VALUE 设置fetch size为int的最小值,这里和oracle/db2有区别.

    Oracle/db2是从服务器一次取出fetch size 条记录放在客户端,客户端处理完成一个批次后再向服务器取下一个批次,直到所有数据处理完成。

    mysql在执行ResultSet.next()方法时,会通过数据库连接一条一条的返回。MySQL按照自己的节奏不断的把buffer写回网络中。flush buffer的过程是阻塞式的,也就是说如果网络中发生了拥塞,send buffer被填满,会导致buffer一直flush不出去,那MySQL的处理线程会阻塞,从而避免数据把客户端内存撑爆。

设置三个参数之后,断点进入到了流式返回结果集ResultsetRowsStreaming。

img

ResultSet数据返回的结果,对象有3种实现方式

img

ResultsetRowsStatic 静态结果集,默认的查询方式,普通查询

ResultsetRowsCursor 游标结果集,服务器端基于游标查询

ResultsetRowsStreaming 动态结果集,流式查询

查看ResultsetRowsStatic类注释

/**
 * Represents an in-memory result set
 */
public class ResultsetRowsStatic extends AbstractResultsetRows implements ResultsetRows {

表示放在内存中的结果集。

查看ResultsetRowsStreaming类注释

/**
 * Provides streaming of Resultset rows. Each next row is consumed from the
 * input stream only on {@link #next()} call. Consumed rows are not cached thus
 * we only stream result sets when they are forward-only, read-only, and the
 * fetch size has been set to Integer.MIN_VALUE (rows are read one by one).
 * 
 * @param <T>
 *            ProtocolEntity type
 */
public class ResultsetRowsStreaming<T extends ProtocolEntity> extends AbstractResultsetRows implements ResultsetRows {

提供了Resultset行的流。获取下一行都是从仅在{@link #next()}调用时输入流。因此不会缓存已使用的行。我们只在结果集只有前进、只读和时才流结果集获取大小已设置为整数。MIN_VALUE(逐个读取行)。

总结

之前使用过db2处理流式查询,设置的fetch size为100,没有问题。这次使用mysql刚开始时也设置的100,发现内存溢出了,后来在网上看到mysql流式获取数据的坑,debug进去果然没走到ResultsetRowsStreaming类,设置fetch size 参数为Integer.MIN_VALUE后,才进了ResultsetRowsStreaming类。

查看原文

赞 0 收藏 0 评论 0

1029 发布了文章 · 2020-03-27

Taro请求域名配置

背景

使用Taro进行小程序的开发,调用后端api获取信息,开发,测试,正式环境的域名是不同的。最初始的版本中,我们在不同环境中不断修改域名信息。这样造成一个问题,提交审核的时候我们一定要记得将域名改成正式版,不然会出现问题。这种人为控制,显然不能保证百分百没有问题。我们就考虑是不是可以通过process.env来区分不同的环境

尝试使用process.env解决问题

修改配置文件

原配置文件

export const baseUrl = 'https://release.com/';

修改后配置文件

let baseUrlPrefix = ''
const env = process.env.NODE_ENV === 'development' ? 'development' : 'production'
console.log(process.env.NODE_ENV)
switch (env) {
    case 'development':
        baseUrlPrefix = 'http://dev.com/'
        break
    case 'production':
        baseUrlPrefix = 'https://release.com/' 
        break
}
export const baseUrl = baseUrlPrefix

执行命令查看

执行命令

npm run dev:weapp

发现控制台输出的process.env.NODE_ENV=development

执行命令

npm run build:weapp

发现控制台输出的process.env.NODE_ENV=production

执行结果说明一个问题,我们是可以通过process.env进行判断的开发环境和正式环境

那么我们遗留下来一个问题:测试环境怎么办?

测试环境怎么设置

执行npm run dev和build的区别

查看package.json文件

"scripts": {
    "build:weapp": "taro build --type weapp",
    "dev:weapp": "npm run build:weapp -- --watch"
 }

可以看出,dev和build的差别就是--watch,那么是不是--watch设置的process.env?

--watch为什么使process.env=development

查看源码 taro-build

program
  .option('--type [typeName]', 'Build type, weapp/swan/alipay/tt/h5/quickapp/rn/qq/jd')
  .option('--watch', 'Watch mode')
  .option('--page [pagePath]', 'Build one page')
  .option('--component [pagePath]', 'Build one component')
  .option('--env [env]', 'Env type')
  .option('--ui', 'Build Taro UI library')
  .option('--ui-index [uiIndexPath]', 'Index file for build Taro UI library')
  .option('--plugin [typeName]', 'Build Taro plugin project, weapp')
  .option('--port [port]', 'Specified port')
  .option('--release', 'Release quickapp')
  .parse(process.argv)

const { type, watch, ui, port, release, page, component, uiIndex } = program
let { env, plugin } = program

env = process.env.NODE_ENV || env

if (env) {
  process.env.NODE_ENV = env
} else {
  if (watch) {
    process.env.NODE_ENV = 'development'
  } else {
    process.env.NODE_ENV = 'production'
  }
}

这里可以看到当env不存在的时候,如果执行参数里面有watch就设置process.env.NODE_ENV = 'development',否则就是'production'。

到这里你是不是发现了什么端倪?

端倪

除了--watch之外,taro执行build命令的时候还接受一个参数--env,设置当前所在环境的,那么我们的测试环境是不是可以通过这个参数设置来实现呢?

我们来修改一下package.json

"scripts": {
    "build:weapp": "taro build --type weapp",
    "dev:weapp": "npm run build:weapp -- --watch",
    "test:weapp": "npm run test:weapp -- --watch --env test"
 }

修改配置文件

let baseUrlPrefix = ''
const env = process.env.NODE_ENV === 'development' ? 'development' : (process.env.NODE_ENV === 'test' ? 'test' : 'production')
console.log(process.env.NODE_ENV)
switch (env) {
    case 'development':
        baseUrlPrefix = 'http://dev.com/'
        break
    case 'test':
        baseUrlPrefix = 'https://test.com/' 
        break
    case 'production':
        baseUrlPrefix = 'https://release.com/' 
        break
}
export const baseUrl = baseUrlPrefix

执行命令

npm run test:weapp

想象一下打印出来的process.env.NODE_ENV是什么?

发生了什么?

process.env.NODE_ENV = 'production',为什么?

我们来看看我们的应用的config文件夹里面的index.js做了什么

module.exports = function (merge) {
    if (process.env.NODE_ENV === 'development') {
        return merge({}, config, require('./dev'))
    }
    return merge({}, config, require('./prod'))
}

我们设置的process.env.NODE_ENV应该是test,走到这里webpack的配置文件使用的是prod的配置,我们看看prod.js里面写了什么

module.exports = {
    env: {
        NODE_ENV: '"production"'
    },
    defineConstants: {
    },
    mini: {},
    h5: {
        /**
         * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
         * 参考代码如下:
         * webpackChain (chain) {
         *   chain.plugin('analyzer')
         *     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
         * }
         */
    }
}

看到了吗,这里把NODE_ENV又重置成了production,我们来尝试调整

增加test配置

增加test.js配置在config文件夹中

module.exports = {
    env: {
        NODE_ENV: '"test"'
    },
    defineConstants: {
    },
    mini: {},
    h5: {}
}

修改index.js

module.exports = function (merge) {
    if (process.env.NODE_ENV === 'development') {
        return merge({}, config, require('./dev'))
    } else if (process.env.NODE_ENV === 'test') {
        return merge({}, config, require('./test'))
    }
    return merge({}, config, require('./prod'))
}
查看原文

赞 0 收藏 0 评论 0

1029 回答了问题 · 2020-03-27

react hooks: useState中的初始值不能被赋值为来自父级的props中的值吗?

const [name, setName] = useState(() => {  
    return props.name || ''
})

关注 5 回答 3

1029 回答了问题 · 2020-03-27

JavaScript的一段代码运行问题

测试.png

这个明明是正常的啊

关注 2 回答 1

1029 赞了回答 · 2019-04-26

解决vue.js的keep-alive include无效

name,是name属性!!!注意一定要给需要缓存的组件都写name属性的值。我一开始还以为是路由的name值,后来发现搞错了 = =

关注 13 回答 8

认证与成就

  • 获得 40 次点赞
  • 获得 10 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 8 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-03-24
个人主页被 1.6k 人浏览