当前位置: 首页 > news >正文

20230624----重返学习-vue-响应式处理思路-仿源码

day-098-ninety-eight-20230624-vue-响应式处理思路-仿源码

vue

vue大体概念

  • Vue是渐进式框架

    • 所谓渐进式框架,就是把一套全面的框架设计体系,拆分成为多个框架,项目中需要用到那些需求,再导入对应的框架,以此来保证外部资源的最小化!
  • Vue2全家桶

    • Vue@2:vue框架的核心!含单个组件状态管理、组件的管理。
      • vue-cli:用于创建项目的脚手架工具。管控webpack等打包功能。
    • vuex@3:实现vue组件间的公共状态管理。
      • vuex-persist 公共状态持久化存储插件。
    • vue-router@3:SPA单页面应用中的路由管理!
    • UI组件库:
      • PC端:饿了么团队element-ui、阿里antd of vue@1、京东iview。
      • 移动端:有赞vant@2、蚂蚁金服cube…
  • Vue3全家桶

    • vue@3
      • vite:用于创建项目的脚手架工具。
    • vuex@4、pinia
    • vue-router@4
    • UI组件库:
      • PC端:element-plus、antd of vue@3…
      • 移动端:vant@3…
  • Vue生态中,完善的项目解决方案:

    • antd pro vue:淘系方案-核心是vue3。
      • pro.antdv官网
      • 免费版:pro.antdv文档
      • 收费版:vue3 + TS
    • 若依
      • 若依-官网

vue常见面试题

  • Vue2框架常见的面试题
    1. 谈谈你对 MVVM / MVC 模式的理解
    2. Vue2框架怎么实现对象和数组的监听?「Vue2响应式原理」
    3. v-model指令实现的原理
    4. v-show 与 v-if 有什么区别?
    5. Class 与 Style 如何动态绑定?
    6. computed 和 watch 的区别和运用的场景?
    7. 谈谈你对 Vue2 生命周期的理解?
    8. Vue怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
    9. 开发中常用的Vue指令有哪些

MVVM与MVC

  • 面试题:谈谈你对 MVVM / MVC 模式的理解?
    • MVVM模式:双向数据驱动,如Vue2与Vue3。

      • model:数据层。

        • 在数据层,我们需要构建出:项目中需要的各种数据与方法。例如:响应式状态、属性、计算属性、监听器、过滤器、方法、钩子函数…
          • 说明:
            • 在vue2中:基于OptionsAPI(配置项)方式,来管理这些内容。

              export default {data(){ return { ... } },props:[...],computed:{},watch:{},filters:{},methods:{},...
              }
              
              <script>
              export default {data(){ return { ...响应式状态 } },props:[...属性],computed:{...计算属性},watch:{...监听器},filters:{...过滤器},methods:{...方法},...
              }
              </script>
              
            • vue3中:基于CompositionAPI(聚合式)&函数式编程方式,来管理这些内容。

      • view 视图层。

        • 视图层的原理:在Vue框架中,我们基于<template>jsx语法构建需要的视图,最后把视图编译为VirtualDOM(虚拟DOM),再经过DOM-diff进行差异化对比,最后把VirtualDOM/补丁包渲染为真实的DOM
          • 步骤说明:
            1. 基于<template>jsx语法构建需要的视图。
              • 这个主要是用户自己手写的,绑定响应式数据与绑定事件。
              • 还基于指令控制数据与视图的联系。
            2. 视图编译为VirtualDOM
              • 在vue2中:基于vue-template-compiler插件,把视图编译为VirtualDOM
              • 在vue3中:基于@vue/compiler-sfc插件,把视图编译为VirtualDOM
            3. 经过DOM-diff进行差异化对比。
              • 这个是vue内部做的,diff算法。
            4. VirtualDOM/补丁包渲染为真实的DOM
              • 渲染周期步骤:
                • 第一次渲染VirtualDOM直接渲染为真实的DOM
                • 非初次渲染补丁包渲染为真实的DOM
                  • 补丁包是通过DOM-diff这一步来对比新旧数据来生成的。性能好,能更快渲染。
              • 这一步基本上都是vue内部做的。
      • viewModel:监听层,Vue框架的核心。

        1. 这个是vue框架内部自动做的。正常不用关心。
        • 监听响应式数据的变化,当数据发生改变后,通知视图更新。
          • Vue2中基于Object.defineProperty对数据进行劫持。
          • Vue3中基于ES6中的Proxy对数据进行劫持。
          • 基于观察者模式通知视图更新。
        • 监听视图的变化(一般指的是Form表单内容的改变),当视图内容改变后,自动修改对应的数据(数据一改,视图紧接着跟着更新)。
          • 监听视图变化主要是基于v-model指令。
    • MVC模式:单向数据驱动框架,如React。

      • model 数据层。

        • 构建项目中需要的数据和方法。例如:状态、属性、钩子函数、普通函数等。
          • 类组件中:基于 state/props/实例 构建状态和属性。
          • 函数组件中:基于useState/useEffectHooks函数,完成上述内容的管理。
      • view 视图层。

        • 在React中,基于jsx语法构建需要的视图React会基于babel-preset-react-appjsx语法编译为React.createElement格式createElement方法执行,会创建出对应的VirtualDOM,经过DOM-diff对比,最后把VirtualDOM/补丁包,基于ReactDOM中的render方法,渲染为真实的DOM
      • controller 控制层。

        • 实现事件绑定和相关的业务逻辑。
        • React框架实现了数据更改可以让视图自动更新的机制。但是React不同于Vue,并没有对状态做数据劫持。如果打算修改状态后,让视图更新,需要基于特定的方法去修改状态才可以!
          • 类组件中:可以用setState/forceUpdate方法。
          • 函数组件中,可以用useState等Hook函数。
        • 但是React中默认并没有实现对视图的监听,这样导致,视图内容改变,对应的状态也不会自动更改!
          • 不过我们可以自己给表单元素做事件绑定,当内容改变后,手动去修改对应的状态。
    • 总结:无论是MVVM还是MVC,都是目前前端主流的框架思想,都是以数据驱动视图渲染核心,告别传统直接操作DOM的方式,转而操作VirtualDOM!再配合对应的生态体系,让项目开发既高效,又提高了性能!…

