对现有的vue3+vite+ts项目采用vike(原vite-plugin-ssr)ssg化

背景:

原项目已经是一个完整的项目了,不过由于seo的需求,需要寻找ssg或者ssr方案,根据vue官网的推荐,最终我决定基于vite使用vike(原vite-plugin-ssr)这个方案。原项目是我个人编写的(对项目结构等熟悉),并是普通网站类那种仅需要预渲染少量的页面,不需要预渲染几百个几千个页面的项目(适合ssg)。

一、安装并配置vike

npm i vike

vite.config.ts文件里面写上( 如果不需要ssg预渲染,仅需要ssr,写vike()即可,其他项目内修改的逻辑是一样的 ):

import vike from 'vike/plugin'

export default defineConfig({
  plugins: [
    vue(),
    vike({ prerender: true })
    ]
})

二、创建renderer文件夹

我把renderer文件夹放在和src同级的地方,基础的renderer内容,直接复制官网例子vike-with-pinia/renderer at main · brillout/vike-with-pinia · GitHub这个项目里面的即可(因为这里面的文件内容行数比较少,适合新手),由于我是ts项目,我还结合了vike/boilerplates/boilerplate-vue-ts/renderer at main · vikejs/vike · GitHub,其中我只保留了:+config.ts、+onBeforeRender.ts、+onRenderClient.ts、+onRenderHtml.ts、app.ts、types.ts、usePageContext.ts这几个文件

1. 关于+onRenderClient.ts

该文件定义页面的浏览器端代码(vite-plugin-ssr里面是.page.client.js),其中我把“判断当前是什么浏览器”的逻辑写到了这里。

2.关于+onRenderHtml.ts

该文件定义页面服务端代码(vite-plugin-ssr里面是.page.server.js),这里会return documentHtml,其中预渲染的html模板就是依据这个documentHtml,所以我们可以在这里判断不同路由预渲染不同的title或者description等(要写上<meta charset="UTF-8" />,不然渲染的html文件中文会乱码)

 // pageContext.urlPathname:获取当前路由
 switch (pageContext.urlPathname) {
    case '/about':
      title = '关于'
      break
    default:
      title = '默认标题'
  }
 
const documentHtml = escapeInject`<!DOCTYPE html>
    <html lang="zh-CN">
      <head>
         <meta charset="UTF-8" />
         <title>${title}</title>
      </head>
      <body>
        <div id="app">${stream}</div>
      </body>
    </html>`
 return {
    documentHtml,
    pageContext: {
      enableEagerStreaming: true
    }
 }

你可能注意到了pageContext.urlPathname可以获取当前页面的路由,在.vue文件里面获取可以参考下面代码:

// 可以在vite.config.ts配置某个符合直接定位到renderer,但是我没有配置
import { usePageContext } from '../../../renderer/usePageContext' 

const pageContext = usePageContext()
const currentPath = pageContext.urlPathname

3.关于app.ts

该文件类似于原来项目里面的main.ts,ssr/ssg模式下,main.ts文件无效,所以pinia等要在这里导入。另外还要注意,预渲染后会生成不同的.html文件,单单的pinia无法满足项目的需求,需要安装pinia-plugin-persistedstate处理持久化。

注意:ssr/ssg模式下,main.ts、App.vue、index.html文件无效

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

import { createSSRApp, h, reactive, markRaw } from 'vue'
import { setPageContext } from './usePageContext'

import '@/assets/main.scss' // 全局scss我也是这里导入

export { createApp }

function createApp(pageContext) {
  let rootComponent
  const app = createSSRApp({
    data: () => ({
      Page: markRaw(pageContext.Page),
      pageProps: markRaw(pageContext.pageProps || {})
    }),
    render() {
      return h(this.Page, this.pageProps)
    },
    created() {
      rootComponent = this
    }
  })

  // 这一步就是安装pinia
  const store = createPinia()
  store.use(piniaPluginPersistedstate)
  app.use(store)

  Object.assign(app, {
    changePage: (pageContext) => {
      Object.assign(pageContextReactive, pageContext)
      rootComponent.Page = markRaw(pageContext.Page)
      rootComponent.pageProps = markRaw(pageContext.pageProps || {})
    }
  })

  const pageContextReactive = reactive(pageContext)

  setPageContext(app, pageContextReactive)

  return { app, store }
}

三、给原来的项目文件夹、文件改名

