1.mixin混入,以后自身生命周期和混入周期的具体实现,mixin的具体实现
定义:分发vue中可复用的功能,本质是一个js对象,它可以包含我们组件中任意功能选项,如data、components、methods、created、computed等等
局部混入 :
定义一个mixin对象,可能是单文件的形式,有data、methods等属性。在组件中可以定义mixins:[]
在组件使用的时候就混合了mixin里面的方法
全局混入:
Vue.mixin({}) 它会影响每一个组件实例,包括第三方组件,常用于插件的编写
注意:当组件与mixin冲突的时候,会优先组件,但如果为生命周期钩子,会合并成一个数组,先执行mixin再执行组件钩子
使用场景:不同的组件使用相同或者相似的代码,这些组件的功能相对独立。eg:modal弹窗和tooltip弹窗
源码分析:
- 优先递归处理
mixins
- 先遍历合并
parent
中的key
,调用mergeField
方法进行合并,然后保存在变量options
- 再遍历
child
,合并补上parent
中没有的key
,调用mergeField
方法进行合并,保存在变量options
- 通过
mergeField
函数进行了合并
合并策略:
- 替换型策略有
props
、methods
、inject
、computed
,就是将新的同名参数替代旧的参数 - 合并型策略是
data
, 通过set
方法进行合并和重新赋值 - 队列型策略有生命周期函数和
watch
,原理是将函数存入一个数组,然后正序遍历依次执行 - 叠加型有
component
、directives
、filters
,通过原型链进行层层的叠加
2.nexttick的场景和实现原理
定义:在下一次dom更新循环结束之前执行延迟回调。vue在更新dom时是异步执行的,当数据发生变化时,vue将开启一个异步更新队列,视图需要等到队列完成再更新
原因:
优化策略:在一些非常频繁操作的情况下,每次数据更新都要触发视图更新更新的话,性能存在问题
{{num}} for(let i=0; i<100000; i++){ num = i } 如果没有nexttick 则需要更新10w次视图
使用场景:想要在修改数据后立刻更新视图
实现原理:
- 把回调函数放入异步操作队列callbacks等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
3.webpack的优化策略,常用插件,热更新的实现原理,如何检测到代码变化
webpack打包经历的几个阶段:
1.根据配置找到打包入口文件;2.根据引用解析并生成一个资源依赖模块树;3.递归这个依赖树,通过文件找到对应的loader;4.打包生成chunk 我们就是围绕着这几个阶段去做优化: 1.loader解析比较耗时 (1) 配置rules.include(exclude高优先级),从源头上减少需要解析的文件 (2) 开启多线程加速loader的解析时间:thread-loader,happypack (3) 利用缓存:cache-loader(mtime),bable-loader(内容缓存开启cacheDirectory:true) (4) noparse: 有些模块没有使用commonjs规范语法,就可以不用转化解析 lodash 2.搜索依赖文件耗时 (1) 设置resolve.alia别名配置路径 (2) resolve.extension(‘xxx’)自动填充文件后缀 默认是[‘.js’,’.json’] 3.打包生成chunk耗时 (1)缓存生成的chunk 设置cache:true 也是默认的 (2)分包(使用webpack-bundle-analyzer,打包依赖工具分析文件并拆分) splitChunksPlugin: cachegroups:{ 设置引用次数,优先级,minsize等等} tree-shaking:usedExports、minimize 生产环境默认开启 sideEffects:用于npm包标记是否有副作用 设置false期望没有。为tree-shaking提供更大的优化空间 其他: IgnorePlugin:webpack内置插件 忽略第三方包的指定目录 externals:有时候我们可能会把一些引入资源放在cdn上,不希望webpack进行打包,但是又想用import方式导入 DLLPlugin:拆分bundle,将不频繁更新的库进行编译,当依赖版本发生变化的时候不需要重新编译 变量提升:scope hoisting:配置new ModuleConcatenationPlugin()分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中去,但前提是不能造成代码冗余。因此只有那些被引用了一次的模块才能被合并。
最后呢,我们在开发阶段呢,也有一些优化我们开发体验的点需要注意: 通过webpack-dev-server:用户开发的http server:可以实现自动编译和自动刷新浏览器,即时修改即时生效,在proxy中配置代理
自动刷新会重新打包,导致浏览器页面丢失。这个时候我们使用模块热更新,集成在web-dev-server里面,不是开箱即用的,需要手动处理热更新逻辑 模块热更新开启:1.web-dev-server –hot 2.通过HotModuleReplacementPlugin插件 配置:module.hot.accept({‘js路径’,‘回调函数(保存状态,remove掉之前的,append一个新的)’},{…图片的话直接将新的地址赋值给图片的src属性})
模块热更新的原理: 热更新的核心就是客户端与服务端建立的websocket连接,服务端监听文件变化,编译后再推送给客户端告之其哪些地方改变了,最终客户端发送ajax请求,获取最新资源,使用文件系统替换修改的内容实现局部更新。 具体步骤:
1.webpack-dev-server启动本地服务(webpack,http server(express),websocket) 2.客户端和服务端使用websocket实现长连接 3.webpack监听源文件的变化,变化后执行重新编译,编译完成后通过socket向客户端推送文件编译后生成的hash串 4.客户端收到推送过来的hash会与上一次的对比,一致则走缓存,不一致则重新发起ajax请求向服务端获取数据 5.使用内存文件系统去替换修改的内容实现局部刷新
常用loader:file-loader、souce-map-loader、babel-loader、ts-loader、css-loader、sass-loader、eslint-loader 常用插件:ignore-plugin、clean-webpack-plugin、html-webpack-plugin、copy-webpack-plugin、hot-module-replacememt-plugin、uglifyjs-webpack-plugin、imagemin-webpack-plugin
4.项目优化方式
vue项目的优化: 1.使用路由懒加载 2.使用keep-alive进行缓存 3.v-for跟v-if不一块使用 4.子组件拆分成更细粒度的,给一些无害组件(没有自己状态,只负责展示展示数据)设置functional,这种情况下可以简化组件的打包体积 5.图片懒加载 我们可以使用lazyload库去实现。原生方法的话都是通过设置一个data-set存储真实的img地址,初始化的时候加载一个占位图或者缩略图,然后根据判断图片是否在可视化范围之内来判断是否加载图片:滚动窗口遍历(节流),new InterceptionObserver(兼容) 6.在组件销毁的时候销毁一些自定义事件,比如监听器。vue组件自动销毁的仅限于组件本身的事件 7.第三方插件按需引入。打包的时候仅会打包引入的部分 8.长列表优化 纯粹数据变化,不用响应式(Object.freeze() or configuration:false;大数据长列表,可以采用虚拟滚动,只渲染少部分内容,用户触发滚动再加载 9.变量的本地化。computed计算属性使用频繁的情况下,单独用变量存储起来再使用 10.服务端渲染ssr 首屏渲染 seo
5.vue项目检测到对象和数组的变化,object.defineProperty和proxy的区别
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
主要是通过getter和setter来实现响应式。修改数据的时候会触发setter,在setter中进行更新操作,如果数据存在多个key的情况下需要遍历,存在嵌套的情况下还需要递归。存在一些问题:
对对象进行删除或者添加属性的操作无法监测到,数组监听也不太凑效。vue2中其实也可以做到遍历监听,但是数据层级深会造成性能问题,所以添加来set、delete这些API去更新。兼容到IE9
proxy
的监听是对象层级的,在js语法层面进行支持。可以直接劫持整个对象并返回一个新对象。同时设置了13中拦截方法:has,deleteProperty,ownkeys等等。它不兼容IE,也没有polyfill
trap函数,Reflect
6.Vue 与 其他前端框架 的区别
vue和jquery:
jquery:更好的api;兼容处理。简化js的dom操作
vue:使用mvvm框架,响应式处理,以数据驱动视图,引入了组件化思想,虚拟dom操作等
vue和react:
相同:1.组件化思想;2.虚拟dom;3.数据驱动视图;4.支持服务端渲染
不同:1.双向与单向数据流;2.diff算法不同(vue对比dom,之后统一批量更新dom,react使用双指针,边对比边更新);3.组件通信方式(vue使用事件和回调函数,react使用回调函数)
7.Vue 如何实现响应式
1.vue通过属性保存选项数据,把data转换成getter/setter,注入到vue实例
中
2.实现observer
数据劫持,监听数据的变化。defineProperty,proxy
3.进行compiler解析
,编译模版,处理文本节点和元素节点
4.实现一个依赖收集器dep
,添加依赖并且通知观察者
5.watcher
观察者在实例化的时候往dep对象中添加自己,数据变化后更新视图
发布/订阅者模式
8.Vue 组件通信
每个组件都有各自的作用域,组件内的数据是无法共享的,但是在开发过程中我们常常会想让组件之间共享数据
1.父子组件之间的通信
- 父组件使用props传递数据,子组件通过$emit触发事件
- ref:父组件在使用子组件的时候使用ref,子组件通过设置的ref值来获取数据
2.兄弟组件之间的通信
- 中央事件总线EventBus:$emit触发,$on监听
- 通过共同祖辈$parent 或者 $root牵线搭桥。this.$parent.emit/on(与EventBus用法相似)
3.祖先与后代组件之间的通信
- project和inject:
在祖先组件定义`provide`属性,返回传递的值 在后代组件通过`inject`接收组件传递过来的值
- $attrs 与$ listeners:v-bind=”$attrs”传入内部组件
4.无关联组件之间的通信
vuex:一个用来存储共享变量的容器
9.Vuex 属性有哪些
- state:用来存放共享变量
- getter:用来获取共享变量的值
- mutation:用来存放修改state的同步方法 存储的方法eg:setData(state, 参数payload(可选,建议对象)) {} 触发方法:this.$store.commit(‘setData’, {});
- action:也是存放修改state的方法,不过是在mutation的基础上进行,常做一些异步操作 在内部触发mutation,方法的第一个参数为复制的store,可以直接用当前参数.commit() 触发方法:this.$store.dispatch(‘setNum’)
定义在state中的变量可以通过this.$store.state.xxx访问
定义在getter中的同样也可以使用this.$store.getters.xxx访问
优雅起见,我们使用mapState、mapGetters、mapMutations、mapActions去解构到计算属性中
import { mapState, mapGetters, mapMutations,mapActions} from ‘vuex’ 这样就可以直接用this进行访问啦,还可以取别名
原理总结:
Vuex的双向绑定通过调用 new Vue实现,然后通过 Vue.mixin 注入到Vue组件的生命周期中,再通过劫持state.get将数据放入组件中
10.Vue2、Vue3 生命周期
vue2生命周期:
beforeCreate:初始化vue,进行数据监测
created:完成数据监测,属性和方法的计算,此时$el并没有被创建
beforeMount:虽然已经完成dom初始化,但未挂载到el选项上
mounted:完成挂载与渲染
beforeUpdate,updated,beforeDestroy,destroyed
activated/deactivated/errorCaptured
vue3生命周期
beforeCreate
-> 使用setup()
created
-> 使用setup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeDestroy
->onBeforeUnmount
destroyed
->onUnmounted
errorCaptured
->onErrorCaptured
在CompositionAPI中,需要将生命周期钩子函数导入在项目中,才能使用,beforeCreated和created被setup方法所取代
11.Vue-router 导航有哪些
使用步骤:
1.vue.use注册路由组件
2.创建路由对象,同时需要把路由规则导出
3.main中注册router对象
4.创建路由组件的占位<router-view/>
5.创建链接<router-link></router-link>
钩子函数(导航守卫):
全局守卫:
全局前置守卫:router.beforeEach((to,from,next)⇒{}),可定义多个守卫
全局解析守卫:router.beforeResolve
全局后置钩子:router.afterEach,全局跳转之后,不接受next参数,也不改变导航本身
路由守卫:路由相关的钩子
beforeEnter: (to, from) => {}
组件守卫:定义在组件内部的守卫
beforeRouteEnter:该守卫内访问不到组件的实例,即 this 为 undefined,在 beforeCreate 生命周期前触发
beforeRouteUpdate:没有next方法
beforeRouteLeave:没有next方法
动态路由的实现方式:
1.通过当前路由规则获取:$route.params.id
2.路由规则中开启props传参,组件中接收参数:props:true
编程式导航:this.$router.push();this.$router.replace()(不记录本次历史);this.$router.go(-1)
hash和history模式的区别:
1.表现形式:#、?等无关字符 vs /
2.原理:
hash:基于锚点(改变锚点,监听锚点变化),监听hashchange事件
history:改变url,基于H5的history API(pushState、replaceState)当调用这两个方法的时候,url会发生变化,浏览器访问历史也会发生变化,但是浏览器不会向后台发送请求。
可以通过监听popstate事件监听history变化,根据path不同渲染不同的dom
history模式的启用:设置mode:history;需要服务器支持(vue-cli服务器配置好了)
如果访问的url不存在,就将单页应用默认的index.html返回给浏览器
实现原理核心:
通过Vue.use注册插件,在插件的install方法中获取用户配置的router对象。当浏览器地址发生变化的时候,根据router对象匹配相应路由,获取组件,并将组件渲染到视图上
12.Vue3 有什么新特性
vue2面临的问题:
1.功能的增长,复杂组件变得越来越难维护
2.缺少一个比较纯粹的能在多个组件中提取和复用逻辑的机制
3.类型推断不够友好
新特性:
TypeScript支持
CompositionAPI
vue2:optionAPI的代码编写方式,每个组件都有自己的method,data等等,组件内会有很多逻辑关注点,在使用mixin去复用相同的逻辑的时候,存在两个很重要的问题:命名冲突,数据来源不清晰。
定义:组件根据逻辑功能来组织,一个功能所定义的所有api会放在一起。高内聚,低耦合。使用setup()获取api里的内容,数据来源清晰,没有命名冲突。几乎都是使用函数的形式,tree-shaking支持,不使用this,减少了this指向不明的问题。适合大型组件使用
性能提升:
数据劫持优化
diff算法优化(为会发生变化的地方增加一个flag静态标记)
静态提升(对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用)
更小:
移除了一些不常用的API
tree-shaking
- 编译阶段利用
ES6 Module
判断哪些模块已经加载 - 判断那些模块和变量未被使用或者引用,进而删除对应代码
源码结构更清晰,以模块划分
vite配套打包工具
- 基于远程ES module来开发,省略了打包步骤(浏览器支持原生的模块化导入)
- 快速的冷启动和模块热更新
- 真正的按需编译(只在需要的时候动态import,不需要提前打包,适用开发环境)
13.对 TypeScript 的认识
typescript并不是一门新的语言,而是js的超集,解决了js类型系统的问题,并最终编译成js代码
1.它可以在代码编译阶段发现并纠正错误
2.强类型语言(限制实参类型,更强的类型约束,不允许任何形式的隐式类型转换),可静态可动态
3.支持模块,泛型和接口
是一门渐进式的语言,可以增强代码的可读性和可维护性
14.对 class、interface 的认识
class:类,是面向对象实现信息封装的基础,ts中的class支持面向对象中的所有特性
包含:类中定义的变量,构造函数和方法
可以使用extends关键字实现继承。类继承后子类可以对父类的方法进行重写,super关键字是对父类的直接引用。
typescript中在此基础上添加了三种访问修饰符:
private:私有属性
public:公共
protect:只允许在子类中访问,可被继承
还有只读修饰符:readonly
静态属性:static,存在于类本身,而不是在类的实例上
抽象类:不能被实例化,通常需要我们创建子类去继承。一般用来约束子类中必须有某种成员
应用场景:使用类的特性完成一些业务代码;还可以将类作为接口,方便统一管理
interface:接口。是一种规范和契约,约定对象成员以及类型,约束对象结构。
固定成员,可选成员,只读成员,动态成员。接口还可以实现继承,如果继承多个父类只需要,分隔
应用:在一个函数中需要用到一些参数,如果这个函数在多处调用没有注释的话,就有可能出现运行时错误,这个时候就可以使用接口
15.前端页面如何优化
(1)更快的网络通信
1.DNS解析层面:使用cdn(全局负载均衡和主服务器缓存系统)
2.服务器通信层面:减少http请求次数(资源合并)tcp连接限制(域名分片)-http2中均不推荐
3.数据传输层面:缓存(开启强缓存和协商缓存),压缩(gzip数据压缩,代码文件,静态资源,请求响应报文等)
4.通信协议层面:http2.0
(2)更高效的数据处理
1.前端代码层面 **减少作用域查找和闭包** 使用**块级作用域** 多使用**伪元素,**减少访问层级 使用**语义化标签**增强dom解析 减少**重绘和回流** 进行**防抖与节流**
16.跨域如何处理
1.JSONP模拟script标签发起请求。非同源策略的方式。因为script、link、img标签没有同源限制,仅支持get请求
2.CORS,服务端设置 Access-Control-Allow-Origin。分为简单请求(get,post,head)和复杂请求,复杂请求在通信前会增加一次http查询请求,预检(option),来确定服务器是否允许跨域。
3.iframe通过postMessage跨域
4.websocket协议。基于tcp连接,但是连接之后不受http协议的控制,可直接双向通信
5.ngnix反向代理
6.nodejs中间件代理。中间代理服务器先接受客户端请求,然后将请求转发给服务器,拿到服务器 响应数据再转发给客户端。服务器与服务器之间并不会存在跨域行为。webpack-dev-server就是使用这种形式:node express应用框架搭建中间件
17.axios 如何修改 headers?如何通过 axios 拦截器实现请求防抖
axios:基于XMLHttpRequest来实现http请求,有非常丰富的配置,支持promise,浏览器和node
可以拦截请求和响应。进行请求和响应数据转换
如何封装:
1.先和后台约定
好一些规范,比如请求头,状态码,超时时间等
2.设置接口请求的前缀
baseUrl,区分好开发、测试和生产环境,开发需要配置dev-server
3.设置请求头
:普适性的请求头作为基础配置,特殊请求头作为参数传入覆盖基础配置
4.设置状态码
:根据不同的code来执行不同的业务操作
5.请求方法
:对于get,post等不同的请求方法做相应的封装,并export暴露出去,api统一管理接口
6.设置请求拦截器
:根据请求头的设定,来决定哪些请求可以访问
axios(或实例).interceptors.request.use((config)⇒{})
7.设置响应拦截器
:根据code不同做不同的业务处理(登录状态,授权等)
请求头的配置:
- 创建axios实例的时候初始化一份:const instance = axios.create({timeout:xxx,} headers: {})
- 在请求拦截器之前通过config.header.xxx来设置
拦截器实现防抖:
判断重复请求并存储进队列中
- 使用map对象存储,只要请求地址,请求方式,请求参数一样我们就可以认为是一样的
- 储存每个请求的值,也就是cancel方法,用于取消请求,cancelToken
- 在请求拦截器中执行取消重复请求并删除队列,并添加当前请求的值
- 在响应拦截器中将该请求删除队列
18.缓存(强缓存、协商缓存)如何设置
缓存优点: 1.减少重复的服务器请求,节省流量 2.降低服务器压力,提高网站性能 3.提升客户端加载速度,提升用户体验 区别: 1.如果命中强缓存,则不需要与服务器通信;而协商缓存最终由服务器来决定是否使用缓存,存在一次通信 2.chrome命中强缓存,会返回200 from cache(disk cache/memory cache快),命中协商缓存是304(not modified)不同浏览器表现不一致,firefox中from cache状态码是304
浏览器请求流程: 1.浏览器在加载资源时,根据请求头的expires和cache-control判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器 2.如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过last-modified和etag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源 3.若都未命中,直接从服务器加载资源
请求头名词解释: Expires:是http1.0提出的一个表示资源过期时间的header,由服务器返回,受限于本地时间,如果本地时间修改则缓存失效 Cache-Control:http1.1提出的相对时间,eg:max-age=3600 代表资源的有效期是 3600 秒 其他设置项:no-store、no-cache、public、private(只能被终端浏览器)
优先级: Cache-Control > expires > Etag > Last-Modified Last-Modified:标记最后文件修改时间。浏览器会在request header加上If-Modified-Since(上次返回的Last-Modified的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来 Etag:由服务器为每一个资源生成的唯一标识串(hash),只要资源有变化就这个值就会改变。接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变
etag解决了Last-Modified什么问题: 1.一些文件也许会周期性的更改,但是内容并不改变 2.文件修改频繁,在秒级以内 3.某些文件服务器不能精确得到文件的最后修改时间
强缓存设置: 后端服务器代码逻辑:res.setHeader(‘Cache-Control ’,‘max-age=3600’) expires同 ngnix配置:add_header Cache-Control “max-age=3600”
协商缓存设置:
1.last-modified,服务器设置last-modified请求头后,浏览器请求同一资源会带if-modified-since头,所以服务端还要获取这个请求头的时间进行对比 2.etag,服务器设置etag请求头(通过md5或者sha1或sha256算法生成hash)浏览器请求同一资源会带if-none-match头,服务端需要或者两次的etag是否一致 Ctrl + F5 强制刷新的时候,会暂时禁用强缓存和协商缓存,从服务器加载资源
19.对 webpack 的理解
webpack是一个模块化的打包工具,解决的是前端整体的模块化。webpack可根据文件输入的依赖关系打包为html、css、js以及各种资源文件
webpack运行在nodejs环境下,配置文件遵循commonjs规范
Webpack在启动后,会从entry开始,递归解析entry依赖的所有Module,每找到一个Module,就会根据Module.rules里配置的Loader规则进行相应的转换处理,对Module进行转换后,再解析出当前Module依赖的Module,这些Module会以entry为单位进行分组,即为一个Chunk。因此一个Chunk,就是一个entry及其所有依赖的Module合并的结果。最后Webpack会将所有的Chunk转换成文件输出Output。在整个构建流程中,Webpack会在恰当的时机执行Plugin里定义的逻辑,从而完成Plugin插件的优化任务。
20.如何通过 webpack 插件获取所有文件名
// 读取输出资源、代码块、模块及其依赖,修改输出资源 **compiler.plugin('emit', function(compilation, callback)** { // 处理逻辑 // ... // compilation.chunks 存放所有代码块,是一个数组 // 读取名称为 fileName 的输出资源 const asset = **compilation.assets**[fileName]; // 获取输出资源的内容 asset.source(); callback() }) // 监听文件变化 compiler.plugin('watch-run', (watching, callback) => { // 获取发生变化的文件列表 const changedFiles = watching.compiler.watchFileSystem.watcher.mtimes; // changedFiles 格式为键值对,键为发生变化的文件路径。 if (changedFiles[filePath] !== undefined) { // filePath 对应的文件发生了变化 } callback(); });
21.http1.1 与 http2 的区别
http1.1的性能瓶颈: 1.头部比较大,没有压缩 2.支持一个tcp连接发送多个请求,因为请求本身有顺序,所以返回也需要有先后顺序,不然对应不上,只是节省了一些tcp连接的时间,限制还是比较多 3.服务器响应慢会导致队头堵塞 3.只支持客户端发起连接
http2.0: 1.启用了头部压缩(hpack压缩算法) 2.支持多路复用,一个连接并发多个请求,无需限制顺序(之前的协议都是以文本的形式,2.0是以二进制桢的形式)每个http请求被切分成多帧,多个http的帧混合在一起在一个tcp连接上传送。请求发送之后,服务器处理需要一定时间,这段空档期再利用
3.允许服务器推送消息
22.nginx 负载均衡
一种调度策略,如果单个服务器出现故障,负载均衡的方法会将流量重定向到其余的集群服务器,以保证服务的稳定性。当新的服务器添加到服务器组后,也可通过负载均衡的方法使其开始自动处理客户端发来的请求。
正向代理:客户端非常明确想要访问的服务器地址,请求代理服务器,替客户端发送请求。这个过程中服务器并不知道发起请求的是谁
反向代理:反向代理隐藏了服务器的信息,它代理的是服务器端。客户端并不知道具体哪台服务器处理了自己的请求
反向代理需要考虑的问题是,如何进行均衡分工,控制流量,避免出现局部节点负载过大的问题
ngnix是基于c语言的高性能web服务器
负载均衡常用算法:轮询(遍历服务器节点列表),加权轮询(遍历的基础上添加weight,性能好的情况下多分配一些)
原创文章,作者:站长,如若转载,请注明出处:https://wsppx.cn/1807/%e7%bd%91%e7%bb%9c%e5%bc%80%e5%8f%91/%e5%89%8d%e7%ab%af/