Vue的学习路线

  • 如何学习Vue?
    • 第一条线:视图线
      • <template>JSX语法
        • 指令「内置的14个指令和自定义指令」
        • JSX语法
      • VirtualDOM编译的机制
      • 掌握DOM-DIFF算法
    • 第二条线:数据线
      • 学习 OptionsAPI/CompositionAPI 中的:语法、原理、区别等内容
        1. OptionsAPI选项
      • 学习 MVVM 的原理
        • 数据是如何被监听的「Vue2和Vue3是不一样的」
        • 监听数据变化后,如何通知视图更新「观察者模式」
        • 如何监听视图的变化,以及如何让状态跟着更改「v-model」
    • 第三条线:组件化开发
      • 单文件组件「含样式私有化方案的原理」
      • 类组件和函数组件
      • 复合组件通信
      • 组件封装的技巧「各种封装技巧」
        • 通用业务组件
        • UI组件库的二次封装
        • 通用功能组件
        • Vue.mixin
        • Vue.directive
        • Vue.extend
    • 第四条线:实战线
      • vuex / vue-router
      • <keep-alive>
      • <transition>
      • <component>
      • 上拉刷新、下拉加载
      • 超长列表性能优化
      • 登录/权限管理模型
      • 前后端数据通信管理方案

OptionsAPI选项式数据

  1. OptionsAPI选项-数据
  2. OptionsAPI选项-DOM
  3. OptionsAPI选项-生命周期钩子
  4. OptionsAPI选项-资源
  5. OptionsAPI选项-组合
  6. OptionsAPI选项-其它

对象和数组的监听

  • 面试题:Vue2框架怎么实现对象和数组的监听?「Vue2响应式原理」

