深入拆解Taro框架多端适配原理

深入解析Taro框架底层实现原理:从架构到多端适配

Taro 作为一款经典的多端统一开发框架,核心目标是让开发者编写一套代码,能够无缝运行在微信小程序、支付宝小程序、H5、React Native 等多个平台。本文将从架构设计、核心原理、性能优化、插件系统、多端适配五个维度,层层拆解 Taro 的底层实现逻辑,结合流程图、代码片段和实际案例,让你彻底理解 Taro 如何实现"一次编写,多端运行"。

一、架构设计层面:Taro 的多端统一基石

Taro 的架构设计核心是"编译时+运行时"双引擎,辅以跨平台虚拟 DOM 层,构建起多端统一的技术底座。

1.1 多端统一架构设计

Taro 的整体架构分为三层,从下到上依次为:基础层核心层应用层 ,整体架构流程如下:

  • 应用层:开发者编写的业务代码,支持 React/Vue 语法,与纯前端开发体验一致;
  • 核心层:Taro 的核心能力层,编译时负责将通用代码转换为各端可识别的代码,运行时负责提供跨平台的 API 和渲染能力;
  • 基础层:对各端原生能力的封装,屏蔽不同平台的 API 差异,向上提供统一的调用接口。

Taro 架构的核心思想是"统一输入,差异化输出":编译时根据目标平台生成差异化代码,运行时通过适配层抹平各端执行环境差异。

1.2 虚拟DOM层:跨平台渲染的核心

Taro 实现跨平台渲染的关键是抽象了一套与平台无关的虚拟 DOM(VNode),再通过各端的渲染器将 VNode 转换为对应平台的原生节点。

1.2.1 虚拟DOM层的工作流程
1.2.2 核心实现代码(简化版)
javascript 复制代码
// 1. 定义统一的VNode结构(与平台无关)
class VNode {
  constructor(tag, props, children, key) {
    this.tag = tag; // 节点标签:view/div/view(统一抽象)
    this.props = props; // 节点属性
    this.children = children; // 子节点
    this.key = key; // 节点唯一标识
    this.platformTag = null; // 平台专属标签(编译时填充)
  }
}

// 2. 各端渲染器适配
const renderers = {
  // 小程序渲染器:将VNode转换为小程序模板和setData数据
  miniProgram: (vnode) => {
    // 转换标签:Taro统一的view → 小程序原生view
    vnode.platformTag = mapTagToMiniProgram(vnode.tag);
    // 生成小程序模板字符串(编译时输出到wxml)
    const template = generateMiniProgramTemplate(vnode);
    // 生成setData需要的数据(运行时更新)
    const data = generateMiniProgramData(vnode);
    return { template, data };
  },
  // H5渲染器:转换为DOM节点
  h5: (vnode) => {
    vnode.platformTag = mapTagToH5(vnode.tag); // view → div
    const el = document.createElement(vnode.platformTag);
    // 设置属性、挂载子节点...
    return el;
  }
};

// 3. 统一渲染入口
function render(vnode, platform) {
  return renderers[platform](vnode);
}

1.3 编译时与运行时的分工协作

Taro 的核心能力由"编译时"和"运行时"共同完成,二者分工明确又紧密协作:

阶段 核心职责 输出产物
编译时 1. JSX/模板 → 统一VNode描述; 2. 语法转换(ES6+/TS → 各端兼容语法); 3. 多端差异化代码裁剪; 4. 生成平台专属模板(wxml/html) 各端可执行的代码+模板文件
运行时 1. 虚拟DOM → 原生节点渲染; 2. 跨平台API调用适配; 3. 状态管理适配; 4. 事件系统封装 运行时适配层+API调用桥接层
协作流程

二、核心实现原理:从代码到多端运行

2.1 JSX到小程序模板的转换过程

小程序不支持JSX和虚拟DOM,Taro 通过编译时转换,将JSX转换为小程序的"模板+数据"模式,这是 Taro 适配小程序的核心环节。

2.1.1 完整转换流程
2.1.2 转换示例与核心代码

输入的JSX代码

jsx 复制代码
function Index() {
  const [count, setCount] = useState(0);
  return (
    <view className="container" key="index">
      <text>{count}</text>
      <button onClick={() => setCount(count + 1)}>点击+1</button>
    </view>
  );
}

