当前位置: 首页 > news >正文

怎样优古网络公司网站后台长春专业网站建设公司排名

怎样优古网络公司网站后台,长春专业网站建设公司排名,东莞网站营销,常用的软件开发文档有哪些大家好#xff0c;我是若川。持续组织了6个月源码共读活动#xff0c;感兴趣的可以点此加我微信 ruochuan02 参与#xff0c;每周大家一起学习200行左右的源码#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列esno我… 大家好我是若川。持续组织了6个月源码共读活动感兴趣的可以点此加我微信 ruochuan02 参与每周大家一起学习200行左右的源码共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列esno我在我的很多源码文章中提到过但没有写文章分享这篇好文章。Node.js 并不支持直接执行 TS 文件如果要执行 TS 文件的话我们就可以借助 ts-node 这个库。相信有些小伙伴在工作中也用过这个库关于 ts-node 这个库的相关内容我就不展开介绍了因为本文的主角是由 antfu 大佬开源的 esno 项目接下来我将带大家一起来揭开这个项目背后的秘密。阅读完本文后你将了解 esno 项目是如何执行 TS 文件。此外你还会了解如何劫持 Node.js 的 require 函数、如何为 ES Module 的 import 语句添加钩子及如何自定义 https 加载器以支持 import React from https://esm.sh/react 导入方式。esno 是什么esno 是基于 esbuild 的 TS/ESNext node 运行时。该库会针对不同的模块化标准采用不同的方案esno - Node in CJS mode - by esbuild-registeresmo - Node in ESM mode - by esbuild-node-loader使用 esno 的方式很简单你可以以全局或局部的方式来安装它全局安装$ npm i -g esno在安装成功后你就可以通过以下方式来直接执行 TS 文件$ esno index.ts $ esmo index.ts局部安装$ npm i esno而对于局部安装的方式来说一般情况下我们会以 npm scripts 的方式来使用它{scripts: {start: esno index.ts},dependencies: {esno: 0.14.0} }esno 是如何工作的在开始分析 esno 的工作原理之前我们先来熟悉一下该项目├── LICENSE ├── README.md ├── esmo.mjs ├── esno.js ├── package.json ├── pnpm-lock.yaml ├── publish.ts └── tsconfig.json观察以上的项目结构可知该项目并不会复杂。在项目根目录下的 package.json 文件中我们看到了前面介绍的 esno 和 esmo 命令。{bin: {esno: esno.js,esmo: esmo.mjs}, }此外在 package.json 的 scripts 字段中我们发现了 release 命令。顾名思义该命令用来发布版本。{scripts: {release: npx bumpp --tag --commit --push  node esmo.mjs publish.ts}, }需要注意的是在 publish.ts 文件中使用到了 2021 年度 Github 上最耀眼的项目 zx利用该项目我们可以轻松地编写命令行脚本。写作本文时它的 Star 数已经高达 27.5K强烈推荐感兴趣的小伙伴关注一下该项目。简单介绍了 esno 项目之后接下来我们来分析 esno.js 文件#!/usr/bin/env nodeconst spawn  require(cross-spawn) const spawnSync  spawn.syncconst register  require.resolve(esbuild-register)const argv  process.argv.slice(2)process.exit(spawnSync(node, [-r, register, ...argv], { stdio: inherit }).status)由以上代码可知当执行 esno index.ts 命令后会通过 spawnSync 来启动 Node.js 程序执行脚本。需要注意的是在执行时使用了 -r 选项该选项的作用是预加载模块-r, --require  ... module to preload (option can be repeated)这里预加载的模块是 esbuild-register该模块就是 esno 命令执行 TS 文件的幕后英雄。esbuild-register 是什么esbuild-register 是一个基于 esbuild 来转换 JSX、TS 和 esnext 特性的工具。你可以通过以下多种方式来安装它$ npm i esbuild esbuild-register -D # Or Yarn $ yarn add esbuild esbuild-register --dev # Or pnpm $ pnpm add esbuild esbuild-register -D在成功安装该模块之后就可以在命令行中直接通过 node 应用程序来执行 ts 文件$ node -r esbuild-register file.ts -r, --require    ... module to preload (option can be repeated)-r 用于指定预加载的文件即在执行 file.ts 文件前提前加载 esbuild-register 模块它将会使用 tsconfig.json 中的 jsxFactory, jsxFragmentFactory 和 target 配置项来执行转换操作。esbuild-register 不仅可以在命令行中使用而且还可以通过 API 的方式进行使用const { register }  require(esbuild-register/dist/node)const { unregister }  register({// ...options })// Unregister the require hook if you dont need it anymore unregister()了解完 esbuild-register 的基本使用之后接下来我们来分析它内部是如何工作的。esbuild-register 是如何工作的esbuild-register 内部利用了 pirates 这个库来劫持 Node.js 的 require 函数从而让你可以在命令行中直接执行 ts 文件。下面我们来看一下 esbuild-register 模块中定义的 register 函数// esbuild-register/src/node.ts import { transformSync, TransformOptions } from esbuild import { addHook } from piratesexport function register(esbuildOptions: RegisterOptions  {}) {const {extensions  DEFAULT_EXTENSIONS,hookIgnoreNodeModules  true,hookMatcher,...overrides}  esbuildOptions// 利用 transformSync const compile: COMPILE  function compile(code, filename, format) {const dir  dirname(filename)const options  getOptions(dir)format  format ?? inferPackageFormat(dir, filename)const {code: js,warnings,map: jsSourceMap,}  transformSync(code, {sourcefile: filename,sourcemap: both,loader: getLoader(filename),target: options.target,jsxFactory: options.jsxFactory,jsxFragment: options.jsxFragment,format,...overrides,})// 省略部分代码}const revert  addHook(compile, {exts: extensions,ignoreNodeModules: hookIgnoreNodeModules,matcher: hookMatcher,})return {unregister() {revert()},} }观察以上的代码可知在 register 函数内部是利用 esbuild 模块提供的 transformSync API 来实现 ts - js 代码的转换。其实最关键的环节还是通过调用 pirates 这个库提供的 addHook 函数来注册编译 ts 文件的钩子。那么 addHook 函数内部到底做了哪些处理呢下面我们来看一下它的实现// pirates-4.0.5/src/index.js export function addHook(hook, opts  {}) {let reverted  false;const loaders  []; // 存放新的loaderconst oldLoaders  []; // 存放旧的loaderlet exts;const originalJSLoader  Module._extensions[.js]; // 原始的JS Loader // 省略部分代码exts.forEach((ext)  {// 获取已注册的loader若未找到则默认使用JS Loaderconst oldLoader  Module._extensions[ext] || originalJSLoader;oldLoaders[ext]  Module._extensions[ext];loaders[ext]  Module._extensions[ext]  function newLoader(mod, filename) {let compile;if (!reverted) {if (shouldCompile(filename, exts, matcher, ignoreNodeModules)) {compile  mod._compile;mod._compile  function _compile(code) {// 这里需要恢复成原来的_compile函数否则会出现死循环mod._compile  compile;// 在编译前先执行用户自定义的hook函数const newCode  hook(code, filename);if (typeof newCode ! string) {throw new Error(HOOK_RETURNED_NOTHING_ERROR_MESSAGE);}return mod._compile(newCode, filename);};}}oldLoader(mod, filename);};}); }其实 addHook 函数的实现并不会复杂该函数内部就是通过替换 mod._compile 方法来实现钩子的功能。即在调用原始的 mod._compile 方法进行编译前会先调用 hook(code, filename) 函数来执行用户自定义的 hook 函数从而对代码进行预处理。而对于 esbuild-register 库中的 register 函数来说当 hook 函数执行时就会调用该函数内部定义的 compile 函数来编译 ts 代码然后再调用mod._compile 方法编译生成的 js 代码。关于 esbuild-register 和 pirates 这两个库的内容就先介绍到这里如果你想详细了解 pirates 这个库是如何工作的可以阅读 如何为 Node.js 的 require 函数添加钩子 这篇文章。现在我们已经分析完 esno.js 文件接下来我们来分析 esmo.mjs 文件。esmo 是如何工作的esmo 命令对应的是 esmo.mjs 文件#!/usr/bin/env nodeimport spawn from cross-spawn import { resolve } from import-meta-resolve const spawnSync  spawn.syncconst argv  process.argv.slice(2) resolve(esbuild-node-loader, import.meta.url).then((path)  {process.exit(spawnSync(node, [--loader, path, ...argv], { stdio: inherit }).status) })由以上代码可知当使用 node 应用程序执行 ES Module 文件时会通过 --loader 选项来指定自定义的 ES Module 加载器。--loader, --experimental-loader  ... use the specified module as a custom loader需要注意的是通过 --loader 选项指定的自定义加载器只适用于 ES Module 的 import 调用并不适用于 CommonJS 的 require 调用。那么自定义加载器有什么作用呢在当前最新的 Node.js v17.4.0 版本中还不支持以 https:// 开头的说明符。我们可以在自定义加载器中利用 Node.js 提供的钩子机制让 Node.js 可以使用 import 导入以 https:// 协议开头的 ES 模块。在分析如何自定义 https 资源加载器前我们需要先介绍一下 import 说明符的概念。import 说明符import 语句的说明符是 from 关键字之后的字符串例如 import { sep } from path 中的 path。说明符也用于 export from 语句并作为 import() 表达式的参数。有三种类型的说明符相对说明符如 ./startup.js 或 ../config.mjs。它们指的是相对于导入文件位置的路径。对于这种类型文件扩展名是必须的。裸说明符如 some-package 或 some-package/shuffle。它们可以通过包名来引用包的主入口点。当包没有 exports 字段的时候才需要包含文件扩展名。绝对说明符如 file:///opt/nodejs/config.js。它们直接且明确地引用完整路径。裸说明符解析由 Node.js 模块解析算法处理所有其他说明符解析始终仅使用标准的相对URL 解析语义进行解析。和 CommonJS 一样包内的模块文件可以通过在包名上添加路径来访问除非包的 package.json 包含一个 exports 字段在这种情况下包中的文件只能通过 exports 中定义的路径访问。介绍完 import 说明符之后接下来我们来看一下如何自定义 https 加载器。自定义 https 加载器resolve 钩子resolve 钩子用于根据模块的说明符和 parentURL 生成导入目标的绝对路径调用该钩子后会返回一个包含 format可选 和 url 属性的对象。// https-loader.mjs import { get } from https;export function resolve(specifier, context, defaultResolve) {const { parentURL  null }  context;if (specifier.startsWith(https://)) {return {url: specifier};} else if (parentURL  parentURL.startsWith(https://)) {return {url: new URL(specifier, parentURL).href};}// 让 Node.js 处理其它的说明符return defaultResolve(specifier, context, defaultResolve); }在以上代码中会先判断 specifier 字符串是否以 https:// 开头如果条件满足的话该字符串的值直接作为 url 属性的值直接返回 { url: specifier } 对象。否则会判断 parentURL 是否以 https:// 开头如果条件满足的话则会调用 URL 构造函数创建 URL 对象。parentURL 是从 context 对象上获取的那它什么时候会有值呢假设在 ES 模块 A 中以相对路径的形式导入 ES 模块 B。在导入 ES 模块 B 时也会调用 resolve 钩子此时 context 对象上的 parentURL 就会有值。load 钩子load 钩子用于定义应该如何解释、检索和解析 URL 的方法调用该方法后会返回包含 format 和 source 属性的对象。其中 format 属性值只能是 builtin、commonjs、json、module 和 wasm 中的一种。而 source 属性值的类型可以为 string、ArrayBuffer 或 TypedArray。import { get } from https;export function load(url, context, defaultLoad) {if (url.startsWith(https://)) {return new Promise((resolve, reject)  {get(url, (res)  {let data  ;res.on(data, (chunk)  data  chunk);res.on(end, ()  resolve({format: module,source: data,}));}).on(error, (err)  reject(err));});}// 让 Node.js 加载其它类型的文件return defaultLoad(url, context, defaultLoad); }在以上代码中会通过 https 模块中的 get 函数来加载 https:// 协议的 ES 模块。如果不是以 https:// 开头则会使用默认的加载器来加载其它类型的文件。创建完 https-loader 之后我们来测试一下该加载器。首先创建一个 main.mjs 文件并输入以下内容// main.mjs import React from https://esm.sh/react17.0.2console.dir(React);然后在命令行输入以下命令$ node --experimental-loader ./https-loader.mjs ./main.mjs当以上命令成功运行之后控制台会输出以下内容{Fragment: Symbol(react.fragment),StrictMode: Symbol(react.strict_mode),Profiler: Symbol(react.profiler),Suspense: Symbol(react.suspense),... }了解完以上的内容后我们回过头来看一下 esmo.mjs 文件中所使用的 esbuild-node-loader 模块。下面我们来简单分析一下 load 钩子// loader.mjsesbuild-node-loader v0.6.4 export function load(url, context, defaultLoad) {if (extensionsRegex.test(new URL(url).pathname)) {const { format }  context;let filename  url;if (!isWindows) filename  fileURLToPath(url);const rawSource  fs.readFileSync(new URL(url), { encoding: utf8 });const { js }  esbuildTransformSync(rawSource, filename, url, format);return {format: module,source: js,};}// Let Node.js handle all other format / sources.return defaultLoad(url, context, defaultLoad); }通过观察以上代码我们可知 load 钩子的核心处理流程可以分为两个步骤步骤一使用 fs.readFileSync 方法读取文件资源的内容步骤二使用 esbuildTransformSync 函数对源代码进行转换。而在 esbuildTransformSync 函数中使用了 esbuild 模块提供的 transformSync 函数来实现代码的转换。该函数的相关代码如下所示// loader.mjsesbuild-node-loader v0.6.4 function esbuildTransformSync(rawSource, filename, url, format) {const {code: js,warnings,map: jsSourceMap,}  transformSync(rawSource.toString(), {sourcefile: filename,sourcemap: both,loader: new URL(url).pathname.match(extensionsRegex)[1],target: node${process.versions.node}, format: format  module ? esm : cjs,});// 省略部分代码return { js, jsSourceMap }; }关于 transformSync 函数的使用方式我就不展开介绍了。感兴趣的小伙伴可以自行阅读一下 esbuild 官网上的相关文档。好的esno 这个项目就介绍到这里。如果你对 Node.js 平台下的 require 和 import hook 机制感兴趣的话可以详细阅读一下 pirates、esbuild-register 和 esbuild-node-loader 这几个项目的源码。若有遇到问题的话可以跟阿宝哥交流哟。参考资源esbuild 官网Node.js 官网 - ESM如何为 Node.js 的 require 函数添加钩子················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇在知乎、掘金收获超百万阅读。从2014年起每年都会写一篇年度总结已经写了7篇点击查看年度总结。同时最近组织了源码共读活动帮助3000前端人学会看源码。公众号愿景帮助5年内前端人走向前列。识别上方二维码加我微信、拉你进源码共读群今日话题略。分享、收藏、点赞、在看我的文章就是对我最大的支持~
http://www.sadfv.cn/news/352861/

