当前位置:首页 > 技术分析 > 正文内容

基于 Vue 3 + TypeScript 的企业级中台系统实践

ruisui883个月前 (02-03)技术分析14

前言

本文介绍一个基于 Vue 3 + TypeScript 的企业级中台系统开发实践。该系统主要解决配送管理、订单处理、库存管理等核心业务场景,重点突破了传统 ERP 系统在性能、用户体验等方面的技术瓶颈。

系统架构

1. 目录结构

src/
├── api/                # API 接口定义
├── assets/            # 静态资源
├── components/        # 公共组件
├── composables/       # 组合式函数
├── directives/        # 自定义指令
├── hooks/             # 业务钩子
├── layouts/           # 布局组件
├── router/            # 路由配置
├── stores/            # 状态管理
├── styles/            # 全局样式
├── types/             # TypeScript 类型定义
├── utils/             # 工具函数
└── views/             # 页面组件

2. 技术选型

  • Vue 3: 采用组合式 API,解决了业务逻辑复用和代码组织问题
  • TypeScript: 在开发阶段通过静态类型检查发现潜在问题
  • Pinia: 基于组合式 API 的状态管理方案,支持 TypeScript
  • Element Plus: 企业级 UI 组件库,提供完整的类型定义
  • VXE Table: 高性能表格组件,解决大数据渲染问题

核心功能实现

1. 权限控制系统

1.1 动态路由实现

// router/permission.ts
import { Router } from 'vue-router'
import { useUserStore } from '@/stores/user'

export function setupPermissionGuard(router: Router) {
    router.beforeEach(async (to, from, next) => {
        const userStore = useUserStore()

        // 检查用户是否已登录
        if (!userStore.isLoggedIn && to.path !== '/login') {
            next('/login')
            return
        }

        // 动态添加路由
        if (!userStore.hasLoadedPermissions) {
            try {
                // 获取用户权限
                const permissions = await userStore.loadUserPermissions()
                // 生成动态路由
                const routes = generateRoutesFromPermissions(permissions)
                // 添加路由
                routes.forEach((route) => router.addRoute(route))

                next({ ...to, replace: true })
                return
            } catch (error) {
                next('/login')
                return
            }
        }

        next()
    })
}

1.2 权限指令实现

// directives/permission.ts
import { DirectiveBinding } from 'vue'
import { useUserStore } from '@/stores/user'

export const permission = {
    mounted(el: HTMLElement, binding: DirectiveBinding) {
        const { value } = binding
        const userStore = useUserStore()

        if (value && !userStore.hasPermission(value)) {
            el.parentNode?.removeChild(el)
        }
    }
}

// 使用示例
<el-button v-permission="'system:user:add'">添加用户</el-button>

2. 高级表单设计

2.1 动态表单验证

// composables/useFormValidation.ts
import { ref } from 'vue'
import type { FormInstance } from 'element-plus'

export function useFormValidation() {
    const formRef = ref<FormInstance>()

    // 自定义验证规则
    const validateField = async (rule: any, value: any, callback: any) => {
        if (!value) {
            callback(new Error('必填项不能为空'))
            return
        }

        try {
            // 远程验证
            const isValid = await validateWithServer(value)
            if (!isValid) {
                callback(new Error('验证失败'))
                return
            }
            callback()
        } catch (error) {
            callback(new Error('验证出错'))
        }
    }

    // 表单验证
    const validateForm = async () => {
        if (!formRef.value) return false

        try {
            await formRef.value.validate()
            return true
        } catch (error) {
            return false
        }
    }

    return {
        formRef,
        validateField,
        validateForm,
    }
}

2.2 表单联动控制

// composables/useFormControl.ts
import { ref, watch } from 'vue'

export function useFormControl() {
    const formData = ref({
        type: '',
        subType: '',
        details: [],
    })

    // 监听表单字段变化
    watch(
        () => formData.value.type,
        async (newType) => {
            if (newType) {
                // 重置关联字段
                formData.value.subType = ''
                formData.value.details = []

                // 加载关联数据
                const subTypes = await loadSubTypes(newType)
                // 更新选项
                updateSubTypeOptions(subTypes)
            }
        }
    )

    return {
        formData,
    }
}

3. 数据处理优化

3.1 大数据渲染优化

// composables/useVirtualList.ts
import { ref, computed } from 'vue'

