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", "name": "koa-ssr",
"type": "module", "type": "module",
"workspaces": [
"packages/*"
],
"scripts": { "scripts": {
"dev": "bun run --watch server.ts", "dev": "bun run --watch server.ts",
"build": "npm run build:client && npm run build:server", "build": "npm run build:client && npm run build:server",
@ -14,6 +17,7 @@
"@types/koa": "^3.0.0", "@types/koa": "^3.0.0",
"@types/koa-send": "^4.1.6", "@types/koa-send": "^4.1.6",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"unplugin-vue-components": "^29.1.0",
"vue-tsc": "^3.1.0" "vue-tsc": "^3.1.0"
}, },
"peerDependencies": { "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" /> <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a> </a>
</div> </div>
<ClientOnly>
<div>Only For Client</div>
</ClientOnly>
<HelloWorld msg="Vite + Vue" /> <HelloWorld msg="Vite + Vue" />
<CookieDemo /> <CookieDemo />
<SimpleTest /> <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"> <div class="btns">
<button @click="setCookie">设置 cookie</button> <button @click="setCookie">设置 cookie</button>
<button @click="removeCookie">删除 cookie</button> <button @click="removeCookie">删除 cookie</button>
<ClientOnly>
<div>fuck ypu</div>
</ClientOnly>
</div> </div>
<small>服务端首屏会尝试设置缺失的 HttpOnly cookie客户端不可读</small> <small>服务端首屏会尝试设置缺失的 HttpOnly cookie客户端不可读</small>
</div> </div>
@ -10,7 +13,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onServerPrefetch } from 'vue' import ClientOnly from './ClientOnly'
import { onMounted, onServerPrefetch } from 'vue'
import { useCookie } from '../compose/useCookie' import { useCookie } from '../compose/useCookie'
const cookie = useCookie('demo_token', { path: '/', sameSite: 'lax' }) const cookie = useCookie('demo_token', { path: '/', sameSite: 'lax' })

9
src/components/DataFetch.vue

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

6
src/compose/useFetch.ts

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

7
vite.config.ts

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

Loading…
Cancel
Save