为什么使用Next.js反应式构建的Shopify应用加载速度如此之慢?

如何解决为什么使用Next.js反应式构建的Shopify应用加载速度如此之慢?

我遵循了本教程:https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react

从一开始,我的应用程序加载速度非常慢,包括更改选项卡时(包括通过ngrok加载并在localhost上运行或部署在应用程序引擎上)。

这可能是什么原因造成的?

P.S .:我是React,Next.js和Shopify App开发的新手,所以答案可能很简单。

P.P.S .:根据红色,构建输出似乎指示“所有人共享的First Load JS”太大。我不知道如何进行调查并减少上述块的大小,尽管仅214KB不能解释这么慢的加载时间,对吗?


构建

enter image description here


React Dev Tools Profiler

enter image description here

enter image description here


@ next / bundle-analyzer输出:

已解析

enter image description here

enter image description here

压缩

enter image description here

enter image description here


package.json

 {
      "name": "ShopifyApp1","version": "1.0.0","description": "","main": "index.js","scripts": {
        "test": "echo \"Error: no test specified\" && exit 1","dev": "node server.js NODE_ENV=dev","build": "next build","deploy": "next build && gcloud app deploy --version=deploy","start": "NODE_ENV=production node server.js","analyze": "cross-env ANALYZE=true npm run build"
      },"keywords": [],"author": "","license": "ISC","dependencies": {
        "@google-cloud/storage": "^5.2.0","@next/bundle-analyzer": "^9.5.2","@sendgrid/mail": "^7.2.3","@shopify/app-bridge-react": "^1.26.2","@shopify/koa-shopify-auth": "^3.1.65","@shopify/koa-shopify-graphql-proxy": "^4.0.1","@shopify/koa-shopify-webhooks": "^2.4.3","@shopify/polaris": "^5.1.0","@zeit/next-css": "^1.0.1","apollo-boost": "^0.4.9","cors": "^2.8.5","cross-env": "^7.0.2","dotenv": "^8.2.0","email-validator": "^2.0.4","extract-domain": "^2.2.1","firebase-admin": "^9.0.0","graphql": "^15.3.0","helmet": "^4.0.0","isomorphic-fetch": "^2.2.1","js-cookie": "^2.2.1","koa": "^2.13.0","koa-body": "^4.2.0","koa-bodyparser": "^4.3.0","koa-helmet": "^5.2.0","koa-router": "^9.1.0","koa-session": "^6.0.0","next": "^9.5.1","react": "^16.13.1","react-apollo": "^3.1.5","react-dom": "^16.13.1","react-infinite-scroll-component": "^5.0.5","sanitize-html": "^1.27.2","scheduler": "^0.19.1","store-js": "^2.0.4","tldts": "^5.6.46"
      },"devDependencies": {
        "webpack-bundle-analyzer": "^3.8.0","webpack-bundle-size-analyzer": "^3.1.0"
      },"browser": {
        "@google-cloud/storage": false,"@sendgrid/mail": false,"@shopify/koa-shopify-auth": false,"@shopify/koa-shopify-graphql-proxy": false,"@shopify/koa-shopify-webhooks": false,"cors": false,"email-validator": false,"extract-domain": false,"firebase-admin": false,"graphql": false,"helmet": false,"isomorphic-fetch": false,"koa": false,"koa-body": false,"koa-bodyparser": false,"koa-helmet": false,"koa-router": false,"koa-session": false,"sanitize-html": false,"tldts": false
      }
    }

Chrome开发工具网络标签

编辑:

npm run dev

enter image description here

由于某些原因,“ webpack-hmr”行的加载时间一直在不断增加。

npm run build && npm run start

enter image description here


next.config.js

require("dotenv").config({path:"live.env"});
const withCSS = require('@zeit/next-css');
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const withBundleAnalyzer = require('@next/bundle-analyzer')({enabled: process.env.ANALYZE === 'true'})
const apiKey =  JSON.stringify(process.env.SHOPIFY_API_KEY);
module.exports =  withBundleAnalyzer(
  withCSS({
    distDir: 'build',webpack: (config) => {
      const env = { API_KEY: apiKey };
      config.plugins.push(new webpack.DefinePlugin(env));
      config.plugins.push(new webpack.DefinePlugin(new BundleAnalyzerPlugin()));
      config.resolve = {
        alias: {
          'react-dom$': 'react-dom/profiling','scheduler/tracing': 'scheduler/tracing-profiling'
        },...config.resolve
      };
      return config;
    }
  })
);