数据初始化

  • vue2源码在/node_modules/vue/dist/vue.js中。
  • new Vue()的时候,OptionsAPI中的data是用来构建响应式数据-即状态的。
    • 特点
      1. 在data中构建的状态,会直接挂载到实例上。
        • 在js中,可以基于实例去访问对应的状态 -> vm.msg/this.xxx;
        • 而挂载到实例上的信息,可以直接在视图中访问 -> {{msg}};
      2. 在data中构建的状态,会被进行数据劫持,即get/set。数据劫持的目的是让其变为响应式的,这样以后修改此状态信息,会触发set劫持函数,在此劫持函数中,不仅修改了状态值,而且还会通知视图更新!
        - 只有在new的时候,写在data中的状态,才会默认被数据劫持,变为响应式状态。
    • Vue2响应式源码:
      1. new Vue()后,首先执行Vue.prototype._init方法,在此方法中做了很多事情,例如:
        • 向实例上挂载很多内置的私有属性。
          • 带$xxx是我们开发者后续要用到的。
          • 带_xxx是给Vue内部用的。
        • 基于callHook$1方法,触发beforeCreate()钩子函数执行。
        • 初始化上下文中的信息。
        • 执行initState方法,初始化属性、状态、计算属性、监听器等信息。
        • 触发created钩子函数执行。
      2. 执行initState方法的时候
        • 基于initProps$1初始化属性。注册接收属性与属性规则校验。
        • 基于initMethods初始化普通函数。
        • 基于initComputed$1初始化计算属性。
        • 基于initWatch初始化监听器。
        • 基于initData初始化状态。
      3. 执行initData方法的时候,主要目的就是初始化状态-也就是把信息做响应式数据劫持。
        • 先判断data是否是一个函数(组件中的data都是函数),如果是函数,先把函数执行(函数中的this是实例,并且传递实例),把执行的返回值,重新赋值给data。

          var data = vm.$options.data;
          data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
          
        • 接下来要确保data是一个纯粹的对象。

          if (!isPlainObject(data)) {data = {};warn$2('data functions should return an object:\n' + 'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm);
          }
          
        • 然后基于Object.keys方法,获取data对象中的可枚举、非Symbol类型的私有属性,然后判断这些属性,是否出现在methods和props中,如果出现了则报错!原因:methods/pprops中编写的信息,也会直接挂在实例上,如果名字,则相互冲突了!

          var keys = Object.keys(data);
          var props = vm.$options.props;
          var methods = vm.$options.methods;
          var i = keys.length;
          while (i--) {...
          }
          
          var keys = Object.keys(data);
          var props = vm.$options.props;
          var methods = vm.$options.methods;
          var i = keys.length;
          while (i--) {var key = keys[i];{if (methods && hasOwn(methods, key)) {warn$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);}}if (props && hasOwn(props, key)) {warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +"Use prop default value instead.", vm);}else if (!isReserved(key)) {proxy(vm, "_data", key);}
          }
          
        • 最后基于observe函数,对data对象中的信息进行数据劫持!

          var ob = observe(data);
          ob && ob.vmCount++;
          
        • 学习总结:真实项目中,建议把状态数据,全部事先写在data中(即便不清楚其值,也先写上,可以赋值初始值)。因为只有写在data中的数据,在最开始渲染阶段,才会被做响应式的数据劫持

      4. 执行observe方法的时候,把data对象传递进去。
        • 如果data对象已经被处理过,则不会重新处理。

          if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {return value.__ob__;
          }
          
        • 而且data对象必须符合好多条件,才可以去处理:是数组或者对象、并且没有被冻结/密封/阻止扩展、并且不是ref对象,也不是VirtualDOM(vnode)…

          if (shouldObserve &&(ssrMockReactivity || !isServerRendering()) &&(isArray(value) || isPlainObject(value)) &&Object.isExtensible(value) &&!value.__v_skip /* ReactiveFlags.SKIP */ &&!isRef(value) &&!(value instanceof VNode)) {return new Observer(value, shallow, ssrMockReactivity);
          }
          

          简洁处理:

          if (... && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value.__v_skip  && !isRef(value) && !(value instanceof VNode)) {return new Observer(value, shallow, ssrMockReactivity);
          }
          
        • 如果符合了全部条件,则创建Obsever类的实例,把data对象传递进去进行处理。

        • 学习总结:如果某个写在data中的对象,我们不期望对其内部做劫持处理,此时我们只需要把这个对象基于Object.freeze()冻结即可!

          • 因为劫持处理的过程是需要消耗性能和时间的。
            • 例如:从服务器获取的数据,我们并没有修改其内部某一项值),让视图更新的需求,那么这些数据压根就不需要做劫持。
      5. 执行new Observer(data),对data对象中的每一项进行数据劫持
        • 但凡被处理过的对象,都会设置一个__ob__属性,属性值是Observer类的实例。

          def(value, '__ob__', this);
          
        • 然后判断data是数组还是对象,两者处理的方式是不一样的。

          • 如果是对象:

            • 基于Object.keys()获取对象所有可枚举、非Symbol类型的私有属性。

            • 然后迭代这些成员,对每一个成员,基于defineReactive()做数据劫持。

              var keys = Object.keys(value);
              for (var i = 0; i < keys.length; i++) {var key = keys[i];defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);
              }
              
          • 如果是数组:

            • 在Vue2中,有一个对象arrayMethods,这个对象的特点:
              • 对象中有7个方法:push/pop/shift/unshift/splice/sort/reverse;
              • 对象.__proto__指向Array.prototype
            • 接下来让data这个数组,拥有arrayMethods上的这七个方法。
              • 在非IE浏览器中,就是让 data数组.__proto__=arrayMethods
              • 在IE浏览器中,迭代arrayMethods中的每一个方法,把这些方法作为data数组的私有方法。
            • 当我们以后调用数组这7个方法的时候,用的都是arrayMethods中的这七个方法。
              • 调用重写的这7个方法,其内部:
                • 获取传递的实参。
                • 基于Array.prototype内置的方法实现对应的功能。
                • 如果调用的是push/unshift/splice,需要把新增的内容,基于observeArray进行递归处理,实现深度的监听劫持。
                • 最后通知视图更新。
            • 执行observeArray对传递的data数组,再次进行递归处理。
      6. 在defineReactive函数中。
        • 首先又对此对象中的某个成员进行校验,验证是否是冻结/密封的,如果是,则不进行数据劫持。

          var property = Object.getOwnPropertyDescriptor(obj, key);
          if (property && property.configurable === false) {return;
          }
          
        • 然后对对象中此成员的值,进行递归处理,目的是进行尝试的监听劫持!

          var childOb = !shallow && observe(val, false, mock);
          
        • 最后基于Object.defineProperty()对此对象中的这个成员做get/set劫持。

      7. 在observeArray方法中:
        • 迭代数组中的每一项,对每一项再基于observe进行递归处理,实现深度的监听劫持。

          for (var i = 0, l = value.length; i < l; i++) {observe(value[i], false, this.mock);
          }
          
          • 深度的监听劫持:

            let vm = new Vue({data: {msg: "哈哈",obj:{x:10,}},
            });
            
            • 不仅对msgobj做了数据劫持,还对obj这个对象的x属性也做了数据劫持
      • 总结:Vue2响应式原理,针对数组和对象,有不同的处理情况:
        • 如果是对象:基于Object.defineProperty对对象中的每个成员(成员特点是可枚举、非Symbol类型),进行深度的监听劫持。
          • 当修改成员值的时候,触发set劫持函数。在set函数中,不仅修改了成员值,而且还对新修改的值做监听劫持。最主要的是通知视图更新!
        • 如果是数组:并不像对象一样,没有对数组中的每个索引项做监听劫持。所以基于索引修改数组某一项的值,视图是不会更新的。而是重写了数组的7个方法-push/shift/unshift/pop/splice/sort/reverse,基于这7个方法修改数组的内容,不仅仅修改了内容,而且对新修改的内容也会做劫持,也会通知视图的更新!最后对数组中的每一项内容,也基于递归的方式,看看是否需要劫持!
      • 代码示例:
        • fang/f20230624/day0624/test.html

          <!DOCTYPE html>
          <html><head><meta charset="UTF-8" /><title>Document</title></head><body><div id="app">{{msg}}-{{text}}</div></body>
          </html>
          <!-- <script src="./node_modules/vue/dist/vue.min.js"></script> -->
          <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
          <script>let vm = new Vue({data: {msg: "哈哈",},});vm.text = "嘿嘿";console.log(`实例:vm-->`, vm);setTimeout(() => {vm.text = "hhh";console.log(2000, `text改值了,但视图并没有自动更新`);}, 2000);setTimeout(() => {vm.msg = "方";console.log(10000, `msg改值了,但视图会自动更新`);}, 10000);vm.$mount("#app");
          </script>
          

