初识vue3

vue3与vue2区别(对vue3的了解)

(1) 性能更高     ① 相对于vue2而言 底层响应原理转换为proxy  ②  虚拟 DOM 的算法进行了优化

(2) 体积更小    删除了一些不常用的api   ①   过滤器 ② EventBus ③ 代码支持按需导入 ④配合Webpack打包工具支持 TreeShaking

(3) 对TS的支持更好    源码就是用TS重写的

(4) Composition API     ①   能把相同功能的数据和业务逻辑组合到一起,代码更容易复用和维护。

② 更适合大型项目

(5)  新特性  Fragment  Teleport  Suspense

Vite 创建项目 

对比 Webpack 和 Vite 如下。

  • Webpack:会将所有模块提前编译、打包,不管这个模块是否被用到,随着项目越来越大,打包启动速度自然越来越慢。

  • Vite:瞬间开启一个服务,并不会先编译所有文件,当浏览器用到某个文件时,Vite 服务会收到请求然后编译后响应到客户端。

创建项目命令

npm create vite
# or
yarn create vite

 vite快捷创建

# 创建普通 Vue 项目
yarn create vite vite-demo --template vue
# 创建基于 TS 模板的 Vue 项目
yarn create vite vite-demo-ts --template vue-ts

编写 Vue 应用

 在main.js中

// 1. 导入 createApp 函数,不再是曾经的 Vue 了
// 2. 编写一个根组件 App.vue,导入进来
// 3. 基于根组件创建应用实例,类似 Vue2 的 vm,但比 vm 更轻量
// 4. 挂载到 index.html 的 #app 容器
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

// Vue2: new Vue()、new VueRouter()、new Vuex.Store()
// Vue3: createApp()、createRouter()、createStore()

 在app.vue中

<template>
    <div>
       我是根组件
    </div>
</template>

<script>
export default {
    setup () {
        

        return {}
    }
}
</script>

<style lang="scss" scoped>

</style>

vue2和vue3的优缺点

Vue2

  • 优点:易于学习和使用,写代码的位置已经约定好。

  • 缺点:对于大型项目,不利于代码的复用、不利于管理和维护。  

 vue3

  • 优点:可以把同一功能的数据业务逻辑组织到一起,方便复用和维护。

  • 缺点:需要有良好的代码组织和拆分能力,相对没有 Vue2 容易上手。

setup 入口函数

 setup是Vue3中新增的组件配置项,作为组合API的入口函数

执行机制 在实例创建前调用 甚至早于beforecreate 

所以在setup中无法获取到data里面的数据,和methods里面的方法  且vue3中this指向undefined 

虽然 Vue2 中的 data 和 methods 配置项虽然在 Vue3 中也能使用,但不建议了,建议数据和方法都写在 setup 函数中,并通过 return 进行返回可在模版中直接使用(一般情况下 setup 不能为异步函数)

面试题  setup 内部一定返回一个对象吗?不一定,其实也可以返回一个渲染函数

<template>
   <h1>name:{{name}}</h1>
</template>
<script>
    export default {
        beforeCreate(){
            console.log('#2 beforecreate')
        },
        setup(){
            // 执行时机 比beforecreate 早
            //注意点:走到这的时候实例还没创建,意味着不能访问data数据和methods方法
            console.log('#1 setup')
            console.log(this) // this指向为undefined
            const name ='12121'
            // 使用数据 :定义的数据如果想要在模板中使用,必须通过return 出去
            return {
                //setup中的 return 返回必须是一个对象吗还可以是渲染函数
                name,
            }
        }
    }
</script>

小结

  • setup 的执行时机是什么?

  • setup 中的 this 指向是什么?

  • 想在模板中使用 setup 中定义的数据,该怎么做?

reactive 

reactive可以将复杂数据类型包装成响应式对象

案例

<template>
<div>
    <input type="text" v-model="user.id"/>
    <input type="text" v-model="user.name"/>
    <button @click="add">提交</button>
    <ul>
        <li v-for="(item,index) in data.arr" :key="item.id" @click="del(index)">{{item.name}}</li>
    </ul>
    </div>