export function useVirtualList(list: any[], itemHeight: number) {
    const containerHeight = ref(0)
    const scrollTop = ref(0)

    // 计算可视区域数据
    const visibleData = computed(() => {
        const start = Math.floor(scrollTop.value / itemHeight)
        const visibleCount = Math.ceil(containerHeight.value / itemHeight)

        return list.slice(start, start + visibleCount + 1)
    })

    // 计算总高度和偏移量
    const wrapperStyle = computed(() => ({
        height: `${list.length * itemHeight}px`,
        transform: `translate3d(0, ${Math.floor(scrollTop.value / itemHeight) * itemHeight}px, 0)`,
    }))

    return {
        visibleData,
        wrapperStyle,
        containerHeight,
        scrollTop,
    }
}

3.2 数据缓存策略

// utils/cache.ts
export class DataCache<T> {
    private cache: Map<
        string,
        {
            data: T
            timestamp: number
            expires: number
        }
    > = new Map()

    constructor(private defaultExpires: number = 5 * 60 * 1000) {}

    set(key: string, data: T, expires?: number) {
        this.cache.set(key, {
            data,
            timestamp: Date.now(),
            expires: expires ?? this.defaultExpires,
        })
    }

    get(key: string): T | null {
        const item = this.cache.get(key)

        if (!item) return null

        if (Date.now() - item.timestamp > item.expires) {
            this.cache.delete(key)
            return null
        }

        return item.data
    }
}

// 使用示例
const dataCache = new DataCache()

async function fetchData(params: any) {
    const cacheKey = JSON.stringify(params)
    const cachedData = dataCache.get(cacheKey)

    if (cachedData) return cachedData

    const data = await api.getData(params)
    dataCache.set(cacheKey, data)
    return data
}

4. 性能优化实践

4.1 组件懒加载

// router/index.ts
const routes = [
    {
        path: '/delivery',
        component: () => import('@/views/delivery/index.vue'),
        children: [
            {
                path: 'wizard',
                component: () => import('@/views/delivery/DeliveryWizard.vue'),
                // 预加载
                props: (route) => ({
                    preloadData: () => import('@/api/delivery').then((m) => m.preloadWizardData()),
                }),
            },
        ],
    },
]

4.2 图片懒加载

// directives/lazyload.ts
export const lazyload = {
    mounted(el: HTMLImageElement, binding: DirectiveBinding) {
        const observer = new IntersectionObserver(
            (entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        el.src = binding.value
                        observer.unobserve(el)
                    }
                })
            },
            {
                rootMargin: '50px'
            }
        )

        observer.observe(el)
    }
}

// 使用示例
<img v-lazyload="imageUrl" alt="lazy image">

4.3 防抖与节流

// utils/performance.ts
export function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
    let timer: NodeJS.Timeout | null = null

    return function (this: any, ...args: Parameters<T>) {
        if (timer) clearTimeout(timer)

        timer = setTimeout(() => {
            fn.apply(this, args)
            timer = null
        }, delay)
    }
}

export function throttle<T extends (...args: any[]) => any>(fn: T, limit: number): (...args: Parameters<T>) => void {
    let inThrottle = false

    return function (this: any, ...args: Parameters<T>) {
        if (!inThrottle) {
            fn.apply(this, args)
            inThrottle = true
            setTimeout(() => {
                inThrottle = false
            }, limit)
        }
    }
}

// 使用示例
const handleScroll = throttle(() => {
    // 处理滚动事件
}, 100)

5. 错误处理机制

5.1 全局错误处理

// utils/error.ts
import { App } from 'vue'
import { message } from '@/utils/message'

export function setupErrorHandle(app: App) {
    app.config.errorHandler = (err, vm, info) => {
        console.error('Vue Error:', err)
        console.error('Error Info:', info)

        // 错误上报
        reportError({
            error: err,
            info,
            location: window.location.href,
            timestamp: new Date().toISOString(),
        })

        // 用户提示
        message.error('系统错误,请稍后重试')
    }

    window.addEventListener('unhandledrejection', (event) => {
        console.error('Unhandled Promise Rejection:', event.reason)

        // 错误上报
        reportError({
            error: event.reason,
            type: 'unhandledrejection',
            location: window.location.href,
            timestamp: new Date().toISOString(),
        })

        event.preventDefault()
    })
}

5.2 API 错误处理

// utils/http.ts
import axios from 'axios'
import { message } from '@/utils/message'

