搜索
首页web前端js教程构建webpack的详细介绍

构建webpack的详细介绍

Jun 13, 2018 am 10:48 AM
webpack构建流程构建项目

目前,几乎所有业务的开发构建都会用到 webpack 。所以下面这篇文章主要给大家介绍了关于webpack构建的详细流程的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

作为模块加载和打包神器,只需配置几个文件,加载各种 loader 就可以享受无痛流程化开发。但对于 webpack 这样一个复杂度较高的插件集合,它的整体流程及思想对我们来说还是很透明的。

本文旨在搞清楚从命令行下敲下 webpack 命令,或者配置 npm script 后执行 package.json 中的命令,到工程目录下出现打包的后的 bundle 文件的过程中,webpack都替我们做了哪些工作。

测试用webpack版本为 webpack@3.4.1

webpack.config.js中定义好相关配置,包括 entry、output、module、plugins等,命令行执行 webpack 命令,webpack 便会根据配置文件中的配置进行打包处理文件,并生成最后打包后的文件。

第一步:执行 webpack 命令时,发生了什么?(bin/webpack.js)

命令行执行 webpack 时,如果全局命令行中未找到webpack命令的话,执行本地的node-modules/bin/webpack.js 文件。

在bin/webpack.js中使用 yargs库 解析了命令行的参数,处理了 webpack 的配置对象 options,调用 processOptions() 函数。

// 处理编译相关,核心函数
function processOptions(options) {
 // promise风格的处理,暂时还没遇到这种情况的配置
 if(typeof options.then === "function") {...} 
 // 处理传入的options为数组的情况
 var firstOptions = [].concat(options)[0];
 var statsPresetToOptions = require("../lib/Stats.js").presetToOptions;
 // 设置输出的options
 var outputOptions = options.stats;
 if(typeof outputOptions === "boolean" || typeof outputOptions === "string") {
 outputOptions = statsPresetToOptions(outputOptions);
 } else if(!outputOptions) {
 outputOptions = {};
 }
 // 处理各种现实相关的参数
 ifArg("display", function(preset) {
 outputOptions = statsPresetToOptions(preset);
 });
 ...
 // 引入lib下的webpack.js,入口文件
 var webpack = require("../lib/webpack.js");
 // 设置最大错误追踪堆栈
 Error.stackTraceLimit = 30;
 var lastHash = null;
 var compiler;
 try {
 // 编译,这里是关键,需要进入lib/webpack.js文件查看
 compiler = webpack(options);
 } catch(e) {
 // 错误处理
 var WebpackOptionsValidationError = require("../lib/WebpackOptionsValidationError");
 if(e instanceof WebpackOptionsValidationError) {
 if(argv.color)
 console.error("\u001b[1m\u001b[31m" + e.message + "\u001b[39m\u001b[22m");
 else
 console.error(e.message);
 process.exit(1); // eslint-disable-line no-process-exit
 }
 throw e;
 }
 // 显示相关参数处理
 if(argv.progress) {
 var ProgressPlugin = require("../lib/ProgressPlugin");
 compiler.apply(new ProgressPlugin({
 profile: argv.profile
 }));
 }
 // 编译完后的回调函数
 function compilerCallback(err, stats) {}
 // watch模式下的处理
 if(firstOptions.watch || options.watch) {
 var watchOptions = firstOptions.watchOptions || firstOptions.watch || options.watch || {};
 if(watchOptions.stdin) {
 process.stdin.on("end", function() {
 process.exit(0); // eslint-disable-line
 });
 process.stdin.resume();
 }
 compiler.watch(watchOptions, compilerCallback);
 console.log("\nWebpack is watching the files…\n");
 } else
 // 调用run()函数,正式进入编译过程
 compiler.run(compilerCallback);
}

第二步: 调用 webpack,返回 compiler 对象的过程(lib/webpack.js)

如下图所示,lib/webpack.js 中的关键函数为 webpack,其中定义了编译相关的一些操作。