</template>
<script>
   import {reactive} from 'vue'
   const useDelFn=()=>{
    const data = reactive({
          arr: [
                    {
                        id: 0,
                        name: 'ifer',
                    },
                    {
                        id: 1,
                        name: 'elser',
                    },
                    {
                        id: 2,
                        name: 'xxx',
                    },
                ],
        })
        const del=(index)=>{
            data.arr.splice(index,1)
        }
        return {data,del}
       }
       const useAddFn=(data)=>{
         // 1. 收集数据(通过 v-model)
 const user=reactive({
            id:'',
            name:''
        })
        // 2. 把收集到的数据怼到 data.arr 里面
        const add =()=>{
            data.arr.push({
               id:user.id,
               name:user.name,
            })
             // 3.清空表单
            user.id=''
            user.name=''
        }
        return {user,add}
       }
export default {
    setup() {
       const {data,del}=useDelFn()
       // 添加数据,不要忘记把 data 传递到 useAddFn 里面供使用
       const {user,add}=useAddFn(data)
        return {data,del,user,add}
    },
}
</script>

抽离函数 拆分文件 

 新建文件夹 hooks 新建文件index.vue

export导出函数

在app.vue中导入函数

<template>
<div>
    <input type="text" v-model="user.id"/>
    <input type="text" v-model="user.name"/>
    <button @click="add">提交</button>
    <ul>
        <li v-for="(item,index) in data.arr" :key="item.id" @click="del(index)">{{item.name}}</li>
    </ul>
    </div>
</template>
<script>
   
   import {useDelFn,useAddFn} from './hooks/index'
export default {
    setup() {
       const {data,del}=useDelFn()
       const {user,add}=useAddFn(data)
        return {data,del,user,add}
    },
}
</script>

ref 

ref将简单数据类型和复杂数据类型包装成响应式对象

修改ref包裹的数据类型时必须加.value

可以通过isRef 来判断数据类型的包裹是否为ref

修改和操作ref包裹的数据时必须加 .value 但是在模板中不能加.value因为他自动添加有.value

ref简单包裹数据类型 

<template>
    <div>
        <p>count:{{ count }}</p>
        <button @click="add">点我加一</button>
        <!-- 在模板内不需要加.value -->
        <button @click="count++">加一</button>
    </div>
</template>

<script>
import { ref, isRef } from 'vue'
export default {
    setup() {
        const count = ref(0)
        // isRef判断某个数据是否是ref
        console.log(isRef(count));
        const add = () => {
            // 修改ref类型包裹的数据时必须要加.value
            count.value++
        }
        return { count, add }
    }
}
</script>

 ref包裹复杂数据类型

<template>
    <div>
        <div>name: {{ data && data.name }}</div>
        <!-- ?. ES2021 新增的操作符,可选链,表示只有 data 存在的情况下才去往后取 name,如果 data 不存在(null、undefined),就直接整体返回 undefined -->
        <div>name: {{ data?.name }}</div>
    </div>
</template>

<script>
import { ref } from 'vue'
// ref可以包裹复杂数据类型为响应式
export default {
    setup() {
        // const data = ref(null)
        const data = ref({
            name: '❤❤❤'
        })
        setTimeout(() => {
            data.value = {
                name: '❤❤'
            }
        }, 3000)
        setTimeout(() => {
            data.value = {
                name: '❤'
            }
        }, 5000)
        return { data }
    }
}
</script>

如何选择什么时候使用ref什么时候使用reactive

当你明确知道要包裹的数据是一个对象的时候,可以使用reactive,Vue3.2 之后,更推荐使用 ref,性能得到了很大的提升

toRef

toRef 函数的作用:转换响应式对象中某个属性为单独响应式数据,并且转换后的值和之前是关联的(ref 函数也可以转换,但值非关联)

toRef的使用 

<template>
    <div>
        <p>name{{ name }}</p>
        <p>age{{ age }}</p>
        
        <button @click="updateName">updateName</button>
    </div>
</template>

