Vue混入(Mixins)与插件开发深度解析
Vue混入(Mixins)与插件开发深度解析
- Vue混入(Mixins)与插件开发深度解析
- 1. Vue混入(Mixins)核心概念
- 1.1 什么是混入
- 1.1.1 本质定义与技术定位
- 1.1.2 混入与相关概念的对比
- 1.1.3 适用场景分析
- 1.1.4 设计哲学与原则
- 1.1.5 底层实现原理
- 1.2 基础使用方式
- 1.2.1 基本使用模式
- 1.2.2 多混入组合
- 1.2.3 混入选项类型支持
- 1.2.4 动态混入模式
- 1.2.5 混入链式调用
- 1.3 选项合并策略
- 1.3.1 默认合并策略表
- 1.3.2 自定义合并策略
- 1.3.3 复杂对象合并示例
- 1.3.4 合并策略源码解析
- 1.4 全局混入及其风险
- 1.4.1 全局混入注册方法
- 1.4.2 适用场景
- 1.4.3 风险控制策略
- 1.4.4 调试技巧
- 1.5 混入的优缺点分析
- 1.5.1 优势详解
- 1.5.2 局限性分析
- 1.5.3 最佳实践指南
- 1.5.4 演进趋势
- 2. 混入实战应用案例
- 2.1 表单验证混入
- 2.1.1 完整验证体系实现
- 2.1.2 高级功能实现
- 2.1.3 组件集成示例
- 2.2 页面权限控制
- 2.2.1 企业级权限管理方案
- 2.2.2 动态菜单渲染
- 2.2.3 按钮级权限控制
- 2.3 通用数据加载逻辑
- 2.3.1 完整数据加载混入
- 2.3.2 组件集成示例
- 2.3.3 高级功能扩展
- 2.4 复杂场景下的混入组合
- 2.4.1 多层混入继承架构
- 2.4.2 混入通信模式
- 2.4.3 动态混入系统
- 2.4.4 混入调试技巧
- 2.4.5 混入组合最佳实践
- 2.5 扩展案例:可视化编辑器混入系统
- 2.5.1 编辑器核心混入
- 2.5.2 快捷键混入
- 2.5.3 组件库混入
- 2.5.4 集成使用示例
- 3. Vue插件开发完全指南
- 3.1 插件的作用与适用场景
- 3.1.1 插件核心价值解析
- 3.1.2 典型应用场景案例
- 案例1:企业级请求插件
- 案例2:混合渲染支持插件
- 3.1.3 插件与混入的协同关系
- 3.2 插件开发基本规范
- 3.2.1 完整插件架构设计
- 3.2.2 Vue 3插件开发适配
- 3.2.3 企业级插件开发规范
- 3.3 常用插件类型分析
- 3.3.1 功能增强型插件开发
- 3.3.2 UI组件库封装方案
- 3.3.3 状态管理集成插件
- 3.3.4 混合类型插件开发
- 3.4 插件发布最佳实践
- 3.4.1 工程化配置方案
- 3.4.2 文档自动化方案
- 3.4.3 持续集成流程
- 3.4.4 企业级发布策略
- 3.5 插件调试与测试
- 3.5.1 单元测试方案
- 3.5.2 浏览器调试技巧
- 3.5.3 性能优化策略
- 3.6 企业级插件架构设计
- 3.6.1 微插件架构模式
- 3.6.2 跨版本兼容方案
- 3.6.3 安全防护策略
- 4. 插件开发实战案例
- 4.1 全局Loading状态管理插件
- 4.2 自定义验证指令插件
- 5. 混入与插件的高级应用
- 5.1 混入与插件的协同使用
- 5.2 TypeScript集成方案
- 总结
Vue混入(Mixins)与插件开发深度解析
1. Vue混入(Mixins)核心概念
1.1 什么是混入
1.1.1 本质定义与技术定位
混入(Mixins)是Vue.js框架中一种高级的代码复用机制,它允许开发者将可复用的组件选项封装为独立模块。从技术实现层面来看,混入本质上是一个包含组件选项的普通JavaScript对象。当组件引用混入时,Vue会通过特定的合并策略将这些选项"混合"到组件的选项中,形成最终的组件定义。
在软件设计模式层面,混入属于"组合优于继承"原则的典型实践。与传统的类继承不同,混入机制提供了一种更灵活的功能扩展方式,允许组件通过"混入"多个功能模块来组合出所需的行为特征,这种设计模式在响应式编程范式中尤为重要。
1.1.2 混入与相关概念的对比
为了更深入理解混入的定位,我们需要将其与相似的代码复用方式进行比较:
1.1.2.1 混入 vs 高阶组件(HOC)
特性 | 混入 | 高阶组件 |
---|---|---|
实现方式 | 选项合并 | 组件包装 |
作用范围 | 组件内部选项 | 组件层次结构 |
复用方式 | 功能注入 | 组件包装器 |
生命周期管理 | 自动合并 | 需手动传递 |
Vue版本支持 | 2.x/3.x | 通用模式 |
1.1.2.2 混入 vs Composition API
特性 | 混入 | Composition API |
---|---|---|
代码组织 | 基于选项 | 基于函数 |
类型支持 | 有限 | 优秀 |
作用域隔离 | 弱 | 强 |
逻辑复用粒度 | 组件级 | 函数级 |
调试难度 | 较高 | 较低 |
Vue版本支持 | 2.x/3.x | 3.x为主 |
1.1.2.3 混入 vs 继承
特性 | 混入 | 继承 |
---|---|---|
关系类型 | 横向组合 | 纵向继承 |
复用方式 | 多源合并 | 单链继承 |
灵活性 | 高 | 低 |
耦合度 | 低 | 高 |
维护成本 | 中等 | 较高 |
1.1.3 适用场景分析
混入在以下场景中表现出显著优势:
-
跨组件共享逻辑:当多个组件需要相同的数据处理、方法实现或生命周期逻辑时
- 示例:表单验证、权限检查、数据获取
-
功能模块解耦:将复杂组件的功能拆分为独立模块
- 示例:编辑器组件拆分为快捷键处理、历史记录、格式维护等混入
-
渐进式功能增强:在不修改原始组件的情况下添加新功能
- 示例:为现有组件添加埋点统计、错误监控
-
第三方功能集成:封装第三方库的集成逻辑
- 示例:地图组件集成、图表库封装
1.1.4 设计哲学与原则
Vue混入机制的设计体现了以下软件工程原则:
- 开闭原则(OCP):通过扩展(混入)而非修改现有组件实现功能增强
- 单一职责原则(SRP):每个混入专注于单一功能领域
- 接口隔离原则(ISP):通过细粒度混入提供精准功能
- DRY原则:避免重复代码,提高可维护性
1.1.5 底层实现原理
Vue内部通过mergeOptions函数实现混入的合并处理,其核心流程如下:
function mergeOptions(parent, child, vm) {// 标准化选项格式normalizeProps(child, vm);normalizeInject(child, vm);normalizeDirectives(child);// 处理extends和mixinsif (!child._base) {if (child.extends) {parent = mergeOptions(parent, child.extends, vm);}if (child.mixins) {for (let i = 0, l = child.mixins.length; i < l; i++) {parent = mergeOptions(parent, child.mixins[i], vm);}}}// 执行选项合并const options = {};for (const key in parent) {mergeField(key);}for (const key in child) {if (!hasOwn(parent, key)) {mergeField(key);}}function mergeField(key) {const strat = strats[key] || defaultStrat;options[key] = strat(parent[key], child[key], vm, key);}return options;
}
关键处理步骤:
- 选项标准化(normalize)
- 处理继承链(extends)
- 递归合并混入(mixins)
- 应用合并策略(strats)
- 生成最终选项
1.2 基础使用方式
1.2.1 基本使用模式
混入的基本使用遵循以下模式:
// 定义混入
const myMixin = {data() {return { mixinData: '混入数据' }},methods: {mixinMethod() {console.log(this.mixinData)}}
}// 使用混入
new Vue({mixins: [myMixin],created() {this.mixinMethod() // 输出:"混入数据"}
})
1.2.2 多混入组合
组件可以同时引用多个混入,Vue会按数组顺序进行合并:
const mixinA = {data: () => ({ a: 1 }),created() { console.log('A created') }
}const mixinB = {data: () => ({ b: 2 }),created() { console.log('B created') }
}new Vue({mixins: [mixinA, mixinB],data: () => ({ c: 3 }),created() {console.log('Component created')console.log(this.$data) // { a: 1, b: 2, c: 3 }}
})// 控制台输出顺序:
// A created
// B created
// Component created
1.2.3 混入选项类型支持
混入支持所有Vue组件选项类型:
数据类选项:
{data() { return {...} },computed: { ... },props: { ... },provide() { return {...} },inject: [...]
}
函数类选项:
{methods: { ... },watch: { ... },filters: { ... }
}
生命周期钩子:
{beforeCreate() {...},created() {...},mounted() {...},// 其他生命周期
}
资源类选项:
{components: { ... },directives: { ... }
}
1.2.4 动态混入模式
可以通过编程方式实现动态混入:
function createDynamicMixin(config) {return {data() {return {dynamicData: config.initialValue}},methods: {updateData(value) {this.dynamicData = value}}}
}new Vue({mixins: [createDynamicMixin({ initialValue: 100 })],created() {console.log(this.dynamicData) // 100this.updateData(200)}
})
1.2.5 混入链式调用
通过函数式编程实现链式混入:
function withLogging(mixin) {return {...mixin,created() {console.log(`[${this.$options.name}] 初始化`)if (mixin.created) mixin.created.call(this)}}
}const baseMixin = { /*...*/ }new Vue({mixins: [withLogging(baseMixin)],name: 'MyComponent'
})
1.3 选项合并策略
1.3.1 默认合并策略表
Vue为不同选项类型提供了预设合并策略:
选项类型 | 合并策略 | 示例说明 |
---|---|---|
data | 递归合并,组件数据优先 | 组件数据覆盖混入同名属性 |
methods | 组件方法覆盖混入方法 | 同名方法以组件为准 |
computed | 合并,组件计算属性优先 | 相同属性名时组件版本生效 |
components | 合并,组件本地注册优先 | 本地组件覆盖混入注册 |
directives | 合并,组件本地指令优先 | 同名指令使用组件版本 |
props | 合并数组,无覆盖行为 | 合并所有props定义 |
provide | 合并函数,组件provide最后执行 | 组件provide可覆盖混入值 |
inject | 合并数组,保留所有注入声明 | 合并所有inject声明 |
watch | 合并为数组,混入观察者先执行 | 两个观察者都会被执行 |
生命周期钩子 | 合并为数组,混入钩子先执行 | 执行顺序:混入A → 混入B → 组件 |
1.3.2 自定义合并策略
Vue允许开发者自定义选项合并策略:
Vue.config.optionMergeStrategies.customOption = (parentVal, childVal) => {return childVal !== undefined ? childVal : parentVal
}const myMixin = {customOption: '混入值'
}new Vue({mixins: [myMixin],customOption: '组件值',created() {console.log(this.$options.customOption) // 输出:"组件值"}
})
1.3.3 复杂对象合并示例
当遇到嵌套对象时,Vue会执行深度合并:
const mixin = {data() {return {obj: {a: 1,b: 2}}}
}new Vue({mixins: [mixin],data() {return {obj: {b: 3,c: 4}}},created() {console.log(this.obj) // { a: 1, b: 3, c: 4 }}
})
1.3.4 合并策略源码解析
以methods的合并策略为例:
strats.methods = function (parentVal, childVal) {const ret = Object.create(null)if (parentVal) extend(ret, parentVal)if (childVal) extend(ret, childVal)return ret
}
该策略实现:
- 创建新对象保持原型链干净
- 优先合并父级(混入)方法
- 用子级(组件)方法覆盖同名方法
1.4 全局混入及其风险
1.4.1 全局混入注册方法
Vue.mixin({created() {console.log('全局混入的created钩子')}
})
1.4.2 适用场景
- 插件开发
- 全局日志记录
- 性能监控
- 错误处理
- 样式注入
1.4.3 风险控制策略
-
命名空间管理:使用特定前缀
Vue.mixin({methods: {$_globalMixin_method() {...}} })
-
条件注入:根据组件特征判断
Vue.mixin({created() {if (this.$options.needAnalytics) {// 注入统计代码}} })
-
性能监控:记录混入执行时间
Vue.mixin({beforeCreate() {this._startTime = Date.now()},mounted() {const cost = Date.now() - this._startTimeif (cost > 1000) {console.warn('组件加载超时:', this.$options.name)}} })
1.4.4 调试技巧
- 使用Vue DevTools检查混入影响
- 在混入中添加唯一标识
Vue.mixin({$_mixinId: 'global-logger',// ... })
- 通过组件选项追溯混入来源
console.log(this.$options.mixins)
1.5 混入的优缺点分析
1.5.1 优势详解
-
逻辑复用效率
- 实现跨组件的功能共享
- 减少重复代码量(平均可减少30%-50%重复代码)
-
功能解耦
- 将复杂组件拆分为多个功能混入
- 提高代码可维护性和可测试性
-
渐进增强
- 无需修改原始组件即可添加功能
- 支持按需组合功能模块
-
兼容性优势
- 支持Vue 2.x全版本
- 在Vue 3.x中保持兼容
1.5.2 局限性分析
-
命名冲突风险
- 数据、方法、计算属性等可能产生覆盖
- 示例:两个混入都定义了
handleSubmit
方法
-
隐式依赖
- 混入可能依赖特定组件结构
- 示例:假设组件中存在
this.formData
属性
-
调试难度
- 问题溯源需要检查多个混入文件
- 堆栈跟踪可能显示混入代码位置
-
类型支持限制
- 在TypeScript中类型推断不够友好
- 需要额外类型声明
1.5.3 最佳实践指南
-
命名规范
- 数据属性:
mixinName_property
(如auth_userInfo
) - 方法命名:
mixinName_action
(如logging_trackEvent
)
- 数据属性:
-
文档规范
## 数据字典 | 属性名 | 类型 | 说明 | |------------|--------|--------------| | loading | Boolean| 数据加载状态 |## 方法列表 - fetchData(): 发起数据请求 - handleError(): 错误处理
-
范围控制
- 单个混入代码不超过300行
- 每个混入专注单一功能领域
- 避免嵌套混入(混入中引用其他混入)
-
测试策略
- 为每个混入编写独立测试用例
- 使用Vue Test Utils的
createLocalVue
进行隔离测试 - 示例:
test('auth mixin', () => {const localVue = createLocalVue()localVue.mixin(authMixin)// 测试逻辑... })
1.5.4 演进趋势
随着Composition API的普及,混入的使用场景正在发生变化:
- Vue 2项目:仍是主要复用方案
- Vue 3项目:
- 简单逻辑:继续使用混入
- 复杂逻辑:优先使用Composition API
- 迁移策略:
- 将混入重构为可组合函数
- 使用
mixins
选项过渡
// Composition API实现混入等价功能
function useAuth() {const user = ref(null)const checkPermission = (role) => {// ...}return { user, checkPermission }
}export default {setup() {const { user, checkPermission } = useAuth()return { user, checkPermission }}
}
2. 混入实战应用案例
2.1 表单验证混入
2.1.1 完整验证体系实现
// validationMixin.js
export default {data() {return {validationErrors: {},isValidationPending: false,initialValidation: false}},computed: {isValidForm() {return Object.keys(this.validationErrors).every(key => !this.validationErrors[key])},firstError() {const errors = Object.values(this.validationErrors).filter(Boolean)return errors.length ? errors[0] : null}},methods: {async validateField(field) {if (!this.validationRules[field]) return trueconst rules = this.validationRules[field]const value = this.formData[field]let error = ''for (const rule of rules) {const result = await this.executeRule(rule, value)if (!result.valid) {error = result.message || rule.messagebreak}}this.$set(this.validationErrors, field, error)return !error},async validateForm() {this.initialValidation = trueconst results = await Promise.all(Object.keys(this.validationRules).map(this.validateField))return results.every(Boolean)},async executeRule(rule, value) {try {const valid = typeof rule.validator === 'function' ? await rule.validator(value, this.formData): rule.regex.test(value)return {valid,message: typeof rule.message === 'function'? rule.message(value): rule.message}} catch (error) {console.error('Validation error:', error)return { valid: false, message: '验证过程发生错误' }}},resetValidation() {this.validationErrors = {}this.initialValidation = false}},watch: {formData: {deep: true,handler() {if (this.initialValidation) {this.validateForm()}}}}
}
2.1.2 高级功能实现
- 跨字段验证:
{validator: (value, form) => {return value === form.password},message: '两次输入密码不一致'
}
- 异步服务端验证:
{validator: async (username) => {const res = await axios.get('/api/check-username', { params: { username } })return res.data.available},message: '用户名已被注册'
}
- 动态错误提示:
{validator: v => v.length >= 6,message: (value) => `密码至少6位,当前长度${value.length}`
}
2.1.3 组件集成示例
<template><form @submit.prevent="handleSubmit"><div class="form-group"><label>邮箱</label><input v-model="formData.email" @blur="validateField('email')"><div class="error">{{ validationErrors.email }}</div></div><div class="form-group"><label>密码</label><input v-model="formData.password" type="password" @input="debouncedValidate('password')"><div class="error">{{ validationErrors.password }}</div></div><button :disabled="isValidationPending">提交</button><div v-if="firstError" class="global-error">{{ firstError }}</div></form>
</template><script>
import validationMixin from './mixins/validationMixin'
import debounce from 'lodash/debounce'export default {mixins: [validationMixin],data() {return {formData: {email: '',password: ''},validationRules: {email: [{ validator: v => !!v, message: '必填字段' },{ regex: /@/, message: '必须包含@符号' }],password: [{ validator: v => v.length >= 6, message: '至少6位' },{ validator: v => /[A-Z]/.test(v), message: '必须包含大写字母' }]}}},methods: {debouncedValidate: debounce(function(field) {this.validateField(field)}, 300),async handleSubmit() {const isValid = await this.validateForm()if (isValid) {// 提交逻辑}}}
}
</script>
2.2 页面权限控制
2.2.1 企业级权限管理方案
// authMixin.js
export default {computed: {user() {return this.$store.state.auth.user},userRoles() {return this.user?.roles || []}},methods: {checkPermission(required) {if (!required) return trueconst requiredRoles = Array.isArray(required) ? required : [required]return requiredRoles.some(role => this.userRoles.includes(role))},checkAnyPermission() {return [...arguments].some(this.checkPermission)},checkAllPermissions() {return [...arguments].every(this.checkPermission)}},beforeRouteEnter(to, from, next) {next(vm => {const required = to.meta.requiredPermissionif (required && !vm.checkPermission(required)) {vm.handleForbidden()return}})},beforeRouteUpdate(to, from, next) {const required = to.meta.requiredPermissionif (required && !this.checkPermission(required)) {this.handleForbidden()return}next()},handleForbidden() {if (this.user) {this.$router.replace('/403')} else {this.$router.replace({path: '/login',query: { redirect: this.$route.fullPath }})}}
}
2.2.2 动态菜单渲染
// menuMixin.js
export default {computed: {filteredMenu() {return this.originalMenu.filter(item => {return this.checkPermission(item.requiredPermission)})}},methods: {generateMenu() {return [{title: '仪表盘',path: '/dashboard',requiredPermission: 'VIEW_DASHBOARD'},{title: '用户管理',path: '/users',requiredPermission: ['MANAGE_USERS', 'ADMIN']},// 其他菜单项...]}}
}
2.2.3 按钮级权限控制
<template><button v-if="hasPermission('DELETE_USER')" @click="handleDelete">删除用户</button>
</template><script>
import authMixin from './mixins/authMixin'export default {mixins: [authMixin],methods: {hasPermission(code) {return this.checkPermission(code)}}
}
</script>
2.3 通用数据加载逻辑
2.3.1 完整数据加载混入
// dataLoaderMixin.js
export default {data() {return {isLoading: false,isLoadingError: false,data: null,pagination: {page: 1,pageSize: 10,total: 0},retryCount: 0}},computed: {hasMore() {return this.pagination.total > this.pagination.page * this.pagination.pageSize}},methods: {async loadData(options = {}) {if (this.isLoading) returntry {this.isLoading = truethis.isLoadingError = falseconst response = await this.fetchData({page: this.pagination.page,pageSize: this.pagination.pageSize,...options})this.handleResponse(response)this.retryCount = 0} catch (error) {this.handleError(error)if (this.retryCount < 3) {setTimeout(() => {this.retryCount++this.loadData(options)}, 1000 * this.retryCount)}} finally {this.isLoading = false}},handleResponse(response) {// 抽象方法,需在组件中实现throw new Error('必须实现 handleResponse 方法')},handleError(error) {this.isLoadingError = trueconsole.error('数据加载失败:', error)this.$emit('load-error', error)},nextPage() {if (this.hasMore && !this.isLoading) {this.pagination.page++this.loadData()}},refresh() {this.pagination.page = 1this.loadData({ forceRefresh: true })}}
}
2.3.2 组件集成示例
<script>
import dataLoaderMixin from './mixins/dataLoaderMixin'export default {mixins: [dataLoaderMixin],data() {return {searchQuery: ''}},created() {this.loadData()},methods: {async fetchData(params) {return axios.get('/api/users', {params: {search: this.searchQuery,...params}})},handleResponse(response) {this.data = response.data.itemsthis.pagination.total = response.data.total},handleSearch() {this.pagination.page = 1this.loadData()}}
}
</script>
2.3.3 高级功能扩展
- 滚动加载:
mounted() {window.addEventListener('scroll', this.handleScroll)
},beforeDestroy() {window.removeEventListener('scroll', this.handleScroll)
},methods: {handleScroll() {const bottomOffset = 100const { scrollTop, scrollHeight, clientHeight } = document.documentElementif (scrollTop + clientHeight >= scrollHeight - bottomOffset) {this.nextPage()}}
}
- 缓存策略:
// dataLoaderMixin.js
cache: {data: null,timestamp: 0
},methods: {async loadData() {if (this.cache.data && Date.now() - this.cache.timestamp < 300000) {this.data = this.cache.datareturn}// 正常加载逻辑...this.cache.data = response.datathis.cache.timestamp = Date.now()}
}
2.4 复杂场景下的混入组合
2.4.1 多层混入继承架构
// baseMixin.js
export default {data() {return {baseData: '基础数据'}},methods: {baseMethod() {console.log('基础方法')}}
}// featureMixin.js
import baseMixin from './baseMixin'export default {mixins: [baseMixin],data() {return {featureData: '特性数据'}},methods: {featureMethod() {this.baseMethod()console.log('特性方法')}}
}// component.js
export default {mixins: [featureMixin],created() {console.log(this.baseData) // 基础数据this.featureMethod() // 基础方法 + 特性方法}
}
2.4.2 混入通信模式
- 事件总线通信:
// eventMixin.js
export default {methods: {$emitGlobal(event, ...args) {this.$root.$emit(`global:${event}`, ...args)},$onGlobal(event, callback) {const listener = (...args) => callback(...args)this.$root.$on(`global:${event}`, listener)this.$on('hook:beforeDestroy', () => {this.$root.$off(`global:${event}`, listener)})}}
}// 组件A
this.$emitGlobal('data-updated', newData)// 组件B
this.$onGlobal('data-updated', this.handleDataUpdate)
- 共享状态管理:
// sharedStateMixin.js
const state = Vue.observable({count: 0
})export default {computed: {sharedCount: {get() { return state.count },set(value) { state.count = value }}}
}
2.4.3 动态混入系统
// dynamicMixin.js
export function createDynamicMixin(options) {return {data() {return {[options.name]: options.initialState}},methods: {[`set${options.name}`](value) {this[options.name] = value}}}
}// 使用示例
const counterMixin = createDynamicMixin({name: 'Counter',initialState: 0
})export default {mixins: [counterMixin],methods: {increment() {this.setCounter(this.Counter + 1)}}
}
2.4.4 混入调试技巧
- 混入追踪标记:
// debugMixin.js
export default {created() {if (this.$options.mixins) {console.log('当前组件混入:', this.$options.mixins.map(m => m.name || '匿名混入'))}}
}
- 性能分析:
// perfMixin.js
export default {beforeCreate() {this.$_perfStart = performance.now()},mounted() {const duration = performance.now() - this.$_perfStartif (duration > 100) {console.warn(`组件渲染耗时: ${duration.toFixed(2)}ms`, this.$options.name)}}
}
2.4.5 混入组合最佳实践
- 命名空间管理:
// 混入定义
export default {methods: {$_myMixin_uniqueMethod() {...}},data() {return {$_myMinxin_privateData: ...}}
}
- 文档规范:
## 数据混入规范### 命名规则
- 全局混入: g_ 前缀
- 功能混入: feature_ 前缀
- 业务混入: biz_ 前缀### 版本记录
| 版本 | 修改内容 | 日期 |
|------|------------------|------------|
| 1.0 | 初始版本 | 2023-08-01 |
| 1.1 | 增加缓存策略 | 2023-08-05 |
- 依赖管理:
// dependencyMixin.js
export default {beforeCreate() {if (!this.$options.components.SomeComponent) {console.error('需要注册 SomeComponent')}if (!this.$router) {console.error('需要安装 Vue Router')}}
}
2.5 扩展案例:可视化编辑器混入系统
2.5.1 编辑器核心混入
// editorCoreMixin.js
export default {data() {return {canvasData: [],activeComponent: null,historyStack: [],historyIndex: -1}},methods: {addComponent(component) {this.canvasData.push(component)this.recordHistory()},recordHistory() {this.historyStack = this.historyStack.slice(0, this.historyIndex + 1)this.historyStack.push(JSON.stringify(this.canvasData))this.historyIndex++},undo() {if (this.historyIndex > 0) {this.historyIndex--this.canvasData = JSON.parse(this.historyStack[this.historyIndex])}},redo() {if (this.historyIndex < this.historyStack.length - 1) {this.historyIndex++this.canvasData = JSON.parse(this.historyStack[this.historyIndex])}}}
}
2.5.2 快捷键混入
// shortcutMixin.js
export default {mounted() {document.addEventListener('keydown', this.handleKeyDown)},beforeDestroy() {document.removeEventListener('keydown', this.handleKeyDown)},methods: {handleKeyDown(e) {if (e.ctrlKey && e.key === 'z') {e.preventDefault()this.undo()}if (e.ctrlKey && e.key === 'y') {e.preventDefault()this.redo()}}}
}
2.5.3 组件库混入
// componentLibMixin.js
export default {data() {return {componentLibrary: [{type: 'text',name: '文本组件',props: { content: '默认文本' }},{type: 'image',name: '图片组件',props: { src: '' }}]}},methods: {getComponentConfig(type) {return this.componentLibrary.find(c => c.type === type)}}
}
2.5.4 集成使用示例
<script>
import editorCoreMixin from './mixins/editorCoreMixin'
import shortcutMixin from './mixins/shortcutMixin'
import componentLibMixin from './mixins/componentLibMixin'export default {mixins: [editorCoreMixin, shortcutMixin, componentLibMixin],methods: {handleAddText() {const textConfig = this.getComponentConfig('text')this.addComponent(textConfig)}}
}
</script>
通过以上扩展,本章节详细展示了混入在各类复杂场景下的应用实践,覆盖表单验证、权限管理、数据加载等常见需求,并深入探讨了混入组合、调试优化等高级主题,为开发者提供了完整的混入应用解决方案。
3. Vue插件开发完全指南
3.1 插件的作用与适用场景
3.1.1 插件核心价值解析
Vue插件系统为框架提供了强大的扩展能力,其主要价值体现在:
-
全局功能注入
- 添加全局方法/属性(如
this.$api
) - 注册全局组件(如
<vue-datepicker>
) - 注入全局指令(如
v-permission
)
- 添加全局方法/属性(如
-
生态系统集成
- 封装第三方库(图表库、地图SDK)
- 集成状态管理(Vuex插件)
- 扩展路由能力(路由守卫增强)
-
企业级方案封装
- 统一错误处理机制
- 构建监控系统
- 实现微前端架构
3.1.2 典型应用场景案例
案例1:企业级请求插件
// api-plugin.js
export default {install(Vue, { endpoints }) {Vue.prototype.$api = Object.keys(endpoints).reduce((api, key) => {api[key] = (params) => axios(endpoints[key](params))return api}, {})}
}// 使用示例
Vue.use(apiPlugin, {endpoints: {getUser: (id) => ({url: `/users/${id}`,method: 'GET'})}
})// 组件中调用
this.$api.getUser(123)
案例2:混合渲染支持插件
// ssr-plugin.js
export default {install(Vue, { ssrContext }) {Vue.mixin({serverPrefetch() {return this.$options.asyncData?.call(this)},beforeMount() {if (window.__INITIAL_STATE__) {this.$data = Object.assign(this.$data, window.__INITIAL_STATE__)}}})}
}
3.1.3 插件与混入的协同关系
维度 | 插件 | 混入 |
---|---|---|
作用范围 | 全局/应用级 | 组件级 |
主要功能 | 框架扩展/集成第三方库 | 组件逻辑复用 |
注册方式 | Vue.use() | mixins 选项 |
生命周期 | 应用初始化阶段 | 组件生命周期 |
典型应用 | 全局指令/过滤器 | 数据获取/权限控制 |
3.2 插件开发基本规范
3.2.1 完整插件架构设计
标准插件模板:
const MyPlugin = {// 必须的install方法install(Vue, options = {}) {// 1. 添加全局方法或属性Vue.$myGlobalMethod = () => { /* ... */ }// 2. 添加全局资源Vue.directive('my-directive', { /* ... */ })// 3. 注入组件选项Vue.mixin({created() { /* ... */ }})// 4. 添加实例方法Vue.prototype.$myMethod = () => { /* ... */ }// 5. 注册全局组件Vue.component('my-component', { /* ... */ })}
}export default MyPlugin
3.2.2 Vue 3插件开发适配
Composition API集成方案:
import { App } from 'vue'interface PluginOptions {prefix?: string
}export default {install(app: App, options: PluginOptions = {}) {const { prefix = 'my' } = options// 提供全局上下文app.provide('pluginContext', {generateId: () => `${prefix}-${Math.random().toString(36).substr(2, 9)}`})// 组合式API集成app.mixin({setup() {const plugin = inject('pluginContext')return { plugin }}})}
}
3.2.3 企业级插件开发规范
-
命名规范
- 全局属性:
$[pluginName]_[feature]
(如$auth_login
) - 全局组件:
[Prefix][ComponentName]
(如VueDatePicker
) - 命名空间:
__private
前缀表示内部方法
- 全局属性:
-
配置管理
const DEFAULT_CONFIG = {debug: false,apiBase: '/api/v1'
}export default {install(Vue, userConfig) {const config = Object.assign({}, DEFAULT_CONFIG, userConfig)Vue.prototype.$pluginConfig = configif (config.debug) {Vue.config.errorHandler = (err) => {console.error(`[Plugin Error] ${err.message}`)}}}
}
- 错误处理机制
// error-handler.js
export default {install(Vue) {const handler = {get(target, prop) {try {return target[prop]} catch (error) {console.error(`Plugin method ${prop} failed:`, error)return () => {}}}}Vue.prototype.$pluginApi = new Proxy({}, handler)}
}
3.3 常用插件类型分析
3.3.1 功能增强型插件开发
全局过滤器插件示例:
// filters-plugin.js
export default {install(Vue) {Vue.filter('currency', (value, symbol = '¥') => {return `${symbol} ${value.toFixed(2)}`})Vue.filter('truncate', (text, length = 30) => {return text.length > length ? text.substr(0, length) + '...' : text})}
}
3.3.2 UI组件库封装方案
组件库插件架构:
components/Button/index.vuestyle.cssModal/index.vuestyle.css
index.js
入口文件实现:
import Button from './components/Button'
import Modal from './components/Modal'const components = {'VButton': Button,'VModal': Modal
}export default {install(Vue, { prefix = 'v' } = {}) {Object.entries(components).forEach(([name, component]) => {Vue.component(`${prefix}-${name.toLowerCase()}`, component)})}
}
3.3.3 状态管理集成插件
Vuex增强插件示例:
// vuex-plugin.js
export default {install(Vue, { store }) {store.registerModule('plugin', {state: () => ({ count: 0 }),mutations: {increment(state) {state.count++}}})Vue.prototype.$pluginStore = {getCount: () => store.state.plugin.count,increment: () => store.commit('plugin/increment')}}
}
3.3.4 混合类型插件开发
全功能插件示例:
export default {install(Vue, options) {// 1. 注册全局组件Vue.component('PluginComponent', { /* ... */ })// 2. 添加全局方法Vue.prototype.$pluginMethod = () => { /* ... */ }// 3. 注入混入Vue.mixin({created() {if (this.$options.needsPlugin) {this.$plugin = new PluginService(options)}}})// 4. 自定义指令Vue.directive('plugin-directive', {bind(el, binding) {// 指令逻辑}})}
}
3.4 插件发布最佳实践
3.4.1 工程化配置方案
推荐工具链配置:
// rollup.config.js
import vue from 'rollup-plugin-vue'
import babel from '@rollup/plugin-babel'
import { terser } from 'rollup-plugin-terser'export default {input: 'src/index.js',output: [{file: 'dist/vue-plugin.esm.js',format: 'es'},{file: 'dist/vue-plugin.umd.js',format: 'umd',name: 'VuePlugin'}],plugins: [vue(),babel({babelHelpers: 'bundled',exclude: 'node_modules/**'}),terser()],external: ['vue']
}
3.4.2 文档自动化方案
JSDoc文档示例:
/*** 全局数据获取方法* @memberof Vue.prototype* @param {string} endpoint - API端点路径* @param {Object} params - 请求参数* @returns {Promise} 包含响应数据的Promise*/
Vue.prototype.$fetch = async function(endpoint, params) {// 方法实现
}
3.4.3 持续集成流程
.github/workflows/publish.yml
name: Publish Packageon:release:types: [created]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- uses: actions/setup-node@v2with:node-version: 14- run: npm ci- run: npm run build- run: npm test- run: npm publishenv:NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
3.4.4 企业级发布策略
版本管理规范:
{"version": "2.1.0","publishConfig": {"access": "public","registry": "https://registry.npmjs.org/"},"files": ["dist/*","src/*","README.md","LICENSE"],"peerDependencies": {"vue": "^2.6.0 || ^3.0.0"},"exports": {".": {"import": "./dist/vue-plugin.esm.js","require": "./dist/vue-plugin.umd.js"},"./components/*": "./src/components/*.vue"}
}
3.5 插件调试与测试
3.5.1 单元测试方案
Jest测试示例:
import { shallowMount } from '@vue/test-utils'
import MyPlugin from '../src'
import Vue from 'vue'describe('MyPlugin', () => {beforeAll(() => {Vue.use(MyPlugin, { test: true })})test('注入全局方法', () => {const wrapper = shallowMount({template: '<div/>'})expect(typeof wrapper.vm.$myMethod).toBe('function')})test('组件注册验证', () => {expect(Vue.options.components['MyComponent']).toBeDefined()})
})
3.5.2 浏览器调试技巧
Source Map配置:
// webpack.config.js
module.exports = {productionSourceMap: true,configureWebpack: {devtool: process.env.NODE_ENV === 'production'? 'source-map': 'cheap-module-source-map'}
}
3.5.3 性能优化策略
懒加载插件实现:
export default {install(Vue, options) {const loadPlugin = () => import('./heavy-module')Vue.prototype.$lazyFeature = {init: async () => {const module = await loadPlugin()return module.initialize(options)}}}
}
3.6 企业级插件架构设计
3.6.1 微插件架构模式
模块化插件系统:
// core-plugin.js
export default {install(Vue, { modules = [] }) {modules.forEach(module => {Vue.use(module)})}
}// feature-module.js
export default {install(Vue) {Vue.component('FeatureComponent', { /* ... */ })}
}
3.6.2 跨版本兼容方案
版本适配插件:
export default {install(Vue) {const version = Number(Vue.version.split('.')[0])if (version === 2) {// Vue 2兼容逻辑Vue.prototype.$nextTick = Vue.nextTick} else if (version === 3) {// Vue 3适配逻辑Vue.config.globalProperties.$nextTick = Vue.nextTick}}
}
3.6.3 安全防护策略
沙箱模式实现:
export default {install(Vue) {const sandbox = {safeEval(code) {return Function('"use strict";return (' + code + ')')()}}Vue.prototype.$sandbox = new Proxy(sandbox, {get(target, prop) {if (prop in target) {return target[prop]}throw new Error(`未授权的沙箱方法调用: ${prop}`)}})}
}
通过以上扩展,本章节系统性地阐述了Vue插件开发的完整知识体系,从基础规范到企业级实践,覆盖插件设计、开发、测试、发布的全生命周期,为开发者构建高质量Vue插件提供了全面指导。
4. 插件开发实战案例
4.1 全局Loading状态管理插件
loading-plugin.js
const LoadingPlugin = {install(Vue, options) {const loadingComponent = Vue.extend({template: `<div v-if="isLoading" class="loading-overlay"><div class="loading-spinner"></div></div>`,data: () => ({isLoading: false})})const loadingInstance = new loadingComponent().$mount()document.body.appendChild(loadingInstance.$el)Vue.prototype.$loading = {show() {loadingInstance.isLoading = true},hide() {loadingInstance.isLoading = false}}}
}export default LoadingPlugin
使用示例:
// main.js
import LoadingPlugin from './plugins/loading-plugin'
Vue.use(LoadingPlugin)// 组件中使用
this.$loading.show()
// API调用完成后
this.$loading.hide()
4.2 自定义验证指令插件
validation-plugin.js
const ValidationPlugin = {install(Vue) {Vue.directive('validate', {bind(el, binding, vnode) {const vm = vnode.contextconst field = binding.expressionel.addEventListener('input', () => {vm.$validateField(field)})el.addEventListener('blur', () => {vm.$validateField(field)})}})Vue.prototype.$validateField = function(field) {// 验证逻辑实现}}
}export default ValidationPlugin
5. 混入与插件的高级应用
5.1 混入与插件的协同使用
场景: 通过插件注册全局混入
const TrackingPlugin = {install(Vue) {Vue.mixin({mounted() {if (this.$options.trackingKey) {analytics.trackMount(this.$options.trackingKey)}}})}
}
5.2 TypeScript集成方案
混入类型定义:
import Vue from 'vue'declare module 'vue/types/vue' {interface Vue {$loading: {show: () => voidhide: () => void}}
}interface ValidationMixin extends Vue {validateForm(): booleanvalidateField(field: string): booleanerrors: Record<string, string>
}const validationMixin = Vue.extend({// 混入实现
}) as ValidationMixin
总结
本文深入探讨了Vue混入和插件开发的各个方面,从基础概念到高级应用,覆盖了实际开发中的典型场景。通过合理使用这些特性,开发者可以显著提升代码的复用性和可维护性。需要注意:
- 混入适合组件级别的逻辑复用
- 插件适用于全局功能扩展
- 注意控制功能边界,避免过度设计
- 结合TypeScript提升类型安全
- 遵循良好的代码组织规范
正确运用这些技术,能够帮助开发者构建更健壮、更易维护的Vue应用程序。
相关文章:
Vue混入(Mixins)与插件开发深度解析
Vue混入(Mixins)与插件开发深度解析 Vue混入(Mixins)与插件开发深度解析1. Vue混入(Mixins)核心概念1.1 什么是混入1.1.1 本质定义与技术定位1.1.2 混入与相关概念的对比1.1.3 适用场景分析1.1.4 设计哲学与…...
Linux里的容器被OOM killed的两种情况
生产上遇到过几次容器实例被OOM的现象,总结一下LInux OOM的两种触发条件。我的虚拟机是ubuntu 24.0.4版本,分配4G内存,在我的虚拟机上复现这两种case。 一 宿主机物理内存不够 当linux上所有应用程序的内存需求加起来超出了物理内存&#x…...
十一、CentOS Stream 9 安装 Docker
一、Docker 环境安装 1、软件源(仓库)信息 使用如下命令可列出当前系统配置的所有软件源(仓库)信息 # 列出所有软件源 dnf repolist 这表明系统有三个仓库 AppStream 、 BaseOS、Extras-Common 被启用 2、配置软件源镜像 使用如下命令可配置 Docker 软件包下载的镜像地址 …...
【数据结构】链表应用-链表重新排序
重新排序 反转链表预期实现思路解题过程code力扣代码核心代码完整代码 总结 删除链表中间节点代码解惑 链表重新排序题目描述解题思路解题过程复杂度代码力扣代码完整代码 反转链表 预期实现 思路 你选用何种方法解题? 我选用了迭代法来反转链表。这是一种经典且高…...
e2studio开发RA2E1(9)----定时器GPT配置输入捕获
e2studio开发RA2E1.9--定时器GPT配置输入捕获 概述视频教学样品申请硬件准备参考程序源码下载选择计时器时钟源UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user_uart_callback ()printf输出重定向到串口定时器输入捕获配…...
qt使用MQTT协议连接阿里云demo
qt使用Mqtt协议连接阿里云。 在配置好qt关于MQTT的环境之后,主要就是根据MQTT的连接参数进行连接即可。 环境配置推荐链接QT编译并部署QtMqtt相关环境跑测demo【超详细教程】_mqtt qt开发教程-CSDN博客 连接核心代码,主要就是根据阿里云的MQTT相关参数进行配置实现连…...
Python分享20个Excel自动化脚本
在数据处理和分析的过程中,Excel文件是我们日常工作中常见的格式。通过Python,我们可以实现对Excel文件的各种自动化操作,提高工作效率。 本文将分享20个实用的Excel自动化脚本,以帮助新手小白更轻松地掌握这些技能。 1. Excel单…...
DNN(深度神经网络)近似 Lyapunov 函数
import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt # from torchviz import make_dot import torchviz# 1. Lyapunov 函数近似器(MLP 结构) class LyapunovNet(nn.Module):def __init__(self, input_dim…...
什么是数据库代理
数据库代理(DB Proxy)是一种位于应用程序和数据库服务器之间的中间件,充当两者之间的“中间人”。它的核心目标是优化数据库访问、提升性能、增强安全性,并简化数据库架构的复杂度,尤其在高并发、分布式或云环境中应用…...
深入浅出 DeepSeek V2 高效的MoE语言模型
今天,我们来聊聊 DeepSeek V2 高效的 MoE 语言模型,带大家一起深入理解这篇论文的精髓,同时,告诉大家如何将这些概念应用到实际中。 🌟 什么是 MoE?——Mixture of Experts(专家混合模型&#x…...
【创建模式-单例模式(Singleton Pattern)】
赐萧瑀 实现方案饿汉模式懒汉式(非线程安全)懒汉模式(线程安全)双重检查锁定静态内部类 攻击方式序列化攻击反射攻击 枚举(最佳实践)枚举是一种类 唐 李世民 疾风知劲草,板荡识诚臣。 勇夫安识义,智者必怀仁…...
计算机毕业设计Python+Vue.js游戏推荐系统 Steam游戏推荐系统 Django Flask 游 戏可视化 游戏数据分析 游戏大数据 爬虫
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
6. 【Vue实战--孢子记账--Web 版开发】-- 主币种设置
从这篇文章开始我们将一起实现孢子记账的功能,这篇文章实现主币种设置。这个功能比较简单,因此我们从这个功能开始做。 一、功能 根据项目前期的需求调研,用户需要在设置主币种的时候查看汇率信息(别问为什么有这么个需求&#…...
RabbitMQ深度探索:前置知识
消息中间件: 消息中间件基于队列模式实现异步 / 同步传输数据作用:可以实现支撑高并发、异步解耦、流量削峰、降低耦合 传统的 HTTP 请求存在的缺点: HTTP 请求基于响应的模型,在高并发的情况下,客户端发送大量的请求…...
【文件上传、秒传、分片上传、断点续传、重传】
文章目录 获取文件对象文件上传(秒传、分片上传、断点续传、重传)优化 获取文件对象 input标签的onchange方法接收到的参数就是用户上传的所有文件 <html lang"en"><head><title>文件上传</title><style>#inp…...
设计模式Python版 组合模式
文章目录 前言一、组合模式二、组合模式实现方式三、组合模式示例四、组合模式在Django中的应用 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式…...
python开发:爬虫示例——GET和POST请求处理
一、Get请求 import json import requests#输入示例:urlhttps://www.baidu.com #RequestHeader:F12标头-请求标头-原始-复制到这(忽略第一句) def GetRequest(url,RequestHeader""):try:dic{}RequestHeaderList RequestHeader.s…...
【3分钟极速部署】在本地快速部署deepseek
第一步,找到网站,下载: 首先找到Ollama , 根据自己的电脑下载对应的版本 。 我个人用的是Windows 我就先尝试用Windows版本了 ,文件不是很大,下载也比较的快 第二部就是安装了 : 安装完成后提示…...
【归属地】批量号码归属地查询按城市高速的分流,基于WPF的解决方案
在现代商业活动中,企业为了提高营销效果和资源利用效率,需要针对不同地区的市场特点开展精准营销。通过批量号码归属地查询并按城市分流,可以为企业的营销决策提供有力支持。 短信营销:一家连锁餐饮企业计划开展促销活动…...
大数据sql查询速度慢有哪些原因
1.索引问题 可能缺少索引,也有可能是索引不生效 2.连接数配置:连接数过少/连接池比较小 连接数过 3.sql本身有问题,响应比较慢,比如多表 4.数据量比较大 -这种最好采用分表设计 或分批查询 5.缓存池大小 可能是缓存问题ÿ…...
安卓路由与aop 以及 Router-api
安卓路由(Android Router)和AOP(面向切面编程)是两个在Android开发中常用的概念。下面我将详细讲解这两个概念及其在Android开发中的应用。 一、安卓路由 安卓路由主要用于在应用程序中管理不同组件之间的导航和通信。它可以简化…...
游戏引擎学习第89天
回顾 由于一直没有渲染器,终于决定开始动手做一个渲染器,虽然开始时并不确定该如何进行,但一旦开始做,发现这其实是正确的决定。因此,接下来可能会花一到两周的时间来编写渲染器,甚至可能更长时间…...
备战蓝桥杯-洛谷
今天打算写一些洛谷上面的题目 P10904 [蓝桥杯 2024 省 C] 挖矿 https://www.luogu.com.cn/problem/P10904 看了大佬写的题解才写出来这道题的:题解:P10904 [蓝桥杯 2024 省 C] 挖矿 - 洛谷专栏 思路: 这是一道贪心的题目,用…...
动手学图神经网络(9):利用图神经网络进行节点分类 WeightsBiases
利用图神经网络进行节点分类Weights&Biases 引言 在本篇博客中,将深入探讨如何使用图神经网络(GNNs)来完成节点分类任务。以 Cora 数据集为例,该数据集是一个引用网络,节点代表文档,推断每个文档的类别。同时,使用 Weights & Biases(W&B)来跟踪实验过程和…...
如何在 FastAPI 中使用本地资源自定义 Swagger UI
要自定义 FastAPI 中的 Swagger UI,且使用本地资源来代替 CDN。只是需要稍微修改一下。 修改后的代码: 步骤: 挂载本地静态文件目录:我们将本地的 Swagger UI 资源文件(如 .js, .css, favicon.png 等)放…...
Swift 进阶:Observation 框架中可观察(@Observable)对象的高级操作(上)
概述 在 WWDC 24 中苹果推出了全新的 Observation 框架,借助于它我们可以更加细粒度的监听可观察(@Observable)对象 。同时,SwiftUI 自身也与时偕行开始全面支持 @Observable 对象的“嵌入”。 然而在这里,我们却另辟蹊径来介绍 @Observable 对象另外一些“鲜为人知”的故…...
aws(学习笔记第二十七课) 使用aws API Gateway+lambda体验REST API
aws(学习笔记第二十七课) 使用aws API Gatewaylambda体验REST API 学习内容: 使用aws API Gatewaylambda 1. 使用aws API Gatewaylambda 作成概要 使用api gateway定义REST API,之后再接收到了http request之后,redirect到lambda进行执行。…...
UE学习日志#23 C++笔记#9 编码风格
注:此文章为学习笔记,只记录个人不熟悉或备忘的内容 1 为代码编写文档 1.1 使用注释的原因 1.说明用途的注释 应该注释的信息:输入,输出含义,参数的类型含义,错误条件和处理,预期用途&#x…...
vue2-vue自定义指令
文章目录 vue2-vue自定义指令1. 什么是指令2. 自定义指令2.1 全局注册2.2 局部注册 3. 自定义指令的钩子函数4. 钩子函数的参数4. 用例 vue2-vue自定义指令 1. 什么是指令 在vue中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统我们平时使用的v-…...
[250202] DocumentDB 开源发布:基于 PostgreSQL 的文档数据库新选择 | Jekyll 4.4.0 发布
目录 DocumentDB 开源发布:基于 PostgreSQL 的文档数据库新选择DocumentDB 的使命DocumentDB 的架构 Jekyll 4.4.0 版本发布🆕 新特性与改进 DocumentDB 开源发布:基于 PostgreSQL 的文档数据库新选择 微软近日宣布开源 DocumentDBÿ…...
matplotlib绘制三维曲面图时遇到的问题及解决方法
在科学计算和数据可视化中,三维曲面图是非常有用的工具,可以直观地展示数据的三维分布和关系。Matplotlib是Python中广泛使用的数据可视化库之一,提供了强大的三维绘图功能。然而,在实际使用过程中,用户可能会遇到各种…...
【数据结构】(4) 线性表 List
一、什么是线性表 线性表就是 n 个相同类型元素的有限序列,每一个元素只有一个前驱和后继(除了第一个和最后一个元素)。 数据结构中,常见的线性表有:顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…...
简单React项目从0到1
文章目录 项目搭建基于CRA创建项目调整项目目录结构 使用scss预处理器组件库antd使用配置基础路由配置别名路径路径编译配置VsCode提示配置 基本结构搭建表单校验实现获取登录表单数据封装request工具模块使用Redux管理token安装Redux相关工具包配置Redux 实现登录逻辑token持久…...
IM 即时通讯系统-46-OpenIM 提供了专为开发者设计的开源即时通讯解决方案
IM 开源系列 IM 即时通讯系统-41-开源 野火IM 专注于即时通讯实时音视频技术,提供优质可控的IMRTC能力 IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统 IM 即时通讯系统-43-简单的仿QQ聊天安卓APP IM 即时通讯系统-44-仿QQ即…...
MFC 学习笔记目录
序章 MFC学习笔记专栏开篇语-CSDN博客 下载与安装 VS2010 下载与安装 VS2019...
一文讲解Java中的ArrayList和LinkedList
ArrayList和LinkedList有什么区别? ArrayList 是基于数组实现的,LinkedList 是基于链表实现的。 二者用途有什么不同? 多数情况下,ArrayList更利于查找,LinkedList更利于增删 由于 ArrayList 是基于数组实现的&#…...
【Linux系统】线程:线程的优点 / 缺点 / 超线程技术 / 异常 / 用途
1、线程的优点 创建和删除线程代价较小 创建一个新线程的代价要比创建一个新进程小得多,删除代价也小。这种说法主要基于以下几个方面: (1)资源共享 内存空间:每个进程都有自己独立的内存空间,包括代码段…...
HTML 复习
文章目录 路径问题标题标签段落标签换行标签列表标签<ol> 有序列表<ul> 无序标签标签嵌套 超链接标签多媒体标签<img> 图片标签<audio> 音频标签<video> 视频标签 表格标签<colspan> 跨行<rowspan> 跨列组合使用 表单标签基本表单标…...
网络爬虫学习:借助DeepSeek完善爬虫软件,增加停止任务功能
一、引言 我从24年11月份开始学习网络爬虫应用开发,经过2个来月的努力,终于完成了开发一款网络爬虫软件的学习目标。这几天对本次学习及应用开发进行一下回顾总结。前面已经发布了两篇日志: 网络爬虫学习:应用selenium从搜*狐搜…...
【数据结构】单向链表(真正的零基础)
放弃眼高手低,你真正投入学习,会因为找到一个新方法产生成就感,学习不仅是片面的记单词、学高数......只要是提升自己的过程,探索到了未知,就是学习。 目录 一.链表的理解 二.链表的分类(重点理解…...
8. k8s二进制集群之Kubectl部署
创建kubectl证书请求文件生成admin证书文件复制admin证书到指定目录生成kubeconfig配置文件接下来完成kubectl配置文件的角色绑定【扩展】kubectl命令补全操作继续上一篇文章《k8s二进制集群之Kube ApiServer部署》下面介绍一下k8s中的命令行管理工具kubectl。 通过kubectl可以…...
115,【7】 攻防世界 web fileinclude
进入靶场 试着访问了几个文件,都没得到信息,f12看看源码 还真有 <?php // 检查是否开启了错误显示功能 // ini_get 函数用于获取 PHP 配置选项的值,这里检查 display_errors 选项是否开启 if( !ini_get(display_errors) ) {// 如果错误…...
RabbitMQ 从入门到精通:从工作模式到集群部署实战(二)
接上篇:《RabbitMQ 从入门到精通:从工作模式到集群部署实战(一)》 链接 文章目录 4.安装RabbitMQ Messaging Topology Operator 裸金属环境部署RabbitMQ部署单实例部署集群 4.安装RabbitMQ Messaging Topology Operator 使用 cer…...
【MySQL】MySQL经典面试题深度解析
文章目录 一、MySQL与C的深度结合1.1 为什么C项目需要MySQL?1.2 典型应用场景 二、基础概念面试题精讲2.1 存储引擎对比2.2 索引原理 三、C专项面试题解析3.1 连接池实现3.2 预处理语句3.3 批量操作优化 四、高级应用面试题剖析4.1 事务隔离级别4.2 锁机制详解4.3 查…...
小程序-基础加强
前言 这一节把基础加强讲完 1. 导入需要用到的小程序项目 2. 初步安装和使用vant组件库 这里还可以扫描二维码 其中步骤四没什么用 右键选择最后一个 在开始之前,我们的项目根目录得有package.json 没有的话,我们就初始化一个 但是我们没有npm这个…...
vscode+CMake+Debug实现 及权限不足等诸多问题汇总
环境说明 有空再补充 直接贴两个json tasks.json {"version": "2.0.0","tasks": [{"label": "cmake","type": "shell","command": "cmake","args": ["../"…...
零基础Vue入门6——Vue router
本节重点: 路由定义路由跳转 前面几节学习的都是单页面的功能(都在专栏里面https://blog.csdn.net/zhanggongzichu/category_12883540.html),涉及到项目研发都是有很多页面的,这里就需要用到路由(vue route…...
【疑海破局】一个注解引发的线上事故
【疑海破局】一个注解引发的线上事故 1、问题背景 在不久前一个阳光明媚的上午,我的思绪正在代码中游走、双手正在键盘上飞舞。突然,公司内部通讯工具上,我被拉进了一个临时工作群,只见群中产品、运营、运维、测试等关键人员全部严阵以待,我就知道大的可能要来了。果不其…...
C语言:函数栈帧的创建和销毁
目录 1.什么是函数栈帧2.理解函数栈帧能解决什么问题3.函数栈帧的创建和销毁的过程解析3.1 什么是栈3.2 认识相关寄存器和汇编指令3.3 解析函数栈帧的创建和销毁过程3.3.1 准备环境3.3.2 函数的调用堆栈3.3.3 转到反汇编3.3.4 函数栈帧的创建和销毁 1.什么是函数栈帧 在写C语言…...
IDEA启动项目慢问题处理
IDEA启动项目慢问题处理 一、问题现象二、问题排查排查点1:idea内存排查点2:应用内存排查点3:shorten command lineclasspath filejar manifest 排查点4:jstack排查 三、问题定位 一、问题现象 多模块工程,启动模块为…...