为什么选择插件vite-ssg + vite-plugin-pages创建官网?
1. CSR、 SSR、 SSG 和 ISR
适合高度动态的 Web 应用程序
客户端渲染 CSR. : 典型代表:单页面应用,内容都是js动态渲染
服务器端渲染 SSR : 在服务端获取数据组装页面,返回到浏览器是html,对服务器要求高,主要应用是交互多的页面需要seo的
适合高度静态的web程序
静态站点生成 SSG: 在build的的时候就已经生成好静态页面,放在服务端的也是静态页面,博客,静态官网都非常合适
增量静态再生 ISR:ISR 是 SSG 的下一个改进,它定期构建和重新验证新页面,以便内容永远不会过时
显而易见,做个官网ssg就非常合适,正好相关插件vite-ssg也有,配合多页面插件vite-plugin-pages来尝试一下
2. 创建项目步骤
第一步:三种任选其一执行
npm create vite@latest
yarn create vite
pnpm create vite
按照命令提示写项目名称,我选择vue-ts作为项目就可以生成项目了
第二步:进入项目,正常启动后准备安装
vite-ssg 需要vue-router 和 @vueuse/head
yarn add vite-plugin-pages vite-ssg vue-router @vueuse/head vite-plugin-md -D
vite-plugin-md
该插件支持使用makdown语法转换为vue
支持在vue文件中直接引入md文件组件,也可以在md文件中直接使用vue组件
需要配置
ViteVue({
include: [/\.vue$/, /\.md$/],
})
第三步:修改配置内容
修改main.ts
import './style.css'
import App from './App.vue'
import { ViteSSG } from 'vite-ssg'
import routes from '~pages'
export const createApp = ViteSSG(
App,
{ routes }
);
如果~pages有提示找不到模块或相应的类型声明,其实文档中已有说明
在vite-env.d.ts中加入声明
// vite-env.d.ts
/// <reference types="vite-plugin-pages/client" />
同理在tsconfig.json
中加入也是一样的,两个地方选择一个加入就可以
"compilerOptions": {
"types": [
"vite-plugin-pages/client"
],
},
修改build脚本
- "build": "vite build"
+ "build": "vite-ssg build"
修改vite.config.ts
import { defineConfig } from "vite";
import ViteVue from "@vitejs/plugin-vue";
import VitePages from 'vite-plugin-pages';
import ViteMarkdown from 'vite-plugin-md';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
plugins: [
ViteVue({
include: [/\.vue$/, /\.md$/],
}),
VitePages({
extensions: ['vue', 'md'],
exclude: ['**/components/*.vue']
}),
ViteMarkdown(),
AutoImport({
imports: ['vue']
}),
Components({
extensions: ['vue', 'md']
}),
],
});
修改app.vue
<template>
<router-view />
</template>
第四步:根据插件vite-plugin-pages
的配置项来新建文件夹
主要看以下几个配置项
interface Options {
/**
* Paths to the directory to search for page components.
* @default 'src/pages'
*/
dirs: string | (string | PageOptions)[];
/**
* Valid file extensions for page components.
* @default ['vue', 'js']
*/
extensions: string[];
/**
* List of path globs to exclude when resolving pages.
*/
exclude: string[];
/**
* Extend route records
*/
extendRoute?: (route: any, parent: any | undefined) => any | void;
}
dirs
默认是src/pages
, 这就是我们新建页面的地方。
extensions
默认是['vue', 'js']
在src/pages
中默认可以转换成页面的文件类型,这里我们将vue文件和md文件转换就可以了,配置为extensions: ['vue', 'md']
extendRoute
通过这个配置可以为路由信息增加额外信息,自由发挥
exclude
可以排除某些vue文件不生成页面。比如我的页面中有组件: exclude: ['**/components/*.vue']
tip:文档中提到一个匹配所有404路由的方式:新建一个[...all].vue文件即可。
src/pages/[...all].vue -> /* (/non-existent-page)
第五步: 最后我们看一下目录结构和生成的dist文件夹
可以发现
- 1.项目会默认打开
src/pages/index.vue
- 2.test文件夹中的components中的child不会生成页面,配置忽略了
- md文件也可以生成页面
3. 继续完善项目的其他配置
可以选择性观察本篇vite + vue3 多页面实战优化续集:eslint+lint-staged+husky+stylelint
官网有新的一行命令生成eslint: npm init @eslint/config
可以替代 npx eslint --init
,
同样的一步步往下选择
上述文章中安装eslint非常顺利,这次配置eslint出现了不少错误提示.可以发现与上次安装插件相比的eslint-config-standard
变成了eslint-config-standard-with-typescript
"@typescript-eslint/eslint-plugin": "^5.44.0",
"eslint": "^8.28.0",
"eslint-config-standard-with-typescript": "^23.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.5.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.8.0",
有变化就要踩坑,从插件名称上看是主要针对ts的写法校验,这里记录一下这次出现的问题:
-
问题一:Error: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
starkoverflow有提到要在.eslintrc
中加入project
parserOptions: {
project: ['./tsconfig.json'],
extraFileExtensions: ['.vue']
}
-
问题二:The extension for the file (
.vue
) is non-standard. You should addparserOptions.extraFileExtensions
to your config。 如上述配置加入extraFileExtensions: ['.vue']
-
问题三:This rule requires the
strictNullChecks
compiler option to be turned on to function correctly @typescript-eslint/strict-boolean-expressions
可以在.eslintrc
的rules中设置为警告,并在tsconfig.json中开启"strictNullChecks": true,修改tsconfig.json
记得重启项目生效 starkoverflow
// .eslintrc
rules: {
"@typescript-eslint/strict-boolean-expressions": "warn",
}
// tsconfig.json
"compilerOptions": {
"strictNullChecks": true
}
以前的null严格检查为了向下兼容都是默认关闭的,现在新建项目建议都开启null检查,可以避免很多空值导致的bug
如果你不想用它,也可以直接设为关闭:‘off’
关于问题二三我在eslint官网也看到了相关配置
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": ["./tsconfig.json"] },
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/strict-boolean-expressions": [
2,
{
"allowString" : false,
"allowNumber" : false
}
]
},
"ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
}
-
问题四:Do not use a triple slash reference for vite/client, use
import
style instead @typescript-eslint/triple-slash-reference
不鼓励使用三斜线,在vite-env.d.ts
中有vite/client
类型引入。关掉这个规则吧
// .eslintrc
rules: {
'@typescript-eslint/triple-slash-reference': 'off'
}
-
问题五:在
.eslintrc.cjs
中的module.exports
飘红:However, that TSConfig does not include this file. Either: - Change ESLint's list of included files to not include this file
- Change that TSConfig to include this file
- Create a new TSConfig that includes this file and include it in your parserOptions.project
提示使用了parserOptions.project.但是tsconfig.json
不包含本文件
那就在tsconfig.json
的include数组中加入.eslintrc.cjs
.重启生效
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue",".eslintrc.cjs"],
-
问题六:
加入parser解决,这里的@typescript-eslint/parser
就不需要单独安装了。
eslint-config-standard-with-typescript
文档中说了
This package has @typescript-eslint/parser in dependencies.
parserOptions: {
parser: '@typescript-eslint/parser',
}
-
问题七: .vue文件可以格式化,但是.ts,.less文件不能正常格式化的
原因:我的vscode在设置中的用户
开启了format on save
,这是针对我的编辑器。
此时我还没在项目中新建针对工作区
的.vscode/settings.json
文件。所以此时保存自动格式化的能力完全来源于用户
中的format on save
解决:
- 为了多人开发保持项目的统一,我应该关闭我自己的
用户
中的format on save
,关闭后任何文件保存就不会自动格式化了 - 新建
.vscode/settings.json
文件,加入以下配置就会修改每个人的工作区
配置,达到项目配置的统一了
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.tslint": true,
"source.fixAll.stylelint": true
}
-
问题八: . 当保存文件时,你会发现单引号或者最后面的分号,保存的时候会消失又出现,并不能正确按照eslint的规则格式化.
直接修改工作区配置,在.vscode/settings.json
中加入
关闭编辑器的格式化,按照eslint的规则来
{
"editor.formatOnSave": false,
}
当然你也可以选择性加入使用prettier,我这里稍微写一下(以下prettier内容可以跳过)
这里使用prettier 集成到 eslint 的校验中
第一步新建json文件
echo {}> .prettierrc.json
// .prettierrc.json
{
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}
- eslint-config-prettier关闭可能与
prettier
冲突的规则 - eslint-plugin-prettier 使用
prettier
代替eslint
格式化
第二步安装
npm i prettier eslint-config-prettier eslint-plugin-prettier -D
第三步:在 .eslintrc.cjs
三个地方 加入prettier
.
module.exports = {
extends: [
'plugin:vue/vue3-essential',
'standard-with-typescript',
'prettier'
],
plugins: [
'vue',
'@typescript-eslint',
'prettier'
],
rules: {
'prettier/prettier': 'error',
}
}
-
问题八: 项目使用了
unplugin-auto-import
自动导入,但是eslint会检测Cannot find name ‘ref’
解决:
- 先在
vite.config.ts
的unplugin-auto-import/vite
配置项中加入eslintrc.enabled 开启为true - 再
npm run dev
运行项目,此时项目根目录会自动生成.eslintrc-auto-import.json
文件 - 在
eslintrc.cjs
的extends加入该文件 - 重启项目生效。生效后记得删掉
eslintrc.enabled
,避免重复生成
// eslintrc.cjs
extends: [
'plugin:vue/vue3-essential',
'standard-with-typescript',
'prettier',
'./.eslintrc-auto-import.json'
]
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
AutoImport({
imports: ['vue', 'vue-router'],
eslintrc: {
enabled: true
}
}),
})
-
问题九: .stylelintrc.js require() of ES modules is not supported. require() of /Users/ruios/web/wisbayar-website/.stylelintrc.js from /Users/ruios/web/wisbayar-website/node_modules/cosmiconfig/dist/loaders.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename .stylelintrc.js to end in .cjs, change the requiring code to use import(), or remove "type": "module"
vscode提示报错,less文件也没有正常校验。其实上面说的很明确了,本次新建的项目package.json
中默认有"type": "module"
,这种情况下,我们平常新建的.stylelintrc.js
或者.eslintrc.js
里面都是使用的module.exports
,和定义"type": "module"
完全使用es模块的import方式是不兼容的。
解决:将.stylelintrc.js
改为.stylelintrc.cjs
,用后缀名使其识别为commonjs方式导出,修改后less文件不规范的地方也被正常识别红色波浪了
2023年1月12新增: 在项目使用过程中发现新的配置问题
-
问题十:在
vite-ssg
的github项目issue中问的最多的就是打包的时候报错:"window is not defined","document is not defined" .
打包是在服务器,是没有BOM浏览器相关的东西的。所以在使用window的时候,我们分两种情况来使用
- 初始化的时候,可以在
ViteSSG
第三个参数函数中的isClient
来判断是否是浏览器中。npm vite-ssg还有详细的案例告诉我们vuex和initialState
来保持数据持久的方式
export const createApp = ViteSSG(App, { routes }, ({ app, router, routes, isClient, initialState }) => {
// app.provide('$wow', WOW)
if (isClient) {
window.onload = fontSize()
}
})
- 当写在hook中需要使用window的时候,可以放在
onBeforeMount,onMounted
生命周期中
export default function () {
const isMobile = ref(false)
const resizeHandler = () => {
if (!document.hidden) {
const rect = document.body.getBoundingClientRect()
isMobile.value = rect.width - 1 < 750
}
}
onMounted(() => {
resizeHandler()
window.addEventListener('resize', resizeHandler)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeHandler)
})
return { isMobile }
}
- 还提到了一个第三方插件报window错误的话,异步引入
export const install = ({ isClient, app }) => {
if (isClient)
import('vue3-scroll-spy').then(({ registerScrollSpy }) => {
registerScrollSpy(app)
})
}
// 你要说想在单个页面引入,那就在onMounted中
onMounted(() => {
import('vue3-scroll-spy').then(({ registerScrollSpy }) => {
registerScrollSpy(app)
})
})
-
问题十一:当我想在这个项目中使用animate.css 加wowjs实现滚动动画的时候。出现了些意外
先安装
npm i animate.css wowjs
main.ts引入
import 'animate.css/animate.min.css'. // 这是
import { WOW } from 'wowjs'
new WOW({
boxClass: 'wow', //需要执行动画元素的Class
animateClass: 'animated', //animation.css动画的Class
offset: 0, //距离可视区域多少开始执行动画
mobile: true, //是否在移动设备执行动画
live: true //异步加载的内容是否有效
}).init()
上面都是默认配置,直接使用默认配置
new WOW().init()
!!注意
这里单独安装了animate.css
,它的类名我们可以从官网复制。它和wow
里面自己依赖的样式名不一样,缺少了前缀animate__
使用
<div class="wow animate__animated animate__bounce"></div>
滚动
动画没生效,浏览器下面有错误WOW.js cannot detect dom mutations, please call .sync() after loading new content.
MutationObserver is not supported by your browser.
百度得知需要支持异步的内容,改成这样。提示是没有了,但是滚动
动画还是无效。
new WOW({live: false}).init()
接下来,尝试在isClient中执行,或者import后执行,均无效无报错。
我在单个页面中单独引入wowjs
,并在onMounted
中初始化。来了个新的报错:Cannot set properties of undefined (setting 'getPropertyValue')
原因
:查看源码得知,只是个很老的库了,直接导出的wow单例,里面用到的外部this,在vue3中肯定是没有的,自然在挂在一些方法的时候就报错了。
解决
:
- 到github找个
wow
压缩包放入public
文件中,在index.html
中引入吧。生效
<script type="text/javascript" src="/wow.min.js"></script>
<script type="text/javascript">
new WOW().init()
</script>
- 发现还有个
wow.js
,安装它。看源码了解到它写了UMD导出,可以使用。
npm i wow.js animate.css
使用。样式还是跟上面一样,使用animate.css中的样式
// main.ts
import 'animate.css/animate.min.css'
import WOW from 'wow.js'
new WOW().init()
- 其实
wow.js
这个库作者已经废弃,推荐使用aos
。这个就更好了,再次向下
滚动后到视野会再次触发动画
下面单独说一下aos
版本,npm介绍上还是第一版本
aos
有两个版本,第一版aos
有自己的少量动画,配置较少,且不能结合animate.css
使用第二版 aos@next
,详细介绍到github仓库去看
安装
npm i aos@next animate.css
使用要用到它的配置项。这里我们还是使用animate.css
,效果更多。就不需要引入aos
的样式了
// main.ts
import 'animate.css/animate.min.css'
import AOS from 'aos'
AOS.init({
// 这个默认是关闭的,它会把`data-aos`上要写的动画样式名,自动加到class上,这样才会有第三方动画效果
useClassNames: true,
// 这个就是关闭掉初始化的样式名,默认值,也就是默认样式名是'aos-init'。这里选用第三方样式,就不要被它内置的干扰了,去掉它
initClassName: null,
// 这里的默认值是'aos-animate',我们替换为animate.css的样式名
animatedClassName: 'animate__animated'
})
在公共样式表做样式覆盖
[data-aos] {
visibility: hidden;
}
[data-aos].animate__animated {
visibility: visible;
}
最后使用起来
<div data-aos="animate__fadeInLeft"></div>
最后配置好的项目,我放到这里gitee,直接取用
最新vite + ssg 项目配置地址