vue生成靜態js文件
by Ond?ej Polesny
通過Ond?ejPolesny
如何立即使用Vue.js生成靜態網站 (How to generate a static website with Vue.js in no time)
You have decided to build a static site, but where do you start? How do you select the right tool for the job without previous experience? How can you ensure that you succeed the first time, while avoiding tools that won’t help you in the end?
您已決定建立一個靜態站點,但是從哪里開始呢? 您如何在沒有經驗的情況下為工作選擇合適的工具? 您如何才能確保第一次成功,同時避免使用最終無法幫助您的工具?
In this article, you will learn how to adjust a Vue.js website to be automatically generated as a static site.
在本文中,您將學習如何將Vue.js網站調整為自動生成為靜態網站。
介紹 (Introduction)
I summarized the key differences between an API based website and static sites in my previous article. As a quick reminder, static sites are:
在上一篇文章中,我總結了基于API的網站與靜態網站之間的主要區別。 快速提醒一下,靜態網站是:
- Blazing fast 快速燃燒
- Secure (as they are just a set of static pages) 安全(因為它們只是一組靜態頁面)
- Regenerated every time editors update the content 每次編輯者更新內容時都會重新生成
- Compatible with additional dynamic functionality 與其他動態功能兼容
什么是靜態網站生成器? (What is a Static Site Generator?)
A static site generator is a tool that generates a static website from a website’s implementation and content.
靜態網站生成器是一種根據網站的實現和內容生成靜態網站的工具。
Content can come from a headless content management system, through a REST API. The website implementation uses one of the JavaScript frameworks like Vue.js or React. The output of a static site generator is a set of static files that form the website.
內容可以通過REST API來自無頭內容管理系統。 該網站實現使用JavaScript框架之一,例如Vue.js或React。 靜態網站生成器的輸出是構成網站的一組靜態文件。
靜態網站實施 (Static Site Implementation)
I chose Vue.js as the JavaScript framework to use. Therefore I will be working with Nuxt.js, which is a static site generator for Vue.js.
我選擇Vue.js作為要使用JavaScript框架。 因此,我將使用Nuxt.js ,它是Vue.js的靜態站點生成器。
If you are using a different framework, look for a static site generator built on top of that framework (for example Gatsby for React.js).
如果您使用其他框架,請尋找在該框架之上構建的靜態站點生成器(例如Gatsby for React.js )。
Essentially, Nuxt is a combination of multiple tools that together enable you to create static sites. The tools include:
本質上,Nuxt是多個工具的組合,可以使您一起創建靜態站點。 這些工具包括:
- Vue2 — Core Vue.js library. Vue2 —核心Vue.js庫。
- Vue Router — Handles URL routing for pages within the website. Vue路由器-處理網站內頁面的URL路由。
- Vuex — Memory store for data that are shared by components. Vuex-內存存儲區,用于共享組件。
- Vue Server Renderer — Enables server side rendering of pages before the actual static files generation Vue Server Renderer —在實際生成靜態文件之前啟用頁面的服務器端渲染
- Vue-Meta — Manages page metadata info Vue-Meta —管理頁面元數據信息
Nuxt also defines how the website needs to be built in order to generate static files.
Nuxt還定義了如何構建網站以生成靜態文件。
安裝 (Installation)
In order to start building websites with Nuxt, you need to install it. See detailed installation instructions on the Nuxt.js webpage. In a nutshell, you need npx
(shipped with NPM by default) installed and run:
為了開始使用Nuxt建立網站,您需要安裝它。 請參閱Nuxt.js網頁上的詳細安裝說明。 簡而言之,您需要安裝npx
(默認情況下附帶NPM)并運行:
npx create-nuxt-app <website-name>
You can just use default selections, unless you have preferences otherwise.
您可以僅使用默認選擇,除非另有選擇。
組件 (Components)
In one of my previous articles I explained how to create a template layout and components. All of them were defined within single file components.js
. That needs to be changed with Nuxt. All components need to be extracted from components.js
file into separate files under folder components
. Take a look at my blog-list
component and its previous implementation:
在我以前的一篇文章中,我解釋了如何創建模板布局和組件。 所有這些都在單個文件components.js
中定義。 這需要通過Nuxt進行更改。 所有組件都需要從components.js
文件中提取到文件夾components
下的單獨文件中。 看一下我的blog-list
組件及其先前的實現:
Vue.component('blog-list', { props: ['limit'], data: function(){ return { articles: null } },
created: function(){ var query = deliveryClient .items() .type('blog_post') .elementsParameter(['link', 'title', 'image_url', 'image', 'teaser']) .orderParameter('elements.published', SortOrder.desc); if (this.limit){ query = query.limitParameter(this.limit); } query .getPromise() .then(response => this.$data.articles = response.items.map(item => ({ url: item.link.value, header: item.title.value, image: item.image_url.value != '' ? item.image_url.value : item.image.assets[0].url, teaser: item.teaser.value })) ); },
template: ` <section class="features"> <article v-for="article in articles"> ... </article> </section> ` });
To separate it, you also need to change the component’s syntax to match the following template:
要分離它,您還需要更改組件的語法以匹配以下模板:
<template> HTML of the component</template><script> export default { Vue.js code }</script>
Therefore my adjusted Blog-list
component looks like this:
因此,我調整后的Blog-list
組件如下所示:
<template> <section class="features"> <article v-for="article in blogPosts"> ... </article> </section></template><script> export default { props: ['limit'], computed: { blogPosts: function(){ return this.$store.state.blogPosts && this.limit && this.$store.state.blogPosts.length > this.limit ? this.$store.state.blogPosts.slice(0, this.limit) : this.$store.state.blogPosts; } } }</script>
You see the template stayed the same. What changed is the implementation that is now within export default
section. Also, there used to be a function gathering data from headless CMS Kentico Cloud.
您會看到模板保持不變。 更改的是export default
部分中的實現。 此外,過去還存在從無頭CMS Kentico Cloud收集數據的功能。
That content is now stored within Vuex store. I will explain this part later. Convert all of your components this way, to contain template within <templa
te> tags and implementation within &l
t;script> tags. You should end up with a similar file structure as I have:
該內容現在存儲在Vuex商店中。 我將在后面解釋這部分。 以此方式轉換所有組件,以將模板包含在<templa
te>標記內,并在ithin &l
t; script>標記內實現。 您應該最終得到與我類似的文件結構:
Note that I have two new components here — Menu and Header. As my aim was to also improve performance, I decided to remove jQuery from my website. jQuery is quite a large and heavy library that was used only for small UI effects. I was able to recreate them using just Vue.js. Therefore, I converted the static HTML representing header to component. I also added the UI related functionality into mounted
function of this component.
請注意,這里有兩個新組件-菜單和標題。 因為我的目的還在于提高性能,所以我決定從我的網站中刪除jQuery。 jQuery是一個龐大而笨重的庫,僅用于小型UI效果。 我只用Vue.js就可以重新創建它們。 因此,我將靜態HTML表示頭轉換為組件。 我還將與UI相關的功能添加到此組件的已mounted
功能中。
mounted: function(){ window.addEventListener(‘scroll’, this.scroll); …},methods: { scroll: function(){ … }}
使用Nuxt處理Vue.js事件 (Handling Vue.js Events with Nuxt)
I used to leverage Vue events in my website. The main reason was reCaptcha control used for form validation. When it was initialized, it would broadcast the event so that form component can unlock the submit button of the contact form.
我曾經在網站上利用Vue事件。 主要原因是reCaptcha控件用于表單驗證。 初始化后,它將廣播事件,以便表單組件可以解鎖聯系表單的提交按鈕。
With Nuxt, I no longer use app.js
or components.js
files. Therefore I created a new recaptcha.js
file that contains a simple function emitting the event when reCaptcha gets ready. Note that instead of creating new Vue.js instance just for events (let bus = new Vue();
in my old code), it is possible to simply use this.$nuxt
the same way.
使用Nuxt,我不再使用app.js
或components.js
文件。 因此,我創建了一個新的recaptcha.js
文件,其中包含一個簡單的函數,當reCaptcha準備就緒時會發出事件。 請注意,與其僅為事件創建新的Vue.js實例let bus = new Vue();
在我的舊代碼中為let bus = new Vue();
),還可以以相同的方式簡單地使用this.$nuxt
。
var recaptchaLoaded = function(){ this.$nuxt.$emit('recaptchaLoaded');}
布局和頁面 (Layout and Pages)
The main frame of the page was index.html
, and each page defined its own layout in constants that were handed over to Vue router.
頁面的主要框架是index.html
,每個頁面都以常量定義了自己的布局,這些常量被移交給Vue路由器。
With Nuxt, the main frame including <ht
ml> tag
, meta tags and other essentials of any HTML page are handled by Nuxt. The actual website implementation is handling only content within
<body> tags. Move the layout that is common for all your pages into layouts
/default.vue and respect the same template as with components. My layout looks like this:
使用Nuxt,包括<ht
ml> tag
,meta標簽和任何HTML頁面的其他要素的主框架都由Nuxt處理。 實際的網站實現僅處理量w ithin
<body>標記。 將所有pages into layouts
通用的布局移動pages into layouts
/default.vue中,并使用與組件相同的模板。 我的布局如下所示:
<template> <div> <Menu></Menu> <div id="page-wrapper"> <Header></Header> <nuxt/> <section id="footer"> <div class="inner"> … <ContactForm></ContactForm> … </div> </section> </div> </div></template><script> import ContactForm from ‘~/components/Contact-form.vue’ import Menu from ‘~/components/Menu.vue’ import Header from ‘~/components/Header.vue’ export default { components: { ContactForm, Menu, Header } } </script>
The layout is basically the HTML markup of my old index.html
. However, note the <scri
pt> section. All of the components I want to use within this layout template need to be imported from their location and specified in exported object.
布局基本上是我的舊index.html
HTML標記。 但是,請注意<scri
pt>部分。 我要在此布局模板中使用的所有組件都需要從其位置導入,并在導出的對象中指定。
Page components were previously defined in app.js
as constants. Take a look at my old Home page for example:
頁面組件先前在app.js
定義為常量。 以我的舊主頁為例:
const Home = { template: ` <div> <banner></banner> <section id="wrapper"> <about-overview></about-overview> ... <blog-list limit="4"></blog-list> <ul class="actions"> <li><a href="/blog" class="button">See all</a></li> </ul> ... </section> </div> `}
All these pages need to be defined in separate files within pages
folder. Main page is always called index.vue
. This is how my new pages/index.vue
(my Homepage) looks like:
所有這些頁面都需要在pages
文件夾內的單獨文件中定義。 主頁始終稱為index.vue
。 這是我的新pages/index.vue
(我的主頁)的樣子:
<template> <div> <Banner></Banner> <section id="wrapper"> <AboutOverview></AboutOverview> ... <BlogList limit="4"></BlogList> <ul class="actions"> <li><a href="/blog" class="button">See all</a></li> </ul> ... </section> </div></template><script> import Banner from ‘~/components/Banner.vue’ import AboutOverview from ‘~/components/About-overview.vue’ import BlogList from ‘~/components/Blog-list.vue’ export default { components: { Banner, AboutOverview, BlogList }, }</script>
資產存放地點 (Where to Store Assets)
On every website there are assets like CSS stylesheets, images, logos, and JavaScripts. With Nuxt, all these static files need to be stored under folder static. So the folder structure currently looks like this:
每個網站上都有CSS樣式表,圖像,徽標和JavaScript等資產。 使用Nuxt,所有這些靜態文件都需要存儲在文件夾static下。 因此,文件夾結構當前如下所示:
When you reference any resources from CSS stylesheets like fonts or images, you need to use static folder as a root:
當您引用CSS樣式表中的任何資源(如字體或圖像)時,您需要使用靜態文件夾作為根目錄:
background-image: url("~/assets/images/bg.jpg");
獲取內容 (Getting Content)
With all the components and pages in place, we finally get to it: getting content into components.
在所有組件和頁面就緒之后,我們終于可以實現:將內容添加到組件中。
Getting content using Nuxt is a bit different than it used to be. The important aspect of this process when using a static site generator is that the content needs to be gathered before all the pages are generated. Otherwise you will end up with a static website, but requests for content would still be dynamic, hitting the headless CMS from each visitor’s browser and you would lose the main benefit.
使用Nuxt獲取內容與以前有所不同。 使用靜態站點生成器時,此過程的重要方面是,在生成所有頁面之前需要收集內容。 否則,您最終將獲得一個靜態網站,但對內容的請求仍將是動態的,從而使每個訪問者的瀏覽器都無法訪問CMS,您將失去主要的好處。
Nuxt contains two special methods that handle asynchronous data fetching at the right time, that is before the pages are generated. These methods are asyncData
and fetch
. They are available only to page components (that is files within pages
folder) and their purpose is the same, but asyncData
will automatically store received data within the component dataset.
Nuxt包含兩個特殊方法,這些方法在正確的時間(即生成頁面之前)處理異步數據獲取。 這些方法是asyncData
和fetch
。 它們僅對頁面組件(即pages
文件夾中的文件)可用,并且用途相同,但是asyncData
會自動將接收到的數據存儲在組件數據集中。
This can be beneficial if you have many components on a single page using the same set of data. In my case, I even have multiple pages with many components that need to share the same data. Therefore I would either need to request the same data on each page or use a shared space that all pages and components could access.
如果您在一個頁面上使用相同數據集的多個組件,這將是有益的。 就我而言,我什至有多個頁面,其中包含需要共享同一數據的許多組件。 因此,我要么需要在每個頁面上請求相同的數據,要么使用所有頁面和組件都可以訪問的共享空間。
I chose the latter. Nuxt makes it very easy for us. It automatically includes Vuex store that enables our components and pages access any data that are within the store. To start using the store all you need to do is create an index.js
file within the store
folder.
我選擇了后者。 Nuxt對我們來說非常容易。 它會自動包含Vuex存儲,這使我們的組件和頁面可以訪問存儲中的任何數據。 要開始使用商店,您需要做的就是在store
文件夾中創建一個index.js
文件。
import Vuex from 'vuex'
const createStore = () => { return new Vuex.Store({ state: () => ({}), mutations: {}, actions: {}, })}export default createStore
You see the instance has a few properties:
您會看到實例具有一些屬性:
State
州
State is similar to data in components. It contains definition of data fields that are used to store data.
狀態類似于組件中的數據。 它包含用于存儲數據的數據字段的定義。
Mutations
變異
Mutation are special functions that are permitted to change data in State (mutate the state).
突變是允許在狀態(突變狀態)中更改數據的特殊功能。
Actions
動作
Actions are simple methods that enable you to, for example, implement content gathering logic.
動作是簡單的方法,使您能夠例如執行內容收集邏輯。
Let’s get back to the Blog-list
component. This component needs an array of blog posts in order to render its markup. Therefore blog posts need to be stored within Vuex store:
讓我們回到Blog-list
組件。 該組件需要一系列博客文章才能呈現其標記。 因此,博客文章需要存儲在Vuex商店中:
…const createStore = () => { return new Vuex.Store({ state: () => ({ blogPosts: null }), mutations: { setBlogPosts(state, blogPosts){ state.blogPosts = blogPosts; } }, actions: { getBlogPosts (context) { logic to get content from Kentico Cloud } }, })}
After adjusting Vuex store this way, the Blog-list
component can use its data:
以這種方式調整Vuex存儲后, Blog-list
組件可以使用其數據:
<article v-for="article in $store.state.blogPosts"> …</article>
I already shared the whole implementation of this component above. If you noticed, it uses computed
function as a middle layer between component markup and Vuex store. That middle layer ensures the component displays only a specific amount of articles as configured in the limit
field.
我已經在上面共享了此組件的整個實現。 如果您注意到了,它將computed
功能用作組件標記和Vuex存儲之間的中間層。 中間層可確保組件僅顯示特定數量的商品,如在“ limit
字段中配置的那樣。
查詢無頭CMS (Querying the Headless CMS)
Maybe you remember the deliveryClient
was used to get data from Kentico Cloud into the components.
也許您還記得deliveryClient
用于將數據從Kentico Cloud獲取到組件中。
Disclaimer: I work for Kentico, a CMS vendor that provides both traditional (coupled) CMS and a new cloud-first headless CMS — Kentico Cloud.
免責聲明:我為Kentico工作,該公司是CMS供應商,既提供傳統(耦合)CMS,又提供新的云計算式無頭CMS-Kentico Cloud。
The very same logic can be used here in the Vuex store actions with a little tweak. Kentico Cloud has a module for Nuxt.js, install it using following command:
只需稍作調整,即可在Vuex存儲操作中使用完全相同的邏輯。 Kentico Cloud具有適用于Nuxt.js的模塊 ,請使用以下命令進行安裝:
npm i kenticocloud-nuxt-module — savenpm i rxjs — save
With this module you can keep using deliveryClient
like before, just with a $
prefix. So in my case the logic to get blog posts looks like this:
使用此模塊,您可以像以前一樣繼續使用deliveryClient
,只是帶有$
前綴。 因此,就我而言,獲取博客帖子的邏輯如下:
…getBlogPosts (context) { return this.$deliveryClient .items() ... .orderParameter('elements.published', SortOrder.desc) .getPromise() .then(response => { context.commit('setBlogPosts', response.items.map(item => ({ url: item.link.value, header: item.title.value, image: item.image_url.value != '' ? item.image_url.value : item.image.assets[0].url, teaser: item.teaser.value }))) }); },…
If you want to use ordering using the orderParameter
, you may need to include another import in the store/index.js
file:
如果要使用orderParameter
使用訂購,則可能需要在store/index.js
文件中包括另一個導入:
import { SortOrder } from 'kentico-cloud-delivery'
Now when the content gathering logic is implemented, it’s time to use the special asynchronous function fetch that I mentioned before. See my implementation in the index.vue
page:
現在,當實現內容收集邏輯時,該使用我前面提到的特殊異步函數提取了。 請參閱index.vue
頁面中的實現:
async fetch ({store, params}) { await store.dispatch('getBlogPosts')}
The call to store.dispatch
automatically invokes getBlogPosts
action. Within the getBlogPosts
implementation note the call for context.commit
. context
refers to Vuex store and commit
will hand over blog posts data to setBlogPosts
mutation. Updating the data set with blog posts changes the state of the store (mutates it). And we are almost finished!
調用store.dispatch
自動調用getBlogPosts
操作。 在getBlogPosts
實現中,請注意對context.commit
的調用。 context
是指Vuex存儲,并且commit
會將博客帖子數據setBlogPosts
給setBlogPosts
突變。 用博客文章更新數據集會更改商店的狀態(對其進行突變)。 我們快完成了!
其他內容存儲選項 (Other content storage options)
I used Kentico Cloud headless CMS and its API here, as I am using it throughout all articles in this series. If you want to also check out other options, you can find an independent list of headless CMSs and their features at headlesscms.org.
我在這里使用了Kentico Cloud無頭CMS及其API,因為我在本系列的所有文章中都使用了它。 如果你也想看看其他的選擇,你可以找到無頭的CMS和他們的特征,在一個獨立的名單headlesscms.org 。
If you don’t want to use a headless CMS and its API, you may decide to store your content in some other way — usually as a set of markdown files directly stored within your project or Git repository. You can find a nice example of this approach in nuxt-markdown-example GitHub repository.
如果您不想使用無頭CMS及其API,則可以決定以其他方式存儲內容-通常作為一組直接存儲在項目或Git存儲庫中的markdown文件。 您可以在nuxt-markdown-example GitHub存儲庫中找到這種方法的一個很好的示例。
Nuxt配置 (Nuxt Configuration)
The whole application needs to be properly configured using Nuxt.config.js
file. This file contains information about used modules, their configuration and site essentials like title or SEO tags. The configuration of my website looks like this:
需要使用Nuxt.config.js
文件正確配置整個應用程序。 該文件包含有關使用的模塊,它們的配置和站點必不可少的信息,例如標題或SEO標簽。 我的網站的配置如下所示:
export default { head: { title: 'Ondrej Polesny', meta: [ { charset: 'utf-8' }, ... { hid: 'description', name: 'description', content: 'Ondrej Polesny — Developer Evangelist + dog lover + freelance bus driver' } ], script: [ { src: 'https://www.google.com/recaptcha/api.js?onload=recaptchaLoaded', type: "text/javascript" }, { src: 'assets/js/recaptcha.js', type: "text/javascript" } ], }, modules: [ 'kenticocloud-nuxt-module' ], kenticocloud: { projectId: '*KenticoCloud projectId*', enableAdvancedLogging: false, previewApiKey: '' }, css: [ {src: 'static/assets/css/main.css'}, ], build: { extractCSS: { allChunks: true } }}
The head section describes website essentials like title
and meta
tags you want to include in header.
頭部分描述了網站要領,例如要包含在標題中的title
和meta
標記。
Note the modules
and kenticocloud
configuration. The first one lists all modules your application depends on and the second one is specific module configuration. This is the place where you need to put your project API key.
注意modules
和kenticocloud
配置。 第一個列出了應用程序依賴的所有模塊,第二個列出了特定的模塊配置。 這是您需要放置項目API密鑰的地方。
To see all the options for meta tags, please refer to https://github.com/declandewet/vue-meta
要查看元標記的所有選項,請參閱https://github.com/declandewet/vue-meta
運行并生成 (Running and Generating)
Static sites need to be generated before anyone can access them or upload them to an FTP server. However, it would be very time consuming to regenerate the site every single time a change has been made during the development phase. Therefore, you can run the application locally using:
在任何人都可以訪問靜態站點或將其上傳到FTP服務器之前,需要生成靜態站點。 但是,在開發階段中每次進行更改都會重新生成站點,這將非常耗時。 因此,您可以使用以下命令在本地運行該應用程序:
npm run dev
This will create a development server for you and enable you to access your website on http://localhost:8000 (or similar). While you keep your console running this command, every change you make in your scripts will have immediate effect on the website.
這將為您創建一個開發服務器,并使您能夠訪問http:// localhost:8000(或類似地址)上的網站。 在保持控制臺運行此命令的同時,您對腳本所做的每項更改都將立即對網站生效。
To generate a true static site, execute following command:
要生成一個真正的靜態站點,請執行以下命令:
npx nuxt generate
The output, that is your static site, will be in dist
folder. Feel free to open any page in your favorite text editor and see if the source code contains content from the headless CMS. If it does, it was successfully fetched.
輸出,即您的靜態站點,將位于dist
文件夾中。 隨時在您喜歡的文本編輯器中打開任何頁面,并查看源代碼是否包含無頭CMS的內容。 如果是這樣,則說明已成功獲取。
結論 (Conclusion)
Having a generated static site will greatly improve the website’s performance. Compared to traditional sites, the webserver does not need to perform any CPU heavy operations. It only serves static files.
具有一個生成的靜態網站將大大提高網站的性能。 與傳統站點相比,Web服務器不需要執行任何CPU繁重的操作。 它僅提供靜態文件。
Compared to API based websites, the clients receive requested data instantly with the very first response. That’s what makes them all that fast — they do not need to wait for external content to be delivered via additional asynchronous requests. The content is already there in the first response from the server. That dramatically improves user experience.
與基于API的網站相比,客戶端會在第一響應中立即收到請求的數據。 這就是使它們如此快速的原因-他們無需等待通過其他異步請求傳遞外部內容。 服務器的第一響應中已經存在該內容。 這極大地改善了用戶體驗。
Converting the site from Vue.js implementation to Nuxt definitions is very straightforward and does not require deep knowledge of all used components and packages.
將網站從Vue.js實現轉換為Nuxt定義非常簡單,并且不需要對所有使用的組件和軟件包有深入的了解。
Have you successfully created your first static site? Have you experienced any struggles? Please leave a comment.
您是否成功創建了第一個靜態網站? 你經歷過任何掙扎嗎? 請發表評論。
In the next article I will focus on automated regeneration of static sites and where to host them, so stay tuned.
在下一篇文章中,我將重點介紹靜態站點的自動再生以及在何處托管靜態站點,請繼續關注。
該系列的其他文章: (Other articles in the series:)
How to start creating an impressive website for the first time
如何首次開始創建令人印象深刻的網站
How to decide on the best technology for your website?
如何為您的網站選擇最佳技術?
How to power up your website with Vue.js and minimal effort
如何通過Vue.js和最少的精力為您的網站加電
How to Mix Headless CMS with a Vue.js Website and Pay Zero
如何將無頭CMS與Vue.js網站混合并支付零費用
How to Make Form Submissions Secure on an API Website
如何在API網站上確保表單提交的安全
Building a super-fast and secure website with a CMS is no big deal. Or is it?
用CMS構建超快速,安全的網站沒什么大不了的。 還是?
How to generate a static website with Vue.js in no time
如何立即使用Vue.js生成靜態網站
How to quickly set up a build process for a static site
如何快速設置靜態站點的構建過程
翻譯自: https://www.freecodecamp.org/news/how-to-generate-a-static-website-with-vue-js-in-no-time-e74e7073b7b8/
vue生成靜態js文件