Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

feat: create shallowSsrRef #49

Merged
merged 29 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6473305
feat(namespace): add namespaces, rewrite ssrRef
mathe42 May 5, 2020
afbce23
feat(namespaces): add async, some refactorings
mathe42 May 5, 2020
4229694
feat(namespacer): bugfixes, now it works
mathe42 May 6, 2020
b9a40ad
Merge branch 'master' into betterAbstractAPI
mathe42 May 6, 2020
a114bf1
feat(namespace): rewrite useAsync, optimize namespaces
mathe42 May 6, 2020
a0008bd
feat(ssr-ref): optimize ssrRef, remove namespaces
mathe42 May 7, 2020
245a030
feat(useasync): remove comment
mathe42 May 7, 2020
b653f33
Apply suggestions from code review
mathe42 May 7, 2020
9cf3ded
Merge branch 'master' into betterAbstractAPI
mathe42 May 7, 2020
ca4e5e9
add tests, some refactorings
mathe42 May 7, 2020
9d90cd8
add babel for useAsync
mathe42 May 7, 2020
2dfa183
Update src/ssr-ref.ts
mathe42 May 7, 2020
f33a38e
Update src/babel.ts
mathe42 May 7, 2020
a313456
fix: typo
danielroe May 7, 2020
d064d6a
Update src/async.ts
mathe42 May 7, 2020
47f8d7d
Merge branch 'master' into betterAbstractAPI
danielroe May 8, 2020
65e9b18
fix(ssrref): fix babel plugin, add support for outside of component, …
mathe42 May 8, 2020
3419654
Update src/babel.ts
mathe42 May 8, 2020
ff11397
Merge branch 'master' into betterAbstractAPI
mathe42 May 8, 2020
3f551ea
Merge branch 'master' into betterAbstractAPI
danielroe May 8, 2020
307992f
add ssrShallowRef
mathe42 May 8, 2020
d1ff5eb
fix test by rewrite onServerPrefetch
mathe42 May 8, 2020
d5427b1
Merge pull request #1 from mathe42/betterAbstractAPI
mathe42 May 8, 2020
5319dc1
Merge branch 'master' into shallow-ref
danielroe May 11, 2020
49f0767
fix: remove old file
danielroe May 11, 2020
38a39cb
fix: typo
danielroe May 11, 2020
b4c17f2
test: add e2e test
danielroe May 11, 2020
278c714
docs: add docs
danielroe May 11, 2020
1b35263
docs: fix example
danielroe May 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
title: 'Helpers',
collapsable: false,
children: [
'/helpers/shallowSsrRef',
'/helpers/ssrRef',
'/helpers/useAsync',
'/helpers/useContext',
Expand Down
41 changes: 41 additions & 0 deletions docs/helpers/shallowSsrRef.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
---

# shallowSsrRef

This helper creates a [`shallowRef`](https://vue-composition-api-rfc.netlify.app/api.html#shallowref) (a ref that tracks its own .value mutation but doesn't make its value reactive) that is synced between client & server.

```ts
import { shallowSsrRef, onMounted } from 'nuxt-composition-api'

const shallow = shallowSsrRef({ v: 'init' })
if (process.server) shallow.value = { v: 'changed' }

// On client-side, shallow.value will be { v: changed }
onMounted(() => {
// This and other changes outside of setup won't trigger component updates.
shallow.value.v = 'Hello World'
})
```

::: warning
At the moment, an `shallowSsrRef` is only suitable for one-offs, unless you provide your own unique key.

This is because server and client `shallowSsrRef` matches up based on line number within your code.

```ts
function useMyFeature() {
// Only one unique key is generated
const feature = shallowSsrRef('')
return feature
}

const a = useMyFeature()
const b = useMyFeature()

b.value = 'changed'
// On client-side, a's value will also be initialised to 'changed'
```

If you want to use this pattern, make sure to set a unique key based on each calling of the function.
:::
2 changes: 1 addition & 1 deletion src/babel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function ssrRefPlugin({ loadOptions, getEnv, types: t }: Babel) {
CallExpression(path) {
if (
!('name' in path.node.callee) ||
!['ssrRef', 'useAsync'].includes(path.node.callee.name)
!['ssrRef', 'useAsync', 'shallowSsrRef'].includes(path.node.callee.name)
)
return

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export { defineComponent } from './component'
export { useContext, withContext } from './context'
export { useFetch } from './fetch'
export { useMeta } from './meta'
export { ssrRef, setSSRContext } from './ssr-ref'
export { ssrRef, shallowSsrRef, setSSRContext } from './ssr-ref'

export type {
ComponentRenderProxy,
Expand Down
58 changes: 58 additions & 0 deletions src/ssr-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,61 @@ export const ssrRef = <T>(value: T | (() => T), key?: string): Ref<T> => {

return proxy as Ref<T>
}

// TODO: remove when https://github.com/vuejs/composition-api/pull/311 is merged
function shallowRef<T>(value: T): Ref<T> {
return computed({
get: () => value,
set: v => (value = v),
})
}

/**
* This helper creates a [`shallowRef`](https://vue-composition-api-rfc.netlify.app/api.html#shallowref) (a ref that tracks its own .value mutation but doesn't make its value reactive) that is synced between client & server.
* @param value This can be an initial value or a factory function that will be executed on server-side to get the initial value.
* @param key Under the hood, `shallowSsrRef` requires a key to ensure that the ref values match between client and server. If you have added `nuxt-composition-api` to your `buildModules`, this will be done automagically by an injected Babel plugin. If you need to do things differently, you can specify a key manually or add `nuxt-composition-api/babel` to your Babel plugins.

* @example
```ts
import { shallowSsrRef, onMounted } from 'nuxt-composition-api'

const shallow = shallowSsrRef({ v: 'init' })
if (process.server) shallow.value = { v: 'changed' }

// On client-side, shallow.value will be { v: changed }
onMounted(() => {
// This and other changes outside of setup won't trigger component updates.
shallow.value.v = 'Hello World'
})
```
*/
export const shallowSsrRef = <T>(
value: T | (() => T),
key?: string
): Ref<T> => {
if (!key) {
throw new Error(
"You must provide a key. You can have it generated automatically by adding 'nuxt-composition-api/babel' to your Babel plugins."
)
}

if (process.client) {
return shallowRef(
(window as any).__NUXT__?.ssrRefs?.[key] ?? getValue(value)
)
}

let _val = getValue(value)

if (value instanceof Function) {
data[key] = sanitise(_val)
}

return computed({
get: () => _val,
set: v => {
data[key] = sanitise(v)
_val = v
},
})
}
4 changes: 2 additions & 2 deletions test/e2e/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export function navigateTo(path: string) {

export function expectOnPage(text: string) {
const selector = Selector('*').withText(new RegExp(text, 'i'))
return t.expect(selector.visible).ok()
return t.expect(selector.visible).ok(`${text} was not found on page`)
}

export function expectNotOnPage(text: string) {
const selector = Selector('*').withText(new RegExp(text, 'i'))
return t.expect(selector.exists).notOk()
return t.expect(selector.exists).notOk(`${text} was found on page`)
}

export const getWindowPathname = ClientFunction(() => window.location.pathname)
Expand Down
3 changes: 3 additions & 0 deletions test/e2e/ssr-refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ test('Shows data on ssr-loaded page', async t => {
await expectOnPage('function-runs SSR or client-side')
await expectOnPage('prefetched-result')
await expectOnPage('on: server')
await expectOnPage('shallow-server')

await t.click(Selector('a').withText('home'))
await t.click(Selector('a').withText('ssr refs'))
await expectOnPage('ref-only SSR rendered')
await expectOnPage('shallow-client')
})

test('Shows appropriate data on client-loaded page', async t => {
Expand All @@ -28,6 +30,7 @@ test('Shows appropriate data on client-loaded page', async t => {
await expectNotOnPage('ref-only SSR rendered')
await expectOnPage('function-runs SSR or client-side')
await expectOnPage('on: client')
await expectOnPage('shallow-client')
})

test('Shows SSR data when an ssrRef is defined outside setup', async () => {
Expand Down
10 changes: 10 additions & 0 deletions test/fixture/pages/ssr-ref.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div>prefetched-{{ prefetchValue }}</div>
<div>on: {{ asyncValue }}</div>
<div>no-change: {{ noChange }}</div>
<div>shallow-{{ shallow.v.deep }}</div>
<nuxt-link to="/">home</nuxt-link>
</div>
</template>
Expand All @@ -18,6 +19,8 @@ import {
ssrRef,
onServerPrefetch,
useAsync,
shallowSsrRef,
onMounted,
} from 'nuxt-composition-api'

export function fetcher(result, time = 100) {
Expand All @@ -35,6 +38,12 @@ export default defineComponent({
const funcValue = ssrRef(() => 'runs SSR or client-side') // function => in __NUXT__
const noChange = ssrRef('initValue') // no Change => not in __NUXT__

const shallow = shallowSsrRef({ v: { deep: 'init' } })
if (process.server) shallow.value = { v: { deep: 'server' } }
onMounted(() => {
shallow.value.v.deep = 'client'
})

const computedVal = computed(() => refValue.value)

if (process.server) refValue.value = 'only SSR rendered'
Expand All @@ -58,6 +67,7 @@ export default defineComponent({
prefetchValue,
asyncValue,
noChange,
shallow,
}
},
})
Expand Down
7 changes: 6 additions & 1 deletion test/tsd/ssr-ref.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType } from 'tsd'

import { ssrRef, Ref } from '../..'
import { ssrRef, Ref, shallowSsrRef } from '../..'

expectType<Ref<number>>(ssrRef(() => 42))
expectType<Ref<string>>(ssrRef('thoughtless'))
Expand All @@ -9,3 +9,8 @@ interface Obj {
}
expectType<Ref<Obj>>(ssrRef({ name: 'today' }))
expectType<Ref<null>>(ssrRef(null))

expectType<Ref<number>>(shallowSsrRef(() => 42))
expectType<Ref<string>>(shallowSsrRef('thoughtless'))
expectType<Ref<Obj>>(shallowSsrRef({ name: 'today' }))
expectType<Ref<null>>(shallowSsrRef(null))