"use strict";
const Compiler = require("./Compiler");
const MultiCompiler = require("./MultiCompiler");
const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin");
const WebpackOptionsApply = require("./WebpackOptionsApply");
const WebpackOptionsDefaulter = require("./WebpackOptionsDefaulter");
const validateSchema = require("./validateSchema");
const WebpackOptionsValidationError = require("./WebpackOptionsValidationError");
const webpackOptionsSchema = require("../schemas/webpackOptionsSchema.json");
// 核心方法,调用该方法,返回Compiler的实例对象compiler
function webpack(options, callback) {...}
exports = module.exports = webpack;
// 设置webpack对象的常用属性
webpack.WebpackOptionsDefaulter = WebpackOptionsDefaulter;
webpack.WebpackOptionsApply = WebpackOptionsApply;
webpack.Compiler = Compiler;
webpack.MultiCompiler = MultiCompiler;
webpack.NodeEnvironmentPlugin = NodeEnvironmentPlugin;
webpack.validate = validateSchema.bind(this, webpackOptionsSchema);
webpack.validateSchema = validateSchema;
webpack.WebpackOptionsValidationError = WebpackOptionsValidationError;
// 对外暴露一些插件
function exportPlugins(obj, mappings) {...}
exportPlugins(exports, {...});
exportPlugins(exports.optimize = {}, {...});

接下来看在webpack函数中主要定义了哪些操作

// 核心方法,调用该方法,返回Compiler的实例对象compiler
function webpack(options, callback) {
 // 验证是否符合格式
 const webpackOptionsValidationErrors = validateSchema(webpackOptionsSchema, options);
 if(webpackOptionsValidationErrors.length) {
 throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
 }
 let compiler;
 // 传入的options为数组的情况,调用MultiCompiler进行处理,目前还没遇到过这种情况的配置
 if(Array.isArray(options)) {
 compiler = new MultiCompiler(options.map(options => webpack(options)));
 } else if(typeof options === "object") {
 // 配置options的默认参数
 new WebpackOptionsDefaulter().process(options);
 // 初始化一个Compiler的实例
 compiler = new Compiler();
 // 设置context的默认值为进程的当前目录,绝对路径
 compiler.context = options.context;
 // 定义compiler的options属性
 compiler.options = options;
 // Node环境插件,其中设置compiler的inputFileSystem,outputFileSystem,watchFileSystem,并定义了before-run的钩子函数
 new NodeEnvironmentPlugin().apply(compiler);
 // 应用每个插件
 if(options.plugins && Array.isArray(options.plugins)) {
 compiler.apply.apply(compiler, options.plugins);
 }
 // 调用environment插件
 compiler.applyPlugins("environment");
 // 调用after-environment插件
 compiler.applyPlugins("after-environment");
 // 处理compiler对象,调用一些必备插件
 compiler.options = new WebpackOptionsApply().process(options, compiler);
 } else {
 throw new Error("Invalid argument: options");
 }
 if(callback) {
 if(typeof callback !== "function") throw new Error("Invalid argument: callback");
 if(options.watch === true || (Array.isArray(options) && options.some(o => o.watch))) {
 const watchOptions = Array.isArray(options) ? options.map(o => o.watchOptions || {}) : (options.watchOptions || {});
 return compiler.watch(watchOptions, callback);
 }
 compiler.run(callback);
 }
 return compiler;
}

webpack函数中主要做了以下两个操作,

  • 实例化 Compiler 类。该类继承自 Tapable 类,Tapable 是一个基于发布订阅的插件架构。webpack 便是基于Tapable的发布订阅模式实现的整个流程。Tapable 中通过 plugins 注册插件名,以及对应的回调函数,通过 apply,applyPlugins,applyPluginsWater,applyPluginsAsync等函数以不同的方式调用注册在某一插件下的回调。

  • 通过WebpackOptionsApply 处理webpack compiler对象,通过 compiler.apply的方式调用了一些必备插件,在这些插件中,注册了一些 plugins,在后面的编译过程中,通过调用一些插件的方式,去处理一些流程。

