如何仅在Sapper的客户端上导入Firebase?

时间:2019-05-26 17:34:35

标签: firebase svelte

我要将Firebase导入到Sapper应用程序中,我不希望在服务器上评估导入。如何确保导入仅在客户端上进行?

我正在使用Sapper运行sapper export来生成静态文件。我尝试过:

  • 在其自己的文件中创建firebase实例,并导出firebase.auth()firebase.firestore()模块。

  • 根据以下错误消息的建议,尝试调整rollup.config.js以不同方式解决依赖关系。这会带来更多的头痛。

  • client.js中创建Firebase实例。不成功。

  • stores.js中创建实例。不成功。

  • 声明变量并在onMount()中进行分配。这使我不得不在不同的块范围内工作。感觉有点。

应用的初始化工作正常:

import firebase from 'firebase/app'

const config = {...}

firebase.initializeApp(config);

我还发现,如果将导入更改为import firebase from 'firebase',则不会出现此服务器错误:

 @firebase/app:
Warning: This is a browser-targeted Firebase bundle but it appears it is being run in a Node environment.  If running in a Node environment, make sure you are using the bundle specified by the "main" field in package.json.

If you are using Webpack, you can specify "main" as the first item in
"resolve.mainFields": https://webpack.js.org/configuration/resolve/#resolvemainfields

If using Rollup, use the rollup-plugin-node-resolve plugin and set "module" to false and "main" to true: https://github.com/rollup/rollup-plugin-node-resolve

我希望只是从文件中导出这些Firebase功能,并将它们导入到我的组件中,例如:

<script>
  import { auth } from "../firebase";
</script>

但是一旦包含了导入,开发服务器就会崩溃。我不想在服务器上使用它,因为我只是在生成静态文件。

有人对仅在客户端实现导入有任何想法吗?

4 个答案:

答案 0 :(得分:2)

干净的方法是使用动态导入,如文档所述:Making a component SSR compatible

解决此问题的方法是从onMount函数(仅在客户端上调用)内部为组件使用动态导入,这样就永远不会调用导入代码服务器。

因此,例如,在这里我们也要导入firebase的 core authentication 包。

<script>
  let firebase;
 
  onMount(async () => {
    const module = await import("firebase/app");
    await import("firebase/auth");
    
    firebase = module.default;

    firebase.initializeApp(firebaseConfig);
  });
<script>

现在您可以使用firebase对象,例如,我们要使用电子邮件和密码登录

  let email;
  let password;

  async function login() {
    try {
      let result = await firebase.auth().signInWithEmailAndPassword(
        email,
        password
      );
      console.log(result.user);

    } catch (error) {
      console.log(error.code, error.message);
    }
  }

答案 1 :(得分:0)

因此,我在此上花费了太多时间。确实没有比onMOun​​t更好的解决方案。

但是,我确实意识到,确实应该使用Sapper的SSR功能。我写了一篇关于如何使用Sapper SSR和Cloud Functions在Firebase上进行设置的文章:

https://dev.to/eckhardtd/how-to-host-a-sapper-js-ssr-app-on-firebase-hmb

原始问题的另一种解决方案是通过src/template.html文件将Firebase CDN放入全局范围。

<body>
    <!-- The application will be rendered inside this element,
         because `app/client.js` references it -->
    <div id='sapper'>%sapper.html%</div>

    <!-- Sapper creates a <script> tag containing `app/client.js`
         and anything else it needs to hydrate the app and
         initialise the router -->
    %sapper.scripts%
      <!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->

  <!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
  <script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-app.js"></script>

  <!-- Add Firebase products that you want to use -->
  <script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-firestore.js"></script>
</body>
</html>

并在组件中:

<script>
import { onMount } from 'svelte';
let database, authentication;

onMount(() => {
  database = firebase.firestore();
  authentication = firebase.auth();
});

const authHandler = () => {
  if (process.browser) {
    authentication
    .createUserWithEmailAndPassword()
    .catch(e => console.error(e));
  }
}
</script>

<button on:click={authHandler}>Sign up</button>

答案 2 :(得分:0)

我能够使用ES6导入firebase。如果您使用汇总,则需要在commonjs插件中配置namedExports:

//--- rollup.config.js ---
...
commonjs({
        namedExports: {
          // left-hand side can be an absolute path, a path
          // relative to the current directory, or the name
          // of a module in node_modules
          'node_modules/idb/build/idb.js': ['openDb'],
          'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
        },
      }),

您可以像这样使用它:

//--- db.js ---
import * as firebase from 'firebase';
import 'firebase/database';
import { firebaseConfig } from '../config'; //<-- Firebase initialization config json

// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export { firebase };
// Initialize db
export const db = firebase.firestore();

并可能在类似这样的服务中使用它:

// --- userService.js ----
import { db } from './common';

const usersCol = db.collection('users');
export default {
  async login(username, password) {
    const userDoc = await usersCol.doc(username).get();
    const user = userDoc.data();
    if (user && user.password === password) {
      return user;
    }
    return null;
  },
};

答案 3 :(得分:0)

为了将 Firebase 与 Sapper 一起使用,您必须导入 firebase 而不是 firebase/app。您确实希望 Firebase 能够在后端使用 SSR 正确加载,而不仅仅是前端。例如,如果您有一些元标记将存储在数据库中,您希望它们加载到后端(未测试)。

您可以只使用 firebase,但随后您会收到烦人的控制台警告。还请记住,firebase 会加载所有 firebase 依赖项,而 firebase/app 不会,这就是您不想在前端使用它的原因。 admin-firebase 可能有办法,但我们希望减少依赖。

根本不要使用 rxfire。你不需要它。它会导致 Sapper 出错。只是普通的 Firebase。

firebase.ts

import firebase from 'firebase/app';
import "firebase/auth";
import "firebase/firestore";
import * as config from "./config.json";

const fb = (process as any).browser ? firebase : require('firebase');

fb.initializeApp(config);
export const auth = fb.auth();
export const googleProvider = new fb.auth.GoogleAuthProvider();
export const db = fb.firestore();

Firebase 函数需要一个额外的步骤,您必须启用 dynamic imports。 (未经测试)

export const functions = (process as any).browser ? async () => {
  await import("firebase/functions");
  return fb.functions()
} : fb.functions();

在编译时,我没有尝试运行 httpsCallable 或确认它将从后端的数据库加载 seo ssr 的 db。让我知道它是否有效。

我怀疑现在 Sapper 已经死了,所有这些都适用于新的 SvelteKit