Vue(十七):Vue3+ts 入门

项目构建

  • 安装

    $ 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>
        &nbsp;
        <button @click="$emit('remove')">删除行</button>
        &nbsp;
        <button @click="removeData">删除行</button>
        &nbsp;
        <button @click="$emit('add', `Row-${Date.now()}`)">新增行</button>
        &nbsp;
        <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">&nbsp;
        <span>{
   
   { dataName }}</span>
    </label>
    <hr>
    <label>
        <span>【toRef】</span>
        <button @click="getToRefData">toRef</button>
        <br>
        <input type="text" v-model="dataAge">&nbsp;
        <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">&nbsp;
        <input type="text" v-model="dataAge">&nbsp;
        <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')

猜你喜欢

转载自blog.csdn.net/weixin_43526371/article/details/124711498