第三步:调用compiler的run的过程(Compiler.js)

run()调用

run函数中主要触发了before-run事件,在before-run事件的回调函数中触发了run事件,run事件中调用了readRecord函数读取文件,并调用compile()函数进行编译。

compile()调用

compile函数中定义了编译的相关流程,主要有以下流程:

  • 创建编译参数

  • 触发 before-compile 事件,

  • 触发 compile 事件,开始编译

  • 创建 compilation对象,负责整个编译过程中具体细节的对象

  • 触发 make 事件,开始创建模块和分析其依赖

  • 根据入口配置的类型,决定是调用哪个plugin中的 make 事件的回调。如单入口的 entry,调用的是SingleEntryPlugin.js下 make 事件注册的回调函数,其他多入口同理。

  • 调用 compilation 对象的 addEntry 函数,创建模块以及依赖。

  • make 事件的回调函数中,通过seal 封装构建的结果

  • run 方法中定义的 onCompiled回调函数被调用,完成emit过程,将结果写入至目标文件

compile函数的定义

compile(callback) {
 // 创建编译参数,包括模块工厂和编译依赖参数数组
 const params = this.newCompilationParams();
 // 触发before-compile 事件,开始整个编译过程
 this.applyPluginsAsync("before-compile", params, err => {
 if(err) return callback(err);
 // 触发compile事件
 this.applyPlugins("compile", params);
 // 构建compilation对象,compilation对象负责具体的编译细节
 const compilation = this.newCompilation(params);
 // 触发make事件,对应的监听make事件的回调函数在不同的EntryPlugin中注册,比如singleEntryPlugin
 this.applyPluginsParallel("make", compilation, err => {
 if(err) return callback(err);
 compilation.finish();
 compilation.seal(err => {
 if(err) return callback(err);
 this.applyPluginsAsync("after-compile", compilation, err => {
 if(err) return callback(err);
 return callback(null, compilation);
 });
 });
 });
 });
}

【问题】make 事件触发后,有哪些插件中注册了make事件并得到了运行的机会呢?

以单入口entry配置为例,在EntryOptionPlugin插件中定义了,不同配置的入口应该调用何种插件进行解析。不同配置的入口插件中注册了对应的 make 事件回调函数,在make事件触发后被调用。

如下所示:

一个插件的apply方法是一个插件的核心方法,当说一个插件被调用时主要是其apply方法被调用。

EntryOptionPlugin 插件在webpackOptionsApply中被调用,其内部定义了使用何种插件来解析入口文件。

const SingleEntryPlugin = require("./SingleEntryPlugin");
const MultiEntryPlugin = require("./MultiEntryPlugin");
const DynamicEntryPlugin = require("./DynamicEntryPlugin");
module.exports = class EntryOptionPlugin {
 apply(compiler) {
 compiler.plugin("entry-option", (context, entry) => {
 function itemToPlugin(item, name) {
 if(Array.isArray(item)) {
 return new MultiEntryPlugin(context, item, name);
 } else {
 return new SingleEntryPlugin(context, item, name);
 }
 }
 // 判断entry字段的类型去调用不同的入口插件去处理
 if(typeof entry === "string" || Array.isArray(entry)) {
 compiler.apply(itemToPlugin(entry, "main"));
 } else if(typeof entry === "object") {
 Object.keys(entry).forEach(name => compiler.apply(itemToPlugin(entry[name], name)));
 } else if(typeof entry === "function") {
 compiler.apply(new DynamicEntryPlugin(context, entry));
 }
 return true;
 });
 }
};

entry-option 事件被触发时,EntryOptionPlugin 插件做了这几个事情:

判断入口的类型,通过 entry 字段来判断,对应了 entry 字段为 string object function的三种情况

每种不同的类型调用不同的插件去处理入口的配置。大致处理逻辑如下:

  • 数组类型的entry调用multiEntryPlugin插件去处理,对应了多入口的场景

  • function的entry调用了DynamicEntryPlugin插件去处理,对应了异步chunk的场景

  • string类型的entry或者object类型的entry,调用SingleEntryPlugin去处理,对应了单入口的场景

