vue

事件修饰符
https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers
.stop.prevent.self.capture.once.passive
v-model
<input :value="text" @input="event => text = event.target.value"><input v-model="text"><input>、<textarea> 绑定 value 监听 input
<input type="checkbox">、<input type="radio"> 绑定 checked property , 侦听 change
自定义
<CustomInput
:model-value="searchText"
@update:model-value="(newValue) => (searchText = newValue)"
/>即 modelValue 与 update:modelValue
修饰符 与 自定义修饰符
修饰符
.number 转换为 number
.trim 去两头的空
自定义修饰符
默认的为 modelModifiers
自定义的为 自定义名+Modifiers,如 title 就为 titleModifiers
// 这是默认,即 v-model
const [model, modifiers] = defineModel();
// 这是带自定义名 的 v-model:first-name
const [firstName, firstNameModifiers] = defineModel("firstName");dom
https://cn.vuejs.org/guide/essentials/template-refs.html
setup 需要注意返回
3.5+
<template>
<input ref="my-input" />
</template>
<script setup>
import { useTemplateRef, onMounted } from "vue";
// 第一个参数必须与模板中的 ref 值匹配
const input = useTemplateRef("my-input");
onMounted(() => {
input.value.focus();
});
</script>旧版本
<template>
<input ref="input" />
</template>
<script setup>
import { ref, onMounted } from "vue";
// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null);
onMounted(() => {
input.value.focus();
});
</script>watch
注意!
参数应该是 ref、reactive、getter。
reactive 应该是整个一起监听,如果是 reactive.xxxx 必须使用 getter!
watchEffect
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
);
data.value = await response.json();
});回调会立即执行。每当 todoId.value 变化时,回调会再次执行。
watchEffect 的副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。
其他
immediate 立即执行
watch(
source,
(newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
},
{ immediate: true }
);once
watch(
source,
(newValue, oldValue) => {
// 当 `source` 变化时,仅触发一次
},
{ once: true }
);触发时机
默认是在 父组件更新 (如有) 之后、所属组件的 DOM 更新之前
调整到素有 DOM 之后,flush: 'post'
任何更新之前触发,flush: 'sync'
终止
watch(id, (newId) => {
const controller = new AbortController();
// 仅在 Vue 3.5+ 中支持,并且不支持异步,即使 await
fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {});
onWatcherCleanup(() => {
// 终止过期请求
controller.abort();
});
});善后
setup 会自动注册和销毁 watchEffect,但是如果是异步中创建的,那就必须自己手动处理。
setTimeout(() => {
const unwatch = watchEffect(() => {});
// ...当该侦听器不再需要时
unwatch();
}, 0);不要异步创建!!!
deep
知道即可 谨慎使用
函数组件
import { ref } from "vue";
export default {
setup() {
const count = ref(0);
return { count };
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`,
// 也可以针对一个 DOM 内联模板:
// template: '#my-template-element'
};function BaseLayout(slots) {
return `<div class="container">
<header>${slots.header}</header>
<main>${slots.default}</main>
<footer>${slots.footer}</footer>
</div>`;
}props
export default {
props: {
title: String,
},
};<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />Attributes 透传
禁止透传
<script setup>
defineOptions({ inheritAttrs: false });
</script>多节点需要自己 设置 Attributes,如:
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>访问 Attributes
<script setup>
import { useAttrs } from "vue";
const attrs = useAttrs();
</script>slot
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div><template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
<!-- 简写 -->
<template #header></template>父组件 想要 子组件的数据:
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
<MyComponent v-slot="{ text, count }"> {{ text }} {{ count }} </MyComponent>
<!-- 具名slot -->
<MyComponent>
<template #header="headerProps"> {{ headerProps }} </template>
</MyComponent><slot :text="greetingMessage" :count="1"></slot>需要注意 name 是 vue 特殊的 attribute
provide / inject
<script setup>
import { provide, inject ,readonly} from "vue";
provide(/* 注入名 */ "message", /* 值 */ "hello!");
provide('read-only-count', readonly(count)) // readonly 可以确保值不会被修改
const message = inject("message", "这是默认值");
</script>provide 值可以是 ref
但是不建议直接修改 provide,而是通过函数闭包实现。
v2 形式
export default {
// 可以返回函数,如同 data 一样
provide: {
message: "hello!",
},
};export default {
inject: ["message"],
inject: {
// 本地别名
ocalMessage: {
from: "message",
},
},
};setup
组合式函数 https://cn.vuejs.org/guide/reusability/composables.html
setup() https://cn.vuejs.org/api/composition-api-setup.html
script setup https://cn.vuejs.org/api/sfc-script-setup.html
export default {
// 第二是 context
setup(props, { attrs, slots, emit, expose }) {},
};深入响应性原理:理解 Vue 响应性系统的底层细节。
状态管理:多个组件间共享状态的管理模式。
测试组合式函数:组合式函数的单元测试技巧。
VueUse:一个日益增长的 Vue 组合式函数集合。源代码本身就是一份不错的学习资料。
toValue
3.3+
toValue(1); // --> 1
toValue(ref(1)); // --> 1
toValue(() => 1); // --> 1todo 自定义指令
JSX 函数组件
https://cn.vuejs.org/guide/extras/render-function.html#render-function-recipes
在 jsx 中,事件要用 onXxxx,
import type { SetupContext } from 'vue'
type FComponentProps = {
message: string
}
type Events = {
sendMessage(message: string): void
}
function FComponent(
props: FComponentProps,
context: SetupContext<Events>
) {
return (
<button onClick={() => context.emit('sendMessage', props.message)}>
{props.message} {' '}
</button>
)
}
FComponent.props = {
message: {
type: String,
required: true
}
}
FComponent.emits = {
sendMessage: (value: unknown) => typeof value === 'string'
}vue vite less 全局变量
css: {
preprocessorOptions: {
less: {
modifyVars: {
hack: `true; @import (reference) "${path.resolve('src/assets/global.less')}";`
},
javascriptEnabled: true
}
}
}path.resolve 是 node 的 api
path.resolve 的 参数是全局 less 文件的路径
部署
vite + nginx
https://blog.csdn.net/qq_43519131/article/details/127408209
config 中获取环境
export default ({ mode }: ConfigEnv): UserConfig => {
// 获取 .env 环境配置文件
const env = loadEnv(mode, process.cwd());
return {
base: env.VITE_BASE_PATH,
};
};server {
listen 80;
listen [::]:80;
server_name show.q123q.cc;
location /at {
alias /myopt/show/at/dist;
index index.html;
try_files $uri $uri/ /at/index.html;
}
location /api {
proxy_pass http://172.16.20.30:9905/;
}
location / {
root $my_path/html;
index /show.html;
}
}体积优化
分析工具
npp add rollup-plugin-visualizer -D// https://vitejs.dev/config/
export default defineConfig({
plugins: [
visualizer({
gzipSize: true,
brotliSize: true,
emitFile: false,
filename: "test.html", //分析图生成的文件名
open: true, //如果存在本地服务端口,将在打包后自动展示
}),
],
});