<script>
import { reactive, toRef, } from 'vue'
export default {
    setup() {
        // 需求在模板中渲染名字和年龄
        const obj = reactive({
            name: '张三',
            age: 10,
            address: '河南',
            sex: '男'
        })
        // #问题 1:模板中使用太麻烦了,每次都要 obj.,重复!
        // #问题 2:明明模板中只用到了 name 和 age,你却把整个 obj 都导出了,没必要(性能不好)
        // 把响应式对象 obj 中的 name 转成了响应式的 ref 数据
        const name = toRef(obj, 'name')
        const age = toRef(obj, 'age')
        // const name =ref(obj,'name')
        // const age =ref(obj,'age')
        // ref转换后的name 和原来的obj是非关联的,意味着对原来数据的修改视图不会更新
        const updateName = () => {
            // 不要忘记.value
            name.value = "李四"
            // toRef 转换后的 name 和原来的 obj 是关联的,意味着对原来数据的修改也会影响转换后的 name
            // obj.name = 'elser'
        }
        return { name, age, updateName }
    }
}
</script>

当需要渲染所有时

toRefs

<template>
  <div>
    <p>name: {{ name }}</p>
    <p>age: {{ age }}</p>
    <p>address: {{ address }}</p>
    <p>sex: {{ sex }}</p>
    <button @click="updateName">update name</button>
  </div>
</template>

<script>
import { reactive, toRef, toRefs } from 'vue'
// 需求:在模板中渲染 name、age、address、sex...。
export default {
  setup() {
    const obj = reactive({
      name: 'ifer',
      age: 10,
      address: '河南',
      sex: '男',
    })
    // 这儿写起来太麻烦了
    /* const name = toRef(obj, 'name')
    const age = toRef(obj, 'age')
    const address = toRef(obj, 'address')
    const sex = toRef(obj, 'sex') */
    // toRefs 可以把响应式对象中的所有属性变成单独的响应式 ref 对象
    const { name, age, address, sex } = toRefs(obj)

    const updateName = () => {
      name.value = 'elser'
    }
    return {name,age,address,sex,updateName}

  },
}
</script>

 简写方法//最佳实践

<template>
    <div class="container">
        <h2>{{ name }} {{ age }}</h2>
        <button @click="updateName">修改数据</button>
    </div>
</template>
<script>
    import { reactive, toRefs } from 'vue'
    export default {
        name: 'App',
        setup() {
            const obj = reactive({
                name: 'ifer',
                age: 10,
            })
            const updateName = () => {
                obj.name = 'xxx'
                obj.age = 18
            }
            return { ...toRefs(obj), updateName }
        },
    }
</script>

computed计算属性

 案例 拼接姓氏 和名字

获取数据是会主动调用get( )方法拿去结果

<template>
  <div>
    <p>firstName: {{ firstName }}</p>
    <p>lastName: {{ lastName }}</p>
    <p>fullName: {{ fullName }}</p>
  </div>
</template>

<script>
import { reactive, computed, toRefs } from 'vue'
export default {
  setup() {
    const person = reactive({
      firstName: '张',
      lastName: '小二',
    })
    /* person.fullName = computed(() => {
      // computed 基于依赖进行缓存,产生一个新结果
      // watch 监听数据的变化,根据变化的后的新值进行副作用相关的处理(发请求,操作 DOM,操作本地数据...)
      return person.firstName + ' ' + person.lastName
    }) */
    // computed 其实还可以接受一个对象
    // 和上面的等价
    person.fullName = computed({
      get() {
        // 获取数据的时候会主动调用 get 函数,拿到结果
        return person.firstName + ' ' + person.lastName
      },
    })
    return {
      ...toRefs(person),
    }
  },
}
</script>

computed的完整用法

 通过v-model双向绑定 和set方法修改姓氏和名字并实现 数据的更新视图更新

<template>
    <div>
<p>firstName{{firstName}}</p>
<p>lastName{{lastName}}</p>
<!-- <p>fullName{{fullName}}</p> -->
<p>
    <input v-model="fullName"/>
</p>
    </div>
</template>

