MeteorJs(简短的“流星”)是具有同构功能的全栈JavaScript框架:您可以编写一次代码并在服务器和客户端上使用它。
这是更快地运输代码的一个很大的优势,但也可以将服务器代码泄漏到客户端或bloat the client bundle。
幸运的是,流星的捆绑工具允许精确的代码分配:如果该领域内导入/需要,则模块只会捆绑到某个架构领域(服务器,客户端)。
如何确定模块是否会捆绑?
领域由入口点模块定义。检查您的package.json
是否以下条目:
{
...
"meteor": {
"mainModule": {
"client": "client/main.js",
"server": "server/main.js"
},
"testModule": "tests/main.js"
},
...
}
从这些输入点开始的每个导入都会导致捆绑器将导入的模块添加到服务器或客户端束或两者兼而有之。一个例外是使用dynamic imports,但我们今天不会介绍。
分裂同构代码
首先,我们应该谈论“同构”一词。该术语本身有些不精确和is sometimes used synonymous with "universal JavaScript"。但是,我们的目标并不总是一个通用模块,因为我们在每个环境中处理不同的依赖性。您将在即将到来的示例中看到这一点。
如果我们谈论代码结构,那么我们可以使用术语“同构”来描述代码,具有相同的签名,并且代表所有环境中的(主要)相同的行为。
让我们看一个简单的例子:
export const SHA512 = {}
/**
* Creates a new SHA512 hash from a given input
* @param {string} input
* @returns {Promise.<String>}
*/
SHA512.create = async () => {}
该模块在服务器和客户端上具有相同的签名和一般行为。它从给定的输入中创建一个SHA512哈希。现在,让我们首先使用NodeJs-builtin crypto module在服务器上实现此代码:
...
if (Meteor.isServer) {
SHA512.create = async input => {
import crypto from 'crypto'
return crypto
.createHash('sha512')
.update(input, 'utf8')
.digest('base64')
}
}
对于客户端,我们改用Web Crypto API。通过这样做
...
if (Meteor.isClient) {
SHA512.create = async input => {
const encoder = new TextEncoder()
const data = encoder.encode(input)
const hash = await window.crypto.subtle.digest({ name: 'SHA-512' }, data)
const buffer = new Uint8Array(hash)
return window.btoa(String.fromCharCode.apply(String, buffer))
}
}
提高可读性
如果涉及较大的模块,上面的示例是最小和可读性大幅下降。因此,我们需要写一个抽象来帮助提高可读性。
幸运的是,这很简单:
import { Meteor } from 'meteor/meteor'
export const isomorphic = ({ client, server }) => {
if (Meteor.isClient && client) return client()
if (Meteor.isServer && server) return server()
}
使用此包装器,我们现在可以定义单个变量或属性,并立即基于仅执行的函数,如果Bundler为特定的体系结构运行,则只能执行。让我们将此包装器应用于我们的SHA512
模块。
export const SHA512 = {}
/**
* Creates a new SHA512 hash from a given input
* @method
* @async
* @param {string} input
* @returns {Promise.<string>}
*/
SHA512.create = isomorphic({
server () {
import crypto from 'crypto'
return async input => crypto
.createHash('sha512')
.update(input, 'utf8')
.digest('base64')
},
client () {
return async input => {
const encoder = new TextEncoder()
const data = encoder.encode(input)
const hash = await window.crypto.subtle.digest({ name: 'SHA-512' }, data)
const buffer = new Uint8Array(hash)
return window.btoa(String.fromCharCode.apply(String, buffer))
}
}
})
运行代码
现在,让我们在服务器和客户端上运行代码。您可以使用以下代码剪切并将其放入入口点(通常是'server/main.js'
和'client/main.js'
):
import { SHA512 } from '../imports/SHA512'
Meteor.startup(async () => {
const input = 'isomorphic code rocks'
const output = await SHA512.create(input)
console.debug(input, '=>', output)
})
在这两个体系结构上都将产生完全相同的控制台输出:
isomorphic code rocks => NfxQJL4a58eszCB64Fi0DRvolnEhABf9x4fVZsMTH6BF296uTdK2MYBbUJzLqHJIuUTLzAJqhzfZlAcEuCuZSQ==
如果您有疑问,这是否真的使用server
和client
中的定义零件,建议您在服务器和客户端上运行调试器并检查运行代码。您会看到,在每个体系结构中,只有特定于架构的代码运行。
hooray!您编写了具有特定体系结构实现的同构模块! ð7
关于我
我在Dev.to上定期发布文章,大约 Meteor 和 javascript 。
您还可以在GitHub,Twitter和LinkedIn上找到(并联系)我。
如果您喜欢阅读的内容并想支持我,则可以sponsor me on GitHub或send me a tip via PayPal。
通过访问their blog*来跟上流星的最新发展,如果您像我一样进入流星并想向世界展示,则应查看Meteor merch store*。
。