Vue2+Nuxt2 从 0 到1 搭建官网~

Vue3+Nuxt3 从0到1搭建官网项目

安装 Nuxt3,创建项目初始化的 package.json项目结构初始化项目pages 文件下创建index.vue引入sass修改 app.vue 文件查看效果

配置公共的css、metaassets下的cssreset.scss 重置文件common.scss

配置nuxt.config.ts现在的package.json查看效果

创建新页面pages目录结构components 创建组件Header 组件Footer 组件引入组件组件效果

多语音系统引入i18n新建lang文件夹入口文件(index.js )中英js文件 对比合并后的JSON图

i18n.config.tsnuxt.config.ts修改index.vue 文件查看效果中文效果英文效果

动态切换语言nuxt.config.tshead文件

composablesstore.tsglobal.tsheader.vueerror.vue404.vue查看效果中文效果英文效果

配置每个页面的TDKplugins 文件夹global.tsindex.vue演示效果中文效果英文效果

图片懒加载懒加载指令页面中引用演示效果

fonts 字体库api请求动画

想开发一个官网,并且支持SEO搜索,当然离不开我们的 Nuxt ,Nuxt2 我们刚刚可以熟练运用,现在有出现了Nuxt3,那通过本篇文章让我们一起了解一下。

安装 Nuxt3,创建项目

安装nuxt3, 需要node v18.10.0以上,大家记得查看自己的node版本。

升级node,可以参考使用nvm 切换不同node版本~

// node v18.10.0

// npx nuxi@latest init

npx nuxi@latest init nuxt3-demo

cd nuxt3-demo

初始化的 package.json

这是项目刚创建后的package.json文件

{

"name": "nuxt3-demo",

"private": true,

"type": "module",

"scripts": {

"dev": "nuxt dev",

"build": "nuxt build",

"generate": "nuxt generate",

"preview": "nuxt preview",

"postinstall": "nuxt prepare"

},

"dependencies": {

"nuxt": "3.8.2",

"vue": "3.3.10",

"vue-router": "4.2.5"

}

}

项目结构

├── app.vue // 主文件

├── assets // 静态资源

├── components // 公共vue组件

├── composables // 将你的Vue组合式函数自动导入到你的应用程序中

├── error.vue // 路由匹配不到时

├── i18n.config.ts // 语言切换配置文件

├── lang // 语言JSON

├── nuxt.config.ts // nuxt 配置文件

├── package.json

├── pages // pages文件夹下面的页面名,默认为 路由地址

├── plugins // 公共插件

├── public // 提供网站的静态资源

├── server

├── tsconfig.json

└── yarn.lock // 包含了应用程序的所有依赖项和脚本

初始化项目

我们首先创建一个首页,将项目运行起来,这样一会儿讲到SEO、语音切换时,方便查看效果

pages 文件下创建index.vue

我们发现页面出现如下错误,提示我们需要引入 sass )

引入sass

// 如果下载失败。记得比对package.json 中依赖的版本号

// node_modules 和 yarn.lock 记得也删除一下

yarn add string-width@7.1.0 sass@1.69.5 sass-loader@13.3.2 --save

修改 app.vue 文件

查看效果

配置公共的css、meta

在我们的项目中,UI风格肯定是有规范(统一)的,因此我们可以将 css 重置文件、公共的css文件,以及Meta提前引入

assets下的css

在assets 文件夹下,我们可以创建css(样式)、img(图片)、fonts(字体库)等文件夹

reset.scss 重置文件

因为UI主体区域为1200px,因此body 我们设置了最大宽度是1350px,这样主体区域两侧有一点占位空间,使内容不至于紧贴设备边界

/* CSS reset */

// /assets/css/reset.scss

html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {

margin:0;

padding:0;

}

body{

font-family:"思源黑体","Microsoft YaHei","微软雅黑",Arial,sans-serif;

font-size: 14px;

color: #333;

min-width: 1350px;

}

*{

-webkit-text-size-adjust: none;

}

*,*:after,*:before{

box-sizing:border-box;

margin: 0;

padding:0;

}

table {

border-collapse:separate;

border-spacing:0;

}

fieldset,img {

border:0;

}

img{

display: block;

-webkit-user-drag: none;

}

img[src=""],img:not([src]){

opacity: 0;

border:none;

visibility: hidden;

max-width: none;

}

address,caption,cite,code,dfn,th,var {

font-style:normal;

font-weight:normal;

}

ol,ul ,li{

list-style:none;

}

caption,th {

text-align:left;

}

h1,h2,h3,h4,h5,h6 {

font-size:100%;

font-weight:normal;

}