const instance = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL,
    timeout: 10000,
})

// 响应拦截器
instance.interceptors.response.use(
    (response) => {
        const { code, data, msg } = response.data

        // 处理业务错误
        if (code !== 0) {
            message.error(msg || '请求失败')
            return Promise.reject(new Error(msg))
        }

        return data
    },
    (error) => {
        // 处理网络错误
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    // 处理未授权
                    handleUnauthorized()
                    break
                case 403:
                    // 处理权限不足
                    handleForbidden()
                    break
                case 404:
                    // 处理资源不存在
                    handleNotFound()
                    break
                default:
                    // 处理其他错误
                    message.error('服务器错误,请稍后重试')
            }
        } else if (error.request) {
            // 处理请求超时
            message.error('网络请求超时,请检查网络连接')
        } else {
            // 处理其他错误
            message.error('请求失败,请稍后重试')
        }

        return Promise.reject(error)
    }
)
// utils/http.ts
import axios from 'axios'
import { message } from '@/utils/message'

const instance = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL,
    timeout: 10000,
})

// 响应拦截器
instance.interceptors.response.use(
    (response) => {
        const { code, data, msg } = response.data

        // 处理业务错误
        if (code !== 0) {
            message.error(msg || '请求失败')
            return Promise.reject(new Error(msg))
        }

        return data
    },
    (error) => {
        // 处理网络错误
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    // 处理未授权
                    handleUnauthorized()
                    break
                case 403:
                    // 处理权限不足
                    handleForbidden()
                    break
                case 404:
                    // 处理资源不存在
                    handleNotFound()
                    break
                default:
                    // 处理其他错误
                    message.error('服务器错误,请稍后重试')
            }
        } else if (error.request) {
            // 处理请求超时
            message.error('网络请求超时,请检查网络连接')
        } else {
            // 处理其他错误
            message.error('请求失败,请稍后重试')
        }

        return Promise.reject(error)
    }
)

项目难点解决

1. 签名验证机制

实现了基于时间戳的签名验证机制,确保请求的安全性:

// utils/sign.ts
function sortAndSerializeParams(params: Record<string, any>): string {
    // 使用 Object.keys 进行字典排序
    const sortedKeys = Object.keys(params).sort()
    const parts: string[] = []

    for (const key of sortedKeys) {
        if (params[key] !== undefined) {
            parts.push(`${key}=${serializeValue(params[key])}`)
        }
    }

    return parts.join('&')
}

function serializeValue(value: any): string {
    if (value === null || value === undefined) {
        return ''
    }

    if (typeof value === 'object') {
        if (Array.isArray(value)) {
            // 处理数组
            return value.map((item) => serializeValue(item)).join(',')
        } else {
            // 处理对象
            return sortAndSerializeParams(value)
        }
    }

    return value.toString()
}

function generateSign(params: Record<string, any>, timestamp: number): string {
    // 添加时间戳
    const paramsWithTimestamp = {
        ...params,
        timestamp: timestamp.toString(),
    }

    // 排序并序列化参数
    const sortedParams = sortAndSerializeParams(paramsWithTimestamp)
    console.log('排序之后的签名参数:', sortedParams)

    // 生成 MD5 签名
    return md5(sortedParams)
}

2. 大数据量处理

在处理大量数据时采用以下策略:

// composables/useTableData.ts
import { ref, computed } from 'vue'
import type { TableColumn } from 'vxe-table'

export function useTableData(
    options = {
        pageSize: 20,
        maxCachePages: 5,
    }
) {
    const currentPage = ref(1)
    const tableData = ref<any[]>([])
    const loading = ref(false)
    const total = ref(0)

    // 数据缓存
    const dataCache = new Map<number, any[]>()

    // 加载数据
    const loadData = async (page: number) => {
        // 检查缓存
        if (dataCache.has(page)) {
            tableData.value = dataCache.get(page)!
            return
        }

        loading.value = true
        try {
            const { list, total: totalCount } = await fetchTableData({
                pageNo: page,
                pageSize: options.pageSize,
            })

            // 更新数据
            tableData.value = list
            total.value = totalCount

            // 缓存数据
            dataCache.set(page, list)

            // 清理过期缓存
            if (dataCache.size > options.maxCachePages) {
                const oldestPage = Math.min(...dataCache.keys())
                dataCache.delete(oldestPage)
            }
        } finally {
            loading.value = false
        }
    }

    // 处理页码变化
    const handlePageChange = (page: number) => {
        currentPage.value = page
        loadData(page)
    }

    // 表格列配置
    const columns = computed<TableColumn[]>(() => [
        { type: 'seq', width: 60, title: '序号' },
        // ... 其他列配置
    ])

    return {
        currentPage,
        tableData,
        loading,
        total,
        columns,
        handlePageChange,
    }
}

