vue3 从入门到入坑

Vue3

1.创建一个vue3项目

1.1 cli创建

cli创建要求vue版本高于4.5.0,我们可以在cmd中通过vue -V查看

然后按照脚手架的方式创建:vue create -name

进入之后选择vue3脚手架即可

1.2 vite创建

vite是新一代的前端构建工具,由vue团队开发,为了挑战webpack的地位

vite的速度更快更轻量级,使用vite构建工程的方法如下:

vue3 从入门到入坑_第1张图片

启动不再是npm run serve而是npm run dev

我们会发现启动快了很多,但是vite是等你进入网址再进行动态加载页面

2.vue3结构

vue3中的main.js中的代码和vue2不同,它使用一个轻量级的app管理组件,提供了挂载和卸载功能

//引入调用vue的工厂函数 createApp
import { createApp } from 'vue'
import App from './App.vue'

// 创建一个app对象,类似于vue2中的vm,但是app更轻
const app = createApp(App)

// 挂载
app.mount('#app')

// 卸载
app.unmount('#app')

在组件中:


tips:我们可以使用#region和#endRegion括住注释的两端,实现折叠功能

3.composition API

3.1 set up

set up是一个配置项,它的值是一个函数,里面存放了数据,方法等(感觉有点像模块化的vuex)

  setup() {
    let name = "tony";
    let age = 18

    function sayHello() {
      alert(`I am ${name},I am ${age} years old`)
    }

    // 在setup中数据需要return出去
    return{
      name,
      age,
      sayHello
    }
  }

return出来的数据可以在外部调用,也可以return一个渲染函数render

而且在模板中引用时需要使用this.xxx而不是xxx.value

  

我是App组件

person:{{this.name}} {{this.age}}

注意set up 不要和vue2中的data,method混用,会有优先级的问题

set up也不能是一个async函数,被async修饰后我们得到的就是一个promise包裹的对象了,所以被禁止了。

但是后期我们学习了异步组件之后,异步组件中的setup可以是一个async函数

3.2 ref

ref在vue2中是一个属性,而在vue3中是一个函数,用于实现数据响应式

先引入ref

import { ref } from "vue";

为了实现ref,我们在定义数据时需要

    let name = ref("tony")
    let age = ref(18)

这样ref将我们的数据转换成了一个对象

RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'tony', _value: 'tony'}
dep: Set(1) {ReactiveEffect}
__v_isRef: true
__v_isShallow: false
_rawValue:" 李四"
_value: "李四"
value: "李四"

我们使用value值就可以调用setter,实现响应式

// 设置响应式数据
    function changeHello(){
      console.log(name);
      name.value = '李四',
      age.value = 48 
    }

我们还可以使用ref操作对象

    let job = ref({
      type:'前端',
      salary:'30k'
    })

但是修改对象中的数据使用的是 job.value.salary='60k'

因为对象的封装不再是refimpl,而是proxy

Proxy
[[Handler]]: Object
[[Target]]: Object
salary: "60k"
type: "前端"
[[Prototype]: Object
[[IsRevoked]]: false

3.3 reactive

reactive适用于管理对象和数组类型的数据,但不能用于管理基本数据类型

也是将对象封装成一个proxy

    let job = reactive({
      type:'前端',
      salary:'30k'
    })

更改数据的时候只需要

      job.salary='60k'
      console.log(job)

我们的ref管理的基本类型数据也可以通过封装成对象中的属性让reactive进行管理

在vue2中我们的新增和删除要体现在页面上需要使用

Vue.delete()和Vue.set()或者this. s e t ( ) 和 t h i s . set()和this. set()this.delete()

vue3中的响应式,只要是reactive中管理的数据,我们就可以直接使用delete删除

vue3是通过proxy调用set和get进行数据的更改,deleteProperty实现删除

vue3 从入门到入坑_第2张图片

在vue2中我们使用props实现父向子传值,子组件接收需要使用props

但是如果不使用props,我们也能在vc身上的$attr中看到

在vue3中setup的执行时间比beforeCreate早

而且setup中的this是undefined

setup中的参数第一个是 props第二个是context

3.3.1使用自定义事件

vue3中的自定义事件与vue2有区别

首先向子组件传值

  

然后在子组件接收

注意与vue2不同vue3中需要使用emits属性获取传入的自定义事件,然后使用context参数(必须是把前面的props写上,因为context是第二个参数)触发传参

    emits: ['hello'],
    setup(prop,context) {
        function hello(){
            context.emit('hello',666)
        }

        return{
            hello
        }
    }
}