响应式处理思路-仿源码

  • fang/f20230624/day0624/test.html

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8" /><title>Document</title></head><body><div id="app">{{msg}}-{{text}}</div></body>
    </html>
    <!-- <script src="./node_modules/vue/dist/vue.min.js"></script> -->
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> -->
    <script>// let vm = new Vue({//   data: {//     msg: "哈哈",//     obj:{//       x:10,//     }//   },// });// vm.text = "嘿嘿";// console.log(`实例:vm-->`, vm);// setTimeout(() => {//   vm.text = "hhh";//   console.log(2000, `text改值了,但视图并没有自动更新`);// }, 2000);// setTimeout(() => {//   vm.msg = "方";//   console.log(10000, `msg改值了,但视图会自动更新`);// }, 10000);// vm.$mount("#app");
    </script><script src="./test.js"></script>
    
  • fang/f20230624/day0624/test.js

    // 检测是否为纯粹对象。
    const toString = Object.prototype.toString;
    const isPlainObject = function isPlainObject(obj) {if (toString.call(obj) !== "[object Object]") return false;let proto = Object.getPrototypeOf(obj);if (!proto) return true;let Ctor = "constructor" in obj && obj.constructor;return Ctor === Object;
    };// 给对象设置不可枚举的属性。
    const define = function define(obj, key, value) {Object.defineProperty(obj, key, {value,enumerable: false,writable: true,configurable: true,});return obj;
    };// 通知视图更新的方法。
    const notify = function notify() {console.log(`视图更新`);
    };// 重写数组7个方法的对象。
    const arrayProto = Array.prototype;
    const arrayMethods = Object.create(arrayProto);
    let methods = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
    methods.forEach((method) => {let original = arrayProto[method]; //对应Array.prototype上的内置方法。define(arrayMethods, method, function mutator(...args) {// 基于内置的方法,把功能先实现。this-我们要操作的数组let result = original.call(this, ...args);// 对于新增或修改的信息,需要基于递归,进行深层次的监听劫持。let inserted;switch (method) {case "push":case "unshift":inserted = args;break;case "splice":inserted = args.slice(2);break;default:break;}if (inserted) {observeArray(inserted);}// 通知视图更新。notify();return result;});
    });
    // ary.push(100, 200, 300);// 数据劫持的处理。
    const defineReactive = function defineReactive(obj, key, proxy) {// 对成员的规则再次校验。let property = Object.getOwnPropertyDescriptor(obj, key);if (property && property.configurable === false) {return;}// 对此成员的值进行深度处理。observe(obj[key]);// 对此成员进行数据劫持。Object.defineProperty(obj, key, {get: function reactiveGetter() {return proxy[key];},set: function reactiveSetter(newVal) {// 新老值相同,则不进行任何的处理。if (Object.is(newVal, obj[key])) {return;}// 修改值proxy[key] = newVal;// 对新设置的值也要进行深度处理。observe(newVal);// 通知视图更新notify();},});
    };// 对数组中的每一项进行响应式处理。
    const observeArray = function observeArray(arr) {// 对传递数组中的每一项,都基于observe进行响应式处理。// debugger;arr.forEach((item) => {observe(item);});
    };
    // 对数组/对象进行响应式处理。
    const observe = function observe(data) {let isArray = Array.isArray(data);let isObject = isPlainObject(data);// 如果是数组/对象,并且不是被冻结/密封/阻止扩展的,我们才处理。if ((isArray || isObject) && Object.isExtensible(data)) {// 防止套娃操作。if (data.hasOwnProperty("__ob__")) {return data;}define(data, "__ob__", true);// 数组:重定向其原型指向 & 对数组每一项进行深度处理。if (isArray) {data.__proto__ = arrayMethods; // Object.setPrototypeOf(data, arrayMethods);observeArray(data);}// 对象:迭代对象中的每一项,对每一项都基于defineProperty进行数据劫持。if (isObject) {let keys = Object.keys(data);let proxy = { ...data };keys.forEach((key) => {defineReactive(data, key, proxy);});}}// console.log(`data-->`, data);return data;
    };// -----做测试。
    let data = {msg: "哈哈",obj: {x: 10,y: {z: [100, 200],},},arr: [1, 2, { n: 1000 }],
    };
    data.data = data;
    observe(data);console.log(`响应式数据:data-->`, data);setTimeout(() => {console.log(`data.arr[1] = "改"`);data.arr[1] = "改"; //视图不更新;
    }, 2000);
    setTimeout(() => {console.log(`data.msg = "改"`);data.msg = "改"; //视图更新;
    }, 1000);
    setTimeout(() => {console.log(`data.arr.push(4, 5, 6)`);data.arr.push(4, 5, 6); //视图更新;
    }, 5000);
    setTimeout(() => {console.log(`data.obj.x = "改了data.obj.x"`);data.obj.x = "改了data.obj.x"; //视图更新;
    }, 10000);// console.log(`data.arr-->`, data.arr);
    