编译时转换核心逻辑(简化版)

javascript 复制代码
// 1. 解析JSX生成AST
const jsxAST = parseJSX(`<view className="container"><text>{count}</text></view>`);

// 2. 转换为小程序模板AST
const miniProgramAST = transformJSXASTToMiniProgramAST(jsxAST, {
  // 标签映射:view → view,text → text
  tagMap: { view: 'view', text: 'text', button: 'button' },
  // 属性映射:className → class
  propMap: { className: 'class', onClick: 'bindtap' }
});

// 3. 生成wxml模板
const wxml = generateWXML(miniProgramAST);
// 输出wxml:<view class="container" key="index"><text>{{count}}</text><button bindtap="__onClick">点击+1</button></view>

// 4. 生成小程序Page的js代码
const pageJS = generatePageJS({
  // 数据映射:React状态 → 小程序data
  data: { count: 0 },
  // 事件映射:React onClick → 小程序bindtap
  methods: {
    __onClick() {
      this.setData({ count: this.data.count + 1 });
    }
  }
});

最终输出的小程序代码

  • index.wxml<view class="container"><text>{``{count}}</text><button bindtap="__onClick">点击+1</button></view>
  • index.js:包含data__onClick方法的小程序Page对象。

2.2 Taro组件系统实现原理

Taro 组件系统兼容 React/Vue 组件语法,底层通过"编译时标准化+运行时桥接"实现多端统一。

2.2.1 组件系统核心架构
2.2.2 核心实现(React组件适配小程序)
javascript 复制代码
// 1. 编译时:将React组件转换为小程序自定义组件配置
function transformReactComponentToMiniProgram(component) {
  // 提取组件属性定义
  const properties = extractProps(component.props);
  // 提取生命周期:React的componentDidMount → 小程序的ready
  const lifetimes = mapLifecycles(component.lifecycles);
  // 提取事件处理函数
  const methods = extractMethods(component.methods);
  // 生成小程序自定义组件配置(json)
  const jsonConfig = {
    component: true,
    properties,
    methods
  };
  // 生成小程序组件js代码
  const jsCode = generateMiniProgramComponentJS({
    lifetimes,
    methods,
    data: component.state // React state → 小程序data
  });
  return { jsonConfig, jsCode };
}

// 2. 运行时:组件通信适配
class TaroComponent {
  constructor(props) {
    this.props = props;
    this.state = {};
    // 小程序端挂载到Page/Component实例
    if (isMiniProgram) {
      this.$miniInstance = getCurrentInstance();
    }
  }

  // 统一的setState方法(适配小程序setData)
  setState(newState) {
    this.state = { ...this.state, ...newState };
    if (isMiniProgram) {
      // React setState → 小程序setData
      this.$miniInstance.setData(newState);
    } else {
      // H5端执行React原生setState
      React.setState(newState);
    }
  }
}

2.3 Redux/Vuex在小程序端的适配方案

小程序原生不支持 Redux/Vuex,Taro 通过"状态隔离+桥接更新"实现适配。

2.3.1 Redux适配流程
2.3.2 核心实现代码
javascript 复制代码
// 1. Taro Redux适配层(小程序端)
class TaroReduxBridge {
  constructor(store, miniInstance) {
    this.store = store;
    this.miniInstance = miniInstance; // 小程序Page/Component实例
    // 监听Store变化
    this.unsubscribe = store.subscribe(() => this.syncToMiniProgramData());
  }

  // 将Redux状态同步到小程序data
  syncToMiniProgramData() {
    const state = this.store.getState();
    // 只更新变化的状态(性能优化)
    const changedData = this.getChangedData(state);
    // 调用小程序setData更新视图
    this.miniInstance.setData(changedData);
  }

  // 包装dispatch:兼容小程序环境
  dispatch(action) {
    // 小程序端处理异步action、中间件等
    return this.store.dispatch(action);
  }

  // 销毁时取消监听
  destroy() {
    this.unsubscribe();
  }
}