3. 文件导出优化

// utils/export.ts
import { message } from '@/utils/message'

export async function handleExport(exportFn: () => Promise<Blob>, fileName: string) {
    try {
        message.loading('正在导出数据...')

        // 调用导出接口
        const blob = await exportFn()

        // 创建下载链接
        const link = document.createElement('a')
        link.href = window.URL.createObjectURL(blob)
        link.download = `${fileName}_${dayjs().format('YYYY-MM-DD')}.xlsx`

        // 触发下载
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(link.href)

        message.success('导出成功')
    } catch (error) {
        console.error('导出失败:', error)
        message.error('导出失败,请重试')
    }
}

// 使用示例
const handleExportData = () => {
    handleExport(() => documentApi.exportDeliveryDocuments(exportParams), '配送单据')
}

项目亮点

1.完整的 TypeScript 支持

    • 自定义类型声明
    • 类型推导和检查
    • 接口定义和复用

2.高性能的数据处理

    • 虚拟滚动
    • 数据缓存
    • 懒加载

3.强大的组件系统

    • 组件复用
    • 插槽设计
    • 组件通信

4.完善的工程化实践

    • 代码规范
    • 自动化测试
    • CI/CD 流程

5.安全性考虑

    • 请求签名
    • XSS 防护
    • CSRF 防护

1. VXE Table 高性能表格解决方案

1.1 基础配置与性能优化

// composables/useVxeTable.ts
import { ref, computed } from 'vue'
import type { VxeGridInstance, VxeGridProps } from 'vxe-table'

export function useVxeTable(
    options = {
        height: 'auto',
        stripe: true,
        border: true,
        align: 'center',
        columnConfig: {
            resizable: true, // 可调整列宽
            isCurrent: true, // 当前列高亮
            isHover: true, // 鼠标悬停高亮
        },
        rowConfig: {
            isCurrent: true, // 当前行高亮
            isHover: true, // 鼠标悬停高亮
        },
    }
) {
    const gridRef = ref<VxeGridInstance>()

    // 表格配置
    const gridOptions = computed<VxeGridProps>(() => ({
        ...options,
        // 虚拟滚动配置
        scrollX: {
            enabled: true, // 启用横向虚拟滚动
            gt: 60, // 大于 60 条时启用
        },
        scrollY: {
            enabled: true, // 启用纵向虚拟滚动
            gt: 100, // 大于 100 条时启用
            scrollToTopOnChange: true, // 数据变化后滚动到顶部
        },
        // 缓存配置
        cacheable: true, // 启用缓存
        checkboxConfig: {
            reserve: true, // 保留选中状态
        },
    }))

    return {
        gridRef,
        gridOptions,
    }
}

1.2 自定义列渲染

<template>
    <vxe-grid ref="gridRef" v-bind="gridOptions">
        <!-- 自定义列模板 -->
        <template #operation="{ row }">
            <vxe-button @click="handleEdit(row)">编辑</vxe-button>
            <vxe-button @click="handleDelete(row)">删除</vxe-button>
        </template>

        <!-- 自定义编辑模板 -->
        <template #edit_name="{ row }">
            <vxe-input v-model="row.name" @change="handleNameChange(row)" />
        </template>

        <!-- 自定义格式化 -->
        <template #format_date="{ row }">
            {{ formatDate(row.date) }}
        </template>
    </vxe-grid>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useVxeTable } from '@/composables/useVxeTable'

const { gridRef, gridOptions } = useVxeTable()

// 列配置
const columns = [
    { type: 'checkbox', width: 60 },
    { type: 'seq', width: 60, title: '序号' },
    { field: 'name', title: '名称', editRender: { name: 'edit_name' } },
    { field: 'date', title: '日期', slots: { default: 'format_date' } },
    { title: '操作', slots: { default: 'operation' }, fixed: 'right', width: 150 },
]
</script>

1.3 高级功能实现

