vue
动态参数(动态方法名)
https://vuejs.org/guide/essentials/template-syntax.html#dynamic-arguments
<a v-on:[eventName]="doSomething"> ... </a>
<!-- shorthand -->
<a @[eventName]="doSomething">代理的代理
const raw = {}
const proxy = reactive(raw)
// proxy is NOT equal to the original.
console.log(proxy === raw) // false
// 代理的代理 与 代理是相同的。。。。
// calling reactive() on the same object returns the same proxy
console.log(reactive(raw) === proxy) // true
// calling reactive() on a proxy returns itself
console.log(reactive(proxy) === proxy) // true可写计算属性
https://vuejs.org/guide/essentials/computed.html#writable-computed
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// Note: we are using destructuring assignment syntax here.
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>组件内哪个元素接收 class 类
<my-component class="baz"></my-component>
<!-- my-component template using $attrs -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>watch 和 watchEffect 区别
https://vuejs.org/guide/essentials/watchers.html#deep-watchers
watch 是监视某个确定的响应式数据。并且只会在数据改变之后触发回调。
watchEffect 是只有回调内的响应式数据改变就会重新触发回调。并且初始化的时候会触发一次回调函数。
watch 的回调函数执行时机
https://vuejs.org/guide/essentials/watchers.html#callback-flush-timing
默认情况下,回调函数的触发时机在组件更新之前。这意味着在回调函数内获取组件的数据是还未更新的。
获取真实的 dom 元素
<script setup>
import { ref, onMounted } from 'vue'
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>获取真实的 dom 元素数组
https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for
组件内触发方法
<script setup>
const emit = defineEmits(['enlarge-text'])
emit('enlarge-text')
</script>
export default {
emits: ['enlarge-text'],
setup(props, ctx) {
ctx.emit('enlarge-text')
}
}动态组件
<!-- Component changes when currentTab changes -->
<component :is="tabs[currentTab]"></component>大小写不敏感
html 的标签和属性命名是不敏感的。这意味着浏览器会断定任何大写字符为小写字符。
这意味着你在模板内使用大小驼峰命名、v-on 事件名,需要将他们转为烧烤串命名法。
// camelCase in JavaScript
const BlogPost = {
props: ['postTitle'],
emits: ['updatePost'],
template: `
<h3>{{ postTitle }}</h3>
`
}
<!-- kebab-case in HTML -->
<blog-post post-title="hello!" @update-post="onUpdatePost"></blog-post>注册全局组件
import MyComponent from './App.vue'
app.component('MyComponent', MyComponent)组件绑定多个属性
const post = {
id: 1,
title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
<BlogPost :id="post.id" :title="post.title" />组件属性接收
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
export default {
props: ['foo'],
setup(props) {
// setup() receives props as the first argument.
console.log(props.foo)
}
}单向数据流动
https://vuejs.org/guide/components/props.html#one-way-data-flow
const props = defineProps(['foo'])
// ❌ warning, props are readonly!
props.foo = 'bar'方式一。将传入数据作为初始值,作为组件内部数据,传入数据改变不会触发视图更新。
const props = defineProps(['initialCounter'])
// counter only uses props.initialCounter as the initial value;
// it is disconnected from future prop updates.
const counter = ref(props.initialCounter)方式二。将传入的数据稍作转换
const props = defineProps(['size'])
// computed property that auto-updates when the prop changes
const normalizedSize = computed(() => props.size.trim().toLowerCase())注意:改变传入的对象、数组类型数据。
虽然子组件不支持直接改变对象、数组的引用地址,但是可以改变对象、数组的属性。
一般还是建议用 emit 改变。除非父子组件之间有强耦合关系。
触发事件验证
https://vuejs.org/guide/components/events.html#events-validation
<script setup>
const emit = defineEmits({
// No validation
click: null,
// Validate submit event
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
</script>处理 v-model 修饰符
https://vuejs.org/guide/components/events.html#usage-with-v-model
<MyComponent v-model.capitalize="myText" />
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
console.log(props.modelModifiers) // { capitalize: true }
<MyComponent v-model:title.capitalize="myText">
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }v-on 监听器继承
https://vuejs.org/guide/components/attrs.html#v-on-listener-inheritance
@click 会绑定到组件的根元素上,若触发会触发父组件的 onClick 方法。若子组件也绑定有方法,那么都会触发。
<MyButton @click="onClick" />将属性精确到组件某个元素上
https://vuejs.org/guide/components/attrs.html#disabling-attribute-inheritance
<script>
// use normal <script> to declare options
export default {
inheritAttrs: false
}
</script>
<script setup>
// ...setup logic
</script>在 js 中获取落空属性
https://vuejs.org/guide/components/attrs.html#accessing-fallthrough-attributes-in-javascript
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>有哪些封装好比较好用的钩子?
请求钩子,统一封装请求的状态,每一个请求都有对应的加载、错误状态,写起来更方便。
export function useHttp<T extends any>(api: (params?: any) => Promise<BaseResponseBody<T>>, options?: UseHttpOptions<T>): UseHttpResult<T> {
const state = reactive<HttpState<T>>({
data: (options?.initialValue ?? []) as any,
loading: !options?.manual,
error: null,
});
async function reload(params?: any) {
state.error = null;
state.loading = true;
try {
const { code, data, msg } = await api(params);
if (isSuccess(code)) {
// @ts-ignore
state.data = data;
} else {
state.error = { code, data, msg };
}
} catch (e) {
//
} finally {
state.loading = false;
}
}
!options?.manual && reload();
return {
...toRefs(state),
// @ts-ignore
state,
reload,
};
}分页钩子,封装一些分页请求重复的逻辑。
export function usePaging<T>(
api: (params: any) => any,
options?: {
manual?: boolean;
pageSize?: number;
transformData?: (data: T) => T[];
}
): PagingRes<T> {
// @ts-ignore
const paging = reactive({
refreshing: options?.manual ?? true,
isLoadMore: false,
noMore: false,
dataSource: [], // 数据
pages: {
current: 1,
size: options?.pageSize ?? 8,
},
isEmpty: computed(() => !paging.refreshing && paging.dataSource.length <= 0),
code: 0,
total: 0
});
function loadMore() {
if (!paging.refreshing && !paging.isLoadMore && !paging.noMore) {
loadData({ current: paging.pages.current + 1 }).catch(catchEmpty);
}
}
async function onRefresh(cb?: Function) {
try {
await loadData({ current: 1 });
// 接口成功回调
cb?.();
} catch (e) {
//
}
}
async function loadData(params: { current: number }) {
paging.refreshing = params.current === 1;
paging.isLoadMore = params.current > 1;
try {
const { code, data = {}, msg } = await api({ ...paging.pages, current: params.current });
paging.code = code;
if (isSuccess(code)) {
if (data) {
const { records = [], current, pages, size, total } = data;
if (current === 1) {
paging.dataSource = (options?.transformData ? options.transformData(records) : records) ?? [];
} else {
// 更多
paging.dataSource = paging.dataSource.concat((options?.transformData ? options.transformData(records) : records) ?? []);
}
paging.total = total
paging.pages.current = current;
paging.noMore = current >= pages;
}
} else {
// 就应该从 http 请求层面去控制是否要显示错误提示,因此要屏蔽这里
// errorModal(msg);
}
} catch (e) {
//
} finally {
paging.isLoadMore = false;
paging.refreshing = false;
}
}
!options?.manual && onRefresh();
return { paging, loadMore, onRefresh };
}批量请求,可以以对象或疏忽格式
export async function requestEvery(collection: Array<any> | Object) {
if(Array.isArray(collection)) {
return await Promise.all(collection)
} else {
const valueRes = await Promise.all(Object.values(collection))
const keys = Object.keys(collection)
return Object.fromEntries(keys.map((item, index) => {
return [item, valueRes[index]]
}))
}
}反馈请求,在请求过程中显示 loading 模态框。
export const callWithFeedback = async (fn: () => Promise<void>, msg?: string) => {
await wx.showToast({
title: '',
icon: 'loading',
});
try {
await fn();
if (msg) {
await wx.showToast({
title: msg,
icon: 'success',
});
}
} catch (e: any) {
await wx.showModal({
content: stringOf(e.message ?? e.msg ?? e),
showCancel: false,
});
} finally {
await wx.hideToast();
}
};弹框组件的三种实现方式
第一种是将控制组件显示与隐藏的变量放在组件内部,通过父组件调用 $ref.model.show() 来显示弹框。
第二种是将显示与隐藏变量放在父组件内,通过 prop 传递给子组件。
第三种还是将变量放在父组件内,但是通过 v-if 来控制子组件的显示隐藏。
区别
第一、二种情况下,子组件的生命周期的创建与销毁,与其父组件保持同步。
第三种情况下,当子组件切换显示状态时,它的生命周期也会相应地销毁与重建。重建时变量也会被置为初始值。
vue 组件中自定义选项
// 自定义选项
// script 中通过 this.$options.customOptions 取值
// template 中通过 $options.customOptions 取值
customOptions: {
keepAliveInclude
}