百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

细聊微内核架构在前端的应用「干货」

toyiye 2024-07-03 01:59 13 浏览 0 评论

作者: semlinker

转发链接:https://mp.weixin.qq.com/s/ywc98dS4TVB4t3L2tIyk8g

一、微内核架构简介

1. 1 微内核的概念

微内核架构(Microkernel Architecture),有时也被称为插件化架构(Plug-in Architecture),是一种面向功能进行拆分的可扩展性架构,通常用于实现基于产品的应用。微内核架构模式允许你将其他应用程序功能作为插件添加到核心应用程序,从而提供可扩展性以及功能分离和隔离。

微内核架构模式包括两种类型的架构组件:核心系统(Core System)和插件模块(Plug-in modules)。应用逻辑被分割为独立的插件模块和核心系统,提供了可扩展性、灵活性、功能隔离和自定义处理逻辑的特性。

图中 Core System 的功能相对稳定,不会因为业务功能扩展而不断修改,而插件模块是可以根据实际业务功能的需要不断地调整或扩展。微内核架构的本质就是将可能需要不断变化的部分封装在插件中,从而达到快速灵活扩展的目的,而又不影响整体系统的稳定。

微内核架构的核心系统通常提供系统运行所需的最小功能集。许多操作系统使用的就是微内核架构,这也是它名字的由来。从商业应用程序的角度来看,核心系统一般是通用业务逻辑,没有特殊情况、特殊规则或复杂情形下的自定义代码。

插件模块是独立的模块,包含特定的处理、额外的功能和自定义代码,来向核心系统增强或扩展额外的业务能力。通常插件模块之间也是独立的,也有一些插件是依赖于若干其它插件的。重要的是,尽量减少插件之间的通信以避免依赖的问题。

1.2 微内核架构的优点

  • 灵活性高:整体灵活性是对环境变化快速响应的能力。由于插件之间的低耦合,改变通常是隔离的,可以快速实现。通常,核心系统是稳定且快速的,具有一定的健壮性,几乎不需要修改。
  • 可测试性:插件可以独立测试,也很容易被模拟,不需修改核心系统就可以演示或构建新特性的原型。
  • 性能高:虽然微内核架构本身不会使应用高性能,但通常使用微内核架构构建的应用性能都还不错,因为可以自定义或者裁剪掉不需要的功能。

介绍完微内核架构相关的基础知识,接下来我们将以西瓜视频播放器为例,分析一下微内核架构在西瓜视频播放器中的应用。

二、西瓜视频播放器简介

西瓜视频播放器是一款带解析器、能节省流量的 HTML5 视频播放器。它从底层解析 MP4、HLS、FLV 探索更大的视频播放可控空间。

