предисловие
6 февраля 2019 года React выпустила версию 16.8.0, добавив функцию хуков. Сразу же Vue также объявил о наиболее важном RFC для Vue3.0 на основных JSConf в 2019 году, а именно API на основе функций. Vue3.0 откажется от предыдущего предложения API класса и выберет API функций. В настоящее время,официальныйОн также предоставляет раннюю версию функции Vue3.0, которая была вызвана некоторое время назад.vue-function-api
, сейчас переименованcomposition-api
.
1. API композиции
Прежде всего, мы должны понять, каково первоначальное намерение дизайна Composition API?
- Логическая комбинация и мультиплексирование
- Вывод типа: одним из основных моментов Vue3.0 является использование рефакторинга TS для обеспечения шелковистой поддержки TS. Функциональный API, естественно, удобен для вывода типов.
- Размер упаковки: каждая функция может быть импортирована отдельно как именованный экспорт ES, что удобно для древовидной структуры; во-вторых, все имена функций и переменные внутри функции настройки могут быть сжаты, что повышает эффективность сжатия.
Давайте посмотрим поближеЛогическая комбинация и мультиплексированиеэтот кусок.
Прежде чем мы начнем, давайте рассмотрим текущий Vue2.x дляСхема логического мультиплексированияКакие есть? Как показано
Где могут существовать Mixins и HOC① Источник данных шаблона не ясенПроблема.
И в свойствах миксина имена методов и инъекция реквизита HOC также могут генерировать②Проблема конфликта пространств имен.
Наконец, поскольку и HOC, и Renderless Components требуют дополнительных экземпляров компонентов для инкапсуляции логики, это приведет к③ Ненужные накладные расходы на производительность.
1. Основное использование
Итак, вы получили общее представление о назначении Composition API. Теперь давайте рассмотрим его базовое использование.
Установить
npm i @vue/composition-api -S
использовать
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
Если ваш проект использует TS, пожалуйста, используйтеcreateComponent
для определения компонентов, чтобы вы могли использовать вывод типа
import { createComponent } from '@vue/composition-api'
const Component = createComponent({
// ...
})
Поскольку я использую TS в своем собственном проекте, я не буду упоминать здесь некоторые варианты использования JS, и я не буду упоминать об этом после последнего большого примера.
import { value, computed, watch, onMounted } from 'vue'
const App = {
template: `
<div>
<span>count is {{ count }}</span>
<span>plusOne is {{ plusOne }}</span>
<button @click="increment">count++</button>
</div>
`,
setup() {
// reactive state
const count = value(0)
// computed state
const plusOne = computed(() => count.value + 1)
// method
const increment = () => { count.value++ }
// watch
watch(() => count.value * 2, val => {
console.log(`count * 2 is ${val}`)
})
// lifecycle
onMounted(() => {
console.log(`mounted`)
})
// expose bindings on render context
return {
count,
plusOne,
increment
}
}
}
Хорошо, вернемся к TS, давайте посмотрим на его основное использование, на самом деле, использование в основном такое же.
<template>
<div class="hooks-one">
<h2>{{ msg }}</h2>
<p>count is {{ count }}</p>
<p>plusOne is {{ plusOne }}</p>
<button @click="increment">count++</button>
</div>
</template>
<script lang="ts">
import { ref, computed, watch, onMounted, Ref, createComponent } from '@vue/composition-api'
export default createComponent({
props: {
name: String
},
setup (props) {
const count: Ref<number> = ref(0)
// computed
const plusOne = computed(() => count.value + 1)
// method
const increment = () => { count.value++ }
// watch
watch(() => count.value * 2, val => {
console.log(`count * 2 is ${val}`)
})
// lifecycle
onMounted(() => {
console.log('onMounted')
})
// expose bindings on render context
return {
count,
plusOne,
increment,
msg: `hello ${props.name}`
}
}
})
</script>
2. Комбинированная функция
Мы уже узнали, что одним из первоначальных намерений Composition API является логическая композиция, которая называетсяКомбинаторные функции.
особенно вVue Function-based API RFCПример прослушивания положения мыши приведен в , Позвольте мне привести здесь пример с бизнес-сценарием.
Сценарий: мне нужно изменить заголовок на некоторых определенных страницах, и я не хочу делать его глобальным.
Традиционным способом мы напрямую закинем логику в миксины, следующим образом
import { Vue, Component } from 'vue-property-decorator'
declare module 'vue/types/vue' {
interface Vue {
setTitle (title: string): void
}
}
function setTitle (title: string) {
document.title = title
}
@Component
export default class SetTitle extends Vue {
setTitle (title: string) {
setTitle.call(this, title)
}
}
Тогда обратитесь на страницу
import SetTitle from '@/mixins/title'
@Component({
mixins: [ SetTitle ]
})
export default class Home extends Vue {
mounted () {
this.setTitle('首页')
}
}
Итак, воспользуемсяComposition APIДавайте сделаем это и посмотрим, как это делается
export function setTitle (title: string) {
document.title = title
}
Тогда обратитесь на страницу
import { setTitle } from '@/hooks/title'
import { onMounted, createComponent } from '@vue/composition-api'
export default createComponent({
setup () {
onMounted(() => {
setTitle('首页')
})
}
})
Видно, что нам нужно извлечь только ту логику, которую нужно переиспользовать, а потом просто напрямуюsetup()
Его можно использовать прямо в системе, что очень удобно.
Конечно, вы не можете сделать его глобальным, в этом случае он обычно превращается в глобальную инструкцию, как показано ниже.
import Vue, { VNodeDirective } from 'vue'
Vue.directive('title', {
inserted (el: any, binding: VNodeDirective) {
document.title = el.dataset.title
}
})
Страница используется следующим образом
<template>
<div class="home" v-title data-title="首页">
home
</div>
</template>
Некоторые друзья могут подумать, что после прочтения этой сцены удобнее использовать глобальные директивы, подобные этой Каковы преимущества функций композиции Vue3.0?
Не волнуйтесь, приведенный выше пример просто показывает вамКак преобразовать ваши текущие миксины, чтобы использовать функции композиции.
В настоящей боевой ссылке, которая следует, есть еще много реальных сцен.Если вы не можете ждать, вы можете перейти ко второй главе.
3. функция настройки()
setup()
Это новая опция компонента, представленная в Vue3.0, место для настройки логики компонента.
I. Время инициализации
setup()
Когда его инициализировать? давайте посмотрим на картинку
setup вызывается при создании экземпляра компонента после инициализации реквизита, но до его создания.
На этом этапе мы можем получить исходные реквизиты в качестве параметров.
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
setup (props) {
console.log('setup', props.test)
return {}
}
})
export default class Hooks extends Vue {
@Prop({ default: 'hello' })
test: string
beforeCreate () {
console.log('beforeCreate')
}
created () {
console.log('created')
}
}
Последовательность печати консоли следующая
Во-вторых, из всех приведенных выше примеров мы можем найти,setup()
иdata()
Точно так же вы можете вернуть объект, и свойства этого объекта напрямую зависят от контекста рендеринга шаблона:
<template>
<div class="hooks">
{{ msg }}
</div>
</template>
<script lang="ts">
import { createComponent } from '@vue/composition-api'
export default createComponent({
props: {
name: String
},
setup (props) {
return {
msg: `hello ${props.name}`
}
}
})
</script>
ii. reactivity api
В отличие от маршрута улучшения функций React Hooks, Vue Hooks идет по пути улучшения ценности, что он делает, так это то, как получить обычные значения и представления из адаптивного значения.
существуетsetup()
Внутри Vue предоставляет нам ряд отзывчивых API, таких как ref, который возвращает объект-оболочку Ref и автоматически расширяется при ссылке на слой представления.
<template>
<div class="hooks">
<button @click="count++">{{ count }}</button>
</div>
</template>
<script lang="ts">
import { ref, Ref, createComponent } from '@vue/composition-api'
export default createComponent({
setup (props) {
const count: Ref<number> = ref(0)
console.log(count.value)
return {
count
}
}
})
</script>
Тогда есть наши общие вычисления и часы
import { ref, computed, Ref, createComponent } from '@vue/composition-api'
export default createComponent({
setup (props) {
const count: Ref<number> = ref(0)
const plusOne = computed(() => count.value + 1)
watch(() => count.value * 2, val => {
console.log(`count * 2 is ${val}`)
})
return {
count,
plusOne
}
}
})
И значение, которое мы генерируем с помощью вычислений, даже без объявления типа, может напрямую получить свой тип для вывода, потому что оно полагается на Ref для вычисления.
setup()
Я не буду слишком подробно рассказывать о других внутренних API и функциях жизненного цикла воригинальный
4. Вычет типа реквизита
Что касается вывода типа реквизита, я сказал в начале, в TS, если вы хотите использовать вывод типа, вы должны определить компонент в функции createComponent.
import { createComponent } from '@vue/composition-api'
const MyComponent = createComponent({
props: {
msg: String
},
setup(props) {
props.msg // string | undefined
return {}
}
})
Конечно, опция props необязательна, если вам не нужна проверка типа props во время выполнения, вы можете объявить ее прямо на уровне типа TS.
import { createComponent } from '@vue/composition-api'
interface Props {
msg: string
}
export default createComponent({
props: ['msg'],
setup (props: Props, { root }) {
const { $createElement: h } = root
return () => h('div', props.msg)
}
})
Для сложных типов реквизита вы можете использовать PropType, предоставленный Vue, для объявления типов реквизита произвольной сложности, но в соответствии с его объявлением типа нам нужно использовать любой для выполнения уровня принуждения.
export type Prop<T> = { (): T } | { new(...args: any[]): T & object } | { new(...args: string[]): Function }
export type PropType<T> = Prop<T> | Prop<T>[]
import { createComponent } from '@vue/composition-api'
import { PropType } from 'vue'
export default createComponent({
props: {
options: (null as any) as PropType<{ msg: string }>
},
setup (props) {
props.options // { msg: string } | undefined
return {}
}
})
2. Деловая практика
На данный момент у нас есть определенное понимание Composition API Vue3.0, а также мы знаем некоторые практические бизнес-сценарии, для которых он подходит.
И каких первых последователей я сделал в конкретном бизнесе? Далее, давайте вместе войдем в настоящую боевую стадию.
1. Список пейджинговых запросов
Сценарий: мне нужно выполнить поисковый запрос по списку в бизнесе, включая два общих условия запроса: номер страницы и размер номера страницы, а также некоторые конкретные условия, такие как ключевые слова, статус и т. д.
В Vue2.x у нас есть два подхода, как показано на рисунке
- Самый простой способ — напрямую хранить общий запрос в одном месте, импортировать его прямо туда, где нужно использовать запрос, а затем проделать ряд повторяющихся операций на странице — это самый тест.
Ctrl + C
,Ctrl + V
власти. - Извлеките его общие переменные и методы для
mixins
Среди них страница может использоваться напрямую, что может сэкономить много повторяющейся работы. Но когда на нашей странице есть более одного списка страниц, возникает проблема, моя переменная будет очищена, что приведет к ошибке запроса.
Итак, теперь давайте попробуем использовать возможности Vue3.0, чтобы извлечь повторяющуюся логику и поместить ее в@/hooks/paging-query.ts
середина
import { ref, Ref, reactive } from '@vue/composition-api'
import { UnwrapRef } from '@vue/composition-api/dist/reactivity'
export function usePaging () {
const conditions: UnwrapRef<{
page: Ref<number>,
pageSize: Ref<number>,
totalCount: Ref<number>
}> = reactive({
page: ref(1),
pageSize: ref(10),
totalCount: ref(1000)
})
const handleSizeChange = (val: number) => {
conditions.pageSize = val
}
const handleCurrentChange = (val: number) => {
conditions.page = val
}
return {
conditions,
handleSizeChange,
handleCurrentChange
}
}
Затем мы объединяем его на определенных страницах для использования
<template>
<div class="paging-demo">
<el-input v-model="query"></el-input>
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="cons.page"
:page-sizes="[10, 20, 30, 50]"
:page-size.sync="cons.pageSize"
layout="prev, pager, next, sizes"
:total="cons.totalCount">
</el-pagination>
</div>
</template>
<script lang="ts">
import { usePaging } from '@/hooks/paging-query'
import { ref, Ref, watch } from '@vue/composition-api'
export default createComponent({
setup () {
const { conditions: cons, handleSizeChange, handleCurrentChange } = usePaging()
const query: Ref<string> = ref('')
watch([
() => cons.page,
() => cons.pageSize,
() => query.value
], ([val1, val2, val3]) => {
console.log('conditions changed,do search', val1, val2, val3)
})
return {
cons,
query,
handleSizeChange,
handleCurrentChange
}
}
})
</script>
Как видно из этого примера, источник свойств, предоставляемых шаблону, очень ясен, непосредственно изusePaging()
Возврат; и может быть переименован по желанию, поэтому не будет проблем с конфликтами пространств имен и не будет потери производительности, вызванной дополнительными экземплярами компонентов.
Как, у вас есть настоящий аромат?
2. компонент, выбираемый пользователем
Сценарий: в бизнесе, за который я отвечаю, есть общий бизнес-компонент, который я называю user-select, который является компонентом подбора персонала.. Как показано
Что касается сравнения до и после преобразования, давайте сначала посмотрим на картинку, чтобы у нас было общее представление.
В Vue2.x его общая бизнес-логика и данные плохо обрабатываются, и общая причина аналогична приведенному выше случаю.
Затем мне нужно делать следующее каждый раз, когда я хочу его использовать, что полностью тренирует меня.Ctrl + C
,Ctrl + V
навык
<template>
<div class="demo">
<user-select
:options="users"
:user.sync="user"
@search="adminSearch" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import { Action } from 'vuex-class'
import UserSelect from '@/views/service/components/user-select.vue'
@Component({
components: { UserSelect }
})
export default class Demo extends Vue {
user = []
users: User[] = []
@Prop()
visible: boolean
@Action('userSearch') userSearch: Function
adminSearch (query: string) {
this.userSearch({ search: query, pageSize: 200 }).then((res: Ajax.AjaxResponse) => {
this.users = res.data.items
})
}
}
</script>
Так можно ли избежать этой ситуации, используя Composition API? Ответ определенно, чтобы избежать этого.
Давайте сначала посмотрим, используем Vue3.0 для преобразованияsetup
Как логика в
import { ref, computed, Ref, watch, createComponent } from '@vue/composition-api'
import { userSearch, IOption } from '@/hooks/user-search'
export default createComponent({
setup (props, { emit, root }) {
let isFirstFoucs: Ref<boolean> = ref(false)
let showCheckbox: Ref<boolean> = ref(true)
// computed
// 当前选中选项
const chooseItems: Ref<string | string[]> = ref(computed(() => props.user))
// 选项去重(包含对象的情况)
const uniqueOptions = computed(() => {
const originArr: IOption[] | any = props.customSearch ? props.options : items.value
const newArr: IOption[] = []
const strArr: string[] = []
originArr.forEach((item: IOption) => {
if (!strArr.includes(JSON.stringify(item))) {
strArr.push(JSON.stringify(item))
newArr.push(item)
}
})
return newArr
})
// watch
watch(() => chooseItems.value, (val) => {
emit('update:user', val)
emit('change', val)
})
// methods
const remoteMethod = (query: string) => {
// 可抛出去自定义,也可使用内部集成好的方法处理 remote
if (props.customSearch) {
emit('search', query)
} else {
handleUserSearch(query)
}
}
const handleFoucs = (event) => {
if (isFirstFoucs.value) {
return false
}
remoteMethod(event.target.value)
isFirstFoucs.value = true
}
const handleOptionClick = (item) => {
emit('option-click', item)
}
// 显示勾选状态,若是单选则无需显示 checkbox
const isChecked = (value: string) => {
let checked: boolean = false
if (typeof chooseItems.value === 'string') {
showCheckbox.value = false
return false
}
chooseItems.value.forEach((item: string) => {
if (item === value) {
checked = true
}
})
return checked
}
return {
isFirstFoucs, showCheckbox, // ref
uniqueOptions, chooseItems, // computed
handleUserSearch, remoteMethod, handleFoucs, handleOptionClick, isChecked // methods
}
}
})
Затем мы извлекаем повторно используемую логику и данные вhooks/user-search.ts
середина
import { ref, Ref } from '@vue/composition-api'
export interface IOption {
[key: string]: string
}
export function userSearch ({ root }) {
const items: Ref<IOption[]> = ref([])
const handleUserSearch = (query: string) => {
root.$store.dispatch('userSearch', { search: query, pageSize: 25 }).then(res => {
items.value = res.data.items
})
}
return { items, handleUserSearch }
}
Затем вы можете использовать его прямо в компоненте (конечно, вы можете переименовать его, как вам нравится)
import { userSearch, IOption } from '@/hooks/user-search'
export default createComponent({
setup (props, { emit, root }) {
const { items, handleUserSearch } = userSearch({ root })
}
})
Наконец, чтобы избежать конфликтов имен, после бизнес-интеграции я теперь использую<user-select>
Компоненты просто нуждаются в этом
<user-select :user.sync="user" />
Вау, так освежает мгновенно.
Суммировать
Статья здесь, и я должен снова попрощаться с моими друзьями.
Первые пользователи Vue3.0 в целом чувствовали себя довольно хорошо. Если вы также хотите попробовать некоторые новые функции Vue3.0 в своем бизнесе, вы можете попробовать это прямо сейчас.
Таким образом, когда Vue3.0 будет действительно выпущен, возможно, вы уже знакомы с использованием и принципами этой части.
Наконец, если статья была вам полезна, пожалуйста, поставьте лайк своим друзьям~
Группа внешней связи: 731175396
Справочная статья:Vue Function-based API RFC