【问题】entry-option 事件是在什么时机被触发的呢?

如下代码所示,是在WebpackOptionsApply.js中,先调用处理入口的EntryOptionPlugin插件,然后触发 entry-option 事件,去调用不同类型的入口处理插件。

注意:调用插件的过程也就是一个注册事件以及回调函数的过程。

WebpackOptionApply.js

// 调用处理入口entry的插件
compiler.apply(new EntryOptionPlugin());
compiler.applyPluginsBailResult("entry-option", options.context, options.entry);

前面说到,make事件触发时,对应的回调逻辑都在不同配置入口的插件中注册的。下面以SingleEntryPlugin为例,说明从 make 事件被触发,到编译结束的整个过程。

SingleEntryPlugin.js

class SingleEntryPlugin {
 constructor(context, entry, name) {
 this.context = context;
 this.entry = entry;
 this.name = name;
 }
 apply(compiler) {
 // compilation 事件在初始化Compilation对象的时候被触发
 compiler.plugin("compilation", (compilation, params) => {
 const normalModuleFactory = params.normalModuleFactory;
 compilation.dependencyFactories.set(SingleEntryDependency, normalModuleFactory);
 });
 // make 事件在执行compile的时候被触发
 compiler.plugin("make", (compilation, callback) => {
 const dep = SingleEntryPlugin.createDependency(this.entry, this.name);
 // 编译的关键,调用Compilation中的addEntry,添加入口,进入编译过程。
 compilation.addEntry(this.context, dep, this.name, callback);
 });
 }
 static createDependency(entry, name) {
 const dep = new SingleEntryDependency(entry);
 dep.loc = name;
 return dep;
 }
}
module.exports = SingleEntryPlugin;

Compilation中负责具体编译的细节,包括如何创建模块以及模块的依赖,根据模板生成js等。如:addEntry,buildModule, processModuleDependencies等。

Compilation.js

addEntry(context, entry, name, callback) {
 const slot = {
 name: name,
 module: null
 };
 this.preparedChunks.push(slot);
 // 添加该chunk上的module依赖
 this._addModuleChain(context, entry, (module) => {
 entry.module = module;
 this.entries.push(module);
 module.issuer = null;
 }, (err, module) => {
 if(err) {
 return callback(err);
 }
 if(module) {
 slot.module = module;
 } else {
 const idx = this.preparedChunks.indexOf(slot);
 this.preparedChunks.splice(idx, 1);
 }
 return callback(null, module);
 });
}
_addModuleChain(context, dependency, onModule, callback) {
 const start = this.profile && Date.now();
 ...
 // 根据模块的类型获取对应的模块工厂并创建模块
 const moduleFactory = this.dependencyFactories.get(dependency.constructor);
 ...
 // 创建模块,将创建好的模块module作为参数传递给回调函数
 moduleFactory.create({
 contextInfo: {
 issuer: "",
 compiler: this.compiler.name
 },
 context: context,
 dependencies: [dependency]
 }, (err, module) => {
 if(err) {
 return errorAndCallback(new EntryModuleNotFoundError(err));
 }
 let afterFactory;
 if(this.profile) {
 if(!module.profile) {
 module.profile = {};
 }
 afterFactory = Date.now();
 module.profile.factory = afterFactory - start;
 }
 const result = this.addModule(module);
 if(!result) {
 module = this.getModule(module);
 onModule(module);
 if(this.profile) {
 const afterBuilding = Date.now();
 module.profile.building = afterBuilding - afterFactory;
 }
 return callback(null, module);
 }
 if(result instanceof Module) {
 if(this.profile) {
 result.profile = module.profile;
 }
 module = result;
 onModule(module);
 moduleReady.call(this);
 return;
 }
 onModule(module);
 // 构建模块,包括调用loader处理文件,使用acorn生成AST,遍历AST收集依赖
 this.buildModule(module, false, null, null, (err) => {
 if(err) {
 return errorAndCallback(err);
 }
 if(this.profile) {
 const afterBuilding = Date.now();
 module.profile.building = afterBuilding - afterFactory;
 }
  // 开始处理收集好的依赖
 moduleReady.call(this);
 });
 function moduleReady() {
 this.processModuleDependencies(module, err => {
 if(err) {
 return callback(err);
 }
 return callback(null, module);
 });
 }
 });
}

