three games 之 桌球

news2025/1/18 8:30:44

接下来介绍一些 Vue4 中的一些进阶使用,希望对大家有所帮助,谢谢。

如果文中有不对、疑惑的地方,欢迎在评论区留言指正🌻

一、项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

1.应用层级的状态应该集中到单个 store 对象中。2.提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。3.异步逻辑都应该封装到 action 里面。只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store├── index.js# 我们组装模块并导出 store 的地方├── actions.js# 根级别的 action├── mutations.js# 根级别的 mutation└── modules├── cart.js # 购物车模块└── products.js # 产品模块 

二、组合式API

可以通过调用 useStore 函数,来在 setup 钩子函数中访问 store。这与在组件中使用选项式 API 访问 this.$store 是等效的。

import { useStore } from 'vuex'

export default {setup () {const store = useStore()}
} 

2.1 访问 State 和 Getter

为了访问 state 和 getter,需要创建 computed 引用以保留响应性,这与在选项式 API 中创建计算属性等效。

import { computed } from 'vue'
import { useStore } from 'vuex'

export default {setup () {const store = useStore()return {// 在 computed 函数中访问 statecount: computed(() => store.state.count),// 在 computed 函数中访问 getterdouble: computed(() => store.getters.double)}}
} 

2.2 访问 Mutation 和 Action

要使用 mutation 和 action 时,只需要在 setup 钩子函数中调用 commitdispatch 函数。

import { useStore } from 'vuex'

export default {setup () {const store = useStore()return {// 使用 mutationincrement: () => store.commit('increment'),// 使用 actionasyncIncrement: () => store.dispatch('asyncIncrement')}}
} 

三、插件

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:

const myPlugin = (store) => {// 当 store 初始化后调用store.subscribe((mutation, state) => {// 每次 mutation 之后调用// mutation 的格式为 { type, payload }})
} 

然后像这样使用:

const store = createStore({// ...plugins: [myPlugin]
}) 

3.1 在插件内提交 Mutation

在插件中不允许直接修改状态——类似于组件,只能通过提交 mutation 来触发变化。

通过提交 mutation,插件可以用来同步数据源到 store。例如,同步 websocket 数据源到 store(下面是个大概例子,实际上 createWebSocketPlugin 方法可以有更过选项来完成复杂任务):

export default function createWebSocketPlugin (socket) {return (store) => {socket.on('data', data => {store.commit('receiveData', data)})store.subscribe(mutation => {if (mutation.type === 'UPDATE_DATA') {socket.emit('update', mutation.payload)}})}
} 
const plugin = createWebSocketPlugin(socket)

const store = createStore({state,mutations,plugins: [plugin]
}) 

3.2 生成 State 快照

有时候插件需要获得状态的“快照”,比较改变的前后状态。想要实现这项功能,你需要对状态对象进行深拷贝:

const myPluginWithSnapshot = (store) => {let prevState = _.cloneDeep(store.state)store.subscribe((mutation, state) => {let nextState = _.cloneDeep(state)// 比较 prevState 和 nextState...// 保存状态,用于下一次 mutationprevState = nextState})
} 

生成状态快照的插件应该只在开发阶段使用,使用 webpack 或 Browserify,让构建工具帮我们处理:

const store = createStore({// ...plugins: process.env.NODE_ENV !== 'production'? [myPluginWithSnapshot]: []
}) 

上面插件会默认启用。在发布阶段,你需要使用 webpack 的 DefinePlugin 或者是 Browserify 的 envify 使 process.env.NODE_ENV !== 'production'false

3.3 内置 Logger 插件

Vuex 自带一个日志插件用于一般的调试:

import { createLogger } from 'vuex'

const store = createStore({plugins: [createLogger()]
}) 

createLogger 函数有几个配置项:

const logger = createLogger({collapsed: false, // 自动展开记录的 mutationfilter (mutation, stateBefore, stateAfter) {// 若 mutation 需要被记录,就让它返回 true 即可// 顺便,`mutation` 是个 { type, payload } 对象return mutation.type !== "aBlocklistedMutation"},actionFilter (action, state) {// 和 `filter` 一样,但是是针对 action 的// `action` 的格式是 `{ type, payload }`return action.type !== "aBlocklistedAction"},transformer (state) {// 在开始记录之前转换状态// 例如,只返回指定的子树return state.subTree},mutationTransformer (mutation) {// mutation 按照 { type, payload } 格式记录// 我们可以按任意方式格式化return mutation.type},actionTransformer (action) {// 和 `mutationTransformer` 一样,但是是针对 action 的return action.type},logActions: true, // 记录 action 日志logMutations: true, // 记录 mutation 日志logger: console, // 自定义 console 实现,默认为 `console`
}) 

日志插件还可以直接通过 <script> 标签引入,它会提供全局方法 createVuexLogger

要注意,logger 插件会生成状态快照,所以仅在开发环境使用。

四、严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = createStore({// ...strict: true
}) 

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

4.1 开发环境与发布环境

不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。

类似于插件,我们可以让构建工具来处理这种情况:

const store = createStore({// ...strict: process.env.NODE_ENV !== 'production'
}) 

五、表单处理

当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手:

<input v-model="obj.message"> 

假设这里的 obj 是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时,v-model 会试图直接修改 obj.message。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。

用“Vuex 的思维”去解决这个问题的方法是:给 <input> 中绑定 value,然后侦听 input 或者 change 事件,在事件回调中调用一个方法:

<input :value="message" @input="updateMessage"> 
// ...
computed: {...mapState({message: state => state.obj.message})
},
methods: {updateMessage (e) {this.$store.commit('updateMessage', e.target.value)}
} 

下面是 mutation 函数:

// ...
mutations: {updateMessage (state, message) {state.obj.message = message}
} 

5.1 双向绑定的计算属性

必须承认,这样做比简单地使用“v-model + 局部状态”要啰嗦得多,并且也损失了一些 v-model 中很有用的特性。另一个方法是使用带有 setter 的双向绑定计算属性:

<input v-model="message"> 
// ...
computed: {message: {get () {return this.$store.state.obj.message},set (value) {this.$store.commit('updateMessage', value)}}
} 

六、测试

我们主要想针对 Vuex 中的 mutation 和 action 进行单元测试。

6.1 测试 Mutation

Mutation 很容易被测试,因为它们仅仅是一些完全依赖参数的函数。这里有一个小技巧,如果你使用了 ES2015 模块,且将 mutation 定义在了 store.js 文件中,那么除了模块的默认导出外,你还应该将 mutation 进行命名导出:

const state = { ... }

// `mutations` 作为命名输出对象
export const mutations = { ... }

export default createStore({state,mutations
}) 

下面是用 Mocha + Chai 测试一个 mutation 的例子(实际上你可以用任何你喜欢的测试框架):

// mutations.js
export const mutations = {increment: state => state.count++
} 
// mutations.spec.js
import { expect } from 'chai'
import { mutations } from './store'

// 解构 `mutations`
const { increment } = mutations

describe('mutations', () => {it('INCREMENT', () => {// 模拟状态const state = { count: 0 }// 应用 mutationincrement(state)// 断言结果expect(state.count).to.equal(1)})
}) 

6.2 测试 Action

Action 应对起来略微棘手,因为它们可能需要调用外部的 API。当测试 action 的时候,我们需要增加一个 mocking 服务层——例如,我们可以把 API 调用抽象成服务,然后在测试文件中用 mock 服务回应 API 调用。为了便于解决 mock 依赖,可以用 webpack 和 inject-loader 打包测试文件。

下面是一个测试异步 action 的例子:

// actions.js
import shop from '../api/shop'

export const getAllProducts = ({ commit }) => {commit('REQUEST_PRODUCTS')shop.getProducts(products => {commit('RECEIVE_PRODUCTS', products)})
} 
// actions.spec.js

// 使用 require 语法处理内联 loaders。
// inject-loader 返回一个允许我们注入 mock 依赖的模块工厂
import { expect } from 'chai'
const actionsInjector = require('inject-loader!./actions')

// 使用 mocks 创建模块
const actions = actionsInjector({'../api/shop': {getProducts (cb) {setTimeout(() => {cb([ /* mocked response */ ])}, 100)}}
})

// 用指定的 mutations 测试 action 的辅助函数
const testAction = (action, args, state, expectedMutations, done) => {let count = 0// 模拟提交const commit = (type, payload) => {const mutation = expectedMutations[count]try {expect(mutation.type).to.equal(type)expect(mutation.payload).to.deep.equal(payload)} catch (error) {done(error)}count++if (count >= expectedMutations.length) {done()}}// 用模拟的 store 和参数调用 actionaction({ commit, state }, ...args)// 检查是否没有 mutation 被 dispatchif (expectedMutations.length === 0) {expect(count).to.equal(0)done()}
}

describe('actions', () => {it('getAllProducts', done => {testAction(actions.getAllProducts, null, {}, [{ type: 'REQUEST_PRODUCTS' },{ type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } }], done)})
}) 

如果在测试环境下有可用的 spy (比如通过 Sinon.JS),你可以使用它们替换辅助函数 testAction

describe('actions', () => {it('getAllProducts', () => {const commit = sinon.spy()const state = {}actions.getAllProducts({ commit, state })expect(commit.args).to.deep.equal([['REQUEST_PRODUCTS'],['RECEIVE_PRODUCTS', { /* mocked response */ }]])})
}) 

6.3 测试 Getter

如果你的 getter 包含很复杂的计算过程,很有必要测试它们。Getter 的测试与 mutation 一样直截了当。

测试一个 getter 的示例:

// getters.js
export const getters = {filteredProducts (state, { filterCategory }) {return state.products.filter(product => {return product.category === filterCategory})}
} 
// getters.spec.js
import { expect } from 'chai'
import { getters } from './getters'

describe('getters', () => {it('filteredProducts', () => {// 模拟状态const state = {products: [{ id: 1, title: 'Apple', category: 'fruit' },{ id: 2, title: 'Orange', category: 'fruit' },{ id: 3, title: 'Carrot', category: 'vegetable' }]}// 模拟 getterconst filterCategory = 'fruit'// 获取 getter 的结果const result = getters.filteredProducts(state, { filterCategory })// 断言结果expect(result).to.deep.equal([{ id: 1, title: 'Apple', category: 'fruit' },{ id: 2, title: 'Orange', category: 'fruit' }])})
}) 

6.4 执行测试

如果你的 mutation 和 action 编写正确,经过合理地 mocking 处理之后这些测试应该不依赖任何浏览器 API,因此你可以直接用 webpack 打包这些测试文件然后在 Node 中执行。换种方式,你也可以用 mocha-loader 或 Karma + karma-webpack 在真实浏览器环境中进行测试。

6.4.1 在 Node 中执行测试

创建以下 webpack 配置(配置好 .babelrc):

// webpack.config.js
module.exports = {entry: './test.js',output: {path: __dirname,filename: 'test-bundle.js'},module: {loaders: [{test: /.js$/,loader: 'babel-loader',exclude: /node_modules/}]}
} 

然后:

webpack
mocha test-bundle.js 

6.4.2 在浏览器中测试

1.安装 mocha-loader
2.把上述 webpack 配置中的 entry 改成 'mocha-loader!babel-loader!./test.js'
3.用以上配置启动 webpack-dev-server
4.访问 localhost:8080/webpack-dev-server/test-bundle

6.4.3 使用 Karma + karma-webpack 在浏览器中执行测试

详见 Vue Loader 的文档。

七、热重载

使用 webpack 的 Hot Module Replacement API,Vuex 支持在开发过程中热重载 mutation、module、action 和 getter。你也可以在 Browserify 中使用 browserify-hmr 插件。

对于 mutation 和模块,你需要使用 store.hotUpdate() 方法:

// store.js
import { createStore } from 'vuex'
import mutations from './mutations'
import moduleA from './modules/a'

const state = { ... }

const store = createStore({state,mutations,modules: {a: moduleA}
})

if (module.hot) {// 使 action 和 mutation 成为可热重载模块module.hot.accept(['./mutations', './modules/a'], () => {// 获取更新后的模块// 因为 babel 6 的模块编译格式问题,这里需要加上 `.default`const newMutations = require('./mutations').defaultconst newModuleA = require('./modules/a').default// 加载新模块store.hotUpdate({mutations: newMutations,modules: {a: newModuleA}})})
} 

7.1 动态模块热重载

如果你仅使用模块,你可以使用 require.context 来动态地加载或热重载所有的模块。

// store.js
import { createStore } from 'vuex'

// 加载所有模块。
function loadModules() {const context = require.context("./modules", false, /([a-z_]+).js$/i)const modules = context.keys().map((key) => ({ key, name: key.match(/([a-z_]+).js$/i)[1] })).reduce((modules, { key, name }) => ({...modules,[name]: context(key).default}),{})return { context, modules }
}

const { context, modules } = loadModules()

const store = new createStore({modules
})

if (module.hot) {// 在任何模块发生改变时进行热重载。module.hot.accept(context.id, () => {const { modules } = loadModules()store.hotUpdate({modules})})
} 

八、TypeScript 支持

Vuex 提供了类型声明,因此可以使用 TypeScript 定义 store,并且不需要任何特殊的 TypeScript 配置。请遵循 Vue 的基本 TypeScript 配置来配置项目。

但是,如果你使用 TypeScript 来编写 Vue 组件,则需要遵循一些步骤才能正确地为 store 提供类型声明。

8.1 Vue 组件中 $store 属性的类型声明

Vuex 没有为 this.$store 属性提供开箱即用的类型声明。如果你要使用 TypeScript,首先需要声明自定义的模块补充 module augmentation 。

为此,需要在项目文件夹中添加一个声明文件来声明 Vue 的自定义类型 ComponentCustomProperties

// vuex.d.ts
import { Store } from 'vuex'

declare module '@vue/runtime-core' {// 声明自己的 store stateinterface State {count: number}// 为 `this.$store` 提供类型声明interface ComponentCustomProperties {$store: Store<State>}
} 

8.2 useStore 组合式函数类型声明

当使用组合式 API 编写 Vue 组件时,您可能希望 useStore 返回类型化的 store。为了 useStore 能正确返回类型化的 store,必须执行以下步骤:

1.定义类型化的 InjectionKey
2.将 store 安装到 Vue 应用时提供类型化的 InjectionKey
3.将类型化的 InjectionKey 传给 useStore 方法。

让我们逐步解决这个问题。首先,使用 Vue 的 InjectionKey 接口和自己的 store 类型定义来定义 key :

// store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'

// 为 store state 声明类型
export interface State {count: number
}

// 定义 injection key
export const key: InjectionKey<Store<State>> = Symbol()

export const store = createStore<State>({state: {count: 0}
}) 

然后,将 store 安装到 Vue 应用时传入定义好的 injection key。

// main.ts
import { createApp } from 'vue'
import { store, key } from './store'

const app = createApp({ ... })

// 传入 injection key
app.use(store, key)

app.mount('#app') 

最后,将上述 injection key 传入 useStore 方法可以获取类型化的 store。

// vue 组件
import { useStore } from 'vuex'
import { key } from './store'

export default {setup () {const store = useStore(key)store.state.count // 类型为 number}
} 

本质上,Vuex 将store 安装到 Vue 应用中使用了 Vue 的 Provide/Inject 特性,这就是 injection key 是很重要的因素的原因。

8.2.1 简化 useStore 用法

引入 InjectionKey 并将其传入 useStore 使用过的任何地方,很快就会成为一项重复性的工作。为了简化问题,可以定义自己的组合式函数来检索类型化的 store :

// store.ts
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'

export interface State {count: number
}

export const key: InjectionKey<Store<State>> = Symbol()

export const store = createStore<State>({state: {count: 0}
})

// 定义自己的 `useStore` 组合式函数
export function useStore () {return baseUseStore(key)
} 

现在,通过引入自定义的组合式函数,不用提供 injection key 和类型声明就可以直接得到类型化的 store:

// vue 组件
import { useStore } from './store'

export default {setup () {const store = useStore()store.state.count // 类型为 number}
} 

每文一句:书山有路勤为径,学海无涯苦作舟。

本次的分享就到这里,如果本章内容对你有所帮助的话欢迎点赞+收藏。文章有不对的地方欢迎指出,有任何疑问都可以在评论区留言。希望大家都能够有所收获,大家一起探讨、进步!

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

移动硬盘怎么分区?

硬盘分区指的是硬盘上被划分出来的区块&#xff0c;可用于分类存储各种数据。而我们日常购买的移动硬盘通常来说分为两种&#xff0c;一种是买回来已分好区的&#xff0c;还有一种是未经过分区的。如果移动硬盘没有经过分区&#xff0c;那么在将它连接到电脑的USB接口时&#x…

android四大组件之四-BroadCast实现原理分析

前言&#xff1a; 一开始的目标是解决各种各样的ANR问题的&#xff0c;但是我们知道&#xff0c;ANR总体上分有四种类型&#xff0c;这四种ANR类型有三种是和四大组件相对应的&#xff0c;所以&#xff0c;如果想了解ANR发生的根因&#xff0c;对安卓四大组件的实现原理必须要…

伙伴云与飞书、金山办公一同入选亿欧2022中国数字化企业服务商TOP50

近日&#xff0c;由中关村国家自主创新示范区展示中心、中关村会展与服务产业联盟与亿欧联合举办的SHOWTECH2022-WIM 创新者年会”在京顺利召开&#xff0c;会上&#xff0c;亿欧网重磅发布《2022世界创新奖榜单》。伙伴云凭借10年来为企业数字化转型赋能的成功经验和卓越贡献&…

数据结构学习-队列

坚持看完&#xff0c;结尾有思维导图总结 这里写目录标题队列的定义于性质如何实现队列的功能初始化队列入队列出队列队列的销毁队列取队头数据队列取队尾数据判断队列是否为空判断队列长度总结队列的定义于性质 队列是一种数据结构&#xff0c;他储存数据的方式就和排队一样 …

二十六、Kubernetes中Horizontal Pod Autoscaler(HPA)控制器详解

1、概述 在kubernetes中&#xff0c;有很多类型的pod控制器&#xff0c;每种都有自己的适合的场景&#xff0c;常见的有下面这些&#xff1a; ReplicationController&#xff1a;比较原始的pod控制器&#xff0c;已经被废弃&#xff0c;由ReplicaSet替代 ReplicaSet&#xff…

年终盘点(三)丨2022计讯物联团队不负韶华,奋力前行

光阴荏苒&#xff0c;时光悄然&#xff0c;成长的齿轮不断转动。2022年&#xff0c;计讯人在挑战中创造不凡&#xff0c;2023年&#xff0c;计讯人在希望中迎接新未来。 回首过去&#xff0c;计讯物联团队不断壮大&#xff0c;在奋勇前行中以坚持书写拼搏&#xff0c;在知难而…

记好这24个ES6方法,用于解决实际开发的JS问题

本文主要介绍24中es6方法&#xff0c;这些方法都挺实用的&#xff0c;本本请记好&#xff0c;时不时翻出来看看。 1.如何隐藏所有指定的元素 1 const hide (el) > Array.from(el).forEach(e > (e.style.display none)); 2 3 // 事例:隐藏页面上所有<img>元素? …

echarts——实现 面积图+柱状图+折线图等——基础积累

因为到年底了&#xff0c;很多项目组都开始做年终汇报&#xff0c;年终汇报的展示形式最常见的就是看板。 样式美观&#xff0c;可以放到电视机或者大屏上&#xff0c;通过图表的形式进行展示&#xff0c;简单明了&#xff0c;通俗易懂。 直接上最终效果图&#xff1a;是一个…

【C++】打开C++的大门

目录前言1.什么是C2.C的发展史3.C关键字&#xff08;C98&#xff09;4.命名空间4.1命名冲突4.2命名空间定义4.3命名空间使用5.输入输出6.缺省参数6.1缺省参数的概念6.2缺省参数分类7.函数重载7.1函数重载概念7.2C函数重载的原理——名字修改8.引用8.1引用的概念8.2引用特性8.3常…

ArcGIS基础实验操作100例--实验94计算栅格图层总和值

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验94 计算栅格图层总和值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

【观察】软硬件底层创新“开花结果”,亚马逊云科技的沉淀与释放

2006年&#xff0c;亚马逊云科技推出了Amazon Web Services&#xff0c;正式“开创”出了云计算市场。同年8月&#xff0c;Amazon Elastic Compute Cloud (EC2) 开放了 beta 测试&#xff0c;启动了云上计算的创新和革命。从此&#xff0c;亚马逊云科技在云计算软硬件底层技术创…

软件测试复习03:动态测试——黑盒测试

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录等价划分法边值分析法错误推测法因果图法示例习题等价划分法 等价类&#xff1a;一个几何&#xf…

阿里云 gradle maven配置中心地址

仓库名称阿里云仓库地址阿里云仓库地址(老版)源地址centralhttps://maven.aliyun.com/repository/centralhttps://maven.aliyun.com/nexus/content/repositories/centralhttps://repo1.maven.org/maven2/jcenterhttps://maven.aliyun.com/repository/publichttps://maven.aliyu…

dp(五) 最长公共子串

最长公共子串_牛客题霸_牛客网 描述 给定两个字符串str1和str2,输出两个字符串的最长公共子串 题目保证str1和str2的最长公共子串存在且唯一。 数据范围&#xff1a; 1≤∣str1∣,∣str2∣≤50001≤∣str1∣,∣str2∣≤5000 要求&#xff1a; 空间复杂度 O(n2)O(n2)&#x…

【阶段三】Python机器学习22篇:机器学习项目实战:GBDT分类模型

本篇的思维导图: 项目实战(GBDT分类模型) 项目背景 应用GBDT算法实现多分类模型,目标是实现GBDT多分类项目的全流程。 数据获取 本次建模数据来源于网络,数据项统计如下: 编号  变量名称 <

聚焦:XuperOS成长计划FAQ

1月12日&#xff0c;百度超级链发布XuperOS成长计划&#xff08;&#x1f449;XuperOS 新年致辞&#xff1a;创世、监督、共建、国际&#xff09;。以下是我们整理的关于成长计划的常见问题&#xff0c;为关心XuperOS的广大朋友答疑解惑。问&#xff1a;XuperChain除了发行这四…

662. 二叉树最大宽度

662. 二叉树最大宽度 难度中等530 给你一棵二叉树的根节点 root &#xff0c;返回树的 最大宽度 。 树的 最大宽度 是所有层中最大的 宽度 。 每一层的 宽度 被定义为该层最左和最右的非空节点&#xff08;即&#xff0c;两个端点&#xff09;之间的长度。将这个二叉树视作…

IT信息化推进那么难,为什么,怎么办 ?

IT信息化推进除了管理层的支持外&#xff0c;更重要的是要赢得业务的支持。但往往业务方有时会排斥IT信息化&#xff0c;这里面有很多的原因&#xff0c;比如&#xff1a;不懂业务的想法难沟通&#xff1b;系统不好用、体验不好、问题太多&#xff0c;不想用。换一波IT团队&…

华为双点双向路由引入实验配置

配置接口IP地址&#xff0c;建立相关的路由邻居 然后在AR2上将ISIS引入OSPF&#xff08;isis-ospf&#xff09;&#xff0c;将OSPF引入ISIS&#xff08;ospf-isis&#xff09; 然后在AR4上将ISIS引入OSPF&#xff08;isis-ospf&#xff09;&#xff0c;将OSPF引入ISIS&#x…

【Electron】解决 npm安装出现 self-signed certificate in certificate

问题 按照Electron 官网步骤安装时 npm install electron --save-dev出现报错 error RequestError: self-signed certificate in certificate chain 解决 解决方式1&#xff1a; 优先尝试 git config --global http.sslVerify false npm set strict-ssl false解决方式2&…