vue3创建项目+基本使用

77 阅读5分钟

环境搭建:

  • 1.node版本大于16.0(查看版本node -v)

    自己去下载安装,官网

    不会安装的可以看一下安装教程:传送门

  • 2.安装并执行create-vue

 npm init vue\@latest

vue3 文件介绍和变化

image.png

组合式API

组合式API写法,<script>标签上加上关键字setup

setup选项在beforeCreate之前自动执行,其特点是 定义数据+函数+以对象形式return,setup中this指向undefined

<template>
    <div>{{msg}}</div>
</template>

<script setup>
    //在script标签上加setup为语法糖,setup标签语法糖为我们做的事如下注释
    // export default {
    //     name: "setupTest",
    //     //setup再beforeCreate之前调用
    //     //vue3中this指向undefined
    //     setup(){
    //         const msg = 'msg';
    //         const logMsg = ()=>{
    //             console.log(msg);
    //         };
    //         //必须return出才能使用
    //         return {
    //             msg,
    //             logMsg,
    //         }
    //     },
    //     beforeCreate(){
    //
    //     }
    // }
    const msg = 'msg';
    const logMsg = () => {
        console.log(msg);
    };
</script>

<style scoped>

</style>

响应式数据

reactive 针对对象类型数据

使用方法

1.从vue中导入reactive

2.执行reactive函数,并传入一个对象类型参数,用一个变量接收它

<script setup>
    //导入
    import {reactive} from 'vue';

    //执行函数 传入一个变量参数 变量接受
    const status = reactive({
        count:0
    });
    const addCount = ()=>{
        status.count++;
    }
</script>

ref 支持所有数据类型

使用方法

1.从vue包中导入ref

2.在<script setup>中执行ref函数并传入初始值,并用变量接收ref函数的返回值

在脚本区更改ref产生的响应式对象数据,必须通过.value属性

<script setup>
    //导入ref
    import {ref} from 'vue';
    //执行ref函数
    const count = ref(0);
    const addCount = ()=>{
        //在脚本区更改ref产生的响应式对象数据,必须通过.value属性
        count.value++
    }
</script>

reactive vs ref

  • 1.reactive不能处理简单类型的数据
  • 2.ref参数类型支持更好但是必须通过.value访问修改
  • 3.ref函数的内部实现依赖于reactive函数

在实际工作中推荐使用ref函数,更加灵活

computed计算属性

计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法

使用方法:

1.导入computed函数

2.执行函数在回调参数中return基于响应式数据做计算的值,用变量接收

<template>
    <div>原始数据:{{arr}}</div>
    <div>计算属性数据:{{state}}</div>
    <button @click="pushValue">push 9 </button>
</template>

<script setup>
    //导入
    import {computed,ref} from 'vue';
    //响应式数组
    const arr = ref([1,2,3,4,5,6,7,8]);
    //执行computed函数
    const state = computed(()=>{
        return arr.value.filter((item)=>{
            if(item>2) return true;
        })
    });
    //验证
    const pushValue = () =>{
        arr.value.push(9);
    }
</script>

计算属性中应只有计算,不应该有“副作用(如:异步请求、修改dom等)”,且避免直接修改计算属性的值,计算属性应该是只读的

watch函数

使用方法:

1.导入watch函数

2.执行watch函数传入要侦听的响应式数据(ref对象)和回调函数

两个属性immediate和deep介绍

  • immediate : 在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调,
  • deep : 通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项, deep开启会有性能损耗,在工作中,绝大多数情况下,不会开启,尽量精准监听

监听单个数据源

<script setup>
    import {watch,ref} from 'vue';
    const state = ref(0);
    const addState = () =>{
        state.value ++ ;
    };
    //代用watch 单个数据源
    watch(state,(newValue,oldValue)=>{
        console.log('state从' + oldValue + '变为' + newValue);
    },{
        immediate:true,//在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调,
        //deep:true,
    });
</script>

监听多个数据源,数据源与新老数据都以数组形式显示

