Vue3
项目构建
- 安装
$ 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"/>
<!--错误写法-->
<!--<Child :item="item.name" :index="index" :key="item.id"/>-->
</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>ProsperLee</span></p></Child>
<!--子组件-->
<template>
<slot></slot>
</template>
- 具名插槽
<!--父组件-->
<template v-slot:default><p>具名插槽 : Hello Msg!!!</p></template>
<template #default><p>具名插槽 : Hello Msg!!!</p></template>
<!--子组件(一个不带 name 的 <slot> 出口会带有隐含的名字“default”)-->
<template>
<slot></slot>
</template>
<!--父组件-->
<template v-slot:slotName><p>具名插槽 : Hello Msg!!!</p></template>
<template #slotName><p>具名插槽 : Hello Msg!!!</p></template>
<!--子组件(一个不带 name 的 <slot> 出口会带有隐含的名字“default”)-->
<template>
<slot name="slotName"></slot>
</template>
<!--父组件-->
<template>
<button @click="changeSlotName">切换插槽</button>
<Child>
<template #[dynamicSlotName]>Lee</template>
</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">ProsperLee</div>
<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>
<!--子组件1-->
<template>
<label>
<span>【ref】</span>
<button @click="getRefData">ref</button>
<br>
<input type="text" v-model="dataName">
<span>{{ dataName }}</span>
</label>
<hr>
<label>
<span>【toRef】</span>
<button @click="getToRefData">toRef</button>
<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>
<!--子组件2-->
<template>
<label>
<span>【toRefs】</span>
<button @click="getToRefsData">toRefs</button>
<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">emit</button>
</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
修改DOM所属父标签的位置
- teleport
<div style="position:absolute;top: 100px;left: 100px;">
<span>Prosper</span>
<teleport to="body">
<span>Lee</span>
</teleport>
</div>
- 在同一目标上使用多个 teleport
<div id="modals"></div>
<teleport to="#modals"><span>Prosper</span></teleport>
<teleport to=".modals"><span>Lee</span></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="/">Home</router-link>
<router-link to="/news">News</router-link>
<router-link to="/about">About</router-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')