4.计算属性与监视

4.1 computed

vue3中也可以vue2的计算属性,但是不推荐

vue3中的计算属性需要引入然后写入在setup中

我们写一个案例

import { reactive, computed } from 'vue';
export default {
    name: 'setUp',
    setup() {
        let person = reactive({
            firstName : 'tony',
            lastName : 'stake',
        })

        // //书写计算属性使用computed-简写形式
        // person.fullName = computed(()=>{
        //     return person.firstName+'-'+person.lastName
        // })

        // getter 和setter形式
        person.fullName = computed({
            get(){
                return person.firstName+'-'+person.lastName
            },
            set(newVal){
                const nameArr = newVal.split('-')
                person.firstName=nameArr[0]
                person.lastName=nameArr[1]
            }
        })
        return{
            person
        }
    }
}

和vue2一样如果需要修改计算得到的值要把计算属性写成扩写形式,写出set方法

4.2 监视属性

ref:

对ref使用监视属性监视简单数据

import { ref, watch } from 'vue';
export default {
    name: 'setUp',
    setup() {
        let sum = ref(0)
        let msg = ref('hello')
        //watch-情况一
        // watch(sum, (newVal,oldVal)=>{
        //     console.log("sum改变了"+oldVal+newVal);
        // })
        
        //watch-情况二-监视多个
        watch([sum, msg], (newVal,oldVal)=>{
            console.log(oldVal,newVal);
        })

        return{
            sum,
            msg
        }
    }
}

注意情况二的数据,得到的oldVal和newVal是数组

[1, 'hello'] => [0, 'hello']

当需要打开深度监视和立即监视的时候在后面写配置项

        watch(sum, (oldVal,newVal)=>{
            console.log("sum改变了"+oldVal+newVal);
        },{immediate:true, deep:true})

reactive:

reactive操作对象数据

个人信息:

我们使用reactive包裹一个对象

        let person = reactive({
            name:'on',
            age:'18'
        })
        //watch-情况三-监视对象
        watch(person,(oldVal,newVal)=>{
            console.log(oldVal,newVal);
        })

但是我们得到的数据是:

Proxy {name: 'jkl', age: '18'} => Proxy {name: 'jkl', age: '18'}

我们发现没有oldValue了,只有newValue,而且自动开启了深度监视

如果我们需要使用oldValue就只有使用ref

还有几种情况:

vue3 从入门到入坑_第3张图片

注意一个特殊情况:这里的job是

            job:{
                j1:{
                    salary:"1K"
                }
            }

如果要监视一种属性,要监听里面的数据,要用到深度监视配置deep

value:

我们使用ref包裹基本数据的时候,很多人会习惯了.value,但是在watch中,我们不能使用.value,因为value取到的是值,但是我们需要监视的是一个RefImpl的结构

但是如果我们包裹的是对象数据,情况就不一样了,我们就需要使用.value,因为对象中的.value是一个proxy,它才是真正的监听数据,一般的person只是一个内存地址

         watch(person.value,(oldVal,newVal)=>{
            console.log(oldVal,newVal);
         })

还有一种不使用.value的方法,开启深度监视

         watch(person,(oldVal,newVal)=>{
            console.log(oldVal,newVal);
         },{deep: true})

watchEffect:

vue3中的新属性

官方文档的定义:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

        watchEffect(()=>{
            const x = person.name;
            console.log("name改变了");
        })

我们发现我们更改person.name的时候,就会触发回调

只要我们在回调中用到的数据改变,就会触发回调

其实watchEffect有点类似computed,回调中依赖的数据变化,就会执行回调

5.生命周期

生命周期大体上和vue2差不多,更新了两个钩子

取消了beforedestroy和destroy

更新了:

beforeUnmount

在一个组件实例被卸载之前调用。

  • 类型

    interface ComponentOptions {
      beforeUnmount?(this: ComponentPublicInstance): void
    }
    
  • 详细信息

    当这个钩子被调用时,组件实例依然还保有全部的功能。

    这个钩子在服务端渲染时不会被调用。

unmounted

在一个组件实例被卸载之后调用。

  • 类型

    interface ComponentOptions {
      unmounted?(this: ComponentPublicInstance): void
    }
    
  • 详细信息

    一个组件在以下情况下被视为已卸载:

    • 其所有子组件都已经被卸载。
    • 所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。

    可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。

    这个钩子在服务端渲染时不会被调用。

一般形式和我们vue2中很像,所以我们这里介绍

组合式API:

vue3 从入门到入坑_第4张图片

导入之后就可以在setUp中写

