Browse Source

feat: 增加一些功能性Api

main
dash 2 months ago
parent
commit
0f6d4b199b
  1. BIN
      bun.lockb
  2. 17
      components.d.ts
  3. 4
      package.json
  4. 3
      src/App.vue
  5. 43
      src/components/ClientOnly.tsx
  6. 6
      src/components/CookieDemo.vue
  7. 9
      src/components/DataFetch.vue
  8. 6
      src/compose/useFetch.ts
  9. 10
      src/vue.d.ts
  10. 2
      tsconfig.json
  11. 7
      vite.config.ts

BIN
bun.lockb

Binary file not shown.

17
components.d.ts

@ -0,0 +1,17 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ClientOnly: typeof import('./src/components/ClientOnly.tsx')['default']
CookieDemo: typeof import('./src/components/CookieDemo.vue')['default']
DataFetch: typeof import('./src/components/DataFetch.vue')['default']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
SimpleTest: typeof import('./src/components/SimpleTest.vue')['default']
}
}

4
package.json

@ -1,6 +1,9 @@
{
"name": "koa-ssr",
"type": "module",
"workspaces": [
"packages/*"
],
"scripts": {
"dev": "bun run --watch server.ts",
"build": "npm run build:client && npm run build:server",
@ -14,6 +17,7 @@
"@types/koa": "^3.0.0",
"@types/koa-send": "^4.1.6",
"cross-env": "^10.1.0",
"unplugin-vue-components": "^29.1.0",
"vue-tsc": "^3.1.0"
},
"peerDependencies": {

3
src/App.vue

@ -16,6 +16,9 @@ import SimpleTest from './components/SimpleTest.vue';
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<ClientOnly>
<div>Only For Client</div>
</ClientOnly>
<HelloWorld msg="Vite + Vue" />
<CookieDemo />
<SimpleTest />

43
src/components/ClientOnly.tsx

@ -0,0 +1,43 @@
import { cloneVNode, createElementBlock, defineComponent, getCurrentInstance, h, InjectionKey, onMounted, provide, shallowRef, SlotsType, VNode } from "vue";
export const clientOnlySymbol: InjectionKey<boolean> = Symbol.for('nuxt:client-only')
export default defineComponent({
name: "ClientOnly",
inheritAttrs: false,
props: ['fallback', 'placeholder', 'placeholderTag', 'fallbackTag'],
...(import.meta.env.DEV && {
slots: Object as SlotsType<{
default?: () => VNode[]
/**
* Specify a content to be rendered on the server and displayed until `<ClientOnly>` is mounted in the browser.
*/
fallback?: () => VNode[]
placeholder?: () => VNode[]
}>,
}),
setup(props, { slots, attrs }) {
const mounted = shallowRef(false)
onMounted(() => { mounted.value = true })
const vm = getCurrentInstance()
if (vm) {
vm._nuxtClientOnly = true
}
provide(clientOnlySymbol, true)
return () => {
if (mounted.value) {
const vnodes = slots.default?.()
if (vnodes && vnodes.length === 1) {
return [cloneVNode(vnodes[0]!, attrs)]
}
return vnodes
}
const slot = slots.fallback || slots.placeholder
if (slot) { return h(slot) }
const fallbackStr = props.fallback || props.placeholder || ''
const fallbackTag = props.fallbackTag || props.placeholderTag || 'span'
return createElementBlock(fallbackTag, attrs, fallbackStr)
}
}
})

6
src/components/CookieDemo.vue

@ -3,6 +3,9 @@
<div class="btns">
<button @click="setCookie">设置 cookie</button>
<button @click="removeCookie">删除 cookie</button>
<ClientOnly>
<div>fuck ypu</div>
</ClientOnly>
</div>
<small>服务端首屏会尝试设置缺失的 HttpOnly cookie客户端不可读</small>
</div>
@ -10,7 +13,8 @@
</template>
<script setup lang="ts">
import { onMounted, onServerPrefetch } from 'vue'
import ClientOnly from './ClientOnly'
import { onMounted, onServerPrefetch } from 'vue'
import { useCookie } from '../compose/useCookie'
const cookie = useCookie('demo_token', { path: '/', sameSite: 'lax' })

9
src/components/DataFetch.vue

@ -26,7 +26,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, watchEffect } from 'vue'
import { useFetch } from '../compose/useFetch'
//
@ -46,13 +46,18 @@ const { data, error, pending, refresh } = useFetch(
id: userData.id,
name: userData.name,
email: userData.email,
website: userData.website
website: userData.website,
}),
onError: (err) => {
console.error('Fetch error:', err)
}
}
)
watchEffect(() => {
if (!pending.value) {
console.log('Fetched user data:', data.value?.email)
}
})
</script>
<style scoped>

6
src/compose/useFetch.ts

@ -9,11 +9,11 @@ const globalCache = new Map<string, any>()
// SSR 上下文类型从 ssrContext.ts 引入
// useFetch 的配置选项
interface UseFetchOptions {
interface UseFetchOptions<T = any> {
key?: string
server?: boolean
default?: () => any
transform?: (data: any) => any
transform?: (data: any) => T
onError?: (error: Error) => void
}
@ -32,7 +32,7 @@ interface UseFetchReturn<T> {
*/
export function useFetch<T = any>(
url: string | (() => string) | (() => Promise<string>),
options: UseFetchOptions = {}
options: UseFetchOptions<T> = {}
): UseFetchReturn<T> {
const {
key,

10
src/vue.d.ts

@ -0,0 +1,10 @@
export { }
declare module 'vue' {
export interface ComponentCustomProperties {
$ssrContext?: Record<string, any>
}
export interface ComponentInternalInstance {
_nuxtClientOnly?: boolean
}
}

2
tsconfig.json

@ -21,6 +21,6 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "components.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

7
vite.config.ts

@ -1,8 +1,13 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
// https://vite.dev/config/
export default defineConfig({
base: './',
plugins: [vue()],
plugins: [
vue(),
Components({ dts: true,
extensions: ['vue', 'tsx'], })
],
})

Loading…
Cancel
Save