进阶参考

  1. OptionsAPI选项-数据

相关文章:

20230624----重返学习-vue-响应式处理思路-仿源码

day-098-ninety-eight-20230624-vue-响应式处理思路-仿源码 vue vue大体概念 Vue是渐进式框架 所谓渐进式框架&#xff0c;就是把一套全面的框架设计体系&#xff0c;拆分成为多个框架&#xff0c;项目中需要用到那些需求&#xff0c;再导入对应的框架&#xff0c;以此来保证…...

【MongoDB】三、使用Java连接MongoDB

【MongoDB】三、使用Java连接MongoDB 实验目的实验内容练习1、开启Eclipse&#xff0c;创建Java Project项目&#xff0c;命名为Mongo12、添加项目依赖的jar包3、创建类MongoDemo4、连接数据库5、查看集合6、创建集合7、删除集合8、查看文档9、插入文档10、更新文档11、删除文档…...

【C++】通讯录的基本实现,附有源码分享

目录 1、运行环境 2、系统实现功能 2.1菜单功能 2.2退出通讯录功能 2.3添加联系人功能 2.4显示联系人功能 2.5删除联系人功能 2.6查找联系人功能 2.7修改联系人功能 2.8清空联系人功能 2.9动态扩容功能 2.10选择优化功能 2.11文件操作 3、源码分享 1、运行环境 …...