<script>
import{reactive,computed, toRefs} from 'vue'
export default {
    setup () {
        const person =reactive({
firstName:'张',
lastName:'小二'
        })
person.fullName=computed({
    // 获取数据的时候会主动调用 get 函数,拿到结果
    get(){
        return person.firstName +' '+ person.lastName
    },
    set(newValue){
         // 设置数据的时候会主动调用 set 函数,newValue 就是修改后的新值
        const arr =newValue.split(' ')
        person.firstName=arr[0]
        person.lastName=arr[1]
    }
})
        return {
            // toRefs批量转化为响应式对象
            // ...toRefs (所有项)
            ...toRefs(person)
        }
    }
}
</script>

watch监听

1.watch监听reactive

监听reactive默认为深度监听 监听不能修改(配置无效)

监听的值是一个 reactive包裹,那么就符合下面特点

深度监听(配置无效)

 其实是监听的 是内部数据的变化

监听对象的时候 newValue 和 oldValue 是全等的。  

 注意1 监听reactive默认为深度监听 监听不能修改(配置无效) 监听对象的时候 newValue 和 oldValue 是全等的。

<template>
  <p>{{ obj.hobby.eat }}</p>
  <button @click="obj.hobby.eat = '面条'">click</button>
</template>
<script>
import { watch, reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    const obj = reactive({
      name: 'ifer',
      hobby: {
        eat: '西瓜',
      },
    })
    // watch reactive 类型的数据,强制是开启了深度监听,没法配置
    watch(
      obj,
      (newValue, oldValue) => {
        // 此时 newValue 和 oldValue 新值和旧值是相等的
        console.log(newValue === oldValue) // true
      },
      {
        deep: false, // 配置无效
      }
    )
    return { obj }
  },
}
</script>

注意 2:reactive 的【内部对象】也是一个 reactive 类型的数据。

 注意3.watch监听的是reactive的内置数据 ,对 reactive 自身的修改则不会触发监听 ,但是可以改变视图数据

<template>
  <p>{{ obj.hobby.eat }}</p>
   <!-- 直接修改 obj 等于一个新对象,不能被监听到,视图也不会更新 -->
  <button @click="obj={hobby:{eat:'面条'}}">click</button>
</template>
<script>
import { watch, reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    const obj = reactive({
      name: 'ifer',
      hobby: {
        eat: '西瓜',
      },
    })
    // watch reactive 类型的数据,强制是开启了深度监听,没法配置
    watch(obj,(newValue, oldValue) => {
        // 此时 newValue 和 oldValue 新值和旧值是相等的
        // console.log(newValue === oldValue) // true
        console.log(1) // 不会打印
      },
    )
    return { obj }
  },
}
</script>

watch监听ref

监听ref.value默认为浅监听 只会监听一层   监听可以修改(配置无效)

当监听一个ref值时,触发后面的回调函数是不需要加.value的

当监听多个ref值时需要加.value

立即触发监听通过  immediate: true

<template>
  <p>age: {{ age }} num: {{ num }}</p>
  <button @click="handleClick">click</button>
</template>

<script>
import { watch, ref } from 'vue'
export default {
  name: 'App',
  setup() {
    const age = ref(18)
    const num = ref(0)

    const handleClick = () => {
      age.value++
      num.value++
    }
    // 数组里面是 ref 数据
    watch(
      [age, num],
      (newValue, oldValue) => {
        console.log(newValue, oldValue)
      },
      {
        immediate: true,
      }
    )

    return { age, num, handleClick }
  },
}
</script>
评论 ( 0 )

 注意:监听ref.value 默认是浅监听的,默认只会监听一层

如果ref内置数据的修改也想要触发监听时可以通过

方法一   开启深度监听

 watch(
      obj,
      (newValue, oldValue) => {
        console.log(newValue)
      },
      {
        deep: true,
      }
    )

 方法二  修改 ref.value  表示只修改 外面的一层 直接修改自身

<template>
  <p>{{ obj.hobby.eat }}</p>
  <button @click="obj = { hobby: { eat: '面条' } }">修改 obj.hobby.eat</button>
  <button @click="handleClick">修改 obj.hobby.eat</button>