_addModuleChain 主要做了以下几件事情:

  • 调用对应的模块工厂类去创建module

  • buildModule,开始构建模块,收集依赖。构建过程中最耗时的一步,主要完成了调用loader处理模块以及模块之间的依赖,使用acorn生成AST的过程,遍历AST循环收集并构建依赖模块的过程。此处可以深入了解webpack使用loader处理模块的原理。

第四步:模块build完成后,使用seal进行module和chunk的一些处理,包括合并、拆分等。

Compilation的 seal 函数在 make 事件的回调函数中进行了调用。

seal(callback) {
 const self = this;
 // 触发seal事件,提供其他插件中seal的执行时机
 self.applyPlugins0("seal");
 self.nextFreeModuleIndex = 0;
 self.nextFreeModuleIndex2 = 0;
 self.preparedChunks.forEach(preparedChunk => {
 const module = preparedChunk.module;
 // 将module保存在chunk的origins中,origins保存了module的信息
 const chunk = self.addChunk(preparedChunk.name, module);
 // 创建一个entrypoint
 const entrypoint = self.entrypoints[chunk.name] = new Entrypoint(chunk.name);
 // 将chunk创建的chunk保存在entrypoint中,并将该entrypoint的实例保存在chunk的entrypoints中
 entrypoint.unshiftChunk(chunk);
 // 将module保存在chunk的_modules数组中
 chunk.addModule(module);
 // module实例上记录chunk的信息
 module.addChunk(chunk);
 // 定义该chunk的entryModule属性
 chunk.entryModule = module;
 self.assignIndex(module);
 self.assignDepth(module);
 self.processDependenciesBlockForChunk(module, chunk);
 });
 self.sortModules(self.modules);
 self.applyPlugins0("optimize");
 while(self.applyPluginsBailResult1("optimize-modules-basic", self.modules) ||
 self.applyPluginsBailResult1("optimize-modules", self.modules) ||
 self.applyPluginsBailResult1("optimize-modules-advanced", self.modules)) { /* empty */ }
 self.applyPlugins1("after-optimize-modules", self.modules);
 while(self.applyPluginsBailResult1("optimize-chunks-basic", self.chunks) ||
 self.applyPluginsBailResult1("optimize-chunks", self.chunks) ||
 self.applyPluginsBailResult1("optimize-chunks-advanced", self.chunks)) { /* empty */ }
 self.applyPlugins1("after-optimize-chunks", self.chunks);
 self.applyPluginsAsyncSeries("optimize-tree", self.chunks, self.modules, function sealPart2(err) {
 if(err) {
 return callback(err);
 }
 self.applyPlugins2("after-optimize-tree", self.chunks, self.modules);
 while(self.applyPluginsBailResult("optimize-chunk-modules-basic", self.chunks, self.modules) ||
 self.applyPluginsBailResult("optimize-chunk-modules", self.chunks, self.modules) ||
 self.applyPluginsBailResult("optimize-chunk-modules-advanced", self.chunks, self.modules)) { /* empty */ }
 self.applyPlugins2("after-optimize-chunk-modules", self.chunks, self.modules);
 const shouldRecord = self.applyPluginsBailResult("should-record") !== false;
 self.applyPlugins2("revive-modules", self.modules, self.records);
 self.applyPlugins1("optimize-module-order", self.modules);
 self.applyPlugins1("advanced-optimize-module-order", self.modules);
 self.applyPlugins1("before-module-ids", self.modules);
 self.applyPlugins1("module-ids", self.modules);
 self.applyModuleIds();
 self.applyPlugins1("optimize-module-ids", self.modules);
 self.applyPlugins1("after-optimize-module-ids", self.modules);
 self.sortItemsWithModuleIds();
 self.applyPlugins2("revive-chunks", self.chunks, self.records);
 self.applyPlugins1("optimize-chunk-order", self.chunks);
 self.applyPlugins1("before-chunk-ids", self.chunks);
 self.applyChunkIds();
 self.applyPlugins1("optimize-chunk-ids", self.chunks);
 self.applyPlugins1("after-optimize-chunk-ids", self.chunks);
 self.sortItemsWithChunkIds();
 if(shouldRecord)
 self.applyPlugins2("record-modules", self.modules, self.records);
 if(shouldRecord)
 self.applyPlugins2("record-chunks", self.chunks, self.records);
 self.applyPlugins0("before-hash");
 // 创建hash
 self.createHash();
 self.applyPlugins0("after-hash");
 if(shouldRecord)
 self.applyPlugins1("record-hash", self.records);
 self.applyPlugins0("before-module-assets");
 self.createModuleAssets();
 if(self.applyPluginsBailResult("should-generate-chunk-assets") !== false) {
 self.applyPlugins0("before-chunk-assets");
 // 使用template创建最后的js代码
 self.createChunkAssets();
 }
 self.applyPlugins1("additional-chunk-assets", self.chunks);
 self.summarizeDependencies();
 if(shouldRecord)
 self.applyPlugins2("record", self, self.records);
 self.applyPluginsAsync("additional-assets", err => {
 if(err) {
 return callback(err);
 }
 self.applyPluginsAsync("optimize-chunk-assets", self.chunks, err => {
 if(err) {
 return callback(err);
 }
 self.applyPlugins1("after-optimize-chunk-assets", self.chunks);
 self.applyPluginsAsync("optimize-assets", self.assets, err => {
 if(err) {
 return callback(err);
 }
 self.applyPlugins1("after-optimize-assets", self.assets);
 if(self.applyPluginsBailResult("need-additional-seal")) {
 self.unseal();
 return self.seal(callback);
 }
 return self.applyPluginsAsync("after-seal", callback);
 });
 });
 });
 });
}

