装置
独立装置
能够在Vue.js官网间接下载最新版本,并用script标签引入
独立装置
CDN形式装置
间接应用script引入<script src="https://unpkg.com/vue@next"></script>
npm形式装置
npm版本需大于3.0
npm install vue@next
命令行工具:
从之前的版本包名扭转了,从vue-cli变为@vue/cli。如果之前已全局装置了vue-cli1.x或vue-cli2.x。首先须要
应用命令
npm uninstall vue-cli -g
或者yarn global remove vue-cli
卸载掉之前的版本,在进行装置Node版本留神点:
Vue CLI 4.x 须要NodeJs的版本>=8.9
npm install -g @vue/cli
或者
yarn global add @vue/cli
留神:vue-cli 3.x 和 vue-cli 2.x 应用了雷同的 vue 命令,如果你之前曾经装置了 vue-cli 2.x,它会被替换为 Vue-cli 3.x。装置 @vue/cli-int:
npm i -g @vue/cli-init
创立我的项目
Vue CLI
应用命令vue create 项目名称
来创立我的项目
而后期待下载对应的模板以及依赖。
运行:
cd 我的项目名
npm run serve
Vite
Vite 是一个 web 开发构建工具,因为其原生 ES 模块导入形式,能够实现闪电般的冷服务器启动。
通过在终端中运行以下命令,能够应用 Vite 疾速构建 Vue 我的项目。
全局装置 create-vite-app:
npm i -g create-vite-app
创立我的项目:
npm init vite-app <我的项目名>
运行:
cd 我的项目名
npm install
npm run dev
Vue3目录构造
命令行工具@vue/cli
目录解析
目录文件 | 阐明 |
---|---|
public | 公共资源目录 |
src | 这里是咱们要开发的目录,基本上要做的事件都在这个目录里 |
.xxxx文件 | 这些是一些配置文件,包含语法配置,git配置等 |
package.json | 我的项目配置文件 |
README.md | 我的项目的阐明文档,markdown 格局 |
Vue3-根底点
起步
以下所以笔记都是基于@vue/cli形式创立我的项目进行阐明
Composition API
为什么须要Composition API
Composition API是Vue3的最大特点,也能够很显著看出他是受到React Hooks的启发
- 解决代码的可读性随着组件变大而变差
- 每一种代码复用的形式,都存在缺点
- TS反对无限
setup
setup 是 Vue3.x 新增的一个选项, 它是组件内应用 Composition API
的入口
setup执行机会
基于VueJs生命周期的比照,发现setup
要早于beforeCreate
执行。
<script>import { defineComponent } from '@vue/composition-api'export default defineComponent({ beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, setup() { console.log('setup') },})</script>
setup参数
应用setup
时,它承受两个参数:
{Data} props
{SetupContext} context
setup 中承受的props
是响应式的, 当传入新的 props 时,会及时被更新。因为是响应式的, 所以不能够应用 ES6 解构,解构会打消它的响应式。错误代码示范:
<script>import { defineComponent } from '@vue/composition-api'export default defineComponent({ setup(props) { const { name } = props console.log('prop name', name) },})</script>
Getting a value from the
props in root scope of
setup() will cause the value to lose reactivity vue/no-setup-props-destructure
setup类型
interface Data { [key: string]: unknown}interface SetupContext { attrs: Data slots: Slots emit: (event: string, ...args: unknown[]) => void}function setup(props: Data, context: SetupContext): Data
从下面ts定义的两个接口能够看出,setup
函数的第二个参数context
对象,有三个属性,别离是:
- attrs:对应vue2.x中的
$attr
属性 - slots: 对应vue2.x中的
slot
插槽 - emit: 对应vue2.x中的
$emit
发送事件
这样设计的目标在于,咱们在setup
函数中不能拜访到this
。并且这几个属性都是主动同步最新的值,所以咱们每次应用拿到的都是最新值。
生命周期
改图起源其余博主
能够通过间接导入onX
办法来注册生命周期钩子函数。
<script>import { defineComponent, onMounted, onUpdated, onUnmounted } from 'vue'export default defineComponent({ setup() { onMounted(() => { console.log('mounted!') }) onUpdated(() => { console.log('updated!') }) onUnmounted(() => { console.log('unmounted!') }) }})</script>
这些生命周期钩子函数在setup
函数里可能同步地应用,因为它们依赖于外部全局状态来定位以后的流动实例(以后正在调用setup
的组件实例)。在没有以后流动实例的状况下调用它们将导致谬误。
组件实例上下文也在生命周期钩子的同步执行期间设置。在卸载组件时候,在生命周期钩子内同步创立的的察看程序watch
和计算属性computed
也将主动删除。
比照Options API 和Composition API 生命周期
beforeCreate
-> usesetup()
created
-> usesetup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeUnmount
->onBeforeUnmount
unmounted
->onUnmounted
errorCaptured
->onErrorCaptured
renderTracked
->onRenderTracked
renderTriggered
->onRenderTriggered
activated
->onActivated
deactivated
->onDeactivated
Options API Hook inside setup
beforeCreate
Not needed* created
Not needed* beforeMount
onBeforeMount
mounted
onMounted
beforeUpdate
onBeforeUpdate
updated
onUpdated
beforeUnmount
onBeforeUnmount
unmounted
onUnmounted
errorCaptured
onErrorCaptured
renderTracked
onRenderTracked
renderTriggered
onRenderTriggered
activated
onActivated
deactivated
onDeactivated
从下面的比照能够看出:
beforeCreate
和created
被setup
替换了- 钩子命名都减少了
on
- 新增用于调试的钩子函数
onRenderTriggered
和onRenderTricked
- 将 Vue2.x 中的
beforeDestroy
名称变更成beforeUnmount
;destroyed
表更为unmounted
provide与inject
provide与inject启动了依赖注入项,两者都只能在setup期间应用以后组件实例进行调用
类型
interface InjectionKey<T> extends Symbol {}function provide<T>(key: InjectionKey<T> | string, value: T): void// without default valuefunction inject<T>(key: InjectionKey<T> | string): T | undefined// with default valuefunction inject<T>(key: InjectionKey<T> | string, defaultValue: T): T// with factoryfunction inject<T>( key: InjectionKey<T> | string, defaultValue: () => T, treatDefaultAsFactory: true): T
reactive、ref、toRefs
在vue2.x中,数据的定义都是在data
函数中,然而在vue3.x中,能够应用reactvie
和ref
来定义数据
reactive
返回一个响应式的对象正本。
const obj = reactive({ count: 0 })
特点:响应式的转换是“深度”的。它会影响所有嵌套属性,基于Proxy
去实现,返回的proxy
对象与原始对象并不相等,倡议只与响应式的proxy
对象应用防止依赖原始对象。
类型
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
函数reactive
承受一个对象作为参数,并返回一个响应式的对象。这里采纳了泛型束缚的形式使得reactive
函数的参数的类型更加具体。
例子:
<template> <div> <p>{{user.name}}</p> <p>{{user.hobby}}</p> <p v-for="item in user.sexs" :key="item.id">{{item.label}}</p> </div></template><script>import { defineComponent, reactive } from 'vue'export default defineComponent({ setup() { const user = reactive({ name: 'DarkCode', hobby: '篮球', sexs: [ { id: 1, label: '男' }, { id: 2, label: '女' } ] }) return { user } }})</script>
联合toRefs应用解构
下面的例子中能够看到,咱们在页面上应用user.name
、user.hobby
等比拟繁琐。那么是否将user
对象进行解构,间接失去它的相干属性呢?这是不能的,因为会打消它的响应式。但咱们能够借助toRefs
。toRefs 用于将一个 reactive 对象转化为属性全副为 ref 对象的一般对象。
<template> <div> <p>{{name}}</p> <p>{{hobby}}</p> <p v-for="item in sexs" :key="item.id">{{item.label}}</p> </div></template><script>import { defineComponent, reactive,toRefs } from 'vue'export default defineComponent({ setup() { const user = reactive({ name: 'DarkCode', hobby: '篮球', sexs: [ { id: 1, label: '男' }, { id: 2, label: '女' } ] }) return { ...toRefs(user) } }})</script>
留神点:reactive
会“解开”所有深层的refs
,同时放弃ref
是响应式的
<script>import { defineComponent, reactive, ref } from 'vue'export default defineComponent({ setup() { const count = ref(1) const obj = reactive({ count }) // ref will be unwrapped console.log(obj.count === count.value) // true // it will update `obj.count` count.value++ console.log(count.value) // 2 console.log(obj.count) // 2 // it will also update `count` ref obj.count++ console.log(obj.count) // 3 console.log(count.value) // 3 }})</script>
在将ref
调配给响应属性时,该ref
将主动解包
<script>import { defineComponent, reactive, ref } from 'vue'export default defineComponent({ setup() { const count = ref(1) const obj = reactive({}) // assigning a ref to a reactive property obj.count = count console.log(obj.count) // 1 // ref will be automatically unwrapped. console.log(obj.count === count.value) // true }})</script>
reactive实质
- 是一个基于
proxy
实现的响应式函数,返回值是一个proxy
的响应式对象 - 函数的参数是对象类型,对于根本数据类型来说不能用
reactive
- 可能深层次地监听到响应式对象属性
<script>import { defineComponent, reactive } from 'vue'export default defineComponent({ setup() { const obj = reactive({ name: 'DarkCode', age: 22 }) console.log(obj) }})</script>
ref
承受一个外部值并返回一个响应式且可变的ref
对象。ref
对象具备指向外部值的单个属性(.value)
如:
<script>import { defineComponent, ref } from 'vue'export default defineComponent({ setup() { const count = ref(0) console.log(count.value) count.value++ console.log(count.value) }})</script>或<template> <div> {{count}} </div></template><script>import { defineComponent, ref } from 'vue'export default defineComponent({ setup() { const count = ref(0) console.log(count.value) setInterval(() => { count.value++ console.log(count.value) },1000) return { count } }})</script>
如果将一个对象调配为ref
的值,则该对象将通过响应式办法reactive
赋予深度响应式
类型
interface Ref<T> { value: T}function ref<T>(value: T): Ref<T>
定义了一个Ref
泛型接口,接口中定义了一个value变量,类型通过泛型进行决定。办法ref
返回值是Ref
类型
如:
<script>import { defineComponent, ref } from 'vue'export default defineComponent({ setup() { const count = ref({ name: 'Darkcode', age: 22 }) console.log(count.value.age) count.value.age++ console.log(count.value.age) return { count } }})</script>
ref总结
- 办法
ref
承受一个参数,类型能够是任何类型 - 办法的返回值是一个接口(对象)类型
- 要拜访或批改值须要通过.value的模式去实现
<script>import { defineComponent, ref } from 'vue'export default defineComponent({ setup() { const count = ref({ name: 'Darkcode', age: 22 }) console.log(count) }})</script>
toRefs
可能将一个响应式对象转换为一个一般对象,其中后果对象的每个属性都指向原始对象相应属性的ref
如:
<script>import { defineComponent, reactive, toRefs } from 'vue'export default defineComponent({ setup() { const state = reactive({ foo: 1, bar: 2 }) /* Type of stateAsRefs: { foo: Ref<number>, bar: Ref<number> } */ const stateAsRefs = toRefs(state) // The ref and the original property is "linked" state.foo++ console.log(stateAsRefs.foo.value)// 2 stateAsRefs.foo.value++ console.log(state.foo)// 3 }})</script>
从组合函数返回响应式对象的时候,toRefs
是很有用的,以便应用组件时能够对返回的对象进行解构/扩大而不会失去"响应式"。
<script>import { defineComponent, reactive, toRefs } from 'vue'function useFeatureX() { const state = reactive({ foo: 1, bar: 2 }) return toRefs(state)}export default defineComponent({ setup() { // can destructure without losing reactivity const { foo, bar } = useFeatureX() return { foo, bar } }})</script>
toRefs
仅为源对象中蕴含的属性生成ref
援用。要为特定属性创立ref
援用,需应用toRef
toRefs总结
- 能将一个响应式对象转换为一个一般对象
- 失去的对象的属性领有一个隐式的value属性
- 失去的对象的每个属性可看成是一个个的
ref
- 在对对象进行解构的时候,
toRefs
很有用
readonly
能将一个对象(响应式或一般对象)或者一个ref
,并返回原始的只读proxy
代理。只读proxy
代理“很深”:拜访的任何嵌套属性也将是只读的。
如:
<script>import { defineComponent, reactive, readonly, watchEffect } from 'vue'export default defineComponent({ setup() { const original = reactive({ count: 0 }) const copy = readonly(original) console.log(copy) watchEffect(() => { // works for reactivity tracking console.log(copy.count) // 0、1 }) // mutating original will trigger watchers relying on the copy original.count++ // mutating the copy will fail and result in a warning copy.count++ //warning }})</script>
与响应式一样,如果任何属性应用了ref
,则通过proxy
代理拜访该属性时,该属性将主动解包。
<script>import { defineComponent, ref, readonly } from 'vue'export default defineComponent({ setup() { const raw = { count: ref(123) } const copy = readonly(raw) console.log(raw.count.value) // 123 console.log(copy.count) // 123 }})</script>
isProxy
检测对象是否由reactive
响应式或readonly
只读形式创立的proxy
代理。
isReactive
检测对象是否由reactive
响应式创立的proxy
代理对象。
如:
<script>import { defineComponent, reactive, isReactive } from 'vue'export default defineComponent({ setup() { const user = reactive({ name: 'DarkCode' }) console.log(isReactive(user)) // true }})</script>
如果通过readonly
创立一个proxy
代理对象,也会返回true
。但包装由reactive
响应式创立的另一个proxy
代理对象。
import { reactive, isReactive, readonly } from 'vue'export default { setup() { const state = reactive({ name: 'John' }) // readonly proxy created from plain object const plain = readonly({ name: 'Mary' }) console.log(isReactive(plain)) // -> false // readonly proxy created from reactive proxy const stateCopy = readonly(state) console.log(isReactive(stateCopy)) // -> true }}
isReadonly
检测对象是否是由readonly
创立的只读proxy
代理。
toRaw
返回reactive
或readonly
代理的原始对象。只是一个本义口,可用于长期读取而不会产生代理拜访/跟踪开销,也可用于写入而不会触发更改。不倡议保留对原始对象的长久援用,应用的时候要审慎。
如:
<script>import { defineComponent, reactive, toRaw } from 'vue'export default defineComponent({ setup() { const foo = { name: 'DarkCode' } const user = reactive(foo) console.log(toRaw(user) === foo) // true }})</script>
markRaw
标记一个对象,使其永远不会转换为代理,而是返回对象自身。
<script>import { defineComponent, reactive, markRaw, isReactive } from 'vue'export default defineComponent({ setup() { const foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // also works when nested inside other reactive objects const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false }})</script>
留神点:
markRaw
以及上面的shallowXXX API使咱们能够有选择地抉择默认的深度响应式/只读转化,并将原始的,非代理的对象嵌入状态图中,有如下理由:
- 不应使某些值具备响应式,如负责的第三方类实例或Vue组件实例对象。
- 渲染具备不可变数据源的大列表时,跳过代理转换能够进步性能。
shallowReactive
创立一个响应式代理,该代理跟踪其本身属性的相应性,但不执行嵌套对象的深度响应式转换。相似咱们的浅拷贝。
如:
<script>import { defineComponent, shallowReactive, isReactive } from 'vue'export default defineComponent({ setup() { const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties is reactive state.foo++ // ...but does not convert nested objects isReactive(state.nested) // false state.nested.bar++ // non-reactive }})</script>
shallowReadonly
相似下面的shallowReactive
,就不多说了。
toRef
用于为源响应式对象上的属性创立ref
,而后能够传递ref
,保留与其源属性的响应式链接。
如:
<script>import { defineComponent, reactive } from 'vue'export default defineComponent({ setup() { const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') fooRef.foo++ console.log(state.foo) // 2 state.foo++ console.log(fooRef.foo) // 3 }})</script>
当要将属性的ref
传递给组合函数时,toRef
很有用。
export default { setup(props) { useSomeFeature(toRef(props, 'foo')) }}
customRef
创立一个自定义ref
,并对其依赖项跟踪进行显式管制,并触发更新。它须要一个工厂函数,该函数接管track
和trigger
函数作为参数,并返回带有get
和set
的对象。
如:
<template> <div> <input type="text" v-model="text" /> </div></template><script>import { defineComponent, customRef } from 'vue'function useDebouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } })}export default defineComponent({ setup() { return { text: useDebouncedRef('hello') } }})</script>
类型
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>type CustomRefFactory<T> = ( track: () => void, trigger: () => void) => { get: () => T set: (value: T) => void}
computed与watch
computed
应用getter
函数,并为getter
返回的值返回一个不可变的响应式ref
对象。
如:
<script>import { defineComponent, computed, ref } from 'vue'export default defineComponent({ setup() { const count = ref(0) const plusOne = computed(() => count.value + 1) // 2 console.log(plusOne.value) plusOne.value++ //error },})</script>
另外,它还能够应用带有get
和set
函数的对象来创立可写的ref
对象。
const count = ref(1)const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 }})plusOne.value = 1console.log(count.value) // 0
类型
// read-onlyfunction computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>>// writablefunction computed<T>(options: { get: () => T; set: (value: T) => void }): Ref<T>
watchEffect
在响应式地跟踪其依赖关系时立刻运行一个函数,并在依赖关系产生更改时从新运行这个函数。
如:
<script>import { defineComponent, watchEffect, ref } from 'vue'export default defineComponent({ setup() { const count = ref(0) watchEffect(() => console.log(count.value)) // 0、1 setTimeout(() => { count.value++ }, 100) },})</script>
类型
function watchEffect( effect: (onInvalidate: InvalidateCbRegistrator) => void, options?: WatchEffectOptions): StopHandleinterface WatchEffectOptions { flush?: 'pre' | 'post' | 'sync' // default: 'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void}interface DebuggerEvent { effect: ReactiveEffect target: any type: OperationTypes key: string | symbol | undefined}type InvalidateCbRegistrator = (invalidate: () => void) => voidtype StopHandle = () => void
watch
watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认状况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。
watch(source, callback, [options])
参数阐明:
- source: 能够反对 string,Object,Function,Array; 用于指定要侦听的响应式变量
- callback: 执行的回调函数
- options:反对 deep、immediate 和 flush 选项。
接下来我会别离介绍这个三个参数都是如何应用的, 如果你对 watch 的应用不明确的请往下看:
监听reactive定义的数据
如:
<script>import { defineComponent, reactive, watch } from 'vue'export default defineComponent({ setup() { const state = reactive({ nickname: 'DarkCode', age: 22 }) setTimeout(() => { state.age++ }, 1000) // 批改age的值时会触发watch的回调 watch( () => state.age, (curAge, preAge) => { console.log('new value:', curAge, 'old value:', preAge) } ) },})</script>
监听ref定义的数据
<script>import { defineComponent, ref, watch } from 'vue'export default defineComponent({ setup() { const count = ref(2021) setTimeout(() => { count.value++ }, 1000) watch(count, (curCount, preCount) => { console.log('new Value and old Value are:', curCount, preCount) // 2022,2021 }) },})</script>
监听多个数据
语法:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */})
如,针对下面监听reactive与ref定义的数据:
<script>import { defineComponent, reactive, ref, watch } from 'vue'export default defineComponent({ setup() { const count = ref(2021) const state = reactive({ nickname: 'DarkCode', age: 22 }) setTimeout(() => { count.value++ state.age++ }, 1000) watch([() => state.age, count], ([curAge, curCount], [preAge, preCount]) => { console.log("新值:", curAge, "老值:", preAge); console.log("新值:", curCount,"老值:", preCount) }) },})</script>
监听简单的嵌套对象
如:
<script>import { defineComponent, reactive, watch } from 'vue'export default defineComponent({ setup() { const state = reactive({ house: { id: 11, attrs: { area: 111.2, height: 12, label: '学区房', owner: 'DarkCode' } } }) setTimeout(() => { state.house.id++ }, 1000) watch( () => state.house, (newT,oldT) => { console.log('new value and oldT are:', newT, oldT) }, { deep: true } ) },})</script>
如果不应用第三个参数deep:true
, 是无奈监听到数据变动的。 后面咱们提到,默认状况下,watch 是惰性的, 那什么状况下不是惰性的, 能够立刻执行回调函数呢?其实应用也很简略, 给第三个参数中设置immediate: true
即可。
stop进行监听
咱们在组件中创立的watch
监听,会在组件被销毁时主动进行。如果在组件销毁之前咱们想要进行掉某个监听, 能够调用watch()
函数的返回值,操作如下:
<script>import { defineComponent, reactive, watch } from 'vue'export default defineComponent({ setup() { const state = reactive({ house: { id: 11, attrs: { area: 111.2, height: 12, label: '学区房', owner: 'DarkCode' } } }) setTimeout(() => { state.house.id++ }, 1000) setTimeout(() => { stopWatchHouse() }, 3000) const stopWatchHouse = watch( () => state.house, (newT,oldT) => { console.log('new value and oldT are:', newT, oldT) }, { deep: true } ) },})</script>
比照watchEffect
:
- watchEffect 不须要手动传入依赖
- watchEffect 会先执行一次用来主动收集依赖
- watchEffect 无奈获取到变动前的值, 只能获取变动后的值
Vue3-高级
自定义hooks
这里如果有相熟react的小伙伴的话,那么对hooks是比拟相熟的。在Vue3.x中,提供了自定义hooks,目标在于代码的重用,与vue2.x中mixins
的区别在于其性能以及浏览性更好。
如:构建一个平时开发中常见的hooks来进行数据的封装申请解决。
咱们将构建两个可组合的hooks。
- 第一个hook将用于间接与其余API进行交互
- 第二个hook将依赖于第一个
/hooks/api.ts
import { ref } from 'vue'export default function useApi(url: RequestInfo, options ?: RequestInit | undefined) { const response = ref() const request = async () => { const res = await fetch(url, options) const data = await res.json() response.value = data } return { response, request }}
/hooks/products.ts
import useApi from './api'import { ref } from 'vue'export default async function useProducts() { const { response: products, request } = useApi( "https://ecomm-products.modus.workers.dev/" ) const loaded = ref(false) if(loaded.value === false) { await request() loaded.value = true } return { products }}
/test.vue
<template> <div> <h3>Customers</h3> <table id="customers" > <tr> <th>ID</th> <th>title</th> <th>category</th> </tr> <tr v-for="product in products" :key="product.id"> <td>{{product.id}}</td> <td>{{product.title}}</td> <td>{{product.category}}</td> </tr> </table> </div></template><script lang="ts">import { defineComponent } from "vue";import useProducts from "../hooks/products";export default defineComponent({ async setup() { const { products } = await useProducts() return { products }; },});</script>
留神,给setup
加上async
。须要给父组件设置<Suspense>
包裹子组件,如parent.vue
:
<template> <div> <Suspense> <template #default> <HelloWorld></HelloWorld> </template> <template #fallback> <h1>Loading...</h1> </template> </Suspense> </div></template>
再来看一个获取用户信息的例子:
/hooks/user.ts
import useApi from "./api";import { ref } from "vue";export interface Location { lat: number; lng: number;}export interface Address { street: string; suite: string; city: string; zipcode: number; geo: Location;}export interface User { id: string; name: string; username: string; email: string; address: Address;}export default async function useUserss() { const { response: users, request } = useApi<User[]>( "https://jsonplaceholder.typicode.com/users" ); const loaded = ref(false); if (loaded.value === false) { await request(); loaded.value = true; } return { users };}
/component/User.vue
<script lang="ts">import { defineComponent } from "vue";import useUsers from "../hooks/users";export default defineComponent({ name: "Users", async setup() { const { users } = await useUsers(); return { users }; },});</script>
Teleport
为什么须要
<teleport /> 准许将一个元素从一个中央移到另一个中央。
有了这个意识,咱们再来看一下为什么须要用到 Teleport 的个性呢,看一个小例子: 在子组件Header
中应用到Dialog
组件,咱们理论开发中常常会在相似的情景下应用到 Dialog
,此时Dialog
就被渲染到一层层子组件外部,解决嵌套组件的定位、z-index
和款式都变得艰难。 Dialog
从用户感知的层面,应该是一个独立的组件,从 dom 构造应该齐全剥离 Vue 顶层组件挂载的 DOM;同时还能够应用到 Vue 组件内的状态(data
或者props
)的值。简略来说就是,即心愿持续在组件外部应用Dialog
, 又心愿渲染的 DOM 构造不嵌套在组件的 DOM 中。 此时就须要 Teleport 上场,咱们能够用<Teleport>
包裹Dialog
, 此时就建设了一个传送门,能够将Dialog
渲染的内容传送到任何指定的中央。 接下来就举个小例子,看看 Teleport 的应用形式
应用
咱们心愿 Dialog 渲染的 dom 和顶层组件是兄弟节点关系, 在index.html
文件中定义一个供挂载的元素。
<body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> <div id="dialog"></div> </body>
Dialog.vue
<template> <teleport to="#dialog"> <div class="dialog"> <div class="dialog_wrapper"> <div class="dialog_header" v-if="title"> <slot name="header"> <span>{{ title }}</span> </slot> </div> </div> <div class="dialog_content"> <slot></slot> </div> <div class="dialog_footer"> <slot name="footer"></slot> </div> </div> </teleport></template><script lang="ts">import { defineComponent } from 'vue'export default defineComponent({ setup() { return { title: '登录' } },})</script>