相关文章:

  • 站长工具推荐seo综合查询网站品牌推广策略
  • 网站上线后所要做的事情微信推广网站建设
  • 网站logo尺寸一般多大深圳空间设计有限公司
  • 广州市省建设厅网站域名注销期间网站还能打开吗
  • 大学校园网站模板图片wordpress 清空数据库表
  • 网站建设要经历哪些步骤深圳公司有哪些
  • 自己建立网站怎么搞汕头建网站
  • 石家庄智能网站建设如何做广告宣传与推广
  • 个人网站可以做电商么网站 收费
  • 网站被k十大原因jsp网站搭建
  • 开创云网站建设支持天津河东网站建设公司
  • dedecms+wordpress兰州seo技术优化排名公司
  • 天津网站建设noajt出入南京最新通知今天
  • vs做网站怎么调试专业网站建设公司首选公司
  • 电子商务创建网站雄安做网站
  • 河曲县城乡建设管理局网站网站建设项目实施方案
  • 可以做贺卡的网站网站建设费用 无形资产
  • 交友营销型网站北京住房投资建设中心网站首
  • 保健食品东莞网站建设闵行区seo快速排名优化哪里好
  • 个人怎么自己建网站河源seo
  • 广州网站推广解决方案换物网站为什么做不起来
  • 东阿网站建设公司系统显示没有安装wordpress
  • 牛商网营销型网站建设商标设计网免费
  • 天津建设银行公积金缴费网站河南项目信息网
  • 2015微信网站wordpress 如何发布
  • 网站建设总体设计代理公司是干什么的
  • 南京网站建设服务站内优化怎么做
  • 企业网站源码哪个最好网络营销导向的网站建设
  • 广告推广平台网站有哪些效果图制作收费标准
  • 湖南网站制作公司推荐做华为网站的还有哪些功能