在 seal 中可以发现,调用了很多不同的插件,主要就是操作chunk和module的一些插件,生成最后的源代码。其中 createHash 用来生成hash,createChunkAssets 用来生成chunk的源码,createModuleAssets 用来生成Module的源码。在 createChunkAssets 中判断了是否是入口chunk,入口的chunk用mainTemplate生成,否则用chunkTemplate生成。

第五步:通过 emitAssets 将生成的代码输入到output的指定位置

在compiler中的 run 方法中定义了compile的回调函数 onCompiled, 在编译结束后,会调用该回调函数。在该回调函数中调用了 emitAsset,触发了 emit 事件,将文件写入到文件系统中的指定位置。

总结

webpack的源码通过采用Tapable控制其事件流,并通过plugin机制,在webpack构建过程中将一些事件钩子暴露给plugin,使得开发者可以通过编写相应的插件来自定义打包。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用Vue.js 2.0如何实现背景视频登录页面

使用Vue开发时间转换指令该怎么做?

angularjs中如何实现页面自适应?

以上是构建webpack的详细介绍的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

Python vs. JavaScript:开发环境和工具Python vs. JavaScript:开发环境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

JavaScript是用C编写的吗?检查证据JavaScript是用C编写的吗?检查证据Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

JavaScript的角色:使网络交互和动态JavaScript的角色:使网络交互和动态Apr 24, 2025 am 12:12 AM

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C和JavaScript:连接解释C和JavaScript:连接解释Apr 23, 2025 am 12:07 AM

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

从网站到应用程序:JavaScript的不同应用从网站到应用程序:JavaScript的不同应用Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python vs. JavaScript:比较用例和应用程序Python vs. JavaScript:比较用例和应用程序Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器