_app.js

import App from 'next/app';
import Head from 'next/head';
import { AppProvider } from '@shopify/polaris';
import { Provider } from '@shopify/app-bridge-react';
import '@shopify/polaris/dist/styles.css'
import translations from '@shopify/polaris/locales/en.json';
import Cookies from 'js-cookie';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';

const client = new ApolloClient({
  fetchOptions: {
    credentials: 'include'
  },});

class MyApp extends App {
  render() {
    const { Component,pageProps } = this.props;
    const config = { apiKey: API_KEY,shopOrigin: Cookies.get("shopOrigin"),forceRedirect: true };
    return (
      <React.Fragment>
        <Head>
          <title>...</title>
          <meta charSet="utf-8" />
        </Head>
        <Provider config={config}>
          <AppProvider i18n={translations}>
            <ApolloProvider client={client}>
              <Component {...pageProps} />
            </ApolloProvider>
          </AppProvider>
        </Provider>
      </React.Fragment>
    );
  }
}

export default MyApp;

Index.js(客户端)

import {
  Button,Card,Form,FormLayout,Layout,Page,Stack,TextField,DisplayText,Toast,Frame
} from '@shopify/polaris';

class Index extends React.Component  {

  state = {
    emails: '',domain: '',alias: '',err: '',message: '',active: false,loadingDomainResponse: false,loadingEmailResponse: false
  };

  componentDidMount() {
    fetch(`/state`,{
      method: 'GET'
    }).then(response => response.json())
    .then(result => {
      if (result.err) {
        this.setState({
          err: result.err,message: result.err,active: true
        })
      }
      else {
        this.setState({
          emails: result.emails,domain: result.domain,alias: result.alias
        })
      }
    });
  };

  

  render() {

    const { emails,domain,alias,err,message,active,loadingEmailResponse,loadingDomainResponse} = this.state;
    

    const toastMarkup = active ? (
      <Toast content={message} error={err} onDismiss={this.handleToast}/> 
    ) : null;


    return (
      <Frame>
         <Page>
          {toastMarkup}
          <Layout>
            <Layout.AnnotatedSection
              title="..."
              description="..."
            >
              <Card sectioned>
                <Form onSubmit={this.handleSubmitEmails}>
                  <FormLayout>
                    <TextField
                      value={emails}
                      onChange={this.handleChange('emails')}
                      label="..."
                      type="emails"
                      maxlength="200"
                    />
                    <Stack distribution="trailing">
                      <Button primary submit loading={loadingEmailResponse}>
                        Save
                      </Button>
                    </Stack>
                  </FormLayout>
                </Form>
              </Card>
            </Layout.AnnotatedSection>
            <Layout.AnnotatedSection
              title="..."
              description="..."
            >
              <Card sectioned>
                <DisplayText size="small"> {domain} </DisplayText>
                <br/>
                <Form onSubmit={this.handleSubmitDomain}>
                  <FormLayout>
                    <TextField
                      value={alias}
                      onChange={this.handleChange('alias')}
                      label="..."
                      type="text"
                      maxlength="50"
                    />
                    <Stack distribution="trailing">
                      <Button primary submit loading={loadingDomainResponse}>
                        Save
                      </Button>
                    </Stack>
                  </FormLayout>
                </Form>
              </Card>
            </Layout.AnnotatedSection>
          </Layout> 
       </Page>
      </Frame>
    );
  }

  handleToast = () => {
    this.setState({
      err: false,message: false,active: false
    })
  };
  
