Nuxt
是处置惩罚SEO
的比较经常运用的处置惩罚方案,跟着Nuxt
也有许多坑,每当打破一个小手艺点的时刻,都有很大的成就感,在这段时间里实在让我痛并快乐着。在这里依据个人进修状况,所踩过的坑做了一个汇总和总结。
Nuxt开辟跨域
项目能够运用Nginx
来反向代办,将外来的要求(这里也注意下将Linux
的防火墙放行响应端口)转发的内部Nuxt
默许的3000
端口上,最简朴的设置文件以下:
nuxtjs.config.js
{
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
proxy: [
[
'/api',
{
target: 'http://localhost:3001', // api主机
pathRewrite: { '^/api' : '/' }
}
]
]
}
@nuxtjs/proxy
须要手动零丁装置。
Nuxt Store 运用
在Nuxt
中运用Vuex
跟传统在Vue
中运用Vuex
还不太一样,起首Nuxt
已集成了Vuex
,不须要我们举行二次装置,直接援用就好,在默许Nuxt
的框架模板下有一个Store
的文件夹,就是我们用来寄存Vuex
的处所。
Nuxt
官方也供应了相干文档,能够简朴的过一下,然则官方文档我看来比较草率。
依据官方文档在store
文件下面建立两个.js
文件,分别是index.js
和todo.js
。并在pages
文件夹下面建立index.vue
。
store – index.js
export const state = () => ({
counter: 0
})
export const mutations = {
increment (state) {
state.counter++
}
}
store – todo.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
}
}
pages – index.vue
<template>
<section class="container">
<div>
<h2 @click="$store.commit('increment')">{{counter}}</h2>
<ul>
<li v-for="(item,index) of list"
:key="index">{{item.text}}</li>
</ul>
</div>
</section>
</template>
<script>
import Logo from '~/components/Logo.vue'
import {mapState} from "vuex";
export default {
components: {
Logo
},
computed:{
...mapState(["counter"]),
...mapState("todos",{
list:state => state.list
})
},
created(){
for(let i =0;i<10;i++){
this.$store.commit("todos/add",i);
}
console.log(this.list)
}
}
</script>
在Nuxt
中能够直接运用this.$store
,而且是默许启用定名空间的。再看一下computed
中的代码,在运用mapState
的时刻,counter
属性是直接猎取出来的,但是todos
属性则是经由过程定名空间才猎取到的。这又是怎样回事?
Nuxt
把store
中的index.js
文件中所有的state、mutations、actions、getters
都作为其大众属性挂载到了,store
实例上,但是其他的文件则是运用的是定名空间,其对应的定名空间的名字就是其文件名。
运转项目的时刻能够在.nuxt
文件夹内找到store.js
看下是怎样完成的。简朴的解释一下代码作用,以及做什么用的。
.nuxt – store.js
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 作为中间件
Vue.use(Vuex)
// 保留console 函数
const log = console
// vuex的属性
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
// store属性容器
let store = {}
// 没有返回值的自实行函数
void (function updateModules() {
// 初始化根数据,也就是上面所说的index文件做为共有数据
store = normalizeRoot(require('@/store/index.js'), 'store/index.js')
// 假如store是函数,提醒异常,住手实行
if (typeof store === 'function') {
// 正告:典范情势的市肆是不赞成的,并将删除在Nuxt 3。
return log.warn('Classic mode for store is deprecated and will be removed in Nuxt 3.')
}
// 实行存储模块
// store - 模块化
store.modules = store.modules || {}
// 处置惩罚存储模块要领
// 引入todos.js 文件,即数据
// 'todos.js' 文件名
resolveStoreModules(require('@/store/todos.js'), 'todos.js')
// 假如环境支撑热重载
if (process.client && module.hot) {
// 不管什么时候更新Vuex模块
module.hot.accept([
'@/store/index.js',
'@/store/todos.js',
], () => {
// 更新的根。模块的最新定义。
updateModules()
// 在store中触发烧更新。
window.$nuxt.$store.hotUpdate(store)
})
}
})()
// 建立store实例
// - 假如 store 是 function 则运用 store
// - 不然建立一个新的实例
export const createStore = store instanceof Function ? store : () => {
// 返回实例
return new Vuex.Store(Object.assign({
strict: (process.env.NODE_ENV !== 'production')
}, store))
}
// 处置惩罚存储模块要领
// moduleData - 导出数据
// filename - 文件名
function resolveStoreModules(moduleData, filename) {
// 猎取导出数据,为了处置惩罚es6 (export default)导出
moduleData = moduleData.default || moduleData
// 长途store src +扩大(./foo/index.js -> foo/index)
const namespace = filename.replace(/\.(js|mjs|ts)$/, '')
// 空间称号
const namespaces = namespace.split('/')
// 模块称号(state,getters等)
let moduleName = namespaces[namespaces.length - 1]
// 文件途径
const filePath = `store/${filename}`
// 假如 moduleName === 'state'
// - 实行 normalizeState - 一般状况
// - 实行 normalizeModule - 标准化模块
moduleData = moduleName === 'state'
? normalizeState(moduleData, filePath)
: normalizeModule(moduleData, filePath)
// 假如是 (state,getters等)实行
if (VUEX_PROPERTIES.includes(moduleName)) {
// module称号
const property = moduleName
// 存储模块 // 猎取存储模块
const storeModule = getStoreModule(store, namespaces, { isProperty: true })
// 兼并属性
mergeProperty(storeModule, moduleData, property)
// 作废后续代码实行
return
}
// 特别处置惩罚index.js
// 模块称号即是index
const isIndexModule = (moduleName === 'index')
// 假如即是
if (isIndexModule) {
// 称号空间弹出末了一个
namespaces.pop()
// 猎取模块称号
moduleName = namespaces[namespaces.length - 1]
}
// 猎取存储模块
const storeModule = getStoreModule(store, namespaces)
// 遍历 VUEX_PROPERTIES
for (const property of VUEX_PROPERTIES) {
// 兼并属性
// storeModule - 存储模块
// moduleData[property] - 存储模块中的某个属性数据
// property - 模块称号
mergeProperty(storeModule, moduleData[property], property)
}
// 假如moduleData.namespaced === false
if (moduleData.namespaced === false) {
// 删除定名空间
delete storeModule.namespaced
}
}
// 初始化根数据
// moduleData - 导出数据
// filePath - 文件途径
function normalizeRoot(moduleData, filePath) {
// 猎取导出数据,为了处置惩罚es6 (export default)导出
moduleData = moduleData.default || moduleData
// 假如导入的数据中存在commit要领,则抛出异常
// - 应当导出一个返回Vuex实例的要领。
if (moduleData.commit) {
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
}
// 假如 moduleData 不是函数,则运用空队形举行兼并处置惩罚
if (typeof moduleData !== 'function') {
// 防止键入毛病:设置在掩盖顶级键时只要getter的属性
moduleData = Object.assign({}, moduleData)
}
// 对模块化举行处置惩罚后返回
return normalizeModule(moduleData, filePath)
}
// 一般状况
// - 模块数据
// - 文件途径
function normalizeState(moduleData, filePath) {
// 假如 moduleData 不是function
if (typeof moduleData !== 'function') {
// 正告提醒
// ${filePath}应当导出一个返回对象的要领
log.warn(`${filePath} should export a method that returns an object`)
// 兼并 state
const state = Object.assign({}, moduleData)
// 以函数情势导出state
return () => state
}
// 对模块化举行处置惩罚
return normalizeModule(moduleData, filePath)
}
// 对模块化举行处置惩罚
// moduleData - 导出数据
// filePath - 文件途径
function normalizeModule(moduleData, filePath) {
// 假如module数据的state存在而且不是function正告提醒
if (moduleData.state && typeof moduleData.state !== 'function') {
// “state”应当是返回${filePath}中的对象的要领
log.warn(`'state' should be a method that returns an object in ${filePath}`)
// 兼并state
const state = Object.assign({}, moduleData.state)
// 掩盖原有state运用函数返回
moduleData = Object.assign({}, moduleData, { state: () => state })
}
// 返回初始化数据
return moduleData
}
// 猎取store的Model
// - storeModule store数据模型
// - namespaces 定名空间称号数组
// - 是不是运用定名空间 默许值 为false
function getStoreModule(storeModule, namespaces, { isProperty = false } = {}) {
// 假如 namespaces 不存在,启动定名空间,定名空间称号长度1
if (!namespaces.length || (isProperty && namespaces.length === 1)) {
// 返回model
return storeModule
}
// 猎取定名空间称号
const namespace = namespaces.shift()
// 保留定名空间中的数据
storeModule.modules[namespace] = storeModule.modules[namespace] || {}
// 启用定名空间
storeModule.modules[namespace].namespaced = true
// 增加定名数据
storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {}
// 递归
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
}
// 兼并属性
// storeModule - 存储模块
// moduleData - 存储模属性数据
// property - 模块称号
function mergeProperty(storeModule, moduleData, property) {
// 假如 moduleData 不存在推出顺序
if (!moduleData) return
// 假如 模块称号 是 state
if (property === 'state') {
// 把state数据分到模块空间内
storeModule.state = moduleData || storeModule.state
} else {
// 其他模块
// 兼并到对应的模块空间内
storeModule[property] = Object.assign({}, storeModule[property], moduleData)
}
}
以上就是编译后的store
文件,大抵的意义就是对store
文件举行遍历处置惩罚,依据差别的文件运用差别的处置惩罚方案,运用定名空间挂载model
。
页面loading
Nuxt
有供应加载Loading
组件,一下是设置。
nuxtjs.config.js
module.exports = {
loading: { color: '#3B8070' }
}
Nuxt
供应的loading
不能满足项目需求,能够有的项目不须要如许加载动画,so~,就须要本身手动设置一个。增加一个loading组件 (官方示例以下,概况可看官方文档)援用该组件。
nuxtjs.config.js
module.exports = {
loading: '~components/loading.vue'
}
一个小插曲在Nuxt中,~与@都指向的是根目录。
components/loading.vue
<template lang="html">
<div class="loading-page" v-if="loading">
<p>Loading...</p>
</div>
</template>
<script>
export default {
data: () => ({
loading: false
}),
methods: {
start () {
this.loading = true
},
finish () {
this.loading = false
}
}
}
</script>
第三方组件库
项目开辟过程当中,难免会用到组件库,与在Vue
中运用的时刻是太一样的,须要增加一些依靠才一般运用。
plugins – element-ui.js
import Vue from 'vue';
import Element from 'element-ui';
import locale from 'element-ui/lib/locale/lang/en';
export default () => {
Vue.use(Element, { locale })
};
nuxtjs.config.js
module.exports = {
css: [
'element-ui/lib/theme-chalk/index.css'
],
plugins: [
'@/plugins/element-ui',
'@/plugins/router'
]
};
运用中间件
中间件Nuxt
没有给出详细的运用文档,而是放入了一个编辑器。这一点我觉得到了一丝丝的 差别。为何要如许。。。简朴的研讨了一下,弄邃晓了也许。
在middleware
中建立想要的中间件。这里借用一下官网的例子。
middleware – visits.js
export default function ({ store, route, redirect }) {
store.commit('ADD_VISIT', route.path)
}
向上面如许就建立好了一个中间件,然则应当怎样运用呢?在运用的时刻有两种体式格局,一种是全局运用,另一种是在页面中零丁运用,文件名会作为其中间件的称号。
++全局运用++
nuxtjs.config.js
export default {
router: {
middleware: ['visits']
}
}
页面中零丁运用
export default {
middleware: 'auth'
}
官网中在页面中的asyncData
中有一段如许的代码。
export default {
asyncData({ store, route, userAgent }) {
return {
userAgent
}
}
}
延续更新。。。
总结
Nuxt
的进修曲线异常小,就像Vue
框架一样,已是一个开箱即用的状况,我们能够直接跨过设置直接开辟。对设置有兴致的能够在Vue
官方文档找到SSR
衬着文档。