英文:
use the same ES6 import both node and web pages for parallelize libs
问题
👋 你好开发者!
我目前正在尝试使用仅使用React和htm创建一个模板引擎。这个想法有点特别,所以不使用CRA,不使用Nextjs,也不使用其他框架或库。所以,这是一种困难的方式。
我需要理解为什么无法在服务器端渲染和客户端渲染中加载相同的库。
NodeJs 的设置是 "type: module",而ssr脚本与csr脚本相等,因为它是它的预运行打印版本。
我为了在Node.js和网页上都运行所有库而疯狂。
示例:
import React from 'react'
import ReactDOM from 'react-dom/client'
import ReactDOMServer from 'react-dom/server'
import htm from 'htm'
const html = htm.bind(React.createElement)
// 它在Node中间件上运行,但在Web上不运行,结果是:
// Uncaught TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../";
文档以与我代码中看到的相同的模式导入它,为什么他可以这样做?
https://github.com/developit/htm
https://react.dev/learn/add-react-to-an-existing-project
<br><hr><br>
目标:找到一种加载和并行化库以及正确设置React和htm在服务器端和客户端网页之间的解决方案或正确方法。
英文:
👋 Hi devs!
Currently i am trying to create a template engine with only react and htm. It's think out the box, so, no CRA, no Nextjs, no other framework or library. So, It's an hard way.
I need to undestand why i can't load the same librs from server-side-rendered into client-side-rendering.
NodeJs is setted in "type:module" and the ssr scripts is equal to that csr because it's the pre-runned printed version of it.
I'm going crazy for run all libs both in nodejs and on the web page.
exemple:
import React from 'react'
import ReactDOM from 'react-dom/client'
import ReactDOMServer from 'react-dom/server'
import htm from 'htm'
const html = htm.bind(React.createElement)
// it run on node middleware, it doesn't run on web and result is:
// Uncaught TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../"
The docs import it in the same mode as you see in my codes, why he can do it???
https://github.com/developit/htm
https://react.dev/learn/add-react-to-an-existing-project
<br><hr><br>
Goal: Finding a solution or correct approach for load and parallize the libraries and set correctly react and htm between server side and client web side.
答案1
得分: 1
你的浏览器无法从react
中导入。浏览器(如ESM Node.js)使用相同的导入语法,但有一个很大的区别:Node了解node_modules
,但浏览器不了解。
通常的解决方案是使用像webpack、vite等打包工具。
如果你想要构建无需打包的通用代码,并且还想使用现有的包,那将非常困难。你也许可以尝试使用导入映射来做一些事情,但如果你完全想避免构建步骤,能否使用导入映射将取决于这些特定包是如何构建的。这肯定会是一项挑战(但也许不是不可能的)。
英文:
Your browser can't import from react
. Browsers (like ESM Node.js) use the same syntax for imports, but there's one really large difference: Node knows about node_modules
, but browsers do not.
The typical solution for this is indeed a bundler like webpack, vite, etc.
If you're looking for a way to build universal code without a bundler, it's going to be very hard if you also want to use existing packages. You may be able to do something with import maps but if you're completely trying to avoid a build step, being able to use import maps is going to depend on how these specific packages are built. It will for sure be a challenge (but perhaps not impossible).
答案2
得分: 0
解决方案:
第一种基本解决方案:
<b><i>警告:下面的方法对于模块不起作用!一些模块是纯脚本,另一些是CommonJS,其他的是UMD... 它们没有适用于这种方式的兼容性。</i></b>
"在将模块静态化后,设置所有的相对路径"
yourexpress.use( '/node_modules/', express.static( yourbaseroot+'/node_modules/') )
import React from '../node_modules/react/index.js'
import { hydrateRoot } from '../node_modules/react-dom/index.js'
import { renderToString } from '../node_modules/react-dom/server.js'
import { html } from '../node_modules/htm/react/index.js'
<br><hr><br>
第二种(更好的)方法:
更好的解决方法之一(或仅有的解决方法)是创建一个跨加载函数。在我的情况下,我创建了一个异步导入器,它是第一个良好的解决方案。
// 导入器:
async function importModule(modulePath) {
if(typeof window === 'undefined'){
return require(modulePath)
} else {
const module = await import(modulePath)
return module.default || module
}
}
//用法:
const MyReactComponent = async (props) => {
var React, MyLib
if(typeof window === 'undefined'){
React = await importModule('react')
MyLib = await importModule('MyLib')
} else {
React = window.React
MyLib = window.MyLib
}
// 做...
}
无论如何,你需要使用将你的应用程序的一部分导出到全局window对象的可怕解决方案(比如构建工具,例如webpack)。
<br><hr><br>
其他注意事项、方法和胡言乱语
在尝试不同的方法之后,我明白正确的方法是尊重导入/导出类型,而不是通过Node.js模块路径直接加载库,这是不切实际的绝对主义。然而,如果你在同一页中使用CommonJS和ES6,且想要共享某些内容,它们是无法和解的敌人。一个锁定另一个,反之亦然,形成递归循环。
因此,理解我们想要做的事情的文件结构是个好主意,设置Node.js以支持CommonJS,并在服务器和Web窗口对象上并行化你的库。
function myLibOrComponent () { ... }
typeof window === 'undefined' ?
module.exports = myLibOrComponent : // Node导出
window.myLibOrComponent = myLibOrComponent // Web导出
然后以相同的方式获取它... 例如:
const myLibOrComponent = typeof window === 'undefined' ?
require('myLibOrComponent ') : // Node导入
window.myLibOrComponent // Web全局对象
组件的另一种方法是:
import('./myLibOrComponent.js').then( () => { 做... })
const myLibOrComponent = React.lazy(() => import('./myLibOrComponent'))
将导出到window或self(类似webpack的全局对象)不是一个好的做法,但不幸的是,我找不到其他方式来调和两种类型的脚本,这些脚本适用于两种(或更多)类型的导入方式。
英文:
Solutions:
First Basic Solution:
<b><i>Warning: THE APPROACH BELOW FOR MODULES CAN'T WORK! Some modules are plain scripts, some else are cjs, other umd... Its doesn't have a compatibility for taken this way.</i></b>
"set all in relative path after make static the modules"
yourexpress.use( '/node_modules/', express.static( yourbaseroot+'/node_modules/') )
import React from '../node_modules/react/index.js'
import { hydrateRoot } from '../node_modules/react-dom/index.js'
import { renderToString } from '../node_modules/react-dom/server.js'
import { html } from '../node_modules/htm/react/index.js'
<br><hr><br>
Second (and better) Approach:
One (or the only) of better solution it's make a cross load function. In my case i make an importer async make a first good solution.
// importer:
async function importModule(modulePath) {
if(typeof window === 'undefined'){
return require(modulePath)
} else {
const module = await import(modulePath)
return module.default || module
}
}
//usage:
const MyReactComponent = async (props) => {
var React, MyLib
if(typeof window === 'undefined'){
React = await importModule('react')
MyLib = await importModule('MyLib')
} else {
React = window.React
MyLib = window.MyLib
}
// do...
}
in any case, however, you need to use the horrible solution of export into window global object part of your app (like a builder, ex webpack)
<br><hr><br>
Other notes, approach and deliriums
After different try I understand that right way is respect imports/exports types and not load the libs direct via nodejs modules paths in search of an unpraticable absolutism. However, commonJS and ES6 are an unconciliable enemies if you have its into the same page and you think to share something. One lock the others and vice versa in recursive loop.
So, it's a good idea to understand (obviously) the file structure of what we want to do, set up node for commonJS and get/share to parallelize your libraries both on the server and the web window object.
function myLibOrComponent () { ... }
typeof window === 'undefined' ?
module.exports = myLibOrComponent : // node export
window.myLibOrComponent = myLibOrComponent // web export
so, get it in same way... exemple:
const myLibOrComponent = typeof window === 'undefined' ?
require('myLibOrComponent ') : // node import
window.myLibOrComponent // web global object
another way for components is:
import('./myLibOrComponent.js').then( () => { do... })
const myLibOrComponent = React.lazy(() => import('./myLibOrComponent'))
Exporting in window or self (like webpack) global object it's not a good practice but, unfortunately, I don't find any other way to reconcile the two type of script, formatted for two (or more) type of importing type.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论