如何用Nuxt.js构建JAMStack多语言博客

2020-05-22 15:49:29

JAMStack(Javascript,API and Markup Stack)是一个围绕制作Web项目的新方式的术语,在这种方式下,您不必托管自己的后端来构建站点,而是在构建时呈现一组静态页面,并将它们部署到内容交付网络(CDN)。这意味着更好的安全性、更高的可扩展性和更好的网站性能。

在本教程中,您将学习如何使用Nuxt.js构建一个JAMStack多语言博客,Nuxt.js是一个强大的Vue框架,支持SPA、SSR和静态生成的渲染,与Strapi Headless CMS一起存储数据并公开它们以生成静态博客。要在本地设置STRIPI,您可以遵循本指南,否则您可以使用在我们的服务器https://strapi.lotrek.net/.上运行的只读实例。

使用Strapi,我构建了一个朴素的结构来支持翻译,其中的Post表包含链接到一个或多个包含翻译的TransPost元素的元素。

_|POST||TRANS_POST|=||已发布||语言||Created_At|<;--(1)-(N)-->;>;|标题|内容|slug|=。

你可以使用GraphQL游乐场来玩它,并探索后端。请记住,本教程的重点是Nuxt.js,您可以使用任何想要生成最终静态站点的后端。

记住选择axios选项(您稍后会用到它)并添加一个UI框架,比如Buefy。

并在services文件夹下创建index.js文件来包装所有查询。此客户端应实现3种方法:

getAllPosts:获取特定语言的所有帖子,显示其他语言的slug、标题、内容和其他帖子的slug,以获取备用URL。

getSinglePost:获取具有特定插件和语言的单个帖子,以其他语言显示所有属性和帖子。

