【Vue2源码学习分析】

# 文件结构

源码目录

# 调试环境搭建

  • 安装依赖: npm i
  • 安装rollup: npm i -g rollup
  • 修改dev脚本,添加sourcemap,package.json
 "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web- full-dev",
    
    
  • 运行开发命令: npm run dev
  • 引入前面创建的vue.js,samples/commits/index.html
<script src="../../dist/vue.js"></script>
    
    

术语解释:

  • runtime:仅包含运行时,不包含编译器
  • common:cjs规范,用于webpack1
  • esm:ES模块,用于webpack2+
  • umd: universal module definition,兼容cjs和amd,用于浏览器

# 入口

dev脚本中 -c scripts/config.js 指明配置文件所在

参数 TARGET:web-full-dev 指明输出文件配置项

// Runtime+compiler development build (Browser){ 'web-full-dev': { entry: resolve('web/entry-runtime-with-compiler.js'), // 入口 dest: resolve('dist/vue.js'),// 目标文件 format: 'umd', // 输出规范 env: 'development', alias: { he: './entity-decoder' }, banner, },}
    
    

# 初始化流程

  1. 入口 platforms/web/entry-runtime-with-compiler.js

扩展默认$mount方法:处理template或el选项

  1. platforms/web/runtime/index.js
  • 安装web平台特有指令和组件
  • 定义__patch__:补丁函数,执行patching算法进行更新
  • 定义$mount:挂载vue实例到指定宿主元素(获得dom并替换宿主元素)
  1. core/index.js

初始化全局api 具体如下:

 Vue.set = setVue.delete = delVue.nextTick = nextTickinitUse(Vue) // 实现Vue.use函数initMixin(Vue) // 实现Vue.mixin函数initExtend(Vue) // 实现Vue.extend函数initAssetRegisters(Vue) // 注册实现Vue.component/directive/filter
    
    
  1. core/instance/index.js

Vue构造函数定义