</template>

<script>
import { watch, ref } from 'vue'
export default {
  name: 'App',
  setup() {
    const obj = ref({
      hobby: {
        eat: '西瓜',
      },
    })
    // 注意:默认是浅监听的,默认只会监听最外面一层(obj.value)
    watch(obj, (newValue, oldValue) => {
      console.log(newValue)
      console.log(oldValue)
    })

    const handleClick = () => {
      obj.value = { hobby: { eat: '热干面' } }
    }

    return { obj, handleClick }
  },
}
</script>

方法三  监听 ref.value 因为obj.value也是一个reactive

// 如果 ref 包裹的是一个复杂数据类型, 其实内部还是借助 reactive 实现的,obj.value 是一个 reactive 类型的数据
    console.log(isReactive(obj.value)) // true
    // 其实监听的是一个 reactive,就符合下面特点
    // 1. 开启深度监听(配置无效)
    // 2. 监听的是 obj.value 内部数据的变化
    watch(obj.value, (newValue, oldValue) => {
      console.log(newValue)
    })

const obj=ref(对象数据) watch(obj,回调) 此时只能监听最外面一层 

 const obj=ref(对象数据) watch(obj.value,回调) 对obj.value相当于reactive数据 因此具有监听reactive的特点

 const obj=ref(对象数据) watch(obj.value,回调{deep:true}) 开启深度监听 此时既有ref数据特点也能监听内部数据

思维导图如下

Day01 | ProcessOn免费在线作图,在线流程图,在线思维导图,ProcessOn免费在线作图,在线流程图,在线思维导图https://www.processon.com/view/link/62cff0cd07912906d7981fdc#map

watch监听普通值


    /watch 的第一个参数可以是一个函数,函数的返回值是一个普通值,就表示监听这个普通值(字符串)
     如果要监听普通值,可以通过函数返回这个普通值

utton @click="obj.hobby.eat = '面条'">修改 obj</button>
</template>

<script>
import { watch, reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    const obj = reactive({
      hobby: {
        eat: '西瓜',
        a: 1,
        b: 2,
        c: 3,
      },
    })

    // 浪费
    /* watch(obj, () => {
      console.log('1')
    }) */
    // 有点浪费
    /* watch(obj.hobby, () => {
      console.log('1')
    }) */

    // 浏览器爆出警告 function, a ref, a reactive object, or an array of these types.
    // 监听的参数只能是 function、ref、reactive、array[function、ref、reactive]
    // 语法错误
    /* watch(obj.hobby.eat, () => {
      console.log(1)
    }) */

    // watch 的第一个参数可以是一个函数,函数的返回值是一个普通值,就表示监听这个普通值(字符串)
    // 如果要监听普通值,可以通过函数返回这个普通值
    watch(
      () => obj.hobby.eat,
      () => {
        console.log(1)
      }
    )

    watch(obj, () => {
      console.log(1)
    })

    watch(obj.hobby, () => {
      console.log(1)
    }) 
    return { obj }
  },
}
</script>

生命周期

  • Vue3(组合 API)常用的生命周期钩子有 7 个,可以多次使用同一个钩子,执行顺序和书写顺序相同。

  • setup、onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted。

<template>
    <p>{{ state.msg }}</p>
    <button @click="state.msg = 'xxx'">update msg</button>
</template>

<script>
    import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, reactive } from 'vue'
    export default {
        name: 'HelloWorld',
        setup() {
            const state = reactive({
                msg: 'Hello World',
            })

            onBeforeMount(() => {
                console.log('onBeforeMount')
            })
            onMounted(() => {
                console.log('onMounted')
            })
            onBeforeUpdate(() => {
                console.log('onBeforeUpdate')
            })
            onUpdated(() => {
                console.log('onUpdated')
            })
            onBeforeUnmount(() => {
                console.log('onBeforeUnmount')
            })
            onUnmounted(() => {
                console.log('onUnmounted')
            })
            return {
                state,
            }
        },
    }
</script>

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值