1. src->views,其中views文件夹需改名为pages文件夹,路由会自动从这个文件夹开始寻找(同时也意味着vue-router没有用了,并且使用router方法跳转的地方,都应该更改为window.location.href = ‘/xx’)。

原文件夹和视图命名为about->aboutView.vue都改为about->+Page.vue(即文件夹名字不变,视图文件改为+Page.vue),这样当路由是/about,时,就会定位到about->+Page.vue这个文件。

如果你原路由是about/test,则你的文件夹和文件路径应该是:about->test->+Page.vue(意思是about里面是test文件夹,再里面是+Page.vue文件)

目录参考(_error->+Page.vue,这样命名文件夹和文件,找不到路由都会被定位到该页面):

四、一些组件可能仅浏览器渲染,需要ClientOnly组件

比如一些用了window.xx等方法的,这些方法只有浏览器端才有,所以需要仅浏览器渲染

ClientOnly组件参考:https://github.com/vikejs/vike-vue/blob/main/packages/vike-vue/src/components/ClientOnly.vue

五、可能会报依赖包损坏的错误

需要在vite.config.ts配置noExternal(下面代码的作用是告诉构建系统在进行服务器端渲染构建时,不要把element-plus当作外部依赖排除出去)

export default defineConfig({
  plugins: [
    vue(),
    vike({ prerender: true })
    ],
  ssr: {
    noExternal: ['element-plus']
  }
})

六、打包

项目的报错都解决后,使用npm run build打包,生成的预渲染html文件位于dist\client里面,都是静态文件,像以前那样放到服务器上即可,不需要服务器额外处理(除非你使用的是ssr)

七、总结

在我写下这篇文章时,使用的vike在网上完全搜不到别人使用的经验(如果不是其他人写的seo太差,这应该是第一篇vike的文章了),它原身vite-plugin-ssr也很少,使用vike问ai,ai也不知道这个是什么,所以前期啃着文档过来的确实感觉有点复杂,幸好vike的文档和官网给的github例子都很详细,然后就这样慢慢一步一步弄出来了,真的vike的文档、vue的生态真的太好了,非常感谢!

这里还想表扬一下自己的文件、文件夹命名、代码都很清晰,组件和视图也分明,所以给views里面的文件改名的时候,很快就改完且没有冲突,给组件套上ClientOnly也很容易定位到什么组件。

另外,推荐vue使用3.4.27版本(修复了样式属性水合的bug,我是使用了postcss-px-to-viewport里面的selectorBlackList(需要忽略的CSS选择器),才发现原来的vue有这个bug的,没想到刚好最新版本的vue修复了)

参考:

1. https://github.com/vikejs/vike-vue/blob/main/packages/vike-vue/src/components/ClientOnly.vue

2. vike-with-pinia/renderer at main · brillout/vike-with-pinia · GitHub

3. vike/boilerplates/boilerplate-vue-ts/renderer at main · vikejs/vike · GitHub

4. V1 Design Migration | Vike

5. Pre-rendering (SSG) | Vike

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
为了在Vue3+TS+Vite项目中使用postcss-px-to-viewport-8-plugin,需要按照以下步骤进行操作: 1. 安装依赖: ```shell npm install postcss-px-to-viewport-8-plugin -D ``` 2. 在项目根目录下创建postcss.config.js文件,并添加以下内容: ```javascript const autoprefixer = require('autoprefixer'); const pxtoviewport = require('postcss-px-to-viewport-8-plugin'); module.exports = { plugins: [ autoprefixer(), pxtoviewport({ viewportWidth: 375, // 视窗的宽度,对应设计稿的宽度 viewportHeight: 667, // 视窗的高度,对应设计稿的高度 unitPrecision: 5, // 指定`px`转换为视窗单位值的小数位数 viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 mediaQuery: false // 允许在媒体查询中转换`px` }) ] } ``` 3. 在vite.config.ts文件中添加postcss配置: ```typescript import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue()], css: { postcss: { plugins: [ require('autoprefixer'), require('postcss-px-to-viewport-8-plugin')({ viewportWidth: 375, viewportHeight: 667, unitPrecision: 5, viewportUnit: 'vw', selectorBlackList: ['.ignore', '.hairlines'], minPixelValue: 1, mediaQuery: false }) ] } } }); ``` 4. 重启项目即可实现px转vw。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值