UI 自动化测试 —— selenium的简单介绍和使用

selenium 是 web 应用中基于 UI 的自动化测试框架&#xff0c;支持多平台、多浏览器、多语言。 提到 UI 自动化就先了解什么是自动化测试&#xff1f; 目录 1. 自动化测试 2. UI 自动化 2.1 UI 自动化的特点 2.2 UI 自动化测试的优缺点 2.3 UI 自动化测试的使用对象 2.4…...

mybatisPlus中apply的使用以进行联表等复杂sql语句

在 MyBatis-Plus 中&#xff0c;apply() 方法可以用于添加任意的 SQL 片段&#xff0c;包括联表查询。因此&#xff0c;你可以使用 apply() 方法来处理各种类型的联表查询。 使用 apply() 方法的好处是可以在查询条件中直接添加原生的 SQL 片段&#xff0c;而不受 MyBatis-Plu…...

自学Python技术的方法

目录 一、Python技术介绍 二、学习前的准备工作 三、学习时的具体操作 四、如何巩固学习 Python是一种高级编程语言&#xff0c;被广泛用于软件开发、数据分析、人工智能和科学计算等领域。它于1991年由Guido van Rossum创建&#xff0c;并且其简洁、易读的语法以及丰富的标…...

python熟悉python基础语法,了解html网络结构,了解json格式数据,含有字符串

前言 Python网络爬虫是利用Python编写的程序&#xff0c;通过自动化地访问网页、解析html或json数据&#xff0c;并提取所需信息的技术。下面将详细介绍一些与Python网络爬虫相关的重要知识点。 1、Python基础语法&#xff1a; 变量和数据类型&#xff1a;学习如何声明变量以及…...

linux mail -s发送邮件异常解决

异常&#xff1a; Error initializing NSS: Unknown error -8015. "/root/dead.letter" 11/301 . . . message not sent. 出现此问题&#xff0c;大概率是和证书相关。如果没有安装证书&#xff0c;请先安装&#xff1a; 1&#xff0c;下载 yum -y install mailx …...

Netty核心技术七--Google Protobuf

1.编码和解码的基本介绍 编写网络应用程序时&#xff0c;因为数据在网络中传输的都是二进制字节码数据&#xff0c;在发送数据时就需要编码&#xff0c;接收数据时就需要解码 codec(编解码器) 的组成部分有两个&#xff1a;decoder(解码器)和encoder(编码器)。encoder 负责把…...

【Docker】Docker常用命令总结

文章目录 一、帮助命令二、镜像命令三、容器命令四、常用的其他命令 在开发过程中&#xff0c;经常涉及到 docker 的相关操作&#xff0c;本文对常用的指令进行汇总。 一、帮助命令 docker version # 显示docker版本信息 docker info # 显示docker系统信息&#xff…...

React 对比class与Effect Hook优化响应式数据更新监听,感受useEffect真正的强大