导入:import { reactive, ref, onBeforeMount } from 'vue';

使用:(在setup中)

    setup() {
        let sum = ref(0)
        let msg = ref('hello')
        let person = reactive({
            name:'on',
            age:'18',
            job:{
                j1:{
                    salary:"1K"
                }
            }
        })
        
        onBeforeMount(()=>{
            console.log("===onBeforeMount===");
        })

        return{
            sum,
            msg,
            person,
        }
    }

但是不提倡组合式api和函数式混用,导致项目结构不清晰

6.hook

hook是一个函数,在setup中对组合式api进行封装

比如我们需要一个获取鼠标坐标的函数,这种函数如果经常用到,我们就需要将它写成模块

这个模块就是hook

新建一个src/hook文件夹:写入文件usePoint.js

引入,暴露,函数,返回值都要有

import { reactive, onMounted, onBeforeUnmount } from 'vue';
export default function () {
    let points = reactive({
        x: 0,
        y: 0,
    })

    //自定义函数
    function savePoint(event) {
        points.x = event.pageX;
        points.y = event.pageY;
        console.log(points.x+" "+points.y);
    }

    //生命周期钩子
    onMounted(()=>{
        window.addEventListener('click', savePoint)
    })

    //销毁钩子
    onBeforeUnmount(()=>{
        window.removeEventListener('click', savePoint)
    })

    //因为要作为函数调用,最后要返回出去
    return points
}

然后这个模块可以在

组件中被引入,然后通过函数调用






7.toRef

当我们需要单独将一个reactive对象中的一个属性比如:name拿出来使用的时候

如果使用

return {
	name: person.name
}

就会出现丢失了响应式的问题,所以我们需要使用到toRef

有需要和之前的person产生联系使用

相当于一个浅拷贝,有引用关系和指针指向问题

vue3 从入门到入坑_第5张图片

或者可以使用toRefs对一个对象的数据进行toRef操作,如果是有深度的数据就需要使用的时候加上层级关系

但是return的时候需要使用...toRefs(xxx),展开来进行返回

import {toRefs} from 'vue';
export default {
  name:'App'
  setup(){
    let obj = {name : 'alice', age : 12};
    let newObj= toRefs(obj);	//相当于将obj中所有的元素都执行了一遍toRef
    function change(){
      newObj.name.value = 'Tom';
      newObj.age.value = 18;
      console.log(obj,newObj)
    }
    return {newObj,change}
  }
}

5. 其他组合式api

1. shallowRef and shallowReactive(性能优化)

shallow也就是浅层的意思,顾名思义,shallowReactive只处理对象浅层的数据的响应式,shallowRef只处理基本类型的数据响应式,不处理对象类型的数据的响应式

  
此时x的值为:{{x.y}}
此时p1的值为:{{sr.p1}}
此时p2.p3的值为:{{sr.p2.p3}}
import {reactive, shallowReactive, shallowRef, toRefs} from 'vue'; export default { name: 'setUp', setup() { let x = shallowRef({ y : 1 }) let sr = shallowReactive({ p1:1, p2:{ p3: 1 } }) return{ x, sr } } }

我们得到的结果是x.y 和p2.p3的值都不会改变 ,但是 p1的值会改变,说明了shallow的浅层响应式的作用

2. readOnly

使用readOnly包裹一个数据,使其变成只读的数据,我们将person改为只读数据

    let person = reactive({
      name:'on',
      age:'18',
      job:{
        j1:{
          salary:"1K"
        }
      }
    })

    person = readonly(person)

此时再更改person中的数据的时候,就会失败而且控制台出现警告

Set operation on key "name" failed: target is readonly.

shallowReadOnly

带有shallow的都是浅层的意思,那么shallowReadOnly就代表只把浅层的数据改为readOnly的形式

    let person = reactive({
      name:'on',
      age:'18',
      job:{
        j1:{
          salary:"1K"
        }
      }
    })

    person = shallowReadonly(person)

这样person内层的数据还是可以改变,但是外层的数据已经不能改变了。

3. toRaw and markRaw

raw类方法实现的是将响应式数据变为普通数据,也就是取消响应式效果(proxy代理)

toRaw: 简单的取消响应式

    person = toRaw(person)

markRaw: 将一个数据永远不设置为响应式

    //给person添加car属性,但car属性不会更改
    function addCar(){
      let car = {brand: '奔驰', price: '40w'}
      //markRaw使得数据永远不会变为响应式
      person.car = markRaw(car)
    }

4. customRef

customRef实现自定义Ref,什么是自定义ref呢?如下代码

其中的myRef就是我们自定义的ref,需要由我们来维护

而维护这个ref就需要使用customRef自己来写响应式的get和set

我们在set中设置了延迟显示,晚一秒再触发trigger模型解析

	//自定义ref
    function myRef(value){
      let timer
    //  使用customRef实现自定义ref
      return customRef((track, trigger)=>{
        //需要return一个对象
        return{
          get(){

            //将myRef中的value返回出去实现get
            //track实现get的多次调用
            track()
            return value
          },
          set(newValue){
            //set可以获取一个newValue参数
            clearTimeout(timer)
            timer = setTimeout(()=>{
              value = newValue
              //trigger实现模板的重新解析
              trigger()
            },1000)
          }
        }
      })
    }

    let hello = myRef("hello");
    return{
        hello
    }

可是问题是:如果更改速度过快或者过多,就会触发很多定时器,产生抖动

所以我们需要在创建一个定时器前清除上一个定时器,保证只有一个定时器

或者我们使用setTimeInterval,循环触发trigger

5. provide inject

这两个api用于祖孙组件间通信

祖组件:使用provide将需要传输的数据传递出去

  setup(){
    let car = reactive({brand:"BMW", price:"40w"})
    provide("car",car)
    return{
      car
    }
  },

孙组件:使用inject获取传出的数据

  setup(){
    let x = inject("car")
    console.log(x)
  }

6.判断是否为响应式

使用isRef,isReactive,isReadOnly,isProxy

分别代表是否是ref等,和是否是Proxy代理的对象

7.vue3新组件

1.fragment

一个vue3中的标签,不参与渲染,比如vue2中vue标签需要一个div包裹整个template,但是vue3中不需要而是一个隐藏的fragment

2.Teleport

也是新标签,用于将组件中的dom节点传送到指定的地方

在我们的文件结构中,父元素是App,子元素是child,孙元素是sun,孙元素拥有一个组件dialog-实现弹窗对话的效果

dialog组件:






但是问题就在于,这时对话框默认是在sun组件里的,如果使用定位调节,也会有很多的repaint的问题

所以我们使用teleport实现传送:

      
        

对话1

对话2

这样我们再写样式,这个dom节点就是依靠body来进行定位

3.Suspence

也是一个内置的标签组件,用于解决异步组件中由于加载快慢引起的抖动问题

首先引入异步组件:同步组件都是一起显示,等到所有的子组件都加在完毕之后再进行加载

​ 而异步组件不同,异步组件是根据层级顺序进行加载,优先加载父组件,加载一个显示一个

而且异步组件中可以使用异步promise函数作为setup的返回对象,setup也可以是async函数

//静态引入-异步组件生成函数
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/child'))
export default {
  name: "App",
  setup(){
    return{}
  },
  components: { Child }
}

为解决异步组件的抖动问题:我们需要Suspence,Suspence其实类似于插槽,需要使用v-slot

  

我是App组件

…some tips

1.vue3全局挂载

在vue2中使用全局挂载是直接调用Vue的原型(prototype)

但是vue3中,我们不再创建vue实例,所以无法在原型上做更改

一些全局的配置都做了替换:

vue3 从入门到入坑_第6张图片

我们引进一个全新的函数 config.globalProperties

const app = createApp(App)
app.config.globalProperties.$axios = axios
app.mount('#app')

这样也可以实现全局挂载的效果

2.跨域配置

vue3的跨域问题配置也是同vue2一样在vue.config.js中(没有就创建一个)

其中target中相当于baseurl是跳转的一个默认地址

changOrigin: true,实现我们允许跨域的效果

//vue.config.js

module.exports = {
    devServer: {
      open: true, //是否自动弹出浏览器页面
      // host: "localhost",
      // port: '8080',
      proxy: {  //配置跨域,可以配置多个跨域
      '/api': {
        target: 'http://127.0.0.1/',  //这里后台的地址模拟的;应该填写你们真实的后台接口
        changOrigin: true,  //允许跨域
        pathRewrite: {
          /* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/api/core/getData/userInfo 时
            实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
           */
          '^/api': '' 
        }
      },
    }
  },
  }

3.获取全局实例

因为vue3中setup不能使用this

所以为了获取我们全局挂载上的方法,我们需要使用getCurrentInstance

   const {proxy} = getCurrentInstance()

   const $axios = proxy.$axios

4.移除keyCode作为v-on的修饰符

究竟怎样的结局配得上这一路颠沛流离

你可能感兴趣的:(vue.js,javascript,前端)