  handleSubmitEmails = () => {
    this.setState({loadingEmailResponse:true});
    fetch(`/emails`,{
      method: 'POST',headers: {
        'Content-Type': 'application/json'
      },body: JSON.stringify({
        emails: this.state.emails
      })
    }).then(response => response.json())
    .then(result => {
      console.log("JSON: "+JSON.stringify(result));
      if (result.err) {
        this.setState({
          err: result.err,active: true
        })
      }
      else {
        this.setState({message: "...",active: true});
      }
      this.setState({loadingEmailResponse:false});
    });
  };

  handleSubmitDomain = () => {
    this.setState({loadingDomainResponse:true});
    fetch(`/domain`,body: 
        JSON.stringify({
          alias: this.state.alias
        })
      }).then(response => response.json())
      .then(result => {
        console.log("JSON: "+JSON.stringify(result));
        if (result.err) {
          this.setState({
            err: result.err,active: true
          })
        }
        else {
          this.setState({message: "...",active: true});
        }
        this.setState({loadingDomainResponse:false});
      });
  };

  


  handleChange = (field) => {
    return (value) => this.setState({ [field]: value });
  };
}

export default Index;

server.js

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();
    server.use(bodyParser());
    server.use(session({ secure: true,sameSite: 'none' },server));
    server.keys = [SHOPIFY_API_SECRET_KEY];
    router.get('/state',async (ctx) => {
        let domain = ctx.session.shop;
        let alias;
        const snap = await global.db.collection("...").doc(ctx.session.shop).get();
            if (snap.data().alias) {
                alias = snap.data().alias;
            }
            let emails = snap.data().emails;
            let emailString = "";
            if (!emails) {
                ctx.response.body = {err: "..."};
            }
            else if(emails.length < 4) {
                for (email of emails) {
                    emailString += (","+email);
                }
                theEmailString = emailString.substring(1);
                let response = {
                    domain: domain,alias: alias,emails: theEmailString
                }
                ctx.response.body = response;
            } 
            else {
                ctx.response.body = {err: "..."};
            }
    });
});

编辑

我提供了一个初步的答案,但我正在寻找一个更好的答案。

另外,似乎有可能使Shopify应用程序桥导航链接使用next.js路由器而不是触发整个页面重新加载:

https://shopify.dev/tools/app-bridge/actions/navigation

如果有人足够详细地分享next.js的操作方法,那将比我的回答要好。

解决方法

根据您的开发工具瀑布,索引的初始加载仅用了18.5KB的数据就花了将近2秒。令人震惊的是,这很慢,而且甚至还没有达到您的其余资源。我首先想到的是网络/服务器延迟。您是在本地还是在某种类型的Web服务器上托管它?

我会尽可能地将其剥离,甚至尝试仅加载带有标头的简单index.html文件。如果加载需要花费几秒钟,那么您可能需要升级或迁移到更好的主机。如果您在本地托管,这可能只是互联网上传速度较低的问题。许多Internet计划的下载速度很快,但上传速度却很慢,因此您不一定总能获得ISP的承诺。

,

enter image description here

在我的问题中,我共享了使用npm run dev测量的加载时间,但这也是有关在prod模式下加载时间的一些信息。

在删除Shopify管理界面中的嵌入内容后,以生产模式(npm run buildnpm run start)运行该应用程序显示该应用程序总共花费约2秒的时间才能以生产模式加载非常慢(Shopify UI增加了大约3秒)。

单击时,Shopify应用程序新娘导航链接将重新加载整个页面,而不是像Next.js链接那样更改页面。

将“应用导航”链接替换为“下一个”链接。

不过,第一次加载的时间为1.86秒,非常慢,可以接受更好的解决方案。

,

尝试通过删除所有不必要的代码来优化代码。尝试使用更多的动态导入,以使您可以更快地完成初始bioler板加载,并在稍后的客户端上获得繁重的代码(如图表,图形和图片)以及视频加载。 从“ next / dynamic”导入dynamic,这将使客户端像youtube一样快速获得第一个绘画视图。

https://nextjs.org/docs/advanced-features/dynamic-import

尝试react Formik(一种用于小型应用程序的优化表单控件),还尝试在类组件上使用功能组件。使用Next,您可以在getStatiProps,getServerSideProps和getStaticPaths中进行大多数数据库调用。对于定期缓存的提取,请使用SWR挂钩。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-