从Apollo-Fetch导入{createApolloFetch}导出默认类BlogClient{structor(){this。apolloFetch=createApolloFetch({uri:`${process.。环境。NUXT_ENV_BACKEND_URL}/raphql`})}getAllPostsHead(Lang){const allPostsQuery=`query AllPosts($lang:string!){transPosts(where:{lang:$lang}){slug title}}`返回此参数。apolloFetch({query:allPostsQuery,Variables:{lang}})}getAllPosts(Lang){const allPostsQuery=`query AllPosts($lang:string!){transPosts(where:{lang:$lang}){slug title content post{Publisher transPosts(where:{lang_ne:$lang}){slug lang}`返回此参数。apolloFetch({query:allPostsQuery,Variables:{lang}})}getSinglePost(slug,lang){const simplePostQuery=`查询发布($slug:string!,$lang:string!){transPosts(where:{slug:$slug,lang:$lang}){slug标题内容POST{发布的transPosts(其中:{lang_ne:$lang}){slug。apolloFetch({query:simplePostQuery,变量:{slug,lang}})}}。

要使BlogClient在您有权访问上下文时可用(例如,在asyncData函数中),请创建plugins/ctx-inject.js文件。

从';~/services';导入BlogClient导出默认值({app},Inject)=>;{app.。$blogClient=new BlogClient()}。

这个博客的结构将非常简单,在主页(/)中会有一个帖子列表和一个阅读文章的链接(/blog/<;postslug>;)。现在您可以从上下文访问BlogClient实例了,现在开始重写主页组件(Pages/index.vue),以使用名为asyncData的特殊方法获取博客帖子,并为每个帖子呈现标题和链接。asyncData接收上下文作为第一个参数,并且可以通过context.app.$blogClient访问您的BlogClient实例。

<;template>;<;Section class=";>;<;div class=";is-mobile";>;<;div v-for=";帖子";:key=";post.slug";>;<;h2>;{{post.title}}<;/H2>;&。博客-slug';,params:{slug:post.slug}}";>;阅读更多.。<;/nuxt-link>;<;/div>;<;/div>;<;/Section>;<;/template>;<;脚本>;导出默认值{name:';主页';,异步异步数据({app}){const postsData=等待APP。$blogClient。getAllPostsHead(';EN';)返回{Posts:postsData。数据。transPosts}},data(){return{post:[]}<;/script>;

添加/blog/<;postslug>;route,创建组件blogpost(page/blog/_slug.vue)。安装Vue Markdown组件以正确呈现文章(纱线添加Vue-markdown)。

<;模板>;<;Section class=";>;<;div class=";is-mobile&34;>;<;h2>;{{post.title}}<;/h2>;<;vue-markdown>;{{post.content}}<;/vue-markdown>;<;/div&。脚本&>导出默认值{name:';blogpost';,组件:{';vue-markdown';:VueMarkdown},异步异步数据({app,route}){const postsData=等待APP。$blogClient。getSinglePost(路由。参数。slug,';en';)返回{POST:postsData。数据。transPosts[0]}},data(){return{post:null}<;/script>;

常量区域设置=[{code:';en';,iso:';en-US&39;},{code:';es';,iso:';es-es';},{code:';it';it-IT';}]const default_locale=';en';导出默认值{//.。I18N:{locales:locales,defaultLocale:default_locale,encodePath:false,vueI18n:{fall backLocale:default_locale,Messages:{en:{readmore:';read more';},es:{readmore:';Lee mass';},it:{readmore:';leggi di pi??';}//.}。

现在您可以修改主页组件了:在nuxt-link中,您应该使用localePath,并使用$t呈现翻译后的标签readmore。

在asyncData中,您可以使用上下文的store.$i18n属性获取帖子列表,以获取当前语言。

//.。异步异步数据({app,store}){const postsData=等待应用。$blogClient。getAllPostsHead(存储。1.18亿美元。区域设置)返回{POSTS:postsData。数据。TransPosts}},//.。

//.。异步异步数据({app,route,store}){const postsData=等待APP。$blogClient。getSinglePost(路由。参数。鼻涕虫,商店。1.18亿美元。区域设置)返回{POST:postsData。数据。TransPosts[0]}},//.。

<;template>;<;b-navbar-dropdown:label=";$i18n.locale";>;<;nuxt-link v-for=";locale";:key=";locale.code";class=";navbar-item";:to=";switchLocalePath(locale.code)。/b-navbar-dropdown>;<;/template>;<;script>;export default{Computed:{availableLocales(){return this。1.18亿美元。地点。筛选器(区域设置=>;区域设置。代码!==这个。1.18亿美元。区域设置)}<;/script>;

并将其包含在布局/default.vue中,以使其在导航栏中可用。此组件调用switchLocalePath以获取指向另一种语言的当前页面的链接。要使语言切换器使用动态路由,您需要使用store.dispatch设置blogpost组件中的slug参数。

//.。异步异步数据({app,route,store}){const postsData=等待APP。$blogClient。getSinglePost(路由。参数。鼻涕虫,商店。1.18亿美元。地点)等待商店。派单(';i18n/setRouteParams';,对象。FromEntries(postsData.。数据。TransPosts[0]。发帖。转弯。map(el=>;[el.。朗,{鼻涕虫:EL。slug}])返回{POST:postsData。数据。TransPosts[0]}},//.

记住使用.env或直接(导出NUXT_ENV_BACKEND_URL=https://strapi.lotrek.net)并启动开发服务器)设置博客客户端使用的NUXT_ENV_BACKEND_URL环境变量。

ℹ生成页面✔生成/它/✔生成/ES/✔生成/✨在43.49秒内完成。

正如您所看到的,不会生成动态路由,因为Nuxt.js不知道如何生成它们。要添加动态路由支持,您必须在nuxt.config.js中的Generate settings下实现routes函数,并返回一个包含您想要生成的路由和包含POST的有效负载的对象列表。

从';导入BlogClient。/services';//.。导出默认值{//.。生成:{routes:async()=>;{const client=new BlogClient()let routes=[]let postsData=[]for(区域设置的常量区域设置){postsData=等待客户端。getAllPosts(区域设置。代码)ROUES=ROUTS。Concat(postsData.。数据。转弯。map((Post)=>;{return{route:`${locale.。code=DEFAULT_LOCALE?';';:';/';+区域设置。code}/blog/${post.。slug}`,有效负载:POST}}))}返回路由}}//.}。

由于有效负载在上下文中可用,您可以重构blogpost组件中的asyncData函数,以从context.payload获得特定的帖子。

const getSinglePostFromContext=async({app,route,store,payload})=>;{if(Payload){return payload}const postsData=等待APP。$blogClient。getSinglePost(路由。参数。鼻涕虫,商店。1.18亿美元。区域设置)返回postsData。数据。transPosts[0]}导出默认值{name:';blogpost';,async asyncData(Context){const singlePost=await getSinglePostFromContext(Context)等待上下文。商店。派单(';i18n/setRouteParams';,对象。FromEntries(singlePost.。发帖。转弯。map(el=>;[el.。朗,{鼻涕虫:EL。slug}])return{post:singlePost}},//.}。

ℹGenerating Pages✔Generated/it/✔Generated/ES/✔Generated/✔Generated/Blog/Hello-World✔Generated/it/Blog/Cao-mondo✨在33.82秒内完成。

静态生成后您可以注意到的一个问题是,在客户端导航期间仍然会进行asyncData调用,这意味着外部API服务器应该在您的静态网站联机时运行(如果Strapi服务器关闭,您将无法获取";消息)。这是一个已知的问题,Nuxt.js团队正在努力在下一个版本中支持nuxt生成的完全静态生成,目前您可以使用nuxt-payload-提取器插件。随一起安装

const getSinglePostFromContext=async({$axios,$payloadURL,app,route,store,payload})=>;{if(Payload){return payload}let postsData=null if(Process.。静态&;&;进程。客户端&;&;$payloadURL){postsData=等待$axios。$get($payloadURL(Route))返回postsData。post}postsData=等待应用程序。$blogClient。getSinglePost(路由。参数。鼻涕虫,商店。1.18亿美元。区域设置)返回postsData。数据。传输发布[0]}。

异步异步数据({$axios,$payloadURL,app,route,store}){if(进程。静态&;&;进程。客户端&;&;$payloadURL){返回等待$axios。$get($payloadURL(Route))}const postsData=等待APP。$blogClient。getAllPostsHead(存储。1.18亿美元。区域设置)返回{POSTS:postsData。数据。传输发布}}。

有时,您可能需要为动态路由配置一个自定义路径,例如,您可能希望为英语保留/blog/:slug path,为西班牙语保留/artículos/:slug route,为意大利语保留/titioli/:slug route。遵循nuxt-i18n文档,您必须在nuxt.config.js的i18n部分中指定这些路由。

I18N{//.。parsePages:false,Pages:{';blog/_slug';:{it:';/jotioli/:slug';,es:';/artículos/:slug';,en:';/blog/:slug';}},//.}。

要使这些设置在I18N配置和生成函数中都可重用,请在单独的文件i18n.config.js中移动自定义路由。

导出默认{Pages:{';blog/_slug';:{it:';/jotioli/:slug';,es:';/artículos/:slug';,en:';/blog/:slug';}。

从';导入i18nConfig。/i18n.config';//.。导出默认值{//.。I18n:{locales:locales,defaultLocale:default_locale,parsePages:false,Pages:I18nConfig。页面,编码路径:FALSE,vueI18n:{fall backLocale:DEFAULT_LOCALE,//.}},//.。

路由:async()=>;{const client=new BlogClient()let routes=[]let postsData=[]for(区域设置的常量区域设置){postsData=等待客户端。getAllPosts(区域设置。代码)ROUES=ROUTS。Concat(postsData.。数据。转弯。map((Post)=>;{return{route:`${locale.。code=DEFAULT_LOCALE?';';:';/';+区域设置。代码}${i18nConfig。Pages[';blog/_slug&39;][区域设置。代码]。更换(';:slug';,POST。slug)}`,有效负载:POST}})}}返回路线}

ℹGenerating Pages✔Generated/Blog/Hello-World✔Generated/it/Artioli/Cao-mondo✔Generated/ES/ARTículos/Hola-Mundo✔Generated/ES/✔Generated/it/✔Generated/✨在33.82秒内完成。

在此存储库中,您可以看到本教程的完整代码,结果部署在Netlify CDN上,网址为https://eager-shockley-a415b7.netlify.app/.。Netlify是我最喜欢的服务之一,它为静态网站提供云托管,提供持续部署、免费SSL、无服务器功能等……。最后的代码向网站添加了一些缺失的功能,例如,它添加了作者支持,为了简单起见使用了这里省略的一些外部组件,并启用了项目的SEO选项来向页面添加元数据(参见nuxt-18n文档中的SEO部分)。

最终代码中包含的另一个有用的东西是由Nuxt.js站点地图模块提供的站点地图。Sitemap很容易设置,因为它在默认情况下采用Generate.routes值,因此会自动包含动态路由。配置非常简单,只需在nuxt.config.js文件的模块数组部分的末尾添加@nuxtjs/sitemap。

导出默认值{//.。站点地图:{hostname:base_url,gzip:true,i18n:default_locale}//.}