还是之前写过的一个组件 import React from "react"export default class index extends React.Component{constructor(props){super(props);this.state {name: "小猫猫"}}componentDidMount ()>{document.title this.state.name;}componentDidUpda…...

AWS Lambda 介绍

计算服务的演进 EC2------Container-------Lambda 虚拟机---容器--------------serverless无服务器架构 什么是AWS Lambda&#xff1f; AWS lambda的核心是事件驱动&#xff0c;驱动可能来自&#xff0c;Alexa,SNS&#xff0c;DynamoDB&#xff0c;S3&#xff0c;Kinesis等&…...

linux之权限管理

目录 1.一.基本小语句 2.文件权限操作chmod 1.一.基本小语句 ls - a 查看此文件夹所有和隐藏内容 ls - l 查看此文件夹权限 chown 改变文所有者 2.文件权限操作chmod chmod 参数 文件名 文件的权限主要针对三类对象进行定义   owner 属主, u:针对前三个部分的权限修改   …...

【设计模式与范式:行为型】61 | 策略模式(下):如何实现一个支持给不同大小文件排序的小程序?

上一节课&#xff0c;我们主要介绍了策略模式的原理和实现&#xff0c;以及如何利用策略模式来移除 if-else 或者 switch-case 分支判断逻辑。今天&#xff0c;我们结合“给文件排序”这样一个具体的例子&#xff0c;来详细讲一讲策略模式的设计意图和应用场景。 除此之外&…...

【C++】auto_ptr为何被唾弃?以及其他智能指针的学习

搭配异常可以让异常的代码更简洁 文章目录 智能指针 内存泄漏的危害 1.auto_ptr(非常不建议使用) 2.unique_ptr 3.shared_ptr 4.weak_ptr总结 智能指针 C中为什么会需要智能指针呢&#xff1f;下面我们看一下样例&#xff1a; int div() {int a, b;cin >&g…...

数据结构练习题1:基本概念

练习题1&#xff1a;基本概念 1 抽象数据类型概念分析2. 逻辑结构与存储结构概念分析3.综合选择题4.综合判断题5.时间复杂度相关习题6 时间复杂度计算方法&#xff08;一、二、三层循环&#xff09; 1 抽象数据类型概念分析 1.可以用&#xff08;抽象数据类型&#xff09;定义…...

如何消除Msxml2.XMLHTTP组件的缓存

之前使用这个组件&#xff0c;是每隔十分钟取数据&#xff0c;没有遇到这个缓存问题&#xff0c; 这次使用它是频繁访问接口&#xff0c;就出现了一直不变的问题。觉得是缓存没有清除的问题。 网上搜了一些方案。最好的方案就是给url地址末尾给一个随机参数。用于让组件觉得是…...

深入理解Java虚拟机jvm-运行时数据区域(基于OpenJDK12)

运行时数据区域 运行时数据区域程序计数器Java虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的…...

(OpenCV) 基础demo

文章目录 前言Demo图片录制播放人脸识别 END 前言 OpenCV - Open Computer Vision Library OpenCV的名声想必不用多说了。 本文介绍4个基础使用demo。分别为&#xff0c;显示图片&#xff0c;录制视频&#xff0c;播放视频和一个基于开源算法库的人脸识别小demo。 只要环境…...

using 的使用

作者: 苏丙榅 链接: https://subingwen.cn/cpp/using/ 在 C 中 using 用于声明命名空间&#xff0c;使用命名空间也可以防止命名冲突。在程序中声明了命名空间之后&#xff0c;就可以直接使用命名空间中的定义的类了。在 C11 中赋予了 using 新的功能&#xff0c;让C变得更年轻…...

Websocket、Socket、HTTP之间的关系

Websocket、Socket、HTTP之间的关系 ★ Websocket是什么&#xff1f;★ Websocket的原理★ websocket具有以下特点&#xff1a;★ webSocket可以用来做什么?★ websocket与socket区别&#xff1a;★ WebSocket与HTTP区别 ★ Websocket是什么&#xff1f; ● Websocket是HTML5下…...

hustoj LiveCD版系统在局域网虚拟机安装和配置

root权限 打开terminal命令行输入sudo su输入初始密码freeproblemsetmysql数据库的密码的位置&#xff0c;如何登陆数据库 数据库账号密码存放在两个配置文件中&#xff1a; /home/judge/etc/judge.conf/home/judge/src/web/include/db_info.inc.php 新版本中&#xff0c;快…...

读书-代码整洁之道10-14

类 类的三大特性&#xff1a;封装、继承、多态&#xff1b;类应该短小&#xff1b;单一权责原则认为&#xff0c;类或模块应有且只有一条加以修改的理由&#xff1b;当类丧失了内聚性&#xff0c;就拆分它&#xff1b;隔离修改 系统 构造和使用是非常不一样的过程。每个应用…...

UDP 广播/组播

广播UDP与单播UDP的区别就是IP地址不同&#xff0c;广播使用广播地址xxx.xxx.xxx.255&#xff0c;将消息发送到在同一广播网络上的每个主机&#xff0c;广播/组播只能用udp进行实现 函数:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_topt…...

高效创作助手:ChatGPT最新版实现批量撰写聚合文章的全新水平

随着人工智能技术的不断发展&#xff0c;ChatGPT最新版作为一款智能创作助手&#xff0c;实现了批量撰写聚合文章的全新水平。它能够在短时间内生成高质量的文章&#xff0c;极大地提高了创作效率。本文将从随机8-20个方面对ChatGPT最新版进行详细的阐述&#xff0c;让我们一起…...

Python中的包是什么,如何创建和使用包?

在Python中&#xff0c;包是一种将相关模块分组在一起的方式。它可以让我们更好地组织和重用代码。 一个Python包实际上是一个文件夹&#xff0c;其中包含该包的Python模块和其他资源文件&#xff08;例如配置文件、数据文件等&#xff09;。包的根目录通常包含一个名为__init…...

Spring Cloud Alibaba Seata(二)

目录 一、Seata 1、Seata-AT模式 1.1、具体案例 1.2、通过Seata的AT模式解决分布式事务 2、Seata-XA模式 3、Seata-TCC模式 4、Seata-SAGA模式 一、Seata 1、Seata-AT模式 概念&#xff1a;AT模式是一种无侵入的分布式事务解决方案&#xff0c;在 AT 模式下&#xff0c…...

如何在 MySQL 中使用 COALESCE 函数

1. 简介 在 MySQL 中&#xff0c;COALESCE 函数可以用来返回参数列表中的第一个非空值。如果所有参数都为空&#xff0c;则返回 NULL。本文将介绍 COALESCE 函数的语法和用法&#xff0c;并通过示例演示其效果。 2. 语法 COALESCE 函数的语法如下所示&#xff1a; COALESCE(…...

Python爬虫之Scrapy框架系列(22)——初识分布式爬虫scrapy_redis

目录: 分布式爬虫(Scrapy\_redis):1.简单介绍:2.Scrapy_redis的安装:分布式爬虫(Scrapy_redis): 官方文档:https://scrapy-redis.readthedocs.io/en/stable/1.简单介绍: scrapy_redis是一个基于Redis的Scrapy组件,用于scrapy项目的分布式部署和开发。 特点: 分布…...

ChatGPT的前世今生

原文首发于博客文章ChatGPT发展概览 ChatGPT 是OpenAI开发的人工智能聊天机器人程序&#xff0c;于2022年11月推出。该程序使用基于 GPT-3.5、GPT-4 架构的大语言模型并以强化学习训练。ChatGPT目前仍以文字方式交互&#xff0c;而除了可以用人类自然对话方式来交互&#xff0c…...

网站建设甲方原因造成停工/模板自助建站

前言 提到MySQL的事务&#xff0c;我相信对MySQL有了解的同学都能聊上几句&#xff0c;无论是面试求职&#xff0c;还是日常开发&#xff0c;MySQL的事务都跟我们息息相关。 而事务的ACID&#xff08;即原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durabili…...

卖老石器老榆木做哪个网站好/seo优化宣传

tar 解压&#xff1a;tar xvf fileName.tar 压缩&#xff1a;tar cvf fileName.tar directoryName rar 1、安装rar 下载RAR https://www.rarlab.com/download.htm安装包&#xff08;建议下载RAR 5.80 beta 1 for macOS (64 bit) 否则安装了可能无法使用&#xff09;, cd 到…...

网站排版/关键词优化软件哪家好

1.算法准备 ∙\bullet∙ 银行家算法&#xff08;资源分配拒绝策略&#xff09;目的是选择合适的资源分配顺序从而避免死锁的发生。 ∙\bullet∙ 该方法允许进程动态的申请资源&#xff0c;使得系统一直保持安全状态。那么什么是安全状态呢&#xff1f; 我们考虑一个系统它有固…...

wordpress子站点用户无角色/免费网站统计工具

在你的童年记忆里&#xff0c;是否有一个会蹦跳&#xff0c;会吃蘑菇的小人&#xff1f; 超级玛丽是一款经典并且流行的小游戏&#xff0c;通过键盘来控制 马里奥的移动&#xff0c;跳跃可以顶掉砖块&#xff0c;下落时还可以踩死蘑菇敌人~~ 《超级玛丽》1985出品的游戏&…...

网站添加支付宝/河北软文搜索引擎推广公司

oracle怎么创建表空间和索引表空间oracle中为什么把表和表的索引建在不同的表空间会不能简单滴说表和索引分开表空间放就能提高效率(性能)。提高性能是有前提条件的&#xff0c;只有在数据表空间和索引表空间分开存放在不同磁盘的时候&#xff0c;由于减少了磁盘I/O的竞争&…...

做视频网站要注意什么/企业网站推广可以选择哪些方法

表分区 partition 当一张表的数据非常多的时候,比如单个.myd文件都达到10G, 这时,必然读取起来效率降低. 可不可以把表的数据分开在几张表上?1: 从业务角度可以解决.. (分表&#xff0c;水平分割)&#xff0c;比如, 通过id%10 , user0 , user1....user9, 这10张表 根据不同的余…...