• 系统性学习vue-vue3


    Vue3简介

    代号:One Piece
    支持Typescript

    创建Vue3.0工程

    使用vue-cli创建

    1. 保证目前的vue-cli的版本在4.5.0以上
      在这里插入图片描述
    2. 在对应目录下创建项目
      在这里插入图片描述
      在这里插入图片描述
      创建期间报错了,看输出可能是node版本太低了
      Node下载官网

    在这里插入图片描述
    还是报错,去掉淘宝镜像npm config set registry https://registry.npmjs.org/可以了= =

    1. 运行 √
      在这里插入图片描述

    使用vite创建工程

    Vite官网
    (上一个项目就是这种)

    优势:

    • 开发环境中,无需打包操作。可快速的冷启动
    • 轻量快速的热重载(HMR)
    • 真正的按需编译,不再等待整个应用编译完成
    1. 创建工程
      npm init vite-app
    2. 安装依赖
      npm i
      在这里插入图片描述
    3. 运行
      npm run dev
      在这里插入图片描述

    分析工程结构(cli创建的)

    main.js

    对比以前写法,可以看到相似性
    但是如果使用原来写法,报错,根本引入不来Vue

    // 引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
    import { createApp } from "vue";
    import App from "./App.vue";
    
    // createApp(App)-创建应用实例对象(类似于vue2中的vm,但比vm更“轻”)
    // .mount("#app")-挂载
    createApp(App).mount("#app");
    
    // vue2的写法
    // new Vue({
    //   render: (h) => h(App),
    // }).$mount("#app");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    App.vue

    组件中的模板结构可以没有根标签
    其他就没有什么区别了

    <template>
      
      <img alt="Vue logo" src="./assets/logo.png" />
      <HelloWorld msg="Welcome to Your Vue.js App" />
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    安装开发者工具

    运行项目到Chrome,看到之前安装的开发者工具没有亮起在这里插入图片描述
    因为版本不对,需要重新下载安装支持Vue3的
    直接去Chrome商店下载在这里插入图片描述
    哎~没梯子
    没事还有第二种方法-离线下载
    发现一个宝藏网站
    安装完,亮起!
    在这里插入图片描述

    初识setup

    • 是Vue3中一个新的配置项,值为一个函数
    • setup是所有Composition API(组合API)“表演的舞台”
    • 组件中所有用到的:数据、方法等等,均要配置在setup中
    • setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用(常用)
    <template>
      <h1>信息:{{ name }}-{{ age }}h1>
      <button @click="sayHello">打招呼button>
    template>
    
    <script>
    export default {
      name: "App",
      setup() {
        // 数据
        let name = "Qiu";
        let age = 18;
    
        // 方法
        function sayHello() {
          alert("Hello! " + name);
        }
    
        // 返回对象
        return {
          name,
          age,
          sayHello,
        };
      },
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    2)若返回一个渲染函数,则可以自定义渲染内容

    <script>
    import { h } from "vue";
    export default {
      name: "App",
      setup() {
        // 返回渲染函数
        return () => h("h1", "Hello World!");
      },
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    注意点:

    1. 是向下兼容的,也可以用data,methods等Vue2配置,但不要混用vue2和vue3配置,因为在setup中不能读取到data或methods中数据,且setup优先级要高
    2. setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的数据

    ref函数

    此ref非之前学的ref属性
    之前写的代码展示目前没有问题,但是数据并没有响应式
    要想实现响应式,需要将数据用ref包装

    import { ref } from "vue";
    
    • 1

    处理基本类型

    // 数据
    let name = ref("Qiu");
    let age = ref(18);
    
    • 1
    • 2
    • 3

    在函数中控制台输出name
    在这里插入图片描述

    RefImpl(reference implement)引用实现对象
    其中setter和getter放在了原型对象中,可以类似理解为vm中的_data
    修改数据就要通过value改

    function changeInfo() {
      name.value = "CAI";
      age.value = "19";
    }
    
    • 1
    • 2
    • 3
    • 4

    而模板中不需要.value因为解析时会自动提取RefImpl对象的value

    处理对象类型

    let school = ref({
      name: "TUST",
      add: "TJ",
    });
    
    • 1
    • 2
    • 3
    • 4

    输出school.value,看到并不是RefImpl对象,而是Proxy对象
    在这里插入图片描述
    因此修改对象数据应该school.value.name = "TJKJDX";

    reactive函数

    作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
    const 代理对象 = reactive(源对象)
    reactive定义的响应式数据时“深层次的”(无论有几层都能定义到)
    内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作

    import { ref, reactive } from "vue";
    //......
    let school = reactive({ 
      name: "TUST",
      add: "TJ",
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出school,直接就是Proxy对象
    在这里插入图片描述
    修改数据就相比要简单school.name = "TJKJDX";
    由此可见,ref函数碰到对象时,也会用reactive将对象包装为Proxy
    reactive也可以处理数组对象

    回顾Vue2的响应式原理

    • 实现原理:
      对象类型:通过Object.definedProperty()对属性的读取、修改进行拦截(数据劫持)
      数组类型:通过重写更新数组的一系列方法来实现拦截
    • 存在问题:
      1)新增属性、删除属性,界面不会更新
      2)直接通过下标修改数组,界面不会自动更新

    vue3响应式原理—Proxy

    试验一下看看vue3会有上面的问题么

    <h1>信息:{{ school.name }}-{{ school.add }}-{{ school.age }}h1>
    <button @click="changeInfo">修改信息button>
    
    • 1
    • 2
    setup() {
      //数据
      let school = reactive({
        name: "TUST",
        add: "TJ",
      });
      function changeInfo() {
        school.age = 60;
      }
    
      // 返回对象
      return {
        school,
        changeInfo,
      };
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    直接看结果
    在这里插入图片描述
    所以vue3新增属性是响应式的

    模拟Vue3的响应式

    let person = {
      name:"Qiu",
        age:18
    }
    // 模拟vue3中实现响应式
    const p = new Proxy(person,{
        get(target,propName){
        console.log("捕获到获取了",propName);
            return target[propName];
        },
        set(target,propName,value){
            console.log(`捕获到修改了${propName},修改为${value}`);
            target[propName] = value;
        },
        // 添加了属性删除回调
        deleteProperty(target,propName){
            console.log("捕获到删除了",propName);
            return delete target[propName];
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    对数据进行增删改查,可以看到都是可以进行响应式的
    在这里插入图片描述
    但其实真正vue3中并不是target[propName] = value;直接对源数据进行操作
    而是使用window上的一个属性Reflect

    // 模拟vue3中实现响应式
    constp = new Proxy(person,{
        // 读取属性回调
        get(target,propName){
        console.log("捕获到获取了",propName);
            // return person[propName];
            return Reflect.get(target,propName);
        },
        // 修改、添加属性回调
        set(target,propName,value){
            console.log(`捕获到修改了${propName},修改为${value}`);
            // person[propName] = value;
            return Reflect.set(target,propName,value);
        },
        // !添加了属性删除回调
        deleteProperty(target,propName){
            console.log("捕获到删除了",propName);
            // return delete target[propName];
            return Reflect.deleteProperty(target,propName);
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    reactive对比ref

    从定义数据角度对比:

    • ref用来定义:基本类型数据
    • reactive用来定义:对象或数组数据类型
    • 备注:ref也可以用来定义对象或数组数据类型,它内部会自定通过reactive转为代理对象

    从原理角度对比:

    • ref通过Object.definedProperty()的get与set来实现响应式(数据劫持)
    • reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据

    从使用角度对比:

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据-均不需要.value

    setup的两个注意点

    setup的执行时机:在beforeCreate之前执行一次,this是undefined
    setup的参数:

    • props:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于vue2中的this.$atters
    • slots:收到的插槽内容,相当于vue2中的this.$slot
    • emit:分发自定义事件的函数,相当于vue2中的this.$emit

    注意:vue3中建议插槽内容template使用v-slot:xxx,因为可能会有兼容性问题

    计算属性与监视

    computed函数

    与vue2中的computed功能配置一致

    import { ref, reactive, computed } from "vue";
    //....
    // 计算属性
    let fullName = computed(() => {
      return person.firstName + person.lastName;
    });
    // 完整写法(考虑读和写)
    let fullName = computed({
      get() {
        return person.firstName + "-" + person.lastName;
      },
      set(value) {
        const nameArr = value.split("-");
        person.firstName = nameArr[0];
        person.lastName = nameArr[1];
      },
    });
    //....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    watch监视

    总结一下下面这些情况:
    主要是看watch函数的第一个参数类型

    • 如果是RefImpl类型的,即用ref定义的数据,默认不开启深度监视(也没必要开启),监视value值内容
    • 如果是Proxy类型对象,即用reactive定义的数据,就会强制开启深度监视,监视所有属性值(但oldValue没有用)
    • 如果是函数或函数数组,返回某一个数据中的属性,默认不开启深度监视,监视当前返回属性,如为object需开启深度监视

    所以当我们监视一个用ref定义的object时,需要.value或者改为深度监视(细品)

    有六种情况

    // 情况一:监视ref所定义的一个响应式数据
    watch(sum, (newValue, oldValue) => {
      console.log("sum变化了", newValue, oldValue);
    });
    // 完整写法
    // deep无效(看情况三)
    watch(
      sum,
      (newValue, oldValue) => {
        console.log("sum变化了", newValue, oldValue);
      },
      { immediate: true, deep: true }
    );
    
    // 情况二:监视ref所定义的多个响应式数据
    // newValue:[Object]
    watch([sum, msg], (newValue, oldValue) => {
      console.log("监视到变化了", newValue, oldValue);
    });
    
    // 情况三:监视reactive所定义的一个响应式数据的全部属性
    // 强制开启了深度监视,deep配置无效
    // 有个坑:此处无法正确获取oldValue
    watch(person, (newValue, oldValue) => {
      console.log("person变化了", newValue, oldValue);
    });
    
    // 情况四:监视reactive所定义的一个响应式数据的某一个属性
    watch(
      () => person.age,
      (newValue, oldValue) => {
        console.log("person.age变化了", newValue, oldValue);
      }
    );
    // 情况五:监视reactive所定义的一个响应式数据的某些属性
    watch([() => person.age, () => person.name], (newValue, oldValue) => {
      console.log("person某些属性变化了", newValue, oldValue);
    });
    
    // 特殊情况(person.job:Object)
    // 又一坑:监视数据中的Object属性,要加上deep,什么鬼
    watch(
      () => person.job,
      (newValue, oldValue) => {
        console.log("person.age变化了", newValue, oldValue);
      },
      { deep: true }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    watchEffect函数

    不用指定监视哪个属性,只要回调函数中使用到的数据发生变化,就直接重新执行回调函数,默认开启immediate(好智能~)

    // school.name发生变化时,回调执行
    watchEffect(() => {
      console.log("===watchEffect===", school.name);
    });
    
    • 1
    • 2
    • 3
    • 4

    Vue3生命周期

    和Vue2区别的地方:

    1. Vue2中是在created之后检测是否有el配置,而Vue3是在一开始就检测配置是否完整
    2. 将beforeDestory和destoryed更名为beforeUnmount和ummounted

    在这里插入图片描述

    使用组合式API使用生命周期钩子,即写在setup中(需要引入)(麻烦, 一般不用)
    优先级要比配置项钩子高
    官网文档

    • beforeCreate ======> setup()
    • created ==========> setup()
    • beforeMount =======> onBeforeMount
    • mounted ==========> onMounted
    • beforeUpdate ======> onBeforeUpdate
    • updated ==========> onUpdated
    • beforeUnmount =====> onBeforeUnmount
    • unmounted ========> onUnmounted

    自定义hook

    本质是一个函数,把setup函数中使用的Composition API进行了封装
    类似于混合 mixin

    在这里插入图片描述

    /**
     * useMousePos.js
     * 鼠标点击位置功能相关
     */
    import { reactive, onMounted, onBeforeUnmount } from "vue";
    export default function () {
      let pos = reactive({ x: 0, y: 0 });
      function getMousePos(event) {
        pos.x = event.pageX;
        pos.y = event.pageY;
      }
    
      onMounted(() => {
        window.addEventListener("click", getMousePos);
      });
      onBeforeUnmount(() => {
        window.removeEventListener("click", getMousePos);
      });
    
      return pos;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这样就可以直接引用使用了

    import useMousePos from "./hooks/useMousePos";
    setup() {
        // ...
        let pos = useMousePos();
        //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    toRef与toRefs

    作用:创建一个ref对象,其value值指向另一个对象中的某个属性
    语法:const name = toRef(person, name)
    应用:要将响应式对象中的某个属性单独提供给外部使用
    扩展:toRefstoRef功能一直,但可以批量创建多个ref对象

    当我们希望在模板中直接使用某个变量时

    <h1>学校名称:{{ mySchoolName }}h1>
    
    • 1
    setup() {
    	let school = reactive({
          name: "TUST",
          add: "TJ",
          teacher: {
            name: "LIU",
            age: "40",
          },
        });
    	return {
    	    mySchoolName: school.name,
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到mySchoolName仅仅是一个字符串数据,并没有响应式,修改school中的数据mySchoolName并不会改变
    在这里插入图片描述
    那用ref包装呢

    mySchoolName: ref(school.name),
    
    • 1

    确实可以变成ref类型,相当于重新定义了一个ref类型对象,与school.name并不是同一个引用,数据还是不互通的
    在这里插入图片描述
    这就要用到toRef了,这样数据就与school.name为同一引用了
    mySchoolName: toRef(school, "name"),
    如果有多个这样的需求,就用toRefs

    return {
      mySchoolName: toRef(school, "name"),
      ...toRefs(school),
    };
    
    • 1
    • 2
    • 3
    • 4

    会返回所有属性改为ref后的对象集合
    在这里插入图片描述
    以上记录的组合式都是比较常用的

    shallowReactive与shallowRef

    shallowReactive:浅层次的响应式
    这样teacher内数据不会是响应式的

    let school = shallowReactive({
      name: "TUST",
      add: "TJ",
      teacher: {
        name: "LIU",
        age: "40",
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    shallowRef:只处理基本数据类型的响应式,不进行对象内部的响应式处理
    shallowRef传入基本类型与ref没有区别
    传入对象类型,就不会在内部使用reactive

    用于性能优化(自己悟吧~)

    readonly与shallowReadonly

    用readonly包装的响应式所有数据都不允许修改(深只读)
    用shallowReadonly包装的响应式只会控制浅层的数据不进行修改(浅只读)

    toRaw与markRaw

    toRaw与reactive是反向操作,会将Proxy响应式对象还原为普通对象(RefImpl不行)

    当向Proxy响应式对象追加一个深层次的数据,且并不需要后续修改,如果正常追加,就也会成为响应式,没必要
    如下,使用markRow包裹的对象就不会成为响应式,添加的car就不会成为响应式数据

    let car = {name:"大众",price:20}
    person.car = markRow(car)
    
    • 1
    • 2

    customRef

    贴个完整代码,比较难理解,
    应该是为了暴露出getter和setter,方便进行自定义操作
    修改数据时工作流程:
    value = newValue 修改value值为更改后数据
    ② 走get()获取最新数据(需要添加track())
    trigger()重新解析模板

    <template>
      <input type="text" v-model="keyWord" />
      <h4>{{ keyWord }}h4>
    template>
    
    <script>
    import { ref, customRef } from "vue";
    export default {
      name: "CustomRefDemo",
      setup() {
        // let keyWord = ref(""); // 精装房
        let keyWord = myRef(""); // 毛坯房 需要自己根据自己需求装修
    
        // 自定义ref
        function myRef(value) {
          return customRef((track, trigger) => {
            return {
              get() {
                console.log("从myRef中读取了数据");
                track(); // 通知vue追踪value数据改变
                return value;
              },
              set(newValue) {
                console.log("myRef中数据修改了");
                value = newValue; // 修改数据
                trigger(); //通知vue重新解析模板
              },
            };
          });
        }
    
        return {
          keyWord,
        };
      },
    };
    script>
    
    <style>style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    provide与inject

    作用:实现祖组件及其后代组件间通信(就不用逐层传了)
    祖组件有一个provide选项来提供数据,孙组件有一个inject选项来开始使用这些数据
    其实不止孙组件,所有后代组件都可以用inject获取数据

    // 祖组件
    provide("data",data);
    
    • 1
    • 2
    // 后代组件
    let data = inject("data");
    
    • 1
    • 2

    响应式数据的判断

    isRef()
    isReactive()
    isReadonly()
    isProxy() – 检查是否为reactive或readyonly包装的对象

    Composition API的优势

    • Vue2中使用的是OpitionsAPI(配置API),即将需求代码放在data,methods等中,那实现一个功能的数据、方法等都要拆分放到这些配置项中,会比较混乱
    • Vue3的组合式API就可以将每个功能的相关放到一起

    新组件

    Fragment

    在Vue2中:组件必须有一个根标签
    而在Vue3中组件可以没有根标签,其实内部是将多个标签包含在一个Fragment虚拟元素中
    优点:减少标签层级,减小内存占用

    Teleport

    如图,在Son组件中使用了Dialog组件来创建弹窗,但是希望是按钮显示在Son组件中 而弹窗显示在外部
    在这里插入图片描述
    就需要在Dialog组件中使用teleport传送组件,将需要传送出去的结构包裹在teleport标签中,并指定要传送的位置

    <template>
      <div>
        <button @click="isShow = true">点我弹个窗button>
        <teleport to="body">
          <div v-show="isShow" class="dialog">
            <h3>我是一个弹窗h3>
            <h4>balabalabalabalabalabalbalah4>
            <button @click="isShow = false">关闭弹窗button>
          div>
        teleport>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    在这里插入图片描述

    Suspense

    一般我们引入组件import Child from "./components/Child.vue"; 静态引入组件,这样引入父组件与子组件是互相等待同时展示出来的
    另一种引入是动态/异步引入组件

    import {defineAsyncComponent} from "vue";
    const Child = defineAsyncComponent(() => import("./components/Child.vue"));
    
    • 1
    • 2

    这样就是谁先准备好了就先展示谁
    但是这样也会有一个问题
    当页面上只展示出来父组件,而子组件还在加载中时,则完全不知道会有子组件存在,而导致忽略一些东西
    这就需要使用Supense组件,将动态引入的组件包裹

    <div class="app">
      <h3>我是APP组件h3>
      <Suspense>
        <template v-slot:default>
          <Child>Child>
        template>
        <template v-slot:fallback>
          <h3>稍等。。。加载中。。。h3>
        template>
      Suspense>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    在这里插入图片描述
    当使用异步引用和suspend组合技时,setup中return的就可以是Promise实例对象

    其他

    全局API的转移

    vue2中我们需要注册全局组件和注册全局指令是使用全局API去配置

    // 全局注册组件
    Vue.component("comp",comp);
    // 全局注册指令
    Vue.directive('focus',{
    	inserted: el => el.focus()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    而Vue3中对这类API做了调整,将Vue.xxx调整为app.xxx
    有一个需要注意的是原来的Vue.config.productionTip在Vue3中已经移除
    还有一个特殊的Vue.prototype调整为app.config.globalProperties

    其他改变

    • data选项必须被声明为一个函数
    • 过渡类型的更改(就是加了个-from)
    // vue2
    .trans-enter,
    .trans-leave-to {
      transform: translateX(-100%);
    }
    
    .trans-enter-to,
    .trans-leave {
      transform: translateX(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    //vue3
    .trans-enter-from,
    .trans-leave-to {
      transform: translateX(-100%);
    }
    
    .trans-enter-to,
    .trans-leave-from {
      transform: translateX(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 移除了keyCode作为v-on的修饰符,同时也不再支持config.keyCodes(自定义别名按键)
    • 移除v-on.native修饰符(Vue3中通过不声明事件指定为原生事件)
    • 移除过滤器 filter (建议使用计算属性或方法调用)

    完结撒花~

  • 相关阅读:
    python列出本地文件路径
    小红书达人怎么对接,博主沟通流程汇总!
    JVM虚拟机:垃圾回收器ZGC和Shenandoah算法
    【编程题】【Scratch四级】2019.12 打棒球
    如何实现 Python Switch Case 语句?
    电影兑换券的推荐策略——二分图最优匹配算法
    在Pyppeteer中实现反爬虫策略和数据保护
    Spring Boot 邮件发送(五种类型的邮件)
    「Kafka」Kafka理论知识解读(一)
    理解ceres
  • 原文地址:https://blog.csdn.net/qq_39685400/article/details/135828716