(图片来源 —— http://h5player.bytedance.com/)

它的功能特色是从底层解析 MP4、HLS、FLV 探索更大的视频播放可控空间并拥有以下特点:

  1. 易扩展:灵活的插件体系、PC/移动端自动切换、安全的白名单机制;
  2. 更丰富:强大的 MP4 控制、点播的无缝切换、有效的带宽节省;
  3. 较完整:完整的产品机制、错误的监控上报、自动的降级处理。

上手西瓜视频播放器只需三步:安装、DOM 占位、实例化即可完成播放器的使用。

(图片来源 —— pingan8787)

西瓜视频播放器主张一切设计都是插件,小到一个播放按钮大到一项直播功能支持。 想更好的自定义播放器完成自己业务的契合,理解插件机制是非常重要的,播放器本身有很多内置插件,比如报错、loading、重播等,如果大家想自定义效果可以关闭内置插件,自己开发即可。

默认情况下插件是自启动的,如果自定义插件不想自启动或者不想改变播放器默认的执行机制,建议以集成播放器类的方式开发。

为了实现 "一切设计都是插件" 的主张,西瓜视频播放器团队采用了微内核的架构,下面我们开始来分析一下西瓜视频播放器的微内核实践。

三、西瓜视频播放器微内核实践

微内核架构模式包括两种类型的架构组件:核心系统和插件模块。在西瓜视频播放器中核心系统是由 Player 类来实现,该类对应的 UML 图如下所示:

(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/player.js)

而插件模块主要就是西瓜视频播放器中的各种内置插件,比如控制条的音量控制组件、播放器贴图、播放器画中画和播放器下载控件等,除了上面提到的插件之外,目前西瓜视频播放器总共提供了 22 个插件,完整的内置插件如下图所示:

(西瓜视频播放器内置插件)

对于微内核的核心系统设计来说,它涉及三个关键技术:插件管理、插件连接和插件通信。下面我们将围绕这三个关键点来逐步分析西瓜视频播放器是如何实现的。

3.1 插件管理

核心系统需要知道当前有哪些插件可用,如何加载这些插件,什么时候加载插件。常见的实现方法是插件注册表机制。核心系统提供插件注册表(可以是配置文件,也可以是代码,还可以是数据库),插件注册表含有每个插件模块的信息,包括它的名字、位置、加载时机(启动就加载,或是按需加载)等。

在分析西瓜视频播放器插件管理机制前,我们先来看一下 xgplayer/packages/xgplayer/src 目录结构:

├── control
│   ├── collect.js
│   ├── cssFullscreen.js
│   ├── danmu.js
│   ├── ....
│   └── volume.js
├── error.js
├── index.js
├── player.js
├── proxy.js
├── style
│   ├── index.scss
│   ├── ...
│   └── variable.scss
└── utils
    ├── animation.js
    ├── database.js
    ├── ...
    └── util.js

通过观察以上目录结构,我们可以发现西瓜视频播放器的插件都统一存放在 control 目录下。那么现在问题来了,这些插件是如何被加载的?什么时候被加载?要回答这个问题,我们从该项目的入口出发:

// packages/xgplayer/src/index.js
import Player from'./player'// ①
import * as Controls from'./control/*.js'// ②
import'./style/index.scss'// ③
exportdefault Player // ④

从 index.js 文件中,我们发现在第二行代码中使用了 import * as Controls from './control/*.js' 语句批量导入播放器的所有内置插件。该功能是借助 babel-plugin-bulk-import 这个插件来实现的。

除了使用上述插件之外,还可以借助 Webpack context API 来实现,通过执行 require.context 函数获取一个特定的上下文,就可以实现自动化导入模块。

在前端工程中,如果遇到从一个文件夹引入很多模块的情况,可以使用这个 API,它会遍历文件夹中的指定文件,然后自动导入模块,而不需要每次显式的调用 import 导入模块。

Webpack context API 的使用示例如下:

const contextRequire = require.context("./modules", true);

const modules = [];
contextRequire.keys().forEach((filename) => {
  if (filename.match(/^\.\/[^_][\w/]*\.([tj])s$/)) {
    modules.push(contextRequire(filename));
  }
});

好的,回到正题。现在我们已经知道西瓜视频播放器的所有内置插件,都是通过 babel-plugin-bulk-import 这个插件在构建阶段完成加载的。如果不想使用播放器中的内置控件,可以通过ignores 配置项关闭,使用自己开发的相同功能插件进行替换:

new Player({
  el:document.querySelector('#mse'),
  url: 'video_url',
  ignores: ['replay'] // 默认值[]
});

下个环节,我们来分析西瓜视频播放器的内置插件是如何连接到核心系统的。

3.2 插件连接

插件连接是指插件如何连接到核心系统。通常来说,核心系统必须指定插件和核心系统的连接规范,然后插件按照规范实现,核心系统按照规范加载即可。

要了解西瓜视频内置插件是如何连接到核心系统,我就需要来分析已有的内置的插件,这里我们以简单的 loading 内置插件为例:

// packages/xgplayer/src/control/loading.js
import Player from'../player'

let loading = function () {
  let player = this;
  let util = Player.util;
  let container = util.createDom('xg-loading', `
    <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewbox="0 0 100 100">
      <path d="M100,50A50,50,0,1,1,50,0"></path>
    </svg>
    `, {}, 'xgplayer-loading')
  player.root.appendChild(container)
}

Player.install('loading', loading)

(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/control/loading.js)

在以上代码中,最重要的是最后一行,即 Player.install('loading', loading) 这一行。顾名思义,install 方法是用来安装插件其具体实现如下:

// packages/xgplayer/src/player.js
class Player extends Proxy {
  static install (name, descriptor) {
    if (!Player.plugins) {
      Player.plugins = {}
    }
    Player.plugins[name] = descriptor
  }
}

通过观察以上代码可知,install 方法支持两个参数 name 和 descriptor,分别表示插件名称和插件描述器。当调用 Player.install 方法后,会把插件信息注册到 Player 类的 plugins 命名空间下。需要注意的是,这里仅仅是完成插件的注册操作。在利用 Player 类创建播放器实例的时候,才会进行插件初始化操作,代码如下:

class Player extends Proxy {
  constructor(options) {
    if (
      this.config.controlStyle &&
      util.typeOf(this.config.controlStyle) === "String"
    ) {
      // ...
      // 从服务器成功获取配置信息后,
      // 再调用self.pluginsCall()
    } else {
      this.pluginsCall();
    }
  }
}

在 Player 类构造函数中会调用 pluginsCall 方法来初始化插件,其中 pluginsCall 方法的具体实现如下:

class Player extends Proxy {
   pluginsCall() {
    let self = this;
    if (Player.plugins) {
      let ignores = this.config.ignores;
      Object.keys(Player.plugins).forEach(name => {
        let descriptor = Player.plugins[name];
        // 忽略ignores配置项关闭的插件
        if (!ignores.some(item => name === item)) {
          if (["pc", "tablet", "mobile"].some(type => type === name)) {
            if (name === sniffer.device) {
              setTimeout(() => {
                descriptor.call(self, self);
              }, 0);
            }
          } else {
            descriptor.call(this, this);
          }
        }
      });
    }
  }
}

了解完上述知识,我们再来介绍一下如何自定义西瓜视频播放器插件。在西瓜视频播放器中,自定义插件只有两个步骤:

1. 开发插件

// pluginName.js
import Player from 'xgplayer';

let pluginName=function(player){
  // 插件逻辑
}

Player.install('pluginName',pluginName);

2. 使用插件

import Player from 'xgplayer';

let player = new Player({
  id: 'xg',
  url: '//abc.com/**/*.mp4'
})

好的,我们继续进入下一个环节,即分析西瓜视频播放器核心系统和插件模块之间是如何通信的。

3.3 插件通信

插件通信是指插件间的通信。虽然设计的时候插件间是完全解耦的,但实际业务运行过程中,必然会出现某个业务流程需要多个插件协作,这就要求两个插件间进行通信;由于插件之间没有直接联系,通信必须通过核心系统,因此核心系统需要提供插件通信机制

这种情况和计算机类似,计算机的 CPU、硬盘、内存、网卡是独立设计的配置,但计算机运行过程中,CPU 和内存、内存和硬盘肯定是有通信的,计算机通过主板上的总线提供了这些组件之间的通信功能。

同样,我们以西瓜视频播放器的内置插件为切入点来分析插件通信机制,下面我们以 poster 内置插件为例。poster 插件用于设置播放器的封面图,该图是当播放器初始化后在用户点击播放按钮前显示的图像。

该插件的使用方式如下:

new Player({
  el:document.querySelector('#mse'),
  url: 'video_url',
  poster: '//abc.com/**/*.png'// 默认值""
});

该插件的对应源码如下:

import Player from'../player'

let poster = function () {
  let player = this;
  let util = Player.util
  let poster = util.createDom('xg-poster', '', {}, 'xgplayer-poster');
  let root = player.root
  if (player.config.poster) {
    poster.style.backgroundImage = `url(${player.config.poster})`
    root.appendChild(poster)
  }

  // 监听播放事件,播放时隐藏封面图
  function playFunc () {
    poster.style.display = 'none'
  }
  player.on('play', playFunc)

  // 监听销毁事件,执行清理操作
  function destroyFunc () {
    player.off('play', playFunc)
    player.off('destroy', destroyFunc)
  }
  player.once('destroy', destroyFunc)
}

Player.install('poster', poster)

(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/control/poster.js)

通过观察源码可知,该插件首先通过监听播放器的 play 事件来隐藏 poster 海报。此外还会监听播放器的 destory 事件来实现清理操作,比如移除 play 事件的监听器和 destroy 事件。

要实现上述功能,在源码中是通过 player 实例提供的 on、off 和 once 三个方法来实现,相信大多数读者对这三个方法都很熟悉了,它们分别用于实现添加监听(on)、移除监听(off)和单次监听(once)。

那么上述的三个方法来自哪里呢?通过阅读西瓜视频播放器的源码,我们发现上述方法是 Player 类通过继承 Proxy 类,在 Proxy 类中又通过构造继承的方式继承于来自 event-emitter 第三方库的 EventEmitter 类来实现的。

在 poster 插件中的监听了播放器的 play 和 destroy 事件,那这些事件是什么时候会触发呢?下面我们来分别分析一下:

1. play 事件

// packages/xgplayer/src/proxy.js
this.ev = ['play', 'playing', 'pause', 'ended', 'error', 'seeking',
  'seeked','timeupdate', 'waiting', 'canplay', 'canplaythrough',
  'durationchange', 'volumechange', 'loadeddata'].map((item) => {
     return {
       [item]: `on${item.charAt(0).toUpperCase()}${item.slice(1)}`
     }
});

this.ev.forEach(item => {
  self.evItem = Object.keys(item)[0]
  let name = Object.keys(item)[0]
  self.video.addEventListener(Object.keys(item)[0], function () {
     if (name === 'error') {
        if (self.video.error) {
          self.emit(name, new Errors('other',
            self.currentTime, self.duration,
            self.networkState, self.readyState,
            self.currentSrc, self.src,
            self.ended, {
                line: 41,
                msg: self.error,
                handle: 'Constructor'
              }))
          }
        } else {
          self.emit(name, self)
      }
});

(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/proxy.js)

在西瓜视频播放器初始化的时候,会通过调用 Video 元素的 addEventListener 方法来监听各种原生事件,在对应的事件处理函数中,会调用 emit 方法进行事件派发。

2. destory 事件

// packages/xgplayer/src/player.js
function destroyFunc() {
  this.emit("destroy");
  // fix video destroy https://stackoverflow.com/questions/3258587/how-to-properly-unload-destroy-a-video-element
  this.video.removeAttribute("src"); // empty source
  this.video.load();
  if (isDelDom) {
    parentNode.removeChild(this.root);
  }
  for (let k inthis) {
    deletethis[k];
  }
  this.off("pause", destroyFunc);
}

(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/player.js)

在西瓜视频播放器销毁时,会调用 destroyFunc 方法,在该方法内部,会继续调用 emit 方法来发射 destroy 事件。之后,若其它插件有监听 destroy 事件,那么将会触发对应的事件处理函数,执行相应的清理工作。而对于插件之间的通信,同样也可以借助 player 播放器对象上事件相关的 API 来实现,这里就不再展开。

前面我们已经从插件管理、插件连接和插件通信这三方面分析了西瓜视频播放器是如何实现微内核架构,下面我们用一张图来总结一下主要的内容:

四、总结

本文以西瓜视频播放器为例,详细介绍了微内核架构的设计要点与实现。其实西瓜视频播放器除了提供大量的内置插件之外,它也提供了一些功能插件,如 flv 和 hls 功能插件,从而来满足不同的播放场景。

此外,通过分析西瓜视频播放器,我们发现要设计一个功能完善的组件是很有挑战的一件事,要考虑非常多的事情,这里我以思维导图的形式简单整理了一下,有兴趣的读者可以参考一下。

想进一步了解西瓜视频播放器的读者,可以阅读我之前整理的 "西瓜视频播放器功能分析" 这篇文章:

细品西瓜播放器功能分析(上)「实践」


推荐JavaScript经典实例学习资料文章

一种高性能的Tree组件实现方案「干货」

进击的JAMStack

前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上

前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中

前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下

一文带你搞懂 babel-plugin-import 插件(上)「源码解析」

一文带你搞懂 babel-plugin-import 插件(下)「源码解析」

JavaScript常用API合集汇总「值得收藏」

推荐10个常用的图片处理小帮手(上)「值得收藏」

推荐10个常用的图片处理小帮手(下)「值得收藏」

JavaScript 中ES6代理的实际用例

12 个实用的前端开发技巧总结

一文带你搞懂搭建企业级的 npm 私有仓库

教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?

细说前端开发UI公共组件的新认识「实践」

细说DOM API中append和appendChild的三个不同点

细品淘系大佬讲前端新人如何上王者「干货」

一文带你彻底解决背景跟随弹窗滚动问题「干货」

推荐常用的5款代码比较工具「值得收藏」

Node.js实现将文字与图片合成技巧

爱奇艺云剪辑Web端的技术实现

我再也不敢说我会写前端 Button组件「实践」

NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」

Node Buffers 完整指南

推荐18个webpack精美插件「干货」

前端开发需要了解常用7种JavaScript设计模式

浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务

了不起的 Webpack HMR 学习指南(上)「含源码讲解」

了不起的 Webpack HMR 学习指南(下)「含源码讲解」

10个打开了我新世界大门的 WebAPI(上)「实践」

10个打开了我新世界大门的 WebAPI(中)「实践」

10个打开了我新世界大门的 WebAPI(下)「实践」

「图文」ESLint 在中大型团队的应用实践

Deno是代码的浏览器,你认同吗?

前端存储除了 localStorage 还有啥?

Javascript 多线程编程?的前世今生

微前端方案 qiankun(实践及总结)

「图文」V8 垃圾回收原来这么简单?

Webpack 5模块联邦引发微前端的革命?

基于 Web 端的人脸识别身份验证「实践」

「前端进阶」高性能渲染十万条数据(时间分片)

「前端进阶」高性能渲染十万条数据(虚拟列表)

图解 Promise 实现原理(一):基础实现

图解 Promise 实现原理(二):Promise 链式调用

图解 Promise 实现原理(三):Promise 原型方法实现

图解 Promise 实现原理(四):Promise 静态方法实现

实践教你从零构建前端 Lint 工作流「干货」

高性能多级多选级联组件开发「JS篇」

深入浅出讲解Node.js CLI 工具最佳实战

延迟加载图像以提高Web网站性能的五种方法「实践」

比较 JavaScript 对象的四种方式「实践」

使用Service Worker让你的 Web 应用如虎添翼(上)「干货」

使用Service Worker让你的 Web 应用如虎添翼(中)「干货」

使用Service Worker让你的 Web 应用如虎添翼(下)「干货」

前端如何一次性处理10万条数据「进阶篇」

推荐三款正则可视化工具「JS篇」

如何让用户选择是否离开当前页面?「JS篇」

JavaScript开发人员更喜欢Deno的五大原因

仅用18行JavaScript实现一个倒数计时器

图文细说JavaScript 的运行机制

一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索

推荐Web程序员常用的15个源代码编辑器

10个实用的JS技巧「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」

细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」

深入JavaScript教你内存泄漏如何防范

手把手教你7个有趣的JavaScript 项目-上「附源码」

手把手教你7个有趣的JavaScript 项目-下「附源码」

JavaScript 使用 mediaDevices API 访问摄像头自拍

手把手教你前端代码如何做错误上报「JS篇」

一文让你彻底搞懂移动前端和Web 前端区别在哪里

63个JavaScript 正则大礼包「值得收藏」

提高你的 JavaScript 技能10 个问答题

JavaScript图表库的5个首选

一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法

可视化的 JS:动态图演示 - 事件循环 Event Loop的过程

教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」

可视化的 js:动态图演示 Promises & Async/Await 的过程

原生JS封装拖动验证滑块你会吗?「实践」

如何实现高性能的在线 PDF 预览

细说使用字体库加密数据-仿58同城

Node.js要完了吗?

Pug 3.0.0正式发布,不再支持 Node.js 6/8

纯JS手写轮播图(代码逻辑清晰,通俗易懂)

JavaScript 20 年 中文版之创立标准

值得收藏的前端常用60余种工具方法「JS篇」

箭头函数和常规函数之间的 5 个区别

通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events

「前端篇」不再为正则烦恼

「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能

深入细品浏览器原理「流程图」

JavaScript 已进入第三个时代,未来将何去何从?

前端上传前预览文件 image、text、json、video、audio「实践」

深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系

推荐13个有用的JavaScript数组技巧「值得收藏」

前端必备基础知识:window.location 详解

不要再依赖CommonJS了

犀牛书作者:最该忘记的JavaScript特性

36个工作中常用的JavaScript函数片段「值得收藏」

Node + H5 实现大文件分片上传、断点续传

一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」

【实践总结】关于小程序挣脱枷锁实现批量上传

手把手教你前端的各种文件上传攻略和大文件断点续传

字节跳动面试官:请你实现一个大文件上传和断点续传

谈谈前端关于文件上传下载那些事【实践】

手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件

最全的 JavaScript 模块化方案和工具

「前端进阶」JS中的内存管理

JavaScript正则深入以及10个非常有意思的正则实战

前端面试者经常忽视的一道JavaScript 面试题

一行JS代码实现一个简单的模板字符串替换「实践」

JS代码是如何被压缩的「前端高级进阶」

前端开发规范:命名规范、html规范、css规范、js规范

【规范篇】前端团队代码规范最佳实践

100个原生JavaScript代码片段知识点详细汇总【实践】

关于前端174道 JavaScript知识点汇总(一)

关于前端174道 JavaScript知识点汇总(二)

关于前端174道 JavaScript知识点汇总(三)

几个非常有意思的javascript知识点总结【实践】

都2020年了,你还不会JavaScript 装饰器?

JavaScript实现图片合成下载

70个JavaScript知识点详细总结(上)【实践】

70个JavaScript知识点详细总结(下)【实践】

开源了一个 JavaScript 版敏感词过滤库

送你 43 道 JavaScript 面试题

3个很棒的小众JavaScript库,你值得拥有

手把手教你深入巩固JavaScript知识体系【思维导图】

推荐7个很棒的JavaScript产品步骤引导库

Echa哥教你彻底弄懂 JavaScript 执行机制

一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧

深入解析高频项目中运用到的知识点汇总【JS篇】

JavaScript 工具函数大全【新】

从JavaScript中看设计模式(总结)

身份证号码的正则表达式及验证详解(JavaScript,Regex)

浏览器中实现JavaScript计时器的4种创新方式

Three.js 动效方案

手把手教你常用的59个JS类方法

127个常用的JS代码片段,每段代码花30秒就能看懂-【上】

深入浅出讲解 js 深拷贝 vs 浅拷贝

手把手教你JS开发H5游戏【消灭星星】

深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】

手把手教你全方位解读JS中this真正含义【实践】

书到用时方恨少,一大波JS开发工具函数来了

干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)

手把手教你JS 异步编程六种方案【实践】

让你减少加班的15条高效JS技巧知识点汇总【实践】

手把手教你JS开发H5游戏【黄金矿工】

手把手教你JS实现监控浏览器上下左右滚动

JS 经典实例知识点整理汇总【实践】

2.6万字JS干货分享,带你领略前端魅力【基础篇】

2.6万字JS干货分享,带你领略前端魅力【实践篇】

简单几步让你的 JS 写得更漂亮

恭喜你获得治疗JS this的详细药方

谈谈前端关于文件上传下载那些事【实践】

面试中教你绕过关于 JavaScript 作用域的 5 个坑

Jquery插件(常用的插件库)

【JS】如何防止重复发送ajax请求

JavaScript+Canvas实现自定义画板

Continuation 在 JS 中的应用「前端篇」

作者: semlinker

转发链接:https://mp.weixin.qq.com/s/ywc98dS4TVB4t3L2tIyk8g

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码