<script setup>
    import {watch,ref} from 'vue';
    const name = ref(123);
    const count = ref(0);
    //使用watch 多个数据源
    watch([name,count],([newName,newCount],[oldName,oldCount])=>{
        console.log('name或count变化了')
    })
</script>

精准监听,多应用于对象,第一个参数为要监听的属性,

<script setup>
    //导入watch、
    import {watch,ref} from 'vue';
    const person = ref({
        name:'lilei',
        age:18,
    });
    const chageAge = ()=>{
        person.value.age ++;
    };
    //精准监听某个具体属性
    watch(
        ()=>person.value.age,//坑:不是对象,是对象属性,注意写法,是箭头函数!!!!!精准监听某个具体属性
        (newValue,oldValue)=>{
            console.log('age +1 了');
        },
        {
            immediate:true,
            // 通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项,
            //deep开启会有性能损耗,绝大多数情况下,不会开启,尽量精准监听
            //deep:true, 
        }
    )
</script>

生命周期

image.png

使用方法:

  • 1导入生命周期函数
  • 2.执行生命周期函数 传入回调
  • 3.生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行

这里以onMounted为例:

<script setup>
    import {onMounted} from 'vue';
    onMounted(()=>{
        //doSomething
        console.log('onMounted1');
    });
    onMounted(()=>{
        //doSomething
        console.log('onMounted2');
    })
</script>

通信

父传子

image.png

与vue2不同的地方在于子组件需要通过defineProps ’编译器宏函数‘接收父组件传递的数据

//语法:
defineProps({
    //属性名:属性类型
})

parent template

<template>
<!--    给子组件绑定parentMsg-->
    <children :count="count" parentMsg="这是parent传给children的值"></children>
</template>

<script setup>
    //引入子组件,可以直接使用
    import children from "./children";
    import {ref} from 'vue';
    const count = ref(0);
</script>

children template

<template>
    <div>{{parentMsg}}</div>
    <div>count:{{count}}</div>
</template>

<script setup>
    //通过defineProps ’编译器宏‘接收父组件传递的数据
    const prop = defineProps({
        parentMsg:String,
        count:Number,//响应式数据
    });
</script>

子传父

image.png

与vue2不同在于,在子组件中需要通过defineEmits编译器宏函数生成emit

//语法:
defineEmits(
    ['事件名数组']
)

parent template

<template>
<!--    绑定自定义事件getChildrenMsg-->
    <children @getChildrenMsg="getChildrenMsg"></children>
</template>

<script setup>
    //引入子组件,可以直接使用
    import children from "./children";
    import {ref} from 'vue';
    const count = ref(0);
    const getChildrenMsg = (childrenMsg)=>{
        console.log(childrenMsg);
    }
</script>

children template

<script setup>
    //通过defineEmits编译器宏函数生成emit
    const emit = defineEmits(['getChildrenMsg']);

    const sendMsg = ()=>{
        //触发自定义事件 并传递参数
        emit('getChildrenMsg','我是子组件传给父组件的msg');
    }
</script>

模板引用 ref

模板引用概念:通过ref标识获取真实的dom对象或者组件实例对象

使用方法:

    1. 调用ref函数生成一个ref对象
    1. 通过ref标识绑定ref对象到标签

默认情况下,组件实例在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问