// 表格工具栏
const toolbarConfig = {
    // 工具栏配置
    buttons: [
        { code: 'insert', name: '新增' },
        { code: 'delete', name: '删除' },
        { code: 'export', name: '导出' },
    ],
    // 刷新按钮
    refresh: true,
    // 自定义列显示隐藏
    custom: true,
    // 表格密度
    zoom: true,
}

// 分页配置
const pagerConfig = {
    // 分页大小
    pageSize: 20,
    // 可选分页大小
    pageSizes: [20, 50, 100, 200],
    // 总数
    total: 0,
    // 布局
    layouts: ['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes', 'Total'],
}

// 编辑配置
const editConfig = {
    // 触发方式
    trigger: 'click',
    // 编辑模式
    mode: 'cell',
    // 显示状态图标
    showStatus: true,
}

// 排序配置
const sortConfig = {
    // 触发方式
    trigger: 'cell',
    // 远程排序
    remote: true,
    // 默认排序
    defaultSort: {
        field: 'date',
        order: 'desc',
    },
}

// 筛选配置
const filterConfig = {
    // 远程筛选
    remote: true,
    // 显示条件
    showCondition: true,
    // 显示重置按钮
    showResetButton: true,
}

1.4 表格事件处理

// 处理表格事件
const handleTableEvents = {
    // 选择事件
    checkboxChange({ records }) {
        console.log('选中记录:', records)
    },

    // 排序事件
    sortChange({ property, order }) {
        loadTableData({
            ...queryParams,
            sortField: property,
            sortOrder: order,
        })
    },

    // 筛选事件
    filterChange({ filters }) {
        loadTableData({
            ...queryParams,
            ...filters,
        })
    },

    // 分页事件
    pageChange({ currentPage, pageSize }) {
        loadTableData({
            ...queryParams,
            pageNo: currentPage,
            pageSize,
        })
    },
}

// 表格数据加载
const loadTableData = async (params: any) => {
    try {
        gridRef.value?.setLoading(true)
        const { list, total } = await fetchTableData(params)
        tableData.value = list
        pagerConfig.total = total
    } finally {
        gridRef.value?.setLoading(false)
    }
}

1.5 表格导出功能

// 导出配置
const exportConfig = {
    // 文件类型
    type: 'xlsx',
    // 文件名
    filename: '导出数据',
    // 导出模式
    mode: 'current',
    // 自定义数据
    data: null,
    // 自定义列
    columns: null,
    // 样式
    style: {
        head: {
            font: { color: '#000000', bold: true },
            alignment: { horizontal: 'center' },
            fill: { type: 'pattern', pattern: 'solid', fgColor: { rgb: 'f6f6f6' } },
        },
    },
}

// 处理导出
const handleExport = async () => {
    const { options } = gridRef.value!

    // 获取表格数据
    const exportData = options.data

    // 获取选中列
    const exportColumns = options.columns.filter((column) => !column.type && column.visible)

    try {
        await gridRef.value?.exportData({
            ...exportConfig,
            data: exportData,
            columns: exportColumns,
            filename: `${exportConfig.filename}_${dayjs().format('YYYY-MM-DD')}`,
        })
        message.success('导出成功')
    } catch (error) {
        message.error('导出失败')
    }
}

1.6 表格打印功能

// 打印配置
const printConfig = {
    // 打印样式
    style: `
        .vxe-table--print .vxe-table--body td {
            color: #666;
            border-color: #ddd;
        }
    `,
    // 打印前处理
    beforePrintMethod({ content }) {
        return content
    },
}

// 处理打印
const handlePrint = () => {
    const { options } = gridRef.value!
    gridRef.value?.print(printConfig)
}

这些功能的实际使用示例:

<template>
    <vxe-grid
        ref="gridRef"
        v-bind="gridOptions"
        :columns="columns"
        :data="tableData"
        :toolbar-config="toolbarConfig"
        :pager-config="pagerConfig"
        :edit-config="editConfig"
        :sort-config="sortConfig"
        :filter-config="filterConfig"
        @checkbox-change="handleTableEvents.checkboxChange"
        @sort-change="handleTableEvents.sortChange"
        @filter-change="handleTableEvents.filterChange"
        @page-change="handleTableEvents.pageChange">
        <!-- 自定义列模板 -->
        <template #toolbar_buttons>
            <vxe-button @click="handleExport">导出</vxe-button>
            <vxe-button @click="handlePrint">打印</vxe-button>
        </template>
    </vxe-grid>