定义Vue实例API

 function Vue (options) { // 构造函数仅执行了_init this._init(options)}initMixin(Vue) // 实现init函数stateMixin(Vue) // 状态相关api $data,$props,$set,$delete,$watch eventsMixin(Vue)// 事件相关api $on,$once,$off,$emit lifecycleMixin(Vue) // 生命周期api _update,$forceUpdate,$destroy renderMixin(Vue)// 渲染api _render,$nextTick
    
    
  1. core/instance/init.js

创建组件实例,初始化其数据、属性、事件等

initLifecycle(vm) // $parent,$root,$children,$refs initEvents(vm) // 处理父组件传递的事件和回调initRender(vm) // $slots,$scopedSlots,_c,$createElement callHook(vm, 'beforeCreate') initInjections(vm) // 获取注入数据initState(vm) // 初始化props,methods,data,computed,watch initProvide(vm) // 提供数据注入callHook(vm, 'created')
    
    
  1. $mount

mountComponent

执行挂载,获取vdom并转换为dom

new Watcher()

创建组件渲染watcher

updateComponent()

执行初始化或更新

update()

初始化或更新,将传入vdom转换为dom,初始化时执行的是dom创建操作

render() src\core\instance\render.js

渲染组件,获取vdom

整体流程

new Vue() => _init() => $mount() => mountComponent() => updateComponent()/new Watcher() => render() => _update()

# 数据响应式

Vue一大特点是数据响应式,数据的变化会作用于UI而不用进行DOM操作。原理上来讲,是利用了JS语 言特性Object.defineProperty(),通过定义对象属性setter方法拦截对象属性变更,从而将数值的变化 转换为UI的变化

具体实现是在Vue初始化时,会调用initState,它会初始化data,props等,这里着重关注data初始 化,

整体流程

  1. initState (vm: Component) src\core\instance\state.js

初始化数据,包括propsmethodsdatacomputedwatch

  1. initData核心代码是将data数据响应化
function initData (vm: Component) { // 执行数据响应化 observe(data, true /* asRootData */)}
    
    
  1. core/observer/index.js

observe方法返回一个Observer实例

  1. core/observer/index.js
  • Observer对象根据数据类型执行对应的响应化操作
  • defineReactive定义对象属性的getter/setter,getter负责添加依赖,setter负责通知更新
  1. core/observer/dep.js

Dep负责管理一组Watcher,包括watcher实例的增删及通知更新

  1. Watcher
  • Watcher解析一个表达式并收集依赖,当数值变化时触发回调函数,常用于$watch API和指令中。
  • 每个组件也会有对应的Watcher,数值变化会触发其update函数导致重新渲染
 export default class Watcher { constructor () {} get () {} addDep (dep: Dep) {} update () {}}
    
    

相关API: $watcher

测试代码examples\test\02-1-reactive.html

# 数组响应化

数组数据变化的侦测跟对象不同,我们操作数组通常使用push、pop、splice等方法,此时没有办法得 知数据变化。所以vue中采取的策略是拦截这些方法并通知dep。

1. src\core\observer\array.js

为数组原型中的7个可以改变内容的方法定义拦截器

2. Observer中覆盖数组原型

 if (Array.isArray(value)) { // 替换数组原型 protoAugment(value, arrayMethods) // value.__proto__ = arrayMethods  this.observeArray(value)}
    
    

测试代码examples\test\02-2-reactive-arr.html

相关API: Vue.set()/delete()

 data: { arr: []}
    
    
 arr.length = 0 arr[index] = xxxVue.set() Vue.del()
    
    

# 异步更新队列

Vue高效的秘诀是一套批量、异步的更新策略

# 概念解释

  • 事件循环Event Loop:浏览器为了协调事件处理、脚本执行、网络请求和渲染等任务而制定的工 作机制。
  • 宏任务Task:代表一个个离散的、独立的工作单元。浏览器完成一个宏任务,在下一个宏任务执行 开始前,会对⻚面进行重新渲染。主要包括创建文档对象、解析HTML、执行主线JS代码以及各种 事件如⻚面加载、输入、网络事件和定时器等。
  • 微任务:微任务是更小的任务,是在当前宏任务执行结束后立即执行的任务。如果存在微任务,浏 览器会清空微任务之后再重新渲染。微任务的例子有 Promise 回调函数、DOM变化等。

# vue中的具体实现

  • 异步:只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变 更。

  • 批量:如果同一个 watcher 被多次触发,只会被推入到队列中一次。去重对于避免不必要的计算 和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列执行实际工作

  • 异步策略:Vue 在内部对异步队列尝试使用原生的 Promise.then 、 MutationObserver

    或 setImmediate ,如果执行环境都不支持,则会采用 setTimeout 代替。

1. update() core\observer\watcher.js

dep.notify()之后watcher执行更新,执行入队操作

2. queueWatcher(watcher) core\observer\scheduler.js

执行watcher入队操作

3. nextTick(flushSchedulerQueue) core\util\next-tick.js

nextTick按照特定异步策略执行队列操作

watcher中update执行三次,但run仅执行一次,且数值变化对dom的影响也不是立竿⻅影的。

请大家研究一下相关API: vm.$nextTick(cb)

# 虚拟DOM

# 概念

虚拟DOM(Virtual DOM)是对DOM的JS抽象表示,它们是JS对象,能够描述DOM结构和关系。应用 的各种状态变化会作用于虚拟DOM,最终映射到DOM上。

# 体验虚拟DOM

vue中虚拟dom基于snabbdom实现,安装snabbdom并体验

< !DOCTYPE html > <html lang = "en" > <head > </head><body><div id="app"></div > <!--安装并引入snabbdom--><script src = "../../node_modules/snabbdom/dist/snabbdom.js" > </script><script> / / 之前编写的响应式函数 function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { get() { return val }, set(newVal) { val = newVal // 通知更新 update() } }) } // 导入patch的工厂init,h是产生vnode的工厂 const { init, h } = snabbdom // 获取patch函数 const patch = init([]) // 上次vnode,由patch()返回 let vnode; // 更新函数,将数据操作转换为dom操作,返回新vnode function update() { if (!vnode) { // 初始化,没有上次vnode,传入宿主元素和vnode vnode = patch(app, render()) } else { // 更新,传入新旧vnode对比并做更新 vnode = patch(vnode, render()) } } // 渲染函数,返回vnode描述dom结构 function render() { return h('div', obj.foo) } // 赋一个日期作为初始值 obj.foo = new Date().toLocaleTimeString() // 定时改变数据,更新函数会重新执行 setInterval(() => { obj.foo = new Date().toLocaleTimeString() }, 1000); < /script></body > </html>
    
    

# 优点

  • 虚拟DOM轻量、快速:当它们发生变化时通过新旧虚拟DOM比对可以得到最小DOM操作量,配 合异步更新策略减少刷新频率,从而提升性能
patch(vnode, h('div', obj.foo))
    
    
  • 跨平台:将虚拟dom更新转换为不同运行时特殊操作实现跨平台
 <script src="../../node_modules/snabbdom/dist/snabbdom-style.js"></script> <script>// 增加style模块const patch = init([snabbdom_style.default])function render() {// 添加节点样式描述return h('div', { style: { color: 'red' } }, obj.foo)}</script>
    
    
  • 兼容性:还可以加入兼容性代码增强操作的兼容性

# 必要性

vue 1.0中有细粒度的数据变化侦测,它是不需要虚拟DOM的,但是细粒度造成了大量开销,这对于大 型项目来说是不可接受的。因此,vue 2.0选择了中等粒度的解决方案,每一个组件一个watcher实例, 这样状态变化时只能通知到组件,再通过引入虚拟DOM去进行比对和渲染。

# 整体流程

1. mountComponent() core/instance/lifecycle.js

渲染、更新组件

 // 定义更新函数const updateComponent = () => {// 实际调用是在lifeCycleMixin中定义的_update和renderMixin中定义的_rendervm._update(vm._render(), hydrating) }
    
    

2. _render core/instance/render.js

生成虚拟dom

3. _update core\instance\lifecycle.js

update负责更新dom,转换vnode为dom

4. patch() platforms/web/runtime/index.js

__patch__是在平台特有代码中指定的

Vue.prototype.__patch__ = inBrowser ? patch : noop
    
    

测试代码,examples\test\04-vdom.html

# patch获取

patch是createPatchFunction的返回值,传递nodeOps和modules是web平台特别实现

 export const patch: Function = createPatchFunction({ nodeOps, modules })
    
    

1. platforms\web\runtime\node-ops.js

定义各种原生dom基础操作方法

2. platforms\web\runtime\modules\index.js

modules 定义了属性更新实现

watcher.run() => componentUpdate() => render() => update() => patch()
    
    

# patch实现

1. patch core\vdom\patch.js

首先进行树级别比较,可能有三种情况:增删改

  • new VNode不存在就删;
  • old VNode 不存在就增;
  • 都存在就执行diff执行更新

  1. patchVnode

比较两个VNode,包括三种类型操作:属性更新、文本更新、子节点更新

具体规则如下:

  • 新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren
  • 如果新节点有子节点而老节点没有子节点,先清空老节点的文本内容,然后为其新增子节点。
  • 当新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点。
  • 当新老节点都无子节点的时候,只是文本的替换。

测试,04-vdom.html

 // patchVnode过程分解// 1.div#demo // 2.h1// 3.text// 4.p// 5.textupdateChildren updateChildren 文本相同跳过 updateChildren setTextContent
    
    

3. updateChildren

updateChildren主要作用是用一种较高效的方式比对新旧两个VNode的children得出最小操作补丁。执 行一个双循环是传统方式,vue中针对web场景特点做了特别的算法优化,我们看图说话:

在新老两组VNode节点的左右头尾两侧都有一个变量标记,在遍历过程中这几个变量都会向中间靠拢。 当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环

下面是遍历规则:

  • 首先,oldStartVnode、oldEndVnode与newStartVnode、newEndVnode两两交叉比较,共有4种比较 方法。
  • 当 oldStartVnode和newStartVnode 或者 oldEndVnode和newEndVnode 满足sameVnode,直接将该 VNode节点进行patchVnode即可,不需再遍历就完成了一次循环。如下图

如果oldStartVnode与newEndVnode满足sameVnode。说明oldStartVnode已经跑到了oldEndVnode 后面去了,进行patchVnode的同时还需要将真实DOM节点移动到oldEndVnode的后面。

如果oldEndVnode与newStartVnode满足sameVnode,说明oldEndVnode跑到了oldStartVnode的前 面,进行patchVnode的同时要将oldEndVnode对应DOM移动到oldStartVnode对应DOM的前面

如果以上情况均不符合,则在old VNode中找与newStartVnode相同的节点,若存在执行 patchVnode,同时将elmToMove移动到oldStartIdx对应的DOM的前面。

当然也有可能newStartVnode在old VNode节点中找不到一致的sameVnode,这个时候会调用 createElm创建一个新的DOM节点。

至此循环结束,但是我们还需要处理剩下的节点。

当结束时oldStartIdx > oldEndIdx,这个时候旧的VNode节点已经遍历完了,但是新的节点还没有。说 明了新的VNode节点实际上比老的VNode节点多,需要将剩下的VNode对应的DOM插入到真实DOM 中,此时调用addVnodes(批量调用createElm接口)

但是,当结束时newStartIdx > newEndIdx时,说明新的VNode节点已经遍历完了,但是老的节点还有 剩余,需要从文档中删 的节点删除。

# 模板编译

模板编译的主要目标是将模板**(template)转换为渲染函数(render)**

template => render()
    
    

# 模板编译必要性

Vue 2.0需要用到VNode描述视图以及各种交互,手写显然不切实际,因此用户只需编写类似HTML代码 的Vue模板,通过编译器将模板转换为可返回VNode的render函数。

# 体验模板编译

带编译器的版本中,可以使用template或el的方式声明模板,06-1-compiler.html

 (function anonymous(){with(this){return _c('div',{attrs:{"id":"demo"}},[_c('h1',[_v("Vue模板编 译")]),_v(" "),_c('p',[_v(_s(foo))]),_v(" "),_c('comp')],1)}})
    
    

输出结果大致如下:

 (function anonymous() {with(this){return _c('div',{attrs:{"id":"demo"}},[_c('h1',[_v("Vue模板编译")]), _v(" "),_c('p',[_v(_s(foo))]), _v(" "),_c('comp')],1)}})
    
    
  • 元素节点使用createElement创建,别名_c _
  • _本文节点使用createTextVNode创建,别名_v
  • 表达式先使用toString格式化,别名_s
  • 其他渲染helpers:src\core\instance\render-helpers\index.js

# 整体流程

1. compileToFunctions

若指定template或el选项,则会执行编译,platforms\web\entry-runtime-with-compiler.js

2. 编译过程

编译分为三步:解析、优化和生成,src\compiler\index.js

测试代码06-1-compiler.html

# 模板编译过程

实现模板编译共有三个阶段:解析、优化和生成

  1. 解析 - parse

解析器将模板解析为抽象语法树,基于AST可以做优化或者代码生成工作。 调试查看得到的AST,**/src/compiler/parser/index.js**,结构如下:

解析器内部分了HTML解析器、文本解析器和过滤器解析器,最主要是HTML解析器

  1. 优化 - optimize

优化器的作用是在AST中找出静态子树并打上标记。静态子树是在AST中永远不变的节点,如纯文本节 点。

标记静态子树的好处:

  • 每次重新渲染,不需要为静态子树创建新节点
  • 虚拟DOM中patch时,可以跳过静态子树

测试代码,06-2-compiler-optimize.html 代码实现,src/compiler/optimizer.js - optimize

  1. 代码生成 - generate
  • 将AST转换成渲染函数中的内容,即代码字符串
  • generate方法生成渲染函数代码,src/compiler/codegen/index.js

生成的code⻓这样

 `_c('div',{attrs:{"id":"demo"}},[ _c('h1',[_v("Vue.js测试")]), _c('p',[_v(_s(foo))])])`
    
    

# 典型指令的实现:v-if、v-for

  1. 解析v-if:parser/index.js

processIf用于处理v-if解析

  1. 代码生成,codegen/index.js

genIfConditions等用于生成条件语句相关代码

生成结果:

 "with(this){return _c('div',{attrs:{"id":"demo"}},[(foo) ? _c('h1',[_v(_s(foo))]) : _c('h1',[_v("no title")]),_v(" "),_c('abc')],1)}"
    
    
  1. 解析v-for:parser/index.js

processFor用于处理v-for指令

解析结果:v-for="item in items" for:'items' alias:'item'

  1. 代码生成,src\compiler\codegen\index.js

genFor用于生成相应代码

生成结果

 "with(this){return _c('div',{attrs:{"id":"demo"}},[_m(0),_v(" "),(foo)?_c('p', [_v(_s(foo))]):_e(),_v(" "),_l((arr),function(s){return _c('b',{key:s},[_v(_s(s))])}),_v(" "),_c('comp')],2)}"
    
    

v-if,v-for这些指令只能在编译器阶段处理,如果我们要在render函数处理条件或循环只能使用if 和for

 Vue.component('comp', {props: ['foo'],render(h) { // 渲染内容跟foo的值挂钩,只能用if语句if (this.foo=='foo') { return h('div', 'foo')}return h('div', 'bar') }})
    
    
 (function anonymous(){with(this){return _c('div',{attrs:{"id":"demo"}},[_m(0),_v(" "),(foo)?_c('p', [_v(_s(foo))]):_e(),_v(" "),_c('comp')],1)}})
    
    

# 组件化机制

# 组件声明:Vue.component()

initAssetRegisters(Vue) src/core/global-api/assets.js

组件注册使用extend方法将配置转换为构造函数并添加到components选项

# 组件实例创建及挂载

观察生成的渲染函数

 "with(this){return _c('div',{attrs:{"id":"demo"}},[ _c('h1',[_v("虚拟DOM")]),_v(" "), _c('p',[_v(_s(foo))]),_v(" "),_c('comp') // 对于组件的处理并无特殊之处],1)}"
    
    

# 整体流程

首先创建的是根实例,首次_render()时,会得到整棵树的VNode结构,其中自定义组件相关的主要有: createComponent() - src/core/vdom/create-component.js

1. 组件vnode创建

createComponent() - src/core/vdom/patch.js

创建组件实例并挂载,vnode转换为dom

2. 整体流程:

new Vue() => $mount() => vm._render() => createElement() => createComponent() => vm._update() => patch() => createElm => createComponent()

# 创建组件VNode

1. _createElement - src\core\vdom\create-element.js

_createElement实际执行VNode创建的函数,由于传入tag是非保留标签,因此判定为自定义组件通过 createComponent去创建

2. createComponent - src/core/vdom/create-component.js

创建组件VNode,保存了上一步处理得到的组件构造函数,props,事件等

# 创建组件实例

根组件执行更新函数时,会递归创建子元素和子组件,入口createElm

1. createEle() core/vdom/patch.js line751

首次执行_update()时,patch()会通过createEle()创建根元素,子元素创建研究从这里开始

2. createComponent core/vdom/patch.js line144

自定义组件创建

// 组件实例创建、挂载if (isDef(i = i.hook) && isDef(i = i.init)) { i(vnode, false /* hydrating */ )}if (isDef(vnode.componentInstance)) { // 元素引用指定vnode.elm,元素属性创建等 initComponent(vnode, insertedVnodeQueue) // 插入到父元素 insert(parentElm, vnode.elm, refElm) if (isTrue(isReactivated)) { reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm) } return true}
    
    

# 总结

Vue源码学习使我们能够深入理解原理,解答很多开发中的疑惑,规避很多潜在的错误,写出更好的代 码。学习大神的代码,能够学习编程思想,设计模式,训练基本功,提升内力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/688991.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW阀性能试验台测控系统

本项目开发的阀性能试验台测控系统是为满足国家和企业相关标准而设计的&#xff0c;主要用于汽车气压制动系统控制装置和调节装置等产品的综合性能测试。系统采用工控机控制&#xff0c;配置电器控制柜&#xff0c;实现运动控制、开关量控制及传感器信号采集&#xff0c;具备数…

后端进阶-分库分表

文章目录 为什么需要分库为什么需要分表 什么时候需要分库分表只需要分库只需要分表 分库分表解决方案垂直分库水平分库垂直分表水平分表 分库分表常用算法范围算法hash分片查表分片 分库分表模式客户端模式代理模式 今天跟着训练营学习了分库分表&#xff0c;整理了学习笔记。…

Spring系统学习 -Spring IOC 的XML管理Bean之bean的获取、依赖注入值的方式

在Spring框架中&#xff0c;XML配置是最传统和最常见的方式之一&#xff0c;用于管理Bean的创建、依赖注入和生命周期等。这个在Spring中我们使用算是常用的&#xff0c;我们需要根据Spring的基于XML管理Bean了解相关Spring中常用的获取bean的方式、依赖注入值的几种方式等等。…

C++ Thread多线程并发记录(8)生产者-消费者模型与信号量(条件变量)

一.生产者-消费者模型 生产者-消费者模型是一个十分经典的多线程并发协作模式。所谓的生产者-消费者&#xff0c;实际上包含了两类线程&#xff0c;一种是生产者线程用于生产数据&#xff0c;另一种是消费者线程用于消费数据&#xff0c;为了解耦生产者和消费者的关系&#xff…

苹果Safari怎么清理缓存?原来快速清除浏览器的历史记录那么容易

在数字化时代&#xff0c;互联网已经成为我们日常生活中不可或缺的一部分。我们使用各种设备&#xff0c;如智能手机、平板电脑和笔记本电脑来浏览网页、获取信息、娱乐和社交。而在这些设备中&#xff0c;iPhone无疑是最受欢迎的选择之一。iPhone搭载的Safari浏览器以其简洁的…

three.js官方案例(animation / multiple)webgl_animation_multiple.html学习笔记

目录 ​编辑 1 骨架工具&#xff08;SkeletonUtils&#xff09; 1.1 clone方法 2 蒙皮网格&#xff08;SkinnedMesh&#xff09; 3 自测 4 webgl_animation_multiple.html全部脚本 1 骨架工具&#xff08;SkeletonUtils&#xff09; 用于操控 Skeleton、 SkinnedMesh、和…

Spring AI 第二讲 之 Chat Model API 第八节Anthropic 3 Chat

Anthropic Claude 是一系列基础人工智能模型&#xff0c;可用于各种应用。对于开发人员和企业来说&#xff0c;您可以利用 API 访问&#xff0c;直接在 Anthropic 的人工智能基础架构之上进行构建。 Spring AI 支持用于同步和流式文本生成的 Anthropic 消息 API。 Anthropic …

因为宇宙一片漆黑,所以地球才有昼夜之分,宇宙为什么是黑的?

因为宇宙一片漆黑&#xff0c;所以地球才有昼夜之分&#xff0c;宇宙为什么是黑的&#xff1f; 地球为何会有昼夜之分&#xff1f; 乍一看&#xff0c;这个问题很是简单&#xff0c;当然是因为地球一直在自转了&#xff0c;当地球的一部分被太阳照射时就是白昼&#xff0c;而…

Servlet搭建博客系统

现在我们可以使用Servlet来搭建一个动态(前后端可以交互)的博客系统了(使用Hexo只能实现一个纯静态的网页,即只能在后台自己上传博客)。有一种"多年媳妇熬成婆"的感觉。 一、准备工作 首先创建好项目,引入相关依赖。具体过程在"Servlet的创建"中介绍了。…

【Vue】单页应用程序介绍

通常基于Vue去开发一整个网站&#xff0c;开发出来的这整个网站应用&#xff0c;我们都会叫做单页应用程序 概念 单页应用程序&#xff1a;SPA【Single Page Application】是指所有的功能都在一个html页面上实现 我们可以将页面共用的部分封装成组件&#xff0c;底下要切换的也…

A6500-LC LVDT 前置器,用于A6500-UM, 导轨安装

电源 22.5V to 32VDC <30mA <0.1%/V <60V( 使用SELV/PELV 供电电源) 约2.2Vrms,5kHz IP20 IEC 60529 -35C to 75C(-31F to 167F) -35C to 85C(-31F to 185F) 电流损耗 供电电压对 运行温度 存储温度 0.35mm(0.014 in ),10 to 55Hz 15g 根据 EN 60068-2-27 根据IEC 613…

nginx配置WebSocket参数wss连接

目录 一、原文连接 二、 配置参数 三、实践 四、重启nginx 五、连接websocket 一、原文连接 nginx配置websocket支持wss-腾讯云开发者社区-腾讯云 二、 配置参数 map $http_upgrade $connection_upgrade { default upgrade; close; } upstream websocket { se…

大数据处理学习笔记

sudo tar -zxvf hadoop-1.1.2.tar.gz -C / #解压到/usr/local目录下 sudo mv hadoop-1.1.2 hadoop #重命名为hadoop sudo chown -R python ./hadoop #修改文件权限 //java安装同上给hadoop配置环境变量&#xff0c;将下面代…

Thinkphp使用Elasticsearch查询

在Thinkphp中调用ES&#xff0c;如果自己手写json格式的query肯定是很麻烦的。我这里使用的是ONGR ElasticsearchDSL 构建 ES 查询。ongr ElasticsearchDSL 的开源项目地址&#xff1a;GitHub - ongr-io/ElasticsearchDSL: Query DSL library for Elasticsearch。ONGR Elastics…

分布式数据库架构:从单实例到分布式,开发人员需及早掌握?

现在互联网应用已经普及,数据量不断增大。对淘宝、美团、百度等互联网业务来说,传统单实例数据库很难支撑其性能和存储的要求,所以分布式架构得到了很大发展。而开发人员、项目经理,一定要认识到数据库技术正在经历一场较大的变革,及早掌握好分布式架构设计,帮助公司从古…

DSP28335模块配置模板系列——定时器中断配置模板

一、配置步骤&#xff1a; 1.使能定时器时钟 EALLOW;SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK 1; // CPU Timer 2EDIS; 2.设置定时器的中断向量 EALLOW;PieVectTable.TINT2 &TIM2_IRQn;EDIS;其中TIM2_IRQn时定时器中断服务程序的名称 &#xff0c;将中断服务函数的地址…

【回溯算法】N皇后问题·构建多叉决策树,遍历决策节点,做出决策(边),收集答案

0、前言 在由树形解空间入手&#xff0c;深入分析回溯、动态规划、分治算法的共同点和不同点这篇博客&#xff0c;其实已经对回溯算法的思想、做题框架做出了详细的阐述。这篇文章我们再从N皇后问题&#xff0c;加深我们对其理解。 这里在简单再次对其进行概述&#xff1a; …

dataphin是什么及其简单使用示例

1.1dataphin是什么&#xff1f; Dataphin是由阿里研发的智能大数据建设平台&#xff0c;提供一站式数据中台&#xff08;大数据平台&#xff09;建设服务。Dataphin通过沙箱&#xff08;项目&#xff09;实现业务及作业资源隔离&#xff0c;运行更快&#xff0c;且数据同步到D…

代码随想录算法训练营第四十八 | ● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 买卖股票的最佳时机 视频讲解&#xff1a;https://www.bilibili.com/video/BV1Xe4y1u77q https://programmercarl.com/0121.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA.html class Solution { public:int ma…

因你而变 共赴新程 | AidLux全新版本震撼发布!

历经400多个日夜&#xff0c;AidLux 2.0&#xff08;基础版&#xff09;终于要与大家见面了。 开发者们问过无数次&#xff0c;新版本何时发布&#xff0c;期待的功能何时上线……在此&#xff0c;让我先真诚地感谢大家长期以来的期待与关心&#xff01; 一年多以来&#xff…