选择环境$ npm init @vitejs/app app-name
选择脚本$ vue
动态参数$ vue-ts
<template>
<p :[attrName]="attrValue">{{ attrName }} : {{ attrValue }}p>
<button @click="updateAttr">更新动态参数button>
<div v-for="item in list" :ref="getChildElement">{{ item }}div>
template>
<script setup lang="ts">
import {ref, reactive} from "vue";
const attrName = ref('data-name');
const attrValue = ref('Lee');
const updateAttr = () => {
attrName.value = 'data-id';
attrValue.value = 'abc-def-ghi';
}
const list = reactive([1, 2, 3]);
const getChildElement = (el: HTMLElement) => console.log(el);
script>
<template>
<button @[eventName]="eventFun">需要绑定事件类型button>
<button @click="updateEvent">更新动态参数button>
template>
<script setup lang="ts">
import {ref} from "vue";
const eventName = ref('data-name');
let eventFun = (e: MouseEvent) => {
console.log(e);
};
const updateEvent = () => {
eventName.value = 'click';
}
script>
lodash
Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。 Lodash 的模块化方法 非常适用于:
遍历 array、object 和 string对值进行 *** 作和检测创建符合功能的函数
$ npm i lodash
$ npm i --dev @types/lodash
import _ from "lodash/index";
_([1, 2, 3, 4, 5, 6]).each(item => {
console.log(item)
})
计算属性
单向<template>
<p>name : {{ name }}p>
<p>fullName : <input type="text" v-model="fullName">p>
template>
<script setup lang="ts">
import _ from "lodash/index";
import {computed, reactive} from "vue";
const name = reactive({
lastName: 'Lee',
firstName: 'Prosper'
});
// 计算属性 --> 单向 (将计算好的数据插值到页面)
const fullName = computed(() => {
return _(name).values().value().join('-');
})
script>
双向<template>
<p>name : {{ name }}p>
<p>fullName : <input type="text" v-model="fullName">p>
template>
<script setup lang="ts">
import _ from "lodash/index";
import {computed, reactive} from "vue";
const name = reactive({
lastName: 'Lee',
firstName: 'Prosper'
});
// 计算属性 --> 双向 (修改页面内插值的同时改变定义的数据)
const fullName = computed({
get() {
return _(name).values().value().join('-');
},
set(value: string) {
const names = _(value).split('-').value();
name.lastName = names[0];
name.firstName = names[1];
}
})
script>
侦听器 (watch、watchEffect)
<template>
<p>{{ data }}p>
<p>msg : <input type="text" v-model="data.msg">p>
<p>name : <input type="text" v-model="data.data.name">p>
template>
<script setup lang="ts">
// import _ from "lodash/index";
import {watch, reactive, watchEffect} from "vue";
const data = reactive({
code: 200,
data: {
name: 'Lee',
age: 25
},
msg: 'ok'
})
// 监听 数据对象
watch(data, value => {
console.log('数据对象', value);
}, {deep: true, immediate: true})
// 监听 单个属性
watch(() => data.data.name, (newValue, oldValue) => {
console.log('单个属性', newValue, oldValue);
}, {deep: true, immediate: true})
// 监听 多个属性
watch([() => data.msg, () => data.data.name], (newValues, oldValues) => {
console.log('多个属性', newValues, oldValues);
}, {deep: true, immediate: true})
// 获取属性变化之后的值(刚方法会在初始化时默认执行)
watchEffect(() => {
console.log(data.msg);
console.log(data.data.name);
})
script>
v-for
<template>
<ul>
<li v-for="(value, name, index) in data">
{{ index }} - {{ name }} - {{ value }}
li>
ul>
template>
<script setup lang="ts">
import {reactive} from "vue";
const data = reactive({
name1: 'value1',
name2: 'value2',
name3: 'value3'
})
script>
在组件上使用 v-for
父组件
父组件 ---> 子组件
子组件 ---> 父组件
<template>
<button @click="addRow(`Row-${Date.now()}`)">新增行button>
<ul>
<template v-for="(item, index) in items" :key="item.id">
<Child :item="item.name" :index="index" @remove="removeRow(index)" @add="addRow"/>
template>
<hr>
<Child v-for="(item, index) in items" :item="item.name" :index="index" :key="item.id"/>
ul>
template>
<script setup lang="ts">
import _ from "lodash/index";
import {reactive} from "vue";
import Child from './Child.vue';
const items = reactive([
{id: 'id-1', name: 'Lee'},
{id: 'id-2', name: 'Tom'},
{id: 'id-3', name: 'Lucy'}
]);
// 删除行
const removeRow = (index: number) => {
_(items).splice(index, 1).value();
console.log(items);
}
// 新增行
const addRow = (name: string) => {
_(items).push({id: `id-${items.length}`, name}).value();
console.log(items);
}
script>
子组件
<template>
<li>
<span>{{ index }} - {{ item }}span>
<button @click="$emit('remove')">删除行button>
<button @click="removeData">删除行button>
<button @click="$emit('add', `Row-${Date.now()}`)">新增行button>
<button @click="addData">新增行button>
li>
template>
<script setup lang="ts">
import {defineProps, defineEmits} from "vue";
defineProps(['item', 'index']);
const emits = defineEmits(['remove', 'add']);
// 删除目标
const removeData = () => {
emits('remove')
}
// 删除目标
const addData = () => {
emits('add', `Row-${Date.now()}`)
}
script>
<style scoped>
span {
display: inline-block;
width: 200px;
}
style>
复选框、选择框、lazy、number
<template>
<p>复选框p>
<label>
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" @change="checkboxChange"/>
<span>复选框span>
label>
<p>选择框p>
<select v-model="selected" @change="selectChange">
<option v-for="item in list" :value="item">{{ item.name }}option>
select>
<span>{{ selected }}span>
<p>lazy : 在调用change方法时改变数据p>
<input v-model.lazy="msg" @change="textChange"/><span>{{ msg }}span>
<p>number : 自动将用户的输入值转为数值类型p>
<input v-model.number="num" @change="numberChange"/><span>{{ num }}span>
template>
<script setup lang="ts">
import _ from "lodash/index";
import {reactive, ref} from "vue";
// 复选框
const toggle = ref('yes');
const checkboxChange = () => {
console.log(toggle.value)
}
// 选择框
const selected = ref({name: 'Tom'});
const list = reactive([{name: 'Lee'}, {name: 'Tom'}]);
const selectChange = () => {
console.log(selected.value)
}
// lazy
const msg = ref('Hello Msg!!!');
const textChange = () => {
console.log(msg)
}
// number
const num = ref(123);
const numberChange = () => {
console.log(num.value)
}
script>
Attribute
// 父组件
<template>
<Child data-msg="Hello" :data-status="activated"/>
<button @click="activated = !activated">修改状态button>
template>
<script setup lang="ts">
import {ref} from "vue";
import Child from './Child.vue';
const activated = ref(true);
script>
// 子组件
<template>
{ data }} 报警告,提示你在msg外加上一层标签-->
<p>{{ data }}p>
template>
<script setup lang="ts">
import {useAttrs} from "vue";
const data = useAttrs();
script>
在组件上使用 v-model
// 父组件
<template>
<p>父p>
<input v-model="firstName">
<input type="text" v-model="lastName">
<hr>
<Child v-model:first-name="firstName" v-model:last-name="lastName"/>
template>
<script setup lang="ts">
import {ref} from "vue";
import Child from './Child.vue';
const firstName = ref('Prosper');
const lastName = ref('Lee');
script>
// 子组件
<template>
<p>子p>
<input :value="firstName" @input="$emit('update:firstName', $event.target.value)">
<input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)">
template>
<script setup lang="ts">
import {defineEmits, defineProps} from "vue";
defineProps(['firstName', 'lastName'])
defineEmits(['update:firstName', 'update:lastName']);
script>
插槽
默认插槽
<Child><p>默认插槽 : <span>ProsperLeespan>p>Child>
<template>
<slot>slot>
template>
具名插槽
<template v-slot:default><p>具名插槽 : Hello Msg!!!p>template>
<template #default><p>具名插槽 : Hello Msg!!!p>template>
<template>
<slot>slot>
template>
<template v-slot:slotName><p>具名插槽 : Hello Msg!!!p>template>
<template #slotName><p>具名插槽 : Hello Msg!!!p>template>
<template>
<slot name="slotName">slot>
template>
<template>
<button @click="changeSlotName">切换插槽button>
<Child>
<template #[dynamicSlotName]>Leetemplate>
Child>
template>
<script setup lang="ts">
import Child from './Child.vue';
import {ref} from "vue";
const toggle = ref(true);
const dynamicSlotName = ref('slotNameA');
const changeSlotName = () => {
toggle.value = !toggle.value;
dynamicSlotName.value = toggle.value ? 'slotNameA' : 'slotNameB';
}
script>
<template>
<p style="color: aqua;">
<slot name="slotNameA">slot>
p>
<p style="color: red;">
<slot name="slotNameB">slot>
p>
template>
作用域插槽
<Child>
<template v-slot:default="scope">作用域插槽 : {{ scope.data }}template>
<template #default="scope">作用域插槽 : {{ scope.data }}template>
<template #default="{data}">作用域插槽 : {{ data }}template>
Child>
<template>
<ul>
<li v-for="( item, index ) in items">
<slot :data="item">{{ index }} - {{item.name}}slot>
li>
ul>
template>
<script setup lang="ts">
import {reactive} from "vue";
const items = reactive([{name: 'Lee'}, {name: 'Tom'}]);
script>
<template>
<Child>
<template v-slot:slotName="scope">作用域插槽 : {{ scope.data }}template>
<template #slotName="scope">作用域插槽 : {{ scope.data }}template>
<template #slotName="{data}">作用域插槽 : {{ data }}template>
Child>
template>
<template>
<ul>
<li v-for="( item, index ) in items">
<slot name="slotName" :data="item">{{ index }} - {{item.name}}slot>
li>
ul>
template>
<script setup lang="ts">
import {reactive} from "vue";
const items = reactive([{name: 'Lee'}, {name: 'Tom'}]);
script>
祖孙组件传值(provide、inject)
// 祖组件
import Grandson from './components/Grandson.vue'
import {provide} from "vue";
provide('msg', 'Hello Msg!!!');
// 孙组件
import {inject} from "vue";
const msg = inject('msg');
console.log(msg);
动态组件
<template>
<button @click="isKeepAlive = !isKeepAlive">是否缓存组件 {{ isKeepAlive }}button>
<hr>
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{ tab }}button>
<p>{{ currentTab }} - {{ currentTabComponent }}p>
<hr>
<keep-alive v-if="isKeepAlive">
<component :is="currentTabComponent">component>
keep-alive>
<component v-else :is="currentTabComponent">component>
template>
<script lang="ts">
import {reactive, ref, computed, defineComponent} from "vue";
import Tab1 from './Tab1.vue';
import Tab2 from './Tab2.vue';
import Tab3 from './Tab3.vue';
export default defineComponent({
components: {
Tab1,
Tab2,
Tab3
},
setup() {
// 是否缓存组件
const isKeepAlive = ref(true);
const currentTab = ref('Tab1');
const tabs = reactive(['Tab1', 'Tab2', 'Tab3']);
const currentTabComponent = computed(() => {
return currentTab.value;
})
return {
isKeepAlive,
currentTab,
tabs,
currentTabComponent
}
}
})
script>
异步组件(defineAsyncComponent、Suspense)
<template>
<button @click="isKeepAlive = !isKeepAlive">是否缓存组件 {{ isKeepAlive }}button>
<hr>
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{ tab }}button>
<p>{{ currentTab }} - {{ currentTabComponent }}p>
<hr>
<Suspense>
<template #default>
<keep-alive v-if="isKeepAlive">
<component :is="currentTabComponent">component>
keep-alive>
<component v-else :is="currentTabComponent">component>
template>
<template #fallback>Loading···template>
Suspense>
template>
<script lang="ts">
import {reactive, ref, computed, defineComponent, defineAsyncComponent} from "vue";
export default defineComponent({
components: {
Tab1: defineAsyncComponent(() => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(import("./Tab1.vue"));
}, 3000);
})),
Tab2: defineAsyncComponent(() => import("./Tab2.vue")),
Tab3: defineAsyncComponent(() => import("./Tab3.vue")),
},
setup() {
// 是否缓存组件
const isKeepAlive = ref(true);
const currentTab = ref("Tab1");
const tabs = reactive(["Tab1", "Tab2", "Tab3"]);
const currentTabComponent = computed(() => {
return currentTab.value;
});
return {
isKeepAlive,
currentTab,
tabs,
currentTabComponent,
};
},
});
script>
模板引用
<template>
<div ref="box">ProsperLeediv>
<Child ref="child"/>
template>
<script setup lang="ts">
import {onMounted, ref} from "vue";
import Child from "./Child.vue";
const box = ref<HTMLElement | null>(null);
const child = ref<HTMLElement | null>(null);
onMounted(() => {
console.log(box.value);
console.log(child.value.$el);
});
script>
ref
使用ref定义变量是为了保持 JavaScript 中不同数据类型的行为统一,因为在 JavaScript 中,Number 或 String 等基本类型是通过值而非引用传递的,使用ref定义值实际上是创建了一个响应式引用。
const count = ref(0);
console.log(count.value);
生命周期
<template>
template>
<script lang="ts">
import {
defineComponent,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onRenderTracked,
onRenderTriggered,
onActivated,
onDeactivated,
} from "vue";
export default defineComponent({
name: "Demo",
setup() {
console.log('setup')
onBeforeMount(() => {
console.log('%cHook inside setup ---> onBeforeMount', 'color: green;')
})
onMounted(() => {
console.log('%cHook inside setup ---> mounted', 'color: green;')
})
onBeforeUpdate(() => {
console.log('%cHook inside setup ---> onBeforeUpdate', 'color: green;')
})
onUpdated(() => {
console.log('%cHook inside setup ---> onUpdated', 'color: green;')
})
onBeforeUnmount(() => {
console.log('%cHook inside setup ---> onBeforeUnmount', 'color: green;')
})
onUnmounted(() => {
console.log('%cHook inside setup ---> onUnmounted', 'color: green;')
})
onErrorCaptured(() => {
console.log('%cHook inside setup ---> onErrorCaptured', 'color: green;')
})
onRenderTracked(() => {
console.log('%cHook inside setup ---> onRenderTracked', 'color: green;')
})
onRenderTriggered(() => {
console.log('%cHook inside setup ---> onRenderTriggered', 'color: green;')
})
onActivated(() => {
console.log('%cHook inside setup ---> onActivated', 'color: green;')
})
onDeactivated(() => {
console.log('%cHook inside setup ---> onDeactivated', 'color: green;')
})
return {};
},
beforeCreate() {
console.log('选项式 API ---> beforeCreate');
},
created() {
console.log('选项式 API ---> created');
},
beforeMount() {
console.log('选项式 API ---> beforeMount')
},
mounted() {
console.log('选项式 API ---> mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('选项式 API ---> updated')
},
beforeUnmount() {
console.log('选项式 API ---> beforeUnmount')
},
unmounted() {
console.log('选项式 API ---> unmounted')
},
errorCaptured() {
console.log('选项式 API ---> errorCaptured')
},
renderTracked() {
console.log('选项式 API ---> renderTracked')
},
renderTriggered() {
console.log('选项式 API ---> renderTriggered')
},
activated() {
console.log('选项式 API ---> activated')
},
deactivated() {
console.log('选项式 API ---> deactivated')
},
});
script>
setup、ref、toRef、toRefs
setup —> props
<template>
<Demo data-name="ProsperLee" :data-age="25"/>
<hr>
<Child data-name="Lee" :data-age="25"/>
template>
<script setup lang="ts">
import Demo from './components/Demo.vue';
import Child from './components/Child.vue';
script>
<template>
<label>
<span>【ref】span>
<button @click="getRefData">refbutton>
<br>
<input type="text" v-model="dataName">
<span>{{ dataName }}span>
label>
<hr>
<label>
<span>【toRef】span>
<button @click="getToRefData">toRefbutton>
<br>
<input type="text" v-model="dataAge">
<span>{{ dataAge }}span>
label>
template>
<script lang="ts">
import {defineComponent, ref, toRef} from "vue";
export default defineComponent({
name: 'Demo',
props: {
'data-name': {
type: String,
default: ''
},
'data-age': {
type: Number,
default: 0
}
},
setup(props: any, context) {
// 响应数据可以修改
const dataName = ref(props["dataName"]);
console.log('ref ---> dataName', dataName);
const getRefData = () => console.log(dataName.value);
// 只读数据不可修改
// [Vue warn] Set operation on key "dataAge" failed: target is readonly.
const dataAge = toRef(props, 'dataAge');
console.log('toRef ---> dataAge', dataName);
const getToRefData = () => console.log(dataAge.value);
return {
dataName,
getRefData,
dataAge,
getToRefData,
}
},
})
script>
<template>
<label>
<span>【toRefs】span>
<button @click="getToRefsData">toRefsbutton>
<br>
<input type="text" v-model="dataName">
<input type="text" v-model="dataAge">
<span>{{ dataName }}-{{ dataAge }} {{ data }}span>
label>
template>
<script lang="ts">
import {defineComponent, toRefs} from "vue";
export default defineComponent({
name: 'Child',
props: {
'data-name': {
type: String,
default: ''
},
'data-age': {
type: Number,
default: 0
}
},
setup(props: any, context) {
// 只读数据不可修改(将响应式对象转化为普通对象,每个对象都是ref)
// [Vue warn] Set operation on key "dataName" failed: target is readonly.
// [Vue warn] Set operation on key "dataAge" failed: target is readonly.
const data = toRefs(props);
console.log('toRefs ---> data', data);
const getToRefsData = () => console.log(data.dataName.value, data.dataAge.value);
return {
data,
...data,
getToRefsData,
}
},
})
script>
setup —> context
<template>
<p>{{ data }}p>
<button @click="evFun">emitbutton>
template>
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
name: 'Demo',
setup(props: any, context) {
// Attribute (非响应式对象,等同于 $attrs)
const data = context.attrs;
// 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots)
// 触发事件 (方法,等同于 $emit)
const evFun = () => context.emit('eventName', 'ProsperLee');
// 暴露公共 property (函数)
console.log(context.expose)
return {
data,
evFun,
}
},
})
script>
Mixin
全局引用
const mixin = {
data() {
const msg = ref('Hello');
return {
msg
}
},
methods: {
sayHi() {
console.log('Hi');
}
},
created() {
(this as any).sayHi();
}
}
createApp(App)
.mixin(mixin)
.mount('#app')
局部引入
import {ref} from "vue";
export default {
data() {
const msg = ref('Hello');
return {
msg
}
},
methods: {
sayHi() {
console.log('Hi');
}
},
created() {
(this as any).sayHi();
}
}
// 组件1
import {defineComponent} from "vue";
import Demo from './components/Demo.vue';
import mixin from "./mixins";
export default defineComponent({
name: 'App',
components: {
Demo
},
mixins: [mixin],
})
// 组件2
import {defineComponent} from "vue";
import mixin from "../mixins";
export default defineComponent({
name: 'Demo',
mixins: [mixin],
})
Teleport
teleport修改DOM所属父标签的位置
<div style="position:absolute;top: 100px;left: 100px;">
<span>Prosperspan>
<teleport to="body">
<span>Leespan>
teleport>
div>
在同一目标上使用多个 teleport
<div id="modals">div>
<teleport to="#modals"><span>Prosperspan>teleport>
<teleport to=".modals"><span>Leespan>teleport>
customRef
自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
<template>
<input type="text" v-model="text">
{{ text }}
template>
<script setup lang="ts">
import {customRef} from "vue";
const useDebouncedRef = (value: string, delay = 200) => {
let timeout: number;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue: string) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger();
}, delay)
}
}
})
}
const text = useDebouncedRef('hello');
script>
readonly、shallowReadonly
<template>
<p>深度只读p>
<input type="text" v-model="data1.name">
<input type="text" v-model="data1.data.msg">
<p>{{ data1 }}p>
<p>浅度只读p>
<input type="text" v-model="data2.name">
<input type="text" v-model="data2.data.msg">
<p>{{ data2 }}p>
template>
<script setup lang="ts">
import {reactive, readonly, shallowReadonly} from "vue";
const obj = reactive({
name: 'Lee',
data: {
msg: 'Hello Lee!!!'
}
})
// 深度只读
const data1 = readonly(obj);
// 浅度只读
const data2 = shallowReadonly(obj);
script>
shallowReactive、shallowRef
<template>
<p>浅度响应数据p>
<button @click="update">修改数据button>
<input type="text" v-model="data.name">
<input type="text" v-model="data.data.msg">
<p>{{ data }}p>
template>
<script setup lang="ts">
import {shallowReactive} from "vue";
// 创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
const data = shallowReactive({name: 'Lee', data: {msg: 'Hello Lee!!!'}});
script>
<template>
<p>浅度响应数据p>
<button @click="update">修改数据button>
<input type="text" v-model="data.name">
<input type="text" v-model="data.data.msg">
<p>{{ data }}p>
template>
<script setup lang="ts">
import {shallowRef} from "vue";
// 创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的
const data = shallowRef({name: 'Lee', data: {msg: 'Hello Lee!!!'}});
script>
toRaw、markRaw
<template>
<p>【toRaw】返回 reactive 或 readonly 代理的原始对象p>
<input type="text" v-model="data1.name">
<input type="text" v-model="data1.data.msg">
{{ data1 }}
<p>【markRaw】标记一个对象,使其永远不会转换为 proxy。返回对象本身p>
<input type="text" v-model="data2.name">
<input type="text" v-model="data2.data.msg">
{{ data2 }}
template>
<script setup lang="ts">
import { reactive, toRaw, markRaw } from "vue";
const obj = reactive<any>({ name: "Lee", data: { msg: "Hello Lee!!!" } });
const data1 = toRaw(obj);
const data2 = reactive<any>({name: "Lee", data: markRaw({ msg: "Hello Lee!!!" })});
script>
路由
$ npm install vue-router@4
<template>
<router-link to="/">Homerouter-link>
<router-link to="/news">Newsrouter-link>
<router-link to="/about">Aboutrouter-link>
<router-view>router-view>
template>
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{ path: '/', component: () => import('../components/Home.vue') },
{ path: '/news', component: () => import('../components/News.vue') },
{ path: '/about', component: () => import('../components/About.vue') },
]
const router = createRouter({
routes,
history: createWebHashHistory()
})
router.beforeEach((to, from, next) => {
console.log(to, from);
next();
})
export default router;
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
vuex
$ npm install vuex@next --save
<template>
<button @click="add">Count : {{ $store.state.count }}button>
template>
<script setup lang="ts">
import store from "./store";
const add = () => {
store.commit("increment");
};
script>
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state: any) {
state.count++
}
}
})
export default store;
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App)
.use(router)
.use(store)
.mount('#app')
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)