Express集成Prismic报错:undefined调用forEach触发TypeError
Prismic集成报错
TypeError: Cannot read properties of undefined (reading 'forEach')修复 问题现象
从零搭建网站开展Prismic集成工作时,终端持续抛出TypeError: Cannot read properties of undefined (reading 'forEach')错误,本地运行的网站完全无法加载,涉及业务代码如下:
require('dotenv').config(); const fetch = require('node-fetch'); const path = require('path'); const express = require('express'); const app = express(); const port = process.env.PORT || 8005; const Prismic = require('@prismicio/client'); const PrismicH = require('@prismicio/helpers'); // Initialize the prismic.io api const initApi = (req) => { return Prismic.createClient(process.env.PRISMIC_ENDPOINT, { accessToken: process.env.PRISMIC_ACCESS_TOKEN, req, fetch, }); }; // Link Resolver const HandleLinkResolver = (doc) => { // Define the url depending on the document type // if (doc.type === 'page') { // return '/page/' + doc.uid; // } else if (doc.type === 'blog_post') { // return '/blog/' + doc.uid; // } // Default to homepage return '/'; }; // Middleware to inject prismic context app.use((req, res, next) => { res.locals.ctx = { endpoint: process.env.PRISMIC_ENDPOINT, linkResolver: HandleLinkResolver, }; res.locals.PrismicH = PrismicH; next(); }); app.set('view engine', 'pug'); app.set('views', path.join(__dirname, 'views')); app.locals.basedir = app.get('views'); const handleRequest = async (api) => { const [meta, home, about, { results: collection }] = await Promise.all([ api.getSingle('meta'), api.getSingle('home'), api.getSingle('about'), api.query(Prismic.Predicates.at('document.type', 'collection'), { fetchLinks: 'product.image', }), ]); const assets = []; home.data.gallery.forEach((item) => { assets.push(item.image.url); }); about.data.gallery.forEach((item) => { assets.push(item.image.url); }); about.data.body.forEach((section) => { if (section.slice_type === 'gallery') { section.items.forEach((item) => { assets.push(item.image.url); }); } }); collection.forEach((collection) => { collection.data.products.forEach((item) => { assets.push(item.products_product.data.image.url); }); }); return { assets, meta, home, collections, about, }; }; app.get('/', async (req, res) => { const api = await initApi(req); const defaults = await handleRequest(api); res.render('pages/home', { ...defaults, }); }); app.get('/about', async (req, res) => { const api = await initApi(req); const defaults = await handleRequest(api); res.render('pages/about', { ...defaults, }); }); app.get('/collection', async (req, res) => { const api = await initApi(req); const defaults = await handleRequest(api); res.render('pages/collection', { ...defaults, }); }); app.get('/detail/:uid', async (req, res) => { const api = await initApi(req); const defaults = await handleRequest(api); const product = await api.getByUID('product', req.params.uid, { fetchLinks: 'collection.title', }); res.render('pages/detail', { ...defaults, product, }); }); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); });
错误原因
报错核心是代码直接对可能为undefined的值调用forEach方法,具体触发场景有三类:
- Prismic后台配置缺失:代码中
getSingle拉取的meta/home/about单文档、query拉取的collection文档如果未在Prismic后台创建,或是文档下没有配置gallery/body/products等对应字段,返回值中对应属性就是undefined,直接调用forEach会直接抛错 - 变量名拼写错误:
handleRequest返回的对象中属性写为collections(复数),但实际存储集合数据的变量是collection(单数),后续模板渲染时如果取collections遍历也会触发同类错误 - 无错误捕获逻辑:异步拉取Prismic数据的逻辑没有加catch兜底,接口请求失败、权限错误等场景下会直接导致进程崩溃
修复步骤
先核对Prismic后台配置
- 确认已创建
meta/home/about三个单类型文档,每个文档都添加了代码中用到的gallery、body字段并填充测试内容 - 确认已创建
collection、product类型的可重复文档,collection文档中添加了关联product的products字段 - 确认
.env文件中配置的PRISMIC_ENDPOINT、PRISMIC_ACCESS_TOKEN和后台信息一致
- 确认已创建
修改代码逻辑,添加空值兜底和错误处理
将原有handleRequest函数替换为带兜底的版本,同时给路由加错误捕获:
const handleRequest = async (api) => { // 给Promise.all加catch,捕获接口请求错误 const [meta, home, about, { results: collection = [] } = {}] = await Promise.all([ api.getSingle('meta').catch(() => ({data: {}})), api.getSingle('home').catch(() => ({data: {}})), api.getSingle('about').catch(() => ({data: {}})), api.query(Prismic.Predicates.at('document.type', 'collection'), { fetchLinks: 'product.image', }).catch(() => ({results: []})), ]); const assets = []; // 所有遍历的字段加空数组兜底,可选链避免嵌套取值报错 (home.data.gallery || []).forEach((item) => { if(item?.image?.url) assets.push(item.image.url); }); (about.data.gallery || []).forEach((item) => { if(item?.image?.url) assets.push(item.image.url); }); (about.data.body || []).forEach((section) => { if (section.slice_type === 'gallery') { (section.items || []).forEach((item) => { if(item?.image?.url) assets.push(item.image.url); }); } }); (collection || []).forEach((colItem) => { // 调整循环内变量名,和外层collection数组区分,避免变量遮蔽 (colItem.data.products || []).forEach((item) => { if(item?.products_product?.data?.image?.url) { assets.push(item.products_product.data.image.url); } }); }); return { assets, meta, home, collection, // 修复之前的拼写错误,和变量名统一 about, }; }; // 给所有路由加错误兜底,以首页为例,其他路由按相同逻辑修改 app.get('/', async (req, res, next) => { try { const api = await initApi(req); const defaults = await handleRequest(api); res.render('pages/home', { ...defaults, }); } catch (err) { next(err); } });
修改后重启服务,只要Prismic后台配置正确,就不会再出现forEach相关的报错。
内容的提问来源于stack exchange,提问作者Niccolo Bianchi
相关产品推荐
相关产品推荐