//语法
 defineExpose(
     {//属性或方法}
 )

refText template

<template>
<!--    2. 通过ref标识绑定ref对象-->
    <div ref="refObj">我是div标签</div>
    <children ref="childRef"></children>
</template>

<script setup>
    import children from "./children";
    import {ref,onMounted} from 'ref';
    // 1.调用ref函数得到ref对象 用null占位置
    const refObj = ref(null);//标签实例对象
    const childRef = ref(null);//组件实例对象

    //验证:组件挂载完毕后才可获取
    onMounted(()=>{
        console.log(refObj.value);
        console.log(childRef.value)
    })

</script>

children template

<script setup>
    import {ref} from 'vue';
   const name = ref('children');
   const setName = ()=>{
       name.value = 'lilei';
   };
   //子组件通过defineExpose公开可访问是属性和方法
    defineExpose({
        name,
        setName
    })
</script>

跨层组件通信——provide和inject

作用:顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

第一层:通过provide提供数据或修改数据方法

<template>
    <div>顶层</div>
    <button @click="setCount">{{count}}</button>
    <br>
    <partition-tow></partition-tow>
</template>

<script setup>
    import partitionTow from "./partitionTow.vue";
    import {provide,ref} from 'vue';
    const count = ref(0);
    //1.顶层提供数据
    provide('data-key','partitionFirstMsg');
    provide('data-count',count);//坑:传的是count不是count.value

    const setCount = ()=>{
        count.value++;
    };
    provide('setCount',setCount);//坑2:如果使用var定义参数,注意provide放置位置,推荐使用let和const定义参数

第n层(不用管是第几层哈,都是同样的用法):使用inject接收使用provide传的数据和方法

<template>
    <div>底层:{{msg}}</div>
    <div>底层响应式数据:{{count}}</div>
    <button @click="setCount">three +count</button>
</template>

<script setup>
    import {inject} from 'vue';
    //2.接收数据
    const msg = inject('data-key');
    const count = inject('data-count');
    //接收方法,更改数据 时需要使用provide传递相应修改函数
    //谁的数据谁修改
    const setCount = inject('setCount');
</script>

自定义指令

钩子函数

一个指令的定义对象可以提供几种钩子函数 (都是可选的):

const vFocus = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
     
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。

    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

局部指令

在 <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。如下面的例子中,vFocus 即可以在模板中以 v-focus 的形式使用。

<script setup> 
    // 在模板中启用 v-focus 
    const vFocus = { 
        mounted: (el) => el.focus() 
    } 
</script> 
<template> 
    <input v-focus /> 
</template>

全局指令

在main.ts中定义指令并注册指令

app.directive('img-lazy',{
   mounted(el,binding){
       //vueuse 实现
       const {stop } = useIntersectionObserver(el,([{ isIntersecting }]) =>{
           if(isIntersecting) {
               el.src = binding.value;
               stop();
           }
       })
   }
})

注意再实际开发中不推荐在main.ts中定义指令,main.ts中应该只是初始化 可以在src中创建directives目录,在目录中添加指令,最后在main.ts中引入并注册 如下面代码和图中所示:

image.png

index.js代码:

import {useIntersectionObserver} from "@vueuse/core";

export const imgLazy = {
    //坑:要在install中写指令哈
    install(app){
        //懒加载指令容器
        app.directive('img-lazy',{
            mounted(el,binding){
                //vueuse 实现
                const {stop } = useIntersectionObserver(el,([{ isIntersecting }]) =>{
                    if(isIntersecting) {
                        el.src = binding.value;
                        stop();
                    }
                })
            }
        })
    }
}

main.ts 代码:

//引入懒加载指令插件并且注册
import {imgLazy} from '@/directives/index.js'
//注册指令
app.use(imgLazy)

注册全局组件

可以在main.ts中使用app.component('组件名',组件配置对象)直接注册全局组件,这里不过多阐述,工作中常用的是下面这种形式进行全局注册。

  • 在components目录中添加index.js,如下图所示:

image.png

index.js代码:

//把components中常用组件进行全局话注册
//引入组件
//坑:要把组件名写完整,包括后缀名!!!!!!
import refTest from '@/components/vueBase/refTest.vue'
export const componentPlugin = {
    install(app){
        //app.component('组件名','组件配置对象')
        app.component('refTest',refTest);
    }
}
  • 在main.ts中引入并注册:
//引入全局组件
import {componentPlugin} from '@/components/index.js'
//以App作为一个参数,创建一个应用实例对象
const app = createApp(App);

//注册全局组件
app.use(componentPlugin);
//挂载到id为app的节点上
app.mount('#app');