// 2. 页面中使用Redux(Taro封装后)
Page({
  onLoad() {
    // 创建Redux桥接实例
    this.reduxBridge = new TaroReduxBridge(store, this);
    // 初始化状态
    this.reduxBridge.syncToMiniProgramData();
  },
  onUnload() {
    this.reduxBridge.destroy();
  },
  handleClick() {
    // 触发Redux action
    this.reduxBridge.dispatch({ type: 'INCREMENT' });
  }
});
2.3.3 关键优化点
  • 状态分片:将全局Store按页面/组件分片,避免单个setData传递过大数据;
  • 增量更新:只同步变化的状态,而非全量更新;
  • 批量更新:合并短时间内的多次状态变化,减少setData调用次数。

三、性能优化机制:让多端运行更高效

Taro 针对小程序等平台的性能瓶颈,设计了多层优化机制,核心围绕"减少setData次数、降低渲染开销、优化资源加载"展开。

3.1 渲染性能优化策略

Taro 的渲染优化核心是"减少不必要的渲染",主要通过以下手段实现:

3.1.1 优化策略总览
3.1.2 虚拟DOM Diff优化(小程序端)

小程序没有DOM,Taro 对虚拟DOM Diff的结果进行特殊处理:

  • 同层比较:只对比同一层级的VNode,避免跨层级操作(小程序模板不支持跨层级更新);
  • key精准复用:强制要求列表项设置key,避免小程序重新创建节点;
  • 静态节点提取:将不变化的节点提取为静态模板,避免重复Diff。

3.2 setData的优化实现原理

setData是小程序性能的核心瓶颈(单次调用有数据大小限制,频繁调用会阻塞渲染),Taro 从编译时和运行时两层优化setData:

3.2.1 优化流程
3.2.2 核心实现代码
javascript 复制代码
// Taro setData优化器
class SetDataOptimizer {
  constructor(miniInstance) {
    this.miniInstance = miniInstance; // 小程序实例
    this.updateQueue = {}; // 更新队列
    this.timer = null; // 防抖定时器
    this.MAX_SIZE = 10 * 1024; // 单次setData最大10KB
  }

  // 批量更新方法
  batchSetData(data) {
    // 合并更新数据
    this.updateQueue = { ...this.updateQueue, ...data };
    // 防抖:50ms内多次调用只执行一次
    clearTimeout(this.timer);
    this.timer = setTimeout(() => {
      this.executeSetData();
    }, 50);
  }

  // 执行setData(分块+裁剪)
  executeSetData() {
    const data = this.updateQueue;
    this.updateQueue = {}; // 清空队列

    // 1. 数据裁剪:只保留变化的叶子节点(例如{a:{b:1}} → 只传a.b)
    const trimmedData = this.trimData(data);

    // 2. 计算数据大小
    const dataStr = JSON.stringify(trimmedData);
    if (dataStr.length <= this.MAX_SIZE) {
      // 直接调用setData
      this.miniInstance.setData(trimmedData);
    } else {
      // 分块处理:拆分为多个小于10KB的chunk
      const chunks = this.splitDataIntoChunks(trimmedData);
      // 异步分块执行,避免阻塞
      chunks.forEach((chunk, index) => {
        setTimeout(() => {
          this.miniInstance.setData(chunk);
        }, index * 20);
      });
    }
  }

  // 数据裁剪:只保留变化的路径
  trimData(data) {
    // 对比当前data,只提取变化的字段(简化版)
    const currentData = this.miniInstance.data;
    const trimmed = {};
    Object.keys(data).forEach(key => {
      if (JSON.stringify(data[key]) !== JSON.stringify(currentData[key])) {
        trimmed[key] = data[key];
      }
    });
    return trimmed;
  }

  // 数据分块
  splitDataIntoChunks(data) {
    const chunks = [];
    let currentChunk = {};
    let currentSize = 0;
    Object.keys(data).forEach(key => {
      const value = data[key];
      const itemSize = JSON.stringify({ [key]: value }).length;
      if (currentSize + itemSize > this.MAX_SIZE) {
        chunks.push(currentChunk);
        currentChunk = {};
        currentSize = 0;
      }
      currentChunk[key] = value;
      currentSize += itemSize;
    });
    chunks.push(currentChunk);
    return chunks;
  }
}

3.3 分包加载和按需加载的实现方式

小程序有主包大小限制(2MB),Taro 实现了分包加载和按需加载,核心是"编译时分包+运行时动态加载"。