</template>

通过以上配置和实现,我们的表格组件具备了以下特性:

1.性能优化

    • 虚拟滚动
    • 数据缓存
    • 选中状态保留

2.功能完善

    • 自定义列渲染
    • 行/列编辑
    • 排序/筛选
    • 导出/打印

3.交互优化

    • 行/列高亮
    • 列宽调整
    • 列显示切换
    • 表格密度调整

4.数据处理

    • 远程排序
    • 远程筛选
    • 分页加载
    • 数据导出

这些功能的组合使得我们的表格组件能够满足各种复杂的业务场景需求,同时保持良好的性能和用户体验。

项目收获

  1. 深入理解了 Vue 3 的组合式 API 和 TypeScript 的类型系统
  2. 掌握了企业级前端项目的架构设计
  3. 积累了大量的性能优化经验
  4. 提升了代码质量和开发效率
  5. 学习了现代化的前端工程化实践

未来展望

1.性能优化

    • 引入 Web Worker 处理复杂计算
    • 使用 Service Worker 实现离线缓存
    • 优化首屏加载速度

2.功能增强

    • 引入微前端架构
    • 支持多主题切换
    • 增加数据可视化功能

3.工程化提升

    • 完善单元测试覆盖率
    • 引入 E2E 测试
    • 优化构建流程

总结

通过这个项目,我们不仅实现了业务需求,还在技术层面有了很大的提升。项目中的很多实践和解决方案都具有普遍的参考价值,希望能对大家有所帮助。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/645.html

标签: node xlsx
分享给朋友:

“基于 Vue 3 + TypeScript 的企业级中台系统实践” 的相关文章

财务未来:数字化全流程自动化报销,让预算管理更轻松

财务管理是企业经营的重中之重,费控管理则是财务管理的核心之一。上至管理层下至普通员工,面对繁琐的费控管理却是“家家有本难念的经”。举个常见的例子:在传统企业的费用管理模式下,员工在进行商务活动时,通常需要自行垫资,之后再经过一系列繁杂的报销审批流程,才能最终实现打款。对于普通员工来说,申报流程繁琐,...

7种超轻量级的Linux发行版,能够帮助你找到适合自己的操作系统

Linux是一种非常受欢迎的开源操作系统,而且有许多版本可以选择。有时候,你需要一种超轻量级的Linux发行版,它可以在资源有限的设备上运行,并且能够快速启动。本文将介绍7种超轻量级的Linux发行版,希望能够帮助你找到适合自己的操作系统。1. Tiny Core LinuxTiny Core Li...

适合旧电脑2022年值得推荐的 10 款轻量级 Linux 发行版

推荐 10 款轻量级Linux 发行版,它们是 2022 年的轻量级、对旧硬件友好的 Linux 发行版。1、Linux LiteLinux Lite 是一款基于#ubuntu# 和 Debian 的、正在不断开发和完善的 Linux 发行版,极好看的 Xfce 桌面,并基于 Ubuntu,采用了...

发行版Vanilla OS 2发布稳定版:彻底重写、改变使用Linux的方式

Vanilla OS 是去年崭露头角的 Linux 发行版,最初的 1.0 版本基于 Ubuntu 构建 —— 亮点之一是系统核心“不可变”。后来改用 Debian 测试分支 (Debian Sid),免费且开源,默认桌面环境是 GNOME。什么是不可变 Linux 发行版?不可变发行版确保操作系统...

Gemini应用在Android上广泛推出2.0闪电模式切换器

#头条精品计划# 快速导读谷歌(搜索)应用的测试频道在安卓设备的双子应用中推出了2.0闪电实验功能,现已向稳定用户开放。双子应用通过谷歌应用运行,目前推出的15.50版本中,用户可通过模型选择器体验不同选项,包括1.5专业版、1.5闪电版和2.0闪电实验版。2.0闪电实验模型提供了更快的响应速度和优...

12种JavaScript中最常用的数组操作整理汇总

数组是最常见的数据结构之一,我们需要绝对自信地使用它。在这里,我将列出 JavaScript 中最重要的几个数组常用操作片段,包括数组长度、替换元素、去重以及许多其他内容。1、数组长度大多数人都知道可以像这样得到数组的长度:const arr = [1, 2, 3]; console.log(a...