abbr,acronym { border:0;

}

a{

text-decoration:none;

}

/* 解决兼容而加的样式 */

a, img {

-webkit-touch-callout: none; /*禁止长按链接与图片弹出菜单*/}

a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0);}

img{

display: block;

}

button,input,optgroup,select,textarea {

outline:none;

/*-webkit-appearance:none; /*去掉webkit默认的表单样式*/}

a,button,input,optgroup,select,textarea {

-webkit-tap-highlight-color:rgba(0,0,0,0); /*去掉a、input和button点击时的蓝色外边框和灰色半透明背景*/

}

input:-ms-input-placeholder{color:#b3b7c0;}

input::-webkit-input-placeholder{color:#b3b7c0;}

input:-moz-placeholder{color:#b3b7c0;}

input::-moz-placeholder{color:#b3b7c0;}

common.scss

/* CSS common */

// 这里大家可以写一些公共的css样式,我这里主要是举例

// /assets/css/common.scss

.g-border{position: relative;}

.g-border1{position: relative;}

.g-border:after{content:'';position: absolute;bottom:0;width:100%;height:1px;background:#e8e8e8;overflow: hidden;left:0;transform:translate(0%,0) scale(1,0.5);}

.g-border1::before{content:'';position: absolute;top:0;width:100%;height:1px;background:#e8e8e8;overflow: hidden;left:0;transform:translate(0%,0) scale(1,0.5);}

.g-border-on::before,.g-border-on:after{

background: #dcdcdc!important;

}

.g-text-ove2{display: -webkit-box;-webkit-box-orient: vertical; -webkit-line-clamp: 2;overflow: hidden;}

.g-text-ove1{overflow: hidden;text-overflow:ellipsis;white-space: nowrap;}

配置nuxt.config.ts

// 熟悉我的小伙伴可能注意到,我非常喜欢在项目中使用 address,哈哈哈

yarn add address@2.0.1 --save

// https://nuxt.com/docs/api/configuration/nuxt-config

const address = require('address')

const localhost = address.ip() || 'localhost'

export default defineNuxtConfig({

ssr:true,

app:{

head: {

meta: [

{ charset: 'utf-8' },

{ name: 'viewport', content: 'width=device-width, initial-scale=1' },

{ hid: 'viewport', name: 'viewport', content:"width=1350, user-scalable=no,viewport-fit=cover"},

{ hid: 'description', name: 'description', content: 'CSDN 作者:拿回忆下酒,介绍Vue3+Nuxt3 从0到1搭建官网项目(SEO搜索、中英文切换、图片懒加载)的dome' },

{ hid: 'keywords', name: 'keywords', content: 'Vue3,Nuxt3,CSDN 拿回忆下酒' },

{ name: 'format-detection', content: 'telephone=no' }

],

link:[

{

rel:'icon',

type:'image/x-icon',

href:'/favicon.ico'

}

]

},

},

css: [

'@/assets/css/reset.scss',

// 公共class

'@/assets/css/common.scss'

],

devtools: {

enabled: true,

ssr:false

},

devServer:{

host: localhost,

port:8303

}

})

现在的package.json

防止小伙伴下错版本号,咱们确定一下 现在的依赖包的版本号

大家也可以复制一下内容,将node_modules 和 yarn.lock 删除,重新执行 yarn install

{

"name": "nuxt3-demo",

"private": true,

"type": "module",

"scripts": {

"dev": "nuxt dev --open",

"build": "nuxt build",

"generate": "nuxt generate",

"preview": "nuxt preview",

"postinstall": "nuxt prepare"

},

"dependencies": {

"@nuxt/devtools": "latest",

"@nuxtjs/i18n": "8.0.0",

"address": "2.0.1",

"nuxt": "3.8.2",

"sass": "1.69.5",

"sass-loader": "13.3.2",

"string-width": "7.1.0",

"vue": "3.3.10",

"vue-router": "4.2.5"

}

}

查看效果

我们可以看到 IP、端口、meta、css 已经都改变了

创建新页面

创建新页面,演示目录结构、路由跳转、多页面TDK配置等等

pages目录结构

我们在pages文件夹下,新建 首页(index)、关于我们(about)、订单(product/order)、管家(product/steward)等页面,用来演示 目录结构 和 路由 的 对应关系

// pages 目录结构

├── nuxt.config.ts

├── package.json

├── pages

│ ├── abouut.vue

│ ├── index.vue

│ └── product

│ ├── order.vue

│ └── steward.vue

components 创建组件

components/ 目录是你放置所有 Vue 组件的地方。我们可以把公共组件放到这里

Header 组件

Footer 组件

引入组件

官网一般header(头部)、footer(底部)都是复用的,因此我们在app.vue文件中引用它们

// app.vue

组件效果

多语音系统

现在好多公司的官网都是多语言,因此我们一起了解一下 i18n

引入i18n

yarn add @nuxtjs/i18n@8.0.0 --save

新建lang文件夹

这是一个自创建的文件夹,用来存放 语言切换 需要用到的数据,我们来了解一下它的目录结构

├── nuxt.config.ts

├── i18n.config.ts

├── lang

│ ├── cn

│ │ ├── about.js

│ │ ├── home.js

│ │ ├── index.js // 入口文件

│ │ ├── order.js

│ │ └── steward.js

│ └── en

│ ├── about.js

│ ├── home.js

│ ├── index.js // 入口文件

│ ├── order.js

│ └── steward.js

入口文件(index.js )

index.js 的代码是一致的,主要作用是 将同一种语言下的文件合并到一起, 我以其中一个为例

// 获取同一目录下 的 js 文件

const files = import.meta.globEager('./*.js');

const modules = Object.keys(files).reduce((prev, cur) => {

// 获取js 文件的 文件名 并转 大写 (这里很重要!!!!)

// 这里 大写的文件名,是日后 dom 中日后替换的关键字

let key = cur.split('/')[1].split('.')[0].toUpperCase()

// 将内容合并在一起,为了方便理解,下面有合并后的JSON图

return {...prev,...{[key]:files[cur]?.default}}

}, {});

export default {...modules};

中英js文件 对比

每个js文件,JSON结构都是一致的,我们以home、about 为例对比一下

// cn/home.js

export default {

'b1': {

'title': '我们的首页',

'txt': '这是一段尝试,json结构可以任意定义,只要保持统一规范即可'

}

}

// en/home.js

export default {

'b1': {

'title': 'Our homepage',

'txt': 'China\'s leading fintech platform for automobiles empowering supply chain ecosystem partners. '

}

}

***********************

// cn/about.js

export default {

'b1': {

'title': '关于我们'

}

}

// en/about.js

export default {

'b1': {

'title': 'About Us'

}

}

合并后的JSON图

i18n.config.ts

import en from './lang/en/index.js'

import cn from './lang/cn/index.js'

export default defineI18nConfig(() => ({

legacy: false, // 是否兼容之前

fallbackLocale: 'cn', // 区配不到的语言就用en

messages: {

en,

cn

}

}))

nuxt.config.ts

export default defineNuxtConfig({

...

modules:[

'@nuxtjs/i18n'

],

i18n: {

strategy: 'prefix_and_default', // 添加路由前缀的方式

locales: ["en", "cn"], //配置语种

defaultLocale: 'cn', // 默认语种

vueI18n: './i18n.config.ts', // 通过vueI18n配置

},

...

})

修改index.vue 文件

我们以index.vue 文件为例,主要看 $t(HOME.b1.title) 部分,HOME 便是当时 lang中的文件名

查看效果

中英文切换,主要是通过不同的路由进行区分的,大家注意看浏览器URL地址

中文效果

英文效果

动态切换语言

我们虽然实现了语言替换,但是官网中并没有实现语言动态切换的功能,因此我们需要进一步完善,首先我们需要引入pinia

yarn add @pinia/nuxt@0.5.1 pinia@2.0.23 --save

nuxt.config.ts

// 添加 @pinia/nuxt

export default defineNuxtConfig({

...

modules:[

'@pinia/nuxt',

'@nuxtjs/i18n'

]

...

})

head文件

lang 文件夹下增加 head.js 文件,用来替换 头部的语言

// lang/cn/head.js

export default {

'home':'首页',

'product':'产品业务',

'order':'订单',

'steward':'管家',

'aboutUs':'关于我们'

}

// lang/en/head.js

export default {

'home':'Home',

'product':'Product',

'order':'Order',

'steward':'Steward',

'aboutUs':'About Us'

}

composables

使用composables/目录将你的Vue组合式函数自动导入到你的应用程序中。

├── components

├── composables

│ ├── global.ts

│ └── store.ts

store.ts

export const useStateStore = defineStore('nuxtStore', () => {

const locale = ref('')

let localeName = ref('')

const localeArr = ref([

{id:'cn',name:'中文'},

{id:'en',name:'English'}

])

const setState = (name: any) => {

let arr = localeArr.value.filter(m=>m.id ==name)

let obj = localeArr.value[0]

if(arr.length>0){

obj = arr[0]

}

locale.value = obj.id

localeName.value = obj.name

}

return {

locale,

localeArr,

localeName,

setState

}

})

global.ts

export default function () {

const routerFn = (fullPath: any) => {

const route = useRoute()

const localeRoute = useLocaleRoute()

const store = useStateStore()

// 你的公共逻辑

let arr = ['cn','en']

let a = arr.filter((m) => fullPath.includes(m))

if(a.length){

let paths = fullPath.split(a[0])

fullPath = '/'+store.locale +paths[1]

} else {

fullPath = '/'+store.locale +fullPath

}

// 用于把当前页面生成对应的语言前缀的路由,例如:/zh/,/zh/about

const routePath = localeRoute({ path:fullPath, query: { ...route.query } })

if (routePath) {

return navigateTo(fullPath) // 路由跳转

}

};

return {

routerFn

};

}

header.vue

我们需要重新改造一下header文件,右上角增加了语言切换按钮,整个导航栏 增加了滚动超出页面后悬浮,按钮切换高亮动画效果等

error.vue

和 app.vue 同一级,增加error.vue 文件

404.vue

和index.vue同一级,增加404.vue 文件,文件内容可以自行编辑。 我这里直接重置到了首页

查看效果

内容有限,我们以首页为例,对比一下 中英文切换效果

中文效果

英文效果

配置每个页面的TDK

TDK指的是搜索引擎优化(SEO)中的页面Title、Description、Keywords

Title:

页面的主要内容,对搜索引擎而言至关重要。简洁、准确地描述页面内容。不超过70个汉字或160个英文字符。

Description:

简要描述页面内容,提供页面主要信息。 不超过200个汉字或~300个英文字符。

Keywords:

关键词应该是与页面内容相关的,用于提升搜索结果的相关性。 不超过1000个汉字或~1600个英文字符。 使用对搜索引擎友好的逗号、分号或空格分隔关键词。

plugins 文件夹

Nuxt拥有一个插件系统,可以在创建Vue应用程序时使用Vue插件和其他功能。

在plugins 文件夹下,增加global.ts

global.ts

//global.ts

export default defineNuxtPlugin((nuxtApp) => {

return {

provide: {

setHead: (page: String) => {

const store = useStateStore()

let {title,keywords,description} = store.headJson[store.locale][page]

return {

title,

meta:[

{hid: 'keywords', name: 'keywords', content: keywords},

{hid: 'description', name: 'description', content: description}

]

}

}

}

};

})

index.vue

在首页中引用 global.ts 中 创建的 $setHead 方法

演示效果

中文效果

英文效果

图片懒加载

官网项目一直有一个弊端,就是项目中 图片资源比较多,导致打开速度慢。因此我们需要对图片资源做一下优化。

常规的优化一般针对图片压缩、图片格式、图片标签属性 和 图片懒加载

我们这里主要讨论一下 ,nuxt项目中如何进行图片懒加载 处理

这里首先声明一下,市面上其实有很多懒加载插件,但是不知道是不是因为nuxt3 和 lazy相关插件版本有兼容的问题,一致配置的不太好使

因此这个懒加载是我自己写的一个 自定义指令(本来也不复杂,所以干脆自己写了)

懒加载指令

在 plugins文件夹下,增加index.ts 文件,申明 v-Mlazy 指令

// plugins/index.js

// 该指令 arg 用来 区分 img src 还是 标签 background

import {useIntersectionObserver} from "@vueuse/core";

export default defineNuxtPlugin((nuxtApp) => {

// 不再是 import Vue from 'vue'的写法了

nuxtApp.vueApp.directive('Mlazy', {

// 不用mounted,在mounted时用户权限集还是空的

mounted(el,{

arg,

value

}) {

if (typeof value != 'string') {

return

} else if(arg == 'show') {

el.style.background = `url(${value}) no-repeat center`

el.style.backgroundSize = `cover`

return

} else if(arg == 'imgShow') {

el.src = value

return

}

const { stop } = useIntersectionObserver(

el,

([{ isIntersecting }]) => {

if (isIntersecting) {

if(arg == 'img'){

el.src = value

} else if(!arg){

el.style.background = `url(${value}) no-repeat center`

el.style.backgroundSize = `cover`

}

// 当组件卸载时,停止监听

stop();

}

}

)

}

})

})

页面中引用

nuxt3.0后 assets 里面的资源已经不允许动态拼接引入。

懒加载的地址,本地需要放到根目录下的 public文件下,网络图片不受限制

演示效果

fonts 字体库

api请求

动画

文章正在努力完善中。。。。。

好文阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: