一文带你了解node中的的模块系统

本篇文章带大家进行node学习,深入浅出的了解node中的的模块系统,希望对大家有所帮助!

一文带你了解node中的的模块系统

两年前写过一篇文章介绍模块系统:理解前端模块概念:CommonJs与ES6Module。这篇文章的知识面都是针对刚入门的,比较浅显。在这也纠正文章的几个错误:

【模块】和【模块系统】 是两码事。模块是软件中的一个单元,而模块系统则是一套语法或者工具,模块系统能让开发者在项目中定义和使用模块。ECMAScript Module缩写是ESM,或者ESModule,而不是ES6Module。

关于模块系统的基础知识都在上一篇文章说的差不多了,所以这篇文章会重点关注模块系统的内部原理以及更加完整的介绍不同模块系统之间的区别,上一篇文章出现的内容在这就不再重复了。【相关教程推荐:nodejs视频教程、编程教学】

模块系统

并不是所有编程语言都有内置的模块系统,JavaScript诞生之后的很长一段时间里都没有模块系统。

在浏览器环境中只能使用标签来引入不用的代码文件,这种方法共享一个全局作用域,可谓是问题多多;加上前端日新月异的发展,这种方法已经不满足当下的需求了。在没官方的模块系统出现前,前端社区自己创建第三方模块系统,用的较多的有:异步模块定义AMD、通用模块定义UMD等,当然最著名还得是CommonJS

由于Node.js它是一个JavaScript的运行环境,可以直接访问底层的文件系统。所以开发者通过它,并按照CommonJS规范实现了一套模块系统。

最开始,CommonJS只能用于Node.js平台,随着Browserify和Webpack之类的模块打包工具的出现,CommonJS也终于能在浏览器端运行了。

到2015年发布了ECMAScript6规范,才有了模块系统的正式标准,按照该标准打造出来的模块系统称为ECMAScript module简称【ESM】,由此ESM就开始统一了Node.js环境与浏览器环境。当然ECMAScript6只是提供了语法和语义,至于实现部分得由各浏览器服务厂商和Node开发者去努力。所以才有了令其他编程语言羡慕不已的babel神器,实现模块系统并不是一件容易的事,Node.js也是到了13.2版本才算是比较稳定的支持ESM。

但不管怎么样,ESM才是JavaScript的“亲儿子”,学习它一定不会有错!

模块系统的基本思路

在刀耕火种的年代中使用JavaScript开发应用,脚本文件只能通过script标签引入。其中遇到比较严重的问题就是缺乏命名空间机制,这意味着每个脚本都共享同一作用域。这个问题在社区中有一个比较好的解决方法:Revevaling module

const myModule = (() => {    const _privateFn = () => {}    const _privateAttr = 1    return {        publicFn: () => {},        publicAttr: 2    }})()console.log(myModule)console.log(myModule.publicFn, myModule._privateFn)

运行结果如下:

一文带你了解node中的的模块系统

这个模式很简单,利用IIFE创建一个私有的作用域,同时使用return需要暴露的变量。而属于内部的变量(比如_privateFn、_privateAttr)是不能从外面的作用域访问的。

【revealing module】正是利用了这些特性,来隐藏私有的信息,同时把应该公布给外界的API导出。后面的模块系统也正是基于这样的思路开发的。

CommonJS

基于上面思路,来开发一个模块加载器。

首先编写一个加载模块内容的函数,并把这个函数包裹在私有作用域里面,然后通过eval()求值,以运行该函数:

function loadModule (filename, module, require) {  const wrappedSrc =     `(function (module, exports, require) {      ${fs.readFileSync(filename, 'utf8)}    }(module, module.exports, require)`  eval(wrappedSrc)}

和【revealing module】一样,把模块的源代码包裹在函数里面,区别在于,还把一系列变量(module, module.exports, require)传给该函数。

