Nuxt.js 备忘单
Nuxt.js 的存在是为了让你的生活更轻松,它也能使 Vue.js 的开发流程更加流畅。然而,尽管它有很多优点,但它也有一些怪癖,会让你忍不住点击 Google 上的每一个链接。
本文旨在避免这些情况,它将涵盖一些常见用例和一些边缘情况,并提供一些快速简便的代码片段。本文不会对这些内容进行过于详细的阐述,但会提供必要的文档,以备不时之需。
注意:您需要充分掌握 Vue.js 概念才能充分利用本文!
在我们讨论具体内容之前,让我先解释一下什么是 Nuxt.js。
什么是 Nuxt.js?
Nuxt.js 是一个基于Vue.js的框架,允许您构建功能齐全的服务器渲染应用程序。
它开箱即用,包含大量有用的软件包:
- 💻Vue
- ↩️ Vue Router(用于轻松路由)
- 💾 Vuex(用于轻松的状态管理)
- 🏎 Vue 服务器渲染器(用于开箱即用的服务器端渲染)
- 🕵️♂️ Vue 元(用于 SEO)
以下是我们将要介绍的内容列表(如果您要搜索特定内容,请随时回到这里):
一般的
路由
状态管理
搜索引擎优化
各种各样的
如果您有任何其他要求或想要添加任何新内容,请随时在 Twitter 上联系我@christo_kade!
创建 Nuxt.js 项目
yarn create nuxt-app <project-name>
这将提示您回答一些问题,包括:
- 在集成的服务器端框架之间进行选择(默认无、Express、Koa 等)
- 选择要安装的功能(PWA 支持、Linter / Formatter、Prettier、Axios)
- 选择您最喜欢的 UI 框架(默认无,Bootstrap、Vuetify、Bulma 等)
- 选择您最喜欢的测试框架(None、Jest、AVA)
- 您想要的 Nuxt 模式(通用或 SPA,更多信息)
完成后,您的依赖项就安装好了:
$ cd <project-name>
$ yarn dev
使用 Nuxt.js 进行测试
大部分测试语法取决于项目创建时选择的测试框架。Nuxt
开箱即用,@vue/test-utils
通过诸如mount()
、shallowMount()
和 等多种方法,使用该包来渲染组件render()
。之后,您将能够测试特定值是否已显示、特定方法是否被调用等等。
Nuxt 还将确保为您设置好一切,您所要做的就是创建您的*.spec.js
或*.test.js
文件并运行yarn test
命令。
以下是 Nuxt 项目中 Vue 组件单元测试的经典(且简短)示例:
import { shallowMount } from "@vue/test-utils"
import cmp from "~/components/navbar/navbar"
// Mocking an icon displayed in my navbar
jest.mock("~/static/icons/svg/icon-menu.svg", () => "")
describe("Navbar component", () => {
// We shallow mount the component while mocking some internal elements
// Most of the time, you'll have to mock context objects such as $store or $route in order to render your component whithout any errors
const wrapper = shallowMount(cmp, {
// Stubbing nuxt-links in the navbar
stubs: ["nuxt-link"],
mocks: {
"nuxt-Link": true,
// Mocking the $store context object
$store: {
state: {
locale: "en",
},
},
// Mocking the $route context object
$route: {
path: "mockedPath",
},
},
})
it("Snapshot testing", () => {
expect(wrapper.html()).toMatchSnapshot()
})
describe("Components validation", () => {
it("should return a valid component", () => {
expect(wrapper.is(cmp)).toBe(true)
})
})
describe("Content validation", () => {
it("should render the link's name", () => {
expect(wrapper.html()).toContain("About")
})
// ...
})
})
创建新路线
在该/pages
文件夹中,创建一个文件,其名称将是路线的名称。
例如:
// /pages/about.vue
<template>
<main>
<h1>About page</h1>
<main/>
</template>
<script>
export default {}
</script>
<style></style>
导航至localhost:3000/about
将显示此组件的内容
创建动态路线
在/pages
文件夹中,创建一个目录和一个以下划线为前缀的文件。
例如以下文件树:
pages/
--| users/
-----| _id.vue
--| index.vue
.nuxt
每当您构建项目时,都会在文件夹内自动生成以下路由器:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
]
}
现在您可以导航到/users/:id
,并将id
其设置为您需要的任何值。
要在组件中检索此值_id.vue
,只需执行以下操作:
// $route is a Nuxt context object, more info: https://nuxtjs.org/api/context
const { id } = this.$route.params
文档,包括嵌套路由和动态嵌套路由。
导航到组件模板中的路由
在任何组件内部:
// /components/example.vue
// Clicking on this nuxt-link will navigate to the /pages/about.vue component
// nuxt-link renders an <a> tag in your HTML
<template>
<section>
<nuxt-link to="/about">
About
</nuxt-link>
</section>
</template>
// ...
通过编程方式导航至路线
// Will add a history entry to the stack
this.$router.push({
path: '/about'
})
// Will not
this.$router.replace({
path: '/about'
})
// Goes back one record
this.$router.go(-1)
创建新的商店模块
文件夹中/store
,每个文件都是一个 Vuex 模块。
// /store/todos.js
export const state = () => ({
list: []
})
export const mutations = {
add(state, text) {
state.list.push({
text: text,
done: false
})
},
remove(state, { todo }) {
state.list.splice(state.list.indexOf(todo), 1)
},
toggle(state, todo) {
todo.done = !todo.done
}
}
现在可以使用上下文对象获取每个模块的突变、动作和状态$store
:
// /components/todo.vue
<template>
<ul>
<li v-for="todo in todos">
<input type="checkbox" :checked="todo.done" @change="toggle(todo)">
<span>{{ todo.text }}</span>
</li>
<li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></li>
</ul>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
todos () {
return this.$store.state.todos.list // highlight-line
}
},
methods: {
addTodo (e) {
this.$store.commit('todos/add', e.target.value) // highlight-line
e.target.value = ''
},
...mapMutations({ // highlight-line
toggle: 'todos/toggle' // highlight-line
}) // highlight-line
}
}
</script>
在渲染组件之前更新 store
有时您需要在渲染组件之前填充给定的状态变量,方法如下:
// In any component
export default {
// Called before rendering the component
fetch ({ store, params }) {
return axios.get('https://dog.ceo/api/breeds/image/random')
.then((res) => {
store.commit('setDog', res.data.message)
})
}
}
警告:您无法通过
this
内部获取访问组件实例,因为它是在启动组件之前调用的(阅读更多)。
动态更改页面的 head 属性
出于 SEO 目的,定义页面标题、描述关键词等会很有用。以下是如何通过编程实现的:
// In any component
export default {
head: {
title: 'Page title',
meta: [
{
hid: 'description', name: 'description',
content: 'Page description'
}
],
// ...
}
}
信息:为避免在子组件中使用重复的元标记,请使用 hid 键为元元素设置唯一标识符(阅读更多)。
动态路由的 SSR
运行时nuxt generate
,默认情况下不会生成动态路线的 HTML 文件。
例如,如果您有一个about.vue
页面和一个_id.vue
,当运行时nuxt generate
,生成的dist
文件夹将包含/about/index.html
但不会为您的动态生成任何内容_id.vue
。
这可能会导致您的动态路线被爬虫错过,从而不会被搜索引擎引用!
自动生成它们的方法如下:
// nuxt.config.js
module.exports = {
// ...
// dynamicRoutes could be a JSON file containing your dynamic routes
// or could be retrieved automatically based on the content of your /pages folder
generate: {
routes: () => {
return dynamicRoutes.map(route => `/articles/${route}`)
},
},
// ...
}
nuxt generate
现在将为该generate
属性返回的每个动态路由生成 HTML 文件。
在整个应用中显示固定组件
有时您需要添加一个导航栏或页脚,无论当前路线如何都会显示。
默认情况下,有一个/layout
文件夹包含以下内容default.vue
。此布局包含<nuxt/>
负责渲染每个页面内容的组件(请参阅创建新路由)。
只需修改该组件以满足您的需要,例如:
<template>
<div>
<navbar/>
<nuxt/>
<footer/>
</div>
</template>
<script>
import navbar from "~/components/navbar/navbar"
import footer from "~/components/footer/footer"
export default {
components: {
cmpNavbar,
cmpFooter,
},
}
</script>
更改项目的路由器基础
在某些情况下,例如当您在 下的 Github Pages 上部署项目时username/my-project
,您需要更改项目的路由器基础,以便正确显示您的资产。
// nuxt.config.js
// Will change the router base to /my-project/ when DEPLOY_ENV equals GH_PAGES
const routerBase = process.env.DEPLOY_ENV === "GH_PAGES"
? {
router: {
base: "/my-project/",
},
}
: {
router: {
base: "/",
},
}
module.exports = {
// ...
routerBase,
// ...
}
并且不要忘记更改您的package.json
设置,以便nuxt.config.js
知道何时为 Github Pages 构建或生成。
// package.json
"scripts": {
"build:gh-pages": "DEPLOY_ENV=GH_PAGES nuxt build",
"generate:gh-pages": "DEPLOY_ENV=GH_PAGES nuxt generate"
},
处理国际化(i18n)
从运行开始yarn add vue-i18n
创建以下文件:
// /plugins/i18n.js
import Vue from "vue"
import VueI18n from "vue-i18n"
Vue.use(VueI18n)
export default ({ app, store }) => {
// Set i18n instance on app
// This way we can use it globally in our components
app.i18n = new VueI18n({
locale: store.state.locale,
fallbackLocale: "fr",
messages: {
// Add the supported languages here AND their associated content files
en: require("~/static/json/data-en.json"),
fr: require("~/static/json/data-fr.json"),
},
})
}
并在您的中添加以下行nuxt.config.js
以告知它我们正在使用该插件:
module.exports = {
// ...
plugins: ["~/plugins/i18n.js"],
// ...
}
在此示例中,当前语言环境基于我的商店的内容,如下所示:
export const state = () => ({
locales: ["en", "fr"],
locale: "fr",
})
export const mutations = {
setLanguage(state, locale) {
if (state.locales.indexOf(locale) !== -1) {
state.locale = locale
}
},
}
因此,无论何时调用setLanguage
,语言环境都会自动更新,并且会加载正确的 JSON 文件!✨
您的文件内容现在可以在整个应用程序中使用,如下所示:
// Here we access the 'users' array in our JSON file
this.$t("users")
将字体导入到你的项目中
// nuxt.config.js
module.exports = {
/*
** Headers of the page
*/
head: {
// ...
link: [
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css?family=Lato",
},
],
},
// ...
}
总结
好了,我想一篇文章就够了。我已经介绍了很多用例,希望对你们中的一些人有用。
如果你有任何疑问或想补充内容,欢迎在 Twitter 上给我留言@christo_kade,也请关注我,以便及时了解我撰写的新文章或与 JavaScript 和 CSS 相关的有趣发现 😄