3.3.1 分包加载实现流程
3.3.2 核心实现(编译时)
javascript 复制代码
// Taro 分包编译插件(简化版)
class TaroSubPackagePlugin {
  apply(compiler) {
    // 监听编译钩子
    compiler.hooks.emit.tap('TaroSubPackagePlugin', (compilation) => {
      // 1. 读取分包配置(src/app.config.js)
      const subPackages = getSubPackageConfig();
      // 2. 拆分代码:根据配置将页面/组件分配到不同分包
      const chunks = splitChunksBySubPackage(compilation.chunks, subPackages);
      // 3. 生成小程序app.json的分包配置
      const appJson = generateAppJsonWithSubPackages(subPackages);
      // 4. 输出分包代码到对应目录
      outputSubPackageFiles(chunks, subPackages);
    });
  }
}
3.3.3 按需加载实现(运行时)
javascript 复制代码
// Taro 按需加载组件(小程序端)
function lazyLoadComponent(componentPath) {
  return new Promise((resolve) => {
    // 小程序端使用requireAsync动态加载组件
    if (isMiniProgram) {
      requireAsync(componentPath).then((component) => {
        // 注册为小程序自定义组件
        registerMiniProgramComponent(component);
        resolve(component);
      });
    } else {
      // H5端使用import()动态导入
      import(componentPath).then(resolve);
    }
  });
}

// 页面中使用
Page({
  onShow() {
    // 按需加载非首屏组件
    lazyLoadComponent('./components/HeavyComponent').then((Component) => {
      this.setData({ heavyComponent: Component });
    });
  }
});

四、插件系统:Taro 的可扩展能力核心

Taro 插件系统基于 Tapable 实现,允许开发者介入编译流程,扩展框架能力(如自定义代码转换、多端适配、资源处理等)。

4.1 插件系统工作原理

Taro 插件系统的核心是"钩子(Hook)"机制:框架在编译、构建的关键节点暴露钩子,插件通过注册钩子回调,介入构建流程。

4.1.1 核心架构
4.1.2 核心实现代码
javascript 复制代码
// 1. 基于Tapable创建插件管理器
const { SyncHook, AsyncSeriesHook } = require('tapable');
class TaroPluginManager {
  constructor() {
    // 定义编译流程的钩子
    this.hooks = {
      init: new SyncHook(['config']), // 初始化钩子
      compile: new AsyncSeriesHook(['compiler']), // 编译钩子(异步)
      transformCode: new SyncHook(['code', 'options']), // 代码转换钩子
      emit: new AsyncSeriesHook(['compilation']), // 输出钩子
      done: new SyncHook(['stats']) // 完成钩子
    };
  }

  // 注册插件
  register(plugin) {
    plugin.apply(this);
  }

  // 触发钩子
  callHook(hookName, ...args) {
    return this.hooks[hookName].call(...args);
  }
}

// 2. 插件编写规范
class TaroPlugin {
  constructor(options = {}) {
    this.options = options;
  }

  apply(pluginManager) {
    // 注册钩子回调
    pluginManager.hooks.transformCode.tap('MyPlugin', (code, options) => {
      // 自定义代码转换逻辑
      return this.transform(code, options);
    });
  }

  transform(code, options) {
    // 例如:将自定义语法转换为标准语法
    return code.replace(/custom-syntax/g, 'standard-syntax');
  }
}

// 3. 使用插件
const pluginManager = new TaroPluginManager();
// 注册插件
pluginManager.register(new TaroPlugin({ /* 插件配置 */ }));
// 编译流程中触发钩子
pluginManager.callHook('transformCode', code, options);

4.2 插件如何介入编译流程

Taro 插件通过以下方式介入编译流程:

  1. 注册钩子 :插件在apply方法中注册框架暴露的钩子;
  2. 修改参数:在钩子回调中修改编译参数(如配置、代码、AST);
  3. 扩展能力:在钩子中添加自定义处理逻辑(如资源压缩、代码注入);
  4. 异步处理:异步钩子支持Promise,可处理耗时操作(如网络请求、文件读写)。

4.3 常用插件实现机制举例