值得注意的是,通过【readFileSync】读取模块内容。一般来说,在调用涉及文件系统的API时,不应该使用同步版本。但此时不同,因为通过CommonJs系统来加载模块,本身就应该实现成同步操作,以确保多个模块能够按照正确的依赖顺序得到引入。

接着模拟require()函数,主要功能是加载模块。

function require(moduleName) {  const id = require.resolve(moduleName)  if (require.cache[id]) {    return require.cache[id].exports  }  // 模块的元数据  const module = {    exports: {},    id  }  // 更新缓存  require.cache[id] = module    // 载入模块  loadModule(id, module, require)    // 返回导出的变量  return module.exports}require.cache = {}require.resolve = (moduleName) => {  // 根据moduleName解析出完整的模块id}

(1)函数接收到moduleName后,首先解析出模块的完整路径,赋值给id。
(2)如果cache[id]为true,说明该模块已经被加载过了,直接返回缓存结果
(3)否则,就配置一套环境,用于首次加载。具体来说,创建module对象,包含exports(也就是导出内容),id(作用如上)
(4)将首次加载的module缓存起来
(5)通过loadModule从模块的源文件中读取源代码
(6)最后return module.exports返回想要导出的内容。

require是同步的

在模拟require函数的时候,有一个很重要的细节:require函数必须是同步的。它的作用仅仅是直接将模块内容返回而已,并没有用到回调机制。Node.js中的require也是如此。所以针对module.exports的赋值操作,也必须是同步的,如果用异步就会出问题:

// 出问题setTimeout(() => {    module.exports = function () {}}, 1000)

require是同步函数这一点对定义模块的方式有着非常重要的影响,因为它迫使我们在定义模块时只能使用同步的代码,以至于Node.js都为此,提供了大多数异步API的同步版本。

早期的Node.js有异步版本的require函数,但很快就移除了,因为这会让函数的功能变得十分复杂。

ESM

ESM是ECMAScript2015规范的一部分,该规范给JavaScript语言指定了一套官方的模块系统,以适应各种执行环境。

在Node.js中使用ESM

Node.js默认会把.js后缀的文件,都当成是采用CommonJS语法所写的。如果直接在.js文件中采用ESM语法,解释器会报错。

有三种方法可以在让Node.js解释器转为ESM语法:
1、把文件后缀名改为.mjs;
2、给最近的package.json文件添加type字段,值为“module”;
3、字符串作为参数传入--eval,或通过STDIN管道传输到node,带有标志--input-type=module
比如:

node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

不同类型模块引用

ESM可以被解析并缓存为URL(这也意味着特殊字符必须是百分比编码)。支持file:node:data:等的URL协议

file:URL
如果用于解析模块的import说明符具有不同的查询或片段,则会多次加载模块

// 被认为是两个不同的模块import './foo.mjs?query=1';import './foo.mjs?query=2';

data:URL
支持使用MIME类型导入:

text/javascript用于ES模块application/json用于JSONapplication/wasm用于Wasm

import 'data:text/javascript,console.log("hello!");';import _ from 'data:application/json,"world!"' assert { type: 'json' };

data:URL仅解析内置模块的裸说明符和绝对说明符。解析相对说明符不起作用,因为data:不是特殊协议,没有相对解析的概念。

导入断言
这个属性为模块导入语句添加了内联语法,以便在模块说明符旁边传入更多信息。

import fooData from './foo.json' assert { type: 'json' };const { default: barData } = await import('./bar.json', { assert: { type: 'json' } });

目前只支持JSON模块,而且assert { type: 'json' }语法是具有强制性的。

导入Wash模块
--experimental-wasm-modules标志下支持导入WebAssembly模块,允许将任何.wasm文件作为普通模块导入,同时也支持它们的模块导入。

// index.mjsimport * as M from './module.wasm';console.log(M)

使用如下命令执行:

node --experimental-wasm-modules index.mjs

顶层await

await关键字可以用在ESM中的顶层。

// a.mjsexport const five = await Promise.resolve(5)// b.mjsimport { five } from './a.mjs'console.log(five) // 5

异步引用

前面说过,import语句对模块依赖的解决是静态的,因此有两项著名的限制:

模块标识符不能等到运行的时候再去构造;模块引入语句,必须写在文件的顶端,而且不能套在控制流语句里;

然而,对于某些情况来说,这两项限制无疑是过于严格。就比如说有一个还算是比较常见的需求:延迟加载

在遇到一个体积很大的模块时,只想在真正需要用到模块里的某个功能时,再去加载这个庞大的模块。

为此,ESM提供了异步引入机制。这种引入操作,可以在程序运行的时候,通过import()运算符实现。从语法上看,相当于一个函数,接收模块标识符作为参数,并返回一个Promise,待Promise resolve后就能得到解析后的模块对象。

ESM的加载过程

用一个循环依赖的例子来说明ESM的加载过程:

// index.jsimport * as foo from './foo.js';import * as bar from './bar.js';console.log(foo);console.log(bar);// foo.jsimport * as Bar from './bar.js'export let loaded = false;export const bar = Bar;loaded = true;// bar.jsimport * as Foo from './foo.js';export let loaded = false;export const foo = Foo;loaded = true

先看看运行结果:

一文带你了解node中的的模块系统

文心大模型 文心大模型

百度飞桨-文心大模型 ERNIE 3.0 文本理解与创作

文心大模型 56 查看详情 文心大模型

通过loaded可以观察到,foo和bar这两个模块都能log出加载完整的模块信息。而CommonJS却不一样,一定会有一个模块无法打印出完整加载后的样子。

我们深入加载过程,看看为什么会出现这样的结果。
加载过程可以分为三个阶段:

第一个阶段:解析第二个阶段:声明第三个阶段:执行

解析阶段:
解释器从入口文件出发(也就是index.js),解析模块之间的依赖关系,以图的形式展示出来,这张图也被称为依赖关系图。

在这个阶段只关注与import语句,并把这些语句想要引入的模块所对应的源码,给加载进来。并以深度解析的方式得到最后的依赖关系图。以上面例子说明:
1、从index.js开始,发现import * as foo from './foo.js'语句,从而去到foo.js文件中。
2、从foo.js文件继续解析,发现import * as Bar from './bar.js'语句,从而去到bar.js中。
3、从bar.js继续解析,发现import * as Foo from './foo.js'语句,形式循环依赖,但由于解释器已经在处理foo.js模块了,所以不会再进入其中,然后继续解析bar模块。
4、解析完bar模块后,发现没有import语句了,所以返回foo.js,并继续往下解析。一路都没有再次发现import语句,返回index.js。
5、在index.js中发现import * as bar from './bar.js',但由于bar.js已经解析过了,所以略过,继续往下执行。

最后通过深度优先的方式把依赖图完整的展示出来:

一文带你了解node中的的模块系统

声明阶段:
解释器从得到的依赖图出发,从底到上的顺序对每个模块进行声明。具体来说,每到达一个模块,就寻找该模块所要导出的全部属性,并在内存中声明导出值的标识符。请注意,该阶段只作声明,不会进行赋值操作。
1、解释器从bar.js模块出发,声明loaded和foo的标识符。
2、向上回溯,到了foo.js模块,声明loaded和bar标识符。
3、到了index.js模块,但这个模块没有导出语句,所以没有声明任何标识符。

一文带你了解node中的的模块系统

声明完所有导出标识符后,再走一遍依赖图,把import引入和export导出的关系连接起来。

一文带你了解node中的的模块系统

可以看到,由import引进来的模块与export所导出值之间,建立了一种类似于const的绑定关系,引入方这一端是只能读而不能写。而且在index.js读取的bar模块,与在foo.js读取的bar模块实质是同一个实例。

所以这就是为什么在这个例子的结果中都能输出完整的解析结果的原因。

这跟CommonJS系统所用的方法有根本的区别。如果有某个模块要引入CommonJS模块,那么系统会对后者的整个exports对象做拷贝,从而将其中的内容复制到当前模块里面,这样的话,如果受引入的那个模块修改了自身的那一份变量,那么用户这边是看不到新值的。

执行阶段:
在这个阶段中,引擎才会去执行模块的代码。依然采用从底向上的顺序访问依赖图,并逐个执行访问到的文件。从bar.js文件开始执行,到foo.js,最后才是index.js。在这个过程中,逐步完善export表中标识符的值。

这套流程与CommonJS看似没有太大区别,但实际有着重大差异。由于CommonJS是动态的,因此它一边解析依赖图,一边执行相关的文件。所以只要看到一条require语句,就可以肯定的说,当程序来到这条语句时,已经把前面的代码都执行完了。因此,require语句不一定非要出现在文件的开头,而是可以出现在任意地方,而且,模块标识符也可以通过变量来构造。

但ESM不同,在ESM里,上述这三个阶段是彼此分离的,它必须先把依赖图完整地构造出来,然后才能执行代码,因此,引入模块与导出模块的操作,都必须是静态的,而不能等到执行代码的时候再去做。

ESM与CommonJS区别

除了前面提到的几个区别之外,还有一些区别是值得注意的:

强制的文件扩展名

在ESM中使用import关键字解析相对或绝对的说明符时,必须提供文件扩展名,还必须完全指定目录索引(‘./path/index.js’)。而CommonJS的require函数则允许省略这个扩展名。

严格模式

ESM是默认运行于严格模式之下,而且该严格模式是不能禁用。所以不能使用未声明的变量,也不能使用那些仅仅在非严格模式下才能使用的特性(例如with)。

ESM不支持CommonJS提供的某些引用

CommonJS中提供了一些全局变量,这些变量不能在ESM下使用,如果试图使用这些变量会导致ReferenceError错误。包括

requireexportsmodule.exports__filename__dirname

其中__filename指的是当前这个模块文件的绝对路径,__dirname则是该文件所在文件夹的绝对路径。这连个变量在构建当前文件的相对路径时很有帮助,所以ESM提供了一些方法去实现两个变量的功能。

在ESM中,可以使用import.meta对象来获取一个引用,这个引用指的是当前文件的URL。具体来说,就是通过import.meta.url来获取当前模块的文件路径,这个路径的格式类似file:///path/to/current_module.js。根据这条路径,构造出__filename__dirname所表达的绝对路径:

import { fileURLToPath } from 'url'import { dirname } from 'path'const __filename = fileURLToPath(import.meta.url)const __dirname = dirname(__filename)

而且还能模拟CommonJS中require()函数

import { createRequire } from 'module'const require = createRequire(import.meta.url)

this指向

在ESM的全局作用域中,this是未定义(undefined),但是在CommonJS模块系统中,它是一个指向exports的引用:

// ESMconsole.log(this) // undefined// CommonJSconsole.log(this === exports) // true

ESM加载CommonJS

上面提到过在ESM中可以模拟CommonJS的require()函数,以此来加载CommonJS的模块。除此之外,还可以使用标准的import语法引入CommonJS模块,不过这种引入方式只能把默认导出的东西给引进来:

import packageMain from 'commonjs-package' // 完全可以import { method } from 'commonjs-package' // 出错

而CommonJS模块的require总是将它引用的文件视为CommonJS。不支持使用require加载ES模块,因为ES模块具有异步执行。但可以使用import()从CommonJS模块中加载ES模块。

导出双重模块

虽然ESM已经推出了7年,node.js也已经稳定支持了,我们开发组件库的时候可以只支持ESM。但为了兼容旧项目,对CommonJS的支持也是必不可少的。有两种广泛使用的方法可以使得组件库同时支持两个模块系统的导出。

使用ES模块封装器

在CommonJS中编写包或将ES模块源代码转换为CommonJS,并创建定义命名导出的ES模块封装文件。使用条件导出,import使用ES模块封装器,require使用CommonJS入口点。举个例子,example模块中

// package.json{    "type": "module",    "exports": {        "import": "./wrapper.mjs",        "require": "./index.cjs"    }}

使用显示扩展名.cjs.mjs,因为只用.js的话,要么是被默认为CommonJS,要么"type": "module"会导致这些文件都被视为ES模块。

// ./index.cjsexport.name = 'name';// ./wrapper.mjsimport cjsModule from './index.cjs'export const name = cjsModule.name;

在这个例子中:

// 使用ESM引入import { name } from 'example'// 使用CommonJS引入const { name } = require('example')

这两种方式引入的name都是相同的单例。

隔离状态

package.json文件可以直接定义单独的CommonJS和ES模块入口点:

// package.json{    "type": "module",    "exports": {        "import": "./index.mjs",        "require": "./index.cjs"    }}

如果包的CommonJS和ESM版本是等效的,则可以做到这一点,例如因为一个是另一个的转译输出;并且包的状态管理被仔细隔离(或包是无状态的)

状态是一个问题的原因是因为包的CommonJS和ESM版本都可能在应用程序中使用;例如,用户的引用程序代码可以importESM版本,而依赖项require CommonJS版本。如果发生这种情况,包的两个副本将被加载到内存中,因此将出现两个不同的状态。这可能会导致难以解决的错误。

除了编写无状态包(例如,如果JavaScript的Math是一个包,它将是无状态的,因为它的所有方法都是静态的),还有一些方法可以隔离状态,以便在可能加载的CommonJS和ESM之间共享它包的实例:

如果可能,在实例化对象中包含所有状态。比如JavaScript的Date,需要实例化包含状态;如果是包,会这样使用:

import Date from 'date';const someDate = new Date();// someDate 包含状态;Date 不包含

new关键字不是必需的;包的函数可以返回新的对象,或修改传入的对象,以保持包外部的状态。

在包的CommonJS和ESM版本之间共享的一个或过个CommonJS文件中隔离状态。比如CommonJS和ESM入口点分别是index.cjs和index.mjs:

// index.cjsconst state = require('./state.cjs')module.exports.state = state;// index.mjsimport state from './state.cjs'export {    state}

即使example在应用程序中通过require和import使用example的每个引用都包含相同的状态;并且任一模块系统修改状态将适用二者皆是。

最后

如果本文对你有帮助,就点个赞支持下吧,你的「赞」是我持续进行创作的动力。

本文引用以下资料:

node.js官方文档Node.js Design Patterns

更多node相关知识,请访问:nodejs 教程!

以上就是一文带你了解node中的的模块系统的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/544265.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 16:37:42
下一篇 2025年11月9日 16:43:23

相关推荐

  • 项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结

    项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结 随着互联网的快速发展,网页设计已经成为了各行各业都离不开的一项技能。优秀的网页设计可以给用户留下深刻的印象,提升用户体验,增加用户的黏性和转化率。而要做出优秀的网页设计,除了对美学的理解和创意的运用外,还需要掌握一些基本的技能,如…

    2025年12月24日
    200
  • 学完HTML和CSS之后我应该做什么?

    网页开发是一段漫长的旅程,但是掌握了HTML和CSS技能意味着你已经赢得了一半的战斗。这两种语言对于学习网页开发技能来说非常重要和基础。现在不可或缺的是下一个问题,学完HTML和CSS之后我该做什么呢? 对这些问题的答案可以分为2-3个部分,你可以继续练习你的HTML和CSS编码,然后了解在学习完H…

    2025年12月24日
    000
  • 聊聊怎么利用CSS实现波浪进度条效果

    本篇文章给大家分享css 高阶技巧,介绍一下如何使用css实现波浪进度条效果,希望对大家有所帮助! 本文是 CSS Houdini 之 CSS Painting API 系列第三篇。 现代 CSS 之高阶图片渐隐消失术现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我们…

    2025年12月24日 好文分享
    200
  • 巧用距离、角度及光影制作炫酷的 3D 文字特效

    如何利用 css 实现3d立体的数字?下面本篇文章就带大家巧用视觉障眼法,构建不一样的 3d 文字特效,希望对大家有所帮助! 最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果: 这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗? 不是特…

    2025年12月24日 好文分享
    000
  • CSS高阶技巧:实现图片渐隐消的多种方法

    将专注于实现复杂布局,兼容设备差异,制作酷炫动画,制作复杂交互,提升可访问性及构建奇思妙想效果等方面的内容。 在兼顾基础概述的同时,注重对技巧的挖掘,结合实际进行运用,欢迎大家关注。 正文从这里开始。 在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样: 立即学习“前端…

    2025年12月24日 好文分享
    000
  • css实现登录按钮炫酷效果(附代码实例)

    今天在网上看到一个炫酷的登录按钮效果;初看时感觉好牛掰;但是一点一点的抛开以后发现,并没有那么难;我会将全部代码贴出来;如果有不对的地方,大家指点一哈。 分析 我们抛开before不谈的话;其实原理和就是通过背景大小以及配合位置达到颜色渐变的效果。 text-transform: uppercase…

    2025年12月24日
    000
  • CSS flex布局属性:align-items和align-content的区别

    在用flex布局时,发现有两个属性功能好像有点类似:align-items和align-content,乍看之下,它们都是用于定义flex容器中元素在交叉轴(主轴为flex-deriction定义的方向,默认为row,那么交叉轴跟主轴垂直即为column,反之它们互调,flex基本的概念如下图所示)…

    2025年12月24日 好文分享
    000
  • 手把手教你用 transition 实现短视频 APP的点赞动画

    怎么使用纯 css 实现有趣的点赞动画?下面本篇文章就带大家了解一下巧妙借助 transition实现点赞动画的方法,希望对大家有所帮助! 在各种短视频界面上,我们经常会看到类似这样的点赞动画: 非常的有意思,有意思的交互会让用户更愿意进行互动。 那么,这么有趣的点赞动画,有没有可能使用纯 CSS …

    2025年12月24日 好文分享
    000
  • 巧用CSS实现各种奇形怪状按钮(附代码)

    本篇文章带大家看看怎么使用 CSS 轻松实现高频出现的各类奇形怪状按钮,希望对大家有所帮助! 怎么样使用 CSS 实现一个内切角按钮呢、怎么样实现一个带箭头的按钮呢? 本文基于一些高频出现在设计稿中的,使用 css 实现稍微有点难度和技巧性的按钮,讲解使用 css 如何尽可能的实现它们。【推荐学习:…

    2025年12月24日 好文分享
    000
  • 原来利用纯CSS也能实现文字轮播与图片轮播!

    怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯css也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助! 今天,分享一个实际业务中能够用得上的动画技巧。【推荐学习:css视频教程】 巧用逐帧动画,配合补间动画实现一个无限循环的轮播效果,像是这样: 立即学习“前端…

    2025年12月24日 好文分享
    000
  • 总结整理:需要避坑的五大常见css错误(收藏)

    本篇文章给大家总结5个最常见的css错误,并介绍一下避坑方法,希望对大家有所帮助! 正如我们今天所知,CSS语言是web的一个重要组成部分。它使我们有能力绘制元素在屏幕、网页或其他媒体中的展示方式。 它简单、强大,而且是声明式的。我们可以很容易地实现复杂的事情,如暗黑/光明模式。然而,对它有很多误解…

    2025年12月24日
    000
  • CSS+JS实现爱心点赞按钮(代码示例)

    本篇文章给大家介绍一下css+js实现一个“爱之满满”点赞按钮的方法,希望对大家有所帮助! 前段时间在看一档说唱节目,被里面的一个说唱歌手JBcob的爱之满满这句词给洗脑了。 于是这次给大家带来一个爱之满满的点赞按钮,让大家在点赞的同时还能感受到被爱包裹的感觉。 立即学习“前端免费学习笔记(深入)”…

    2025年12月24日 好文分享
    000
  • 让人眼前一亮的五个前端小技巧

    为了让大家编程更轻松一些,本挑选一些有用的但相对比较少见有用的技巧。废话不多说,开车了。 1.快速隐藏 要隐藏一个DOM元素,不需要JavaScript。一个原生的HTML属性就足以隐藏。其效果类似于添加一个style display: none;。 该段落在页面上是不可见的,它对HTML是隐藏的。…

    2025年12月24日
    000
  • 如何实现炫酷的数字大屏

    依托强大无远开发平台,可以快速实现带各种酷炫联动效果的数字化大屏。一起来看一下吧 DEMO 地址:https://previewer.wuyuan.io/p… 配置地址:https://workbench.wuyuan.io/p… 效果图 1 效果图 2 实现步骤 1. 完成…

    2025年12月24日 好文分享
    000
  • 使用JS或CSS如何实现瀑布流布局,几种方案介绍

    本篇文章带大家了解一下瀑布流布局,介绍一下三种靠谱js方案,以及n种不靠谱css方案。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 本着实用精神,我们今天来分享一下瀑布流布局(昨天有个小兄弟问我怎么做,我找了半天没找到,啊原来写在内网了)。 演示地址: http://www.li…

    2025年12月24日 好文分享
    000
  • 详解CSS3+SVG滤镜实现不规则边框的方法

    本篇文章将介绍一种配合 svg 滤镜实现各种不规则图形添加边框的小技巧。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 需求背景,给不规则图形添加边框 在我们日常开发中,时长会遇到一些非矩形、非圆形的图案。类似下面这些: 使用纯 CSS,搭配一些技巧,是可以制作出上面的图形的,当然…

    2025年12月24日 好文分享
    000
  • 5个有用的css函数(分享)

    CSS 包含了许多函数,而且它能够完成许多早期需要用 JavaScript才能完成的事情。每年都有新的特性被添加进来,这让我们的开发更加轻松,也减少了对JavaScript的依赖。CSS 函数是它所具有的最强大的特性之一,在本文中,我将介绍一些我认为有用的函数。 (学习视频分享:css视频教程) a…

    2025年12月24日 好文分享
    000
  • CSS元素选择器的运作原理介绍

    推荐教程:CSS视频教程 在前端工程师的日常工作中,使用 CSS 元素选择器是稀松平常的事;无论你是编写一般的 CSS 还是需要经过编译的 SASS,SCSS,LESS等,最终都被编译成一行一行的 CSS 样式属性,最终交给浏览器解析并套用。但是你想过没有这是如何实现的呢? 浏览器渲染 我们先看一下…

    2025年12月24日 好文分享
    000
  • 清除浮动和闭合浮动的介绍

    本篇文章给大家带来的内容是关于清除浮动和闭合浮动的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 为什么要清除浮动 子元素 float:left; 脱离文档流,会造成父元素塌陷(撑不起来) 父元素 靠子元素撑起来 清除浮动  clear:both 清除浮动 // 伪元素 :af…

    好文分享 2025年12月24日
    000
  • 选择器(picker)插件的实现方法介绍(代码)

    本篇文章给大家带来的内容是关于选择器(picker)插件的实现方法介绍(代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 一个正常的选择器插件是非常细致的,一步一步来描述就是。手指滑动内容跟随手指滚动,当内容到底或触顶的时候就不能在滚动并且内容要一直保持在正确的位置上。 第一步…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信