4.3.1 Taro UI插件(组件按需引入)
javascript 复制代码
class TaroUIPlugin {
  apply(pluginManager) {
    // 监听代码转换钩子
    pluginManager.hooks.transformCode.tap('TaroUIPlugin', (code, options) => {
      // 匹配import { Button } from 'taro-ui'
      const regex = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]taro-ui['"]/g;
      return code.replace(regex, (match, components) => {
        // 将按需引入的组件转换为直接引入具体文件
        return components.split(',').map(comp => {
          const compName = comp.trim();
          return `import ${compName} from 'taro-ui/lib/components/${compName.toLowerCase()}'`;
        }).join('\n');
      });
    });
  }
}
4.3.2 Taro 压缩插件
javascript 复制代码
const Terser = require('terser');
class TaroCompressPlugin {
  apply(pluginManager) {
    // 监听输出前钩子(异步)
    pluginManager.hooks.emit.tapAsync('TaroCompressPlugin', async (compilation, callback) => {
      // 遍历所有输出文件
      for (const filename of compilation.files) {
        if (filename.endsWith('.js')) {
          // 获取文件内容
          const code = compilation.assets[filename].source();
          // 压缩代码
          const minified = await Terser.minify(code);
          // 替换为压缩后的代码
          compilation.assets[filename] = {
            source: () => minified.code,
            size: () => minified.code.length
          };
        }
      }
      callback();
    });
  }
}

五、多端适配:抹平各平台的差异

Taro 多端适配的核心是"统一抽象+差异化处理",通过编译时条件编译、运行时环境判断、API适配层,实现一套代码多端运行。

5.1 不同平台的特有适配方案

Taro 针对主流平台的特有特性,设计了专属适配方案:

平台 核心适配点 实现方式
微信小程序 1. 自定义组件适配; 2. 小程序API封装; 3. 分包加载; 4. 登录/支付 编译时转换为小程序自定义组件; 运行时封装wx.xx API为Taro.xx
支付宝小程序 1. API差异(my.xx vs wx.xx); 2. 样式单位(rpx vs rem) API适配层映射my.xx; 编译时转换样式单位
H5 1. 路由适配; 2. 样式兼容; 3. 本地存储 封装react-router为Taro路由; 样式前缀自动补全; localStorage适配
React Native 1. 原生组件桥接; 2. 手势系统; 3. 导航栏 VNode映射为RN组件; 封装RN手势API; 自定义导航栏组件
5.1.1 API适配层实现代码
javascript 复制代码
// Taro 跨平台API适配层
const apiAdapter = {
  // 统一的API入口
  request: (options) => {
    if (isWeChatMiniProgram) {
      // 微信小程序:wx.request
      return new Promise((resolve, reject) => {
        wx.request({
          ...options,
          success: resolve,
          fail: reject
        });
      });
    } else if (isAlipayMiniProgram) {
      // 支付宝小程序:my.request
      return new Promise((resolve, reject) => {
        my.request({
          ...options,
          success: resolve,
          fail: reject
        });
      });
    } else if (isH5) {
      // H5:fetch
      return fetch(options.url, {
        method: options.method || 'GET',
        body: options.data,
        headers: options.header
      }).then(res => res.json());
    }
  },
  // 其他API:getStorage、navigateTo等...
};

// 挂载到Taro全局对象
Taro.request = apiAdapter.request;

5.2 各端运行时环境的差异处理

Taro 通过"环境检测+差异化逻辑"处理各端运行时差异,核心实现如下:

5.2.1 环境检测
javascript 复制代码
// Taro 运行时环境检测
const env = {
  // 编译时注入环境变量
  platform: process.env.TARO_PLATFORM, // 'weapp'/'alipay'/'h5'/'rn'
  // 运行时验证
  isMiniProgram: false,
  isWeChatMiniProgram: false,
  isAlipayMiniProgram: false,
  isH5: false,
  isRN: false
};

// 运行时初始化环境
function initEnv() {
  if (typeof wx !== 'undefined' && wx.getAccountInfoSync) {
    env.isMiniProgram = true;
    env.isWeChatMiniProgram = true;
  } else if (typeof my !== 'undefined' && my.getAuthCode) {
    env.isMiniProgram = true;
    env.isAlipayMiniProgram = true;
  } else if (typeof window !== 'undefined') {
    env.isH5 = true;
  } else if (typeof ReactNative !== 'undefined') {
    env.isRN = true;
  }
}

initEnv();
Taro.env = env;
5.2.2 差异化逻辑处理
javascript 复制代码
// 页面跳转逻辑(处理各端差异)
function navigateToPage(url) {
  if (env.isMiniProgram) {
    // 小程序:使用小程序导航API
    Taro.navigateTo({ url });
  } else if (env.isH5) {
    // H5:使用路由跳转
    Taro.router.push(url);
  } else if (env.isRN) {
    // RN:使用React Navigation
    Taro.navigator.push({ screen: url });
  }
}

5.3 条件编译的实现原理

条件编译是 Taro 处理多端差异化代码的核心能力,分为"编译时条件编译"和"运行时条件编译"。

5.3.1 编译时条件编译

原理:编译时根据目标平台,删除不符合条件的代码,只保留当前平台的逻辑。

使用示例

javascript 复制代码
// 编译时条件编译
if (process.env.TARO_PLATFORM === 'weapp') {
  // 只有微信小程序会保留这段代码
  wx.login({});
} else if (process.env.TARO_PLATFORM === 'h5') {
  // 只有H5会保留这段代码
  location.href = '/login';
}

实现原理(编译时)

javascript 复制代码
// Taro 条件编译插件
class TaroConditionCompilePlugin {
  apply(compiler) {
    compiler.hooks.transformCode.tap('TaroConditionCompilePlugin', (code) => {
      // 获取目标平台
      const platform = process.env.TARO_PLATFORM;
      // 正则匹配条件编译代码
      const regex = /if\s*\(\s*process\.env\.TARO_PLATFORM\s*===\s*['"]([^'"]+)['"]\s*\)\s*\{\s*([\s\S]*?)\s*\}/g;
      return code.replace(regex, (match, targetPlatform, content) => {
        // 只保留目标平台的代码
        return targetPlatform === platform ? content : '';
      });
    });
  }
}
5.3.2 运行时条件编译

原理:运行时通过环境变量判断,执行对应平台的逻辑(代码会打包到所有平台,运行时判断)。

使用示例

javascript 复制代码
// 运行时条件编译
if (Taro.env.isWeChatMiniProgram) {
  Taro.login();
} else if (Taro.env.isH5) {
  // H5登录逻辑
}

六、性能优化建议与最佳实践

6.1 通用优化建议

  1. 合理设置key:列表项必须设置唯一key,避免小程序重新渲染节点;
  2. 减少setData数据量:单次setData数据不超过10KB,避免全量更新;
  3. 组件按需加载 :非首屏组件使用lazyLoadComponent动态加载;
  4. 避免频繁状态更新:对高频事件(如滚动)做节流/防抖处理;
  5. 使用静态模板:不变化的内容提取为静态节点,减少Diff开销。

6.2 小程序端专项优化

  1. 分包合理拆分:主包控制在2MB内,页面按业务域拆分到分包;
  2. 避免深层嵌套:小程序模板嵌套不超过10层,否则渲染性能下降;
  3. 复用自定义组件:将高频组件注册为全局组件,避免重复初始化;
  4. 减少wx:if使用:优先使用hidden替代wx:if(减少节点销毁/重建)。

6.3 H5端专项优化

  1. 路由懒加载 :使用React.lazy/import()动态加载路由组件;
  2. 样式按需引入:只加载当前页面的样式,避免全量样式打包;
  3. 图片懒加载:使用Taro内置的图片懒加载组件;
  4. 缓存策略:合理使用localStorage缓存接口数据,减少请求。

七、总结

Taro 作为多端统一开发框架,其底层实现的核心是"编译时统一抽象+运行时差异化适配":

  • 架构层:通过虚拟DOM抽象层屏蔽各端渲染差异,编译时+运行时分工协作;
  • 核心层:将JSX转换为小程序"模板+数据"模式,适配组件系统和状态管理;
  • 优化层:围绕setData、渲染、资源加载做多层性能优化;
  • 扩展层:基于Tapable实现插件系统,支持自定义编译流程;
  • 适配层:通过条件编译、API桥接、环境判断,抹平多端差异。

理解 Taro 的底层原理,不仅能帮助我们写出更高效的多端代码,也能为自定义多端框架提供思路。在实际开发中,结合框架特性和各平台最佳实践,才能真正发挥 Taro "一次编写,多端运行"的价值。

相关推荐
kingwebo'sZone4 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09015 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农5 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king5 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳5 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵6 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星6 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_6 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝6 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions7 小时前
2026年,微前端终于“死“了
前端·状态模式