Vue3笔记

本文详细介绍了Vue3.0的环境集成,包括使用vue-cli和vite创建项目。深入探讨了Vue3的Composition API,如setup函数、ref和reactive函数,以及响应式原理。此外,还讲解了组件、计算属性、监听属性、生命周期、Teleport标签、自定义事件、属性传值、CSS变量、Suspense和异步组件、组件注册、响应式数据解构以及只读响应式对象。最后,讨论了Vue3的配置,如公共属性、网络配置、路由配置和仓库配置,特别提到了Vuex的替代方案Pinia。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、Vue3.0环境集成

1.使用vue-cli创建

## 1.查看@vue/cli的版本,确保@vue/cli版本在4.5.0以上
vue --version
## 2.安装或者升级你的@vue-cli
## 3.创建
vue create vueproject
#  4.启动
cd vueproject
npm run serve

2.使用vite创建

vite:新一代的前端工具

优点:

  • 开发环境中,无需打包,可快速的冷启动

  • 轻量快速的热加载

    //1.创建
    npm init vite hello-vue3 -- --template vue 
    npm init vite 项目名  可以自己选择集成环境
    

2、常用的Composition API(组合API)

官方文档:https://cn.vuejs.org/api/

1、setup函数

  • 总结:Vue3.0中一个新的配置向,值为一个函数,组件中所有用到的:数据、方法等等,均要配置在setup中。

  • setup函数的两种返回值:

    1. 若返回一个对象,则对象中的数据、方法、在模板中均可直接使用(重点

      <script setup>
          var name = "marry";
          var obj = {
              age: 18,
              gender: "女",
              class: '二'
          }
      </script>
      
      <template>
          <div>
              <span>{{obj.class}}班的{{name}}是一名{{obj.gender}},今年{{obj.age}}岁了~~~</span>
          </div>
      </template>
      
      //二班的marry是一名女生,今年18岁了~~~
      
    2. 若返回一个渲染函数,则可以自定义渲染内容(了解)

      //页面中会显示一个h1标签,标签的内容是"vue3返回渲染函数"
      <script>
      import {h} from 'vue'
      export default {
        setup() {
          // 返回一个渲染函数
          // h相当于document.CreateElement()
          return ()=>{return h("h1","vue3返回渲染函数")}
        }
      }
      </script>
      
      
  • 注意:

    1. 虽然它可以使用Vue2.0的配置,但是不要2.0 \3.0一起混用;
    2. Vue2.x配置(data,methods,computed…)中可以访问setup中的属性,方法,但在setup中不能访问到Vue2.x配置(data,methods,computed…)
    3. 如有重名,setup优先
    4. setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性
    5. setup在组件加载期间,只会运行一次
    6. 语法糖:在script里面加入 setup , 这个属性会让打包工具打包时,直接帮我们把setup函数内部声明的变量、函数return 然后组件就可以直接使用
    7. setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之前的函数
    8. 执行 setup 时,组件实例尚未被创建(在 setup() 内部,this 不会是该活跃实例的引用,即不指向vue实例,Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined)
    9. setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。但是,因为 props 是响应式的,你不能使用 ES6 解构,因为它会消除 prop 的响应性。
    10. 从 setup() 中返回的对象上的 property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加 .value

2、ref函数

  • 总结:定义一个响应式的数据

  • import {ref} from “vue”

  • 语法:const xxx = ref(“value”)

    • 创建一个包含响应式的数据的引用对象(reference对象)
    • js中操作数据:xxx.value
    • 模板中读取数据不需要.value,直接
      {{xxx}}
  • 注意

    • 接收的数据类型可以是基本数据类型也可以是引用数据类型(对象,数组等 xxx.value.属性值/[下标])
    • 基本类型的数据:响应式依然是靠Object.defineProperty()的get和set完成的
    • 对象类型的数据:内部“求助”了Vue3.0的一个新的函数------reactive函数
    <script setup>
        import {
            ref
        } from "vue"
        var name = "marry";
        var obj = ref({
            age: 18,
            gender: "女",
            class: '二'
        })
    
        function change() {
            obj.value.age++
        }
    </script>
    
    <template>
        <div>
            <span>{{obj.class}}班的{{name}}是一名{{obj.gender}},今年{{obj.age}}岁了~~~</span>
            <button @click="change">年龄+1</button>
        </div>
    </template>
    
    

3、reactive函数

  • 作用:定义一个对象类型的响应式数据(基本数据类型别用它,用ref函数)
  • 引入:import {reactive} from ‘vue’
  • 语法:const 代理一个对象 = reactive(被代理的对象) 接收一个对象(或数组),返回一个代理器对象(proxy对象)
  • reactive定义的响应式数据是“深层次的”
  • 内部基于ES6的Proxy实现,通过代理对象内部的数据都是响应式的
<script setup>
    import {
        reactive
    } from "vue"
    var name = "marry";
    var obj = reactive({
        age: 18,
        gender: "女",
        class: '二'
    })

    function change() {
        obj.age++
    }
</script>

<template>
    <div>
        <span>{{obj.class}}班的{{name}}是一名{{obj.gender}},今年{{obj.age}}岁了~~~</span>
        <button @click="change">年龄+1</button>
    </div>
</template>

ref \reactive 区别用法?

  • 都是用来放响应式数据;
  • 引入方式: ref: import {ref} from ‘vue’ / import {reactive} from ‘vue’
  • ref( )里面放的是基本数据,和嵌套层数不是很深的引用数据; / reactive( )基本数据不用他,里面放嵌套层数比较的引用数据;
  • ref 用里面的数据时,数据方法**.value**里面, / reactive 用里面的数据时 ,直接使用。
  • 响应式设计原理:ref: 监听了value的改变,劫持value属性的setter ,getter ,因此ref一般用在基本数据,或者嵌套不深的引用数据上; / reactive: 和ref一样,但是底层采用的是ES6的Proxy代理了整个引用数据。

4、Vue3.0中的响应式原理

vue2.0的响应式

  • 实现原理

    • 对象类型:通过Object.definedProperty()对属性的读取、修改进行拦截(数据劫持)

    • 数组类型:通过重写更新数据的一系列方法来实现拦截。(对数组的方法进行了包裹)

      Object.defineProperty(data,"count",{
          get(){},
          set(){}
      })
      

    存在问题:

    • 新增属性,删除属性都不会刷新界面
    • 直接通过下标修改数组,界面不会自动更新
            let person = {
                name:"李国栋",
                age:18
            }
    
            let p = {};
    
            Object.defineProperty(p,"name",{
                get(){
                    console.log("有人读取数据时调用");
                    return person.name
                },
    
                set(value){
                    console.log("有人修改了数据,我要去更新页面");
                    person.name = value
                }
            })
    

vue3.0的响应式

  • 实现原理
    • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性的读写,属性的添加,属性的删除等
    • 通过Reflect(反射):对被代理对象的属性进行操作
    • MDN文档中描述的Proxy与Reflect:
      • Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
      • Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
        let person = {
            name:"李国栋",
            age:18
        }

        let p = new Proxy(person,{
            // 读取
            get(target,proname){
                // target表示原对象  proname表示对象名
                console.log("有人读取了person上的属性",target);
                return target[proname]
            },
            // 修改或者增加
            set(target,proname,value){
                console.log("有人修改了person上的属性,我要去更新了");
                target[proname] = value
            },
            // 删除
            deleteProperty(target,proname){
                console.log("有人删除了person上面的属性,我要去调用了");
                return delete target[proname]
            },
            
        });

5、组件

  • 引入:import xxx from (“组件路径”)

  • 引入后直接使用:

6、计算属性

  • 引入:import {computed} from “vue”
  • 使用:computed( 函数)
<script setup>
    import {
        ref,
        reactive,
        computed
    } from "vue"

    let arr = reactive([{
            title: '鱼香肉丝',
            price: 20,
            count: 1
        },
        {
            title: '水煮肉片',
            price: 35,
            count: 1
        },
        {
            title: '白米饭',
            price: 3,
            count: 1
        },
    ])
    let total = computed(() => {
        console.log(66666, "计算总价")
        return arr.reduce((n1, n2) => {
            return n1 + n2.price * n2.count
        }, 0)
    })
    let change1 = () => {
        arr[0].count = 10
    }

    function add(index) {
        arr[index].count++
    }

    function des(index) {
        arr[index].count--
    }
</script>

<template>
    <div class="box">

        <div v-for="(el,index) in arr">
            <div>{{el.title}}----{{el.price}}----
                <button @click="des(index)">-</button>
                <span>{{el.count}}</span>
                <button @click="add(index)">+</button>
            </div>
        </div>
        <button @click="change1">修改</button>
        <button>{{total}}</button>
    </div>
</template>

7、监听属性

1.监视reactive定义的响应式数据的时候:oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效)
2.监视reactive定义的响应式数据中某个属性的时候:deep配置有效
具体请看下面代码以及注释

<template>
    <div class="box">
        <h1>当前求和为: {{sum}}</h1>
        <button @click="sum++">点我+1</button>
        <hr>
        <h1>当前信息为: {{msg}}</h1>
        <button @click="msg+='!' ">修改信息</button>
        <hr>
        <h2>姓名: {{person.name}}</h2>
        <h2>年龄: {{person.age}}</h2>
        <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增长年龄</button>
    </div>
</template>

<script>
    //使用setup的注意事项
    import {
        watch,
        ref,
        reactive
    } from 'vue'

    export default {
        name: 'test5',
        props: ['msg'],
        emits: ['hello'],
        setup() {
            let sum = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name: '张三',
                age: 18,
                job: {
                    salary: '15k'
                },
            })
            //由于这里的this是指的是undefined,所以使用箭头函数
           ####情况一:监视ref所定义的一个响应式数据
            // watch(sum, (newValue,oldValue)=>{
            //     console.log('新的值',newValue);
            //     console.log('旧的值',oldValue);
            // })

            ####情况二:监视ref所定义的多个响应式数据
            watch([sum, msg], (newValue, oldValue) => {
                console.log('新的值', newValue); //['sum的newValue', 'msg的newValue']
                console.log('旧的值', oldValue); //['sum的oldValue', 'msg的oldValue']
            }, {
                immediate: true,
                deep: true
            }) //这里vue3的deep是有点小问题的,可以不用deep

            ####情况三:监视reactive定义的所有响应式数据,
            //1.此处无法获取正确的oldValue(newValue与oldValue是一致值),且目前无法解决
            //2.强制开启了深度监视(deep配置无效)
            watch(person, (newValue, oldValue) => {
                console.log('新的值', newValue);
                console.log('旧的值', oldValue);
            })

           ####情况四:监视reactive对象中某一个属性的值,
            //注意: 这里监视某一个属性的时候可以监听到oldValue
            watch(() => person.name, (newValue, oldValue) => {
                console.log('新的值', newValue);
                console.log('旧的值', oldValue);
            })

           ####情况五:监视reactive对象中某一些属性的值
            watch([() => person.name, () => person.age], (newValue, oldValue) => {
                console.log('新的值', newValue);
                console.log('旧的值', oldValue);
            })

            ####特殊情况: 监视reactive响应式数据中深层次的对象,此时deep的配置奏效了
            watch(() => person.job, (newValue, oldValue) => {
                console.log('新的值', newValue);
                console.log('旧的值', oldValue);
            }, {
                deep: true
            }) //此时deep有用

            return {
                sum,
                msg,
                person,
            }
        },

    }
</script>

<style scoped lang="scss">
    .box {
        width: 500px;
        background-color: pink;
    }
</style>

提示余额不足:

<script setup>
  import {
    ref,
    reactive,
    computed,
    watch
  } from "vue"
  //引入组件
  let money = ref(100);

  watch(money, (newvalue, oldvalue) => {
    if (newvalue < 50) {
      alert('余额不足!!!')
    }
  })

  function change() {
    money.value -= 10;
  }
</script>



<template>
  <div>
    <div>银行卡余额,低于50元时弹窗显示"余额不足:{{money}}</div>
    <button @click="change">-10</button>
  </div>

</template>
watchEffect函数

watch的套路是:既要指明监视的属性,也要指明监视的回调
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
watchEffect有点像computed:
但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值

<script>
    //使用setup的注意事项
    import { ref,reactive,watchEffect } from 'vue'

    export default {
        name: 'test5',
        props: ['msg'],
        emits:['hello'],
        setup(){
            let sum  = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name: '张三',
                age: 18,
                job:{
                    salary: '15k'
                },
            })
            
            //用处: 如果是比较复杂的业务,发票报销等,那就不许需要去监听其他依赖,只要发生变化,立马重新回调
            //注重逻辑过程,你发生改变了我就重新执行回调,不用就不执行,只执行一次
            watchEffect(()=>{
                //这里面你用到了谁就监视谁,里面就发生回调
                const x1 = sum.value
                console.log('我调用了');
            })

            return {
                sum,
                msg,
                person,
            }
        },
        
    }
</script>

8、生命周期

组件中:选项式API

创建前后:setup代替

挂载前后:beforeMount mounted

更新前后:beforeUpdate updated

销毁前后:beforeunmount unmounted

setup组合式API

挂在前后:onBeforeMount onMounted

更新前后:onBeforeUpdate onUpdated

销毁前后: onBeforeUnmount onUnmounted

onErrorCaptured
onRenderTracked
onRenderTriggered

effect 重点

9、Teleport标签

  • 什么是Teleport? —— Teleport是一种能够将我们组件html结构移动到指定位置的技术(开发的时候非常有用)
//弹窗实现
<script setup>
import { ref,inject } from 'vue';
let isShow = ref(false)
</script>

<template>
  <div>
      <button @click="isShow = true">点我弹窗</button>
      <teleport to="body"> 
          //定位到body
        <div class="mask" v-if="isShow">
            <div class="dialog">
                <h4>我是一个弹窗</h4>
                <h5>内容</h5>
                <h5>内容</h5>
                <h5>内容</h5>
                <button @click="isShow = false">关闭</button>
            </div>
        </div>
      </teleport>
  </div>
</template>

<style>
.dialog{
    width: 300px;
    height: 300px;
    text-align: center;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    background-color: blueviolet;
    margin: 0 auto;
}
.mask{
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.5);
}
</style>

10、自定义事件

与组件和 prop 一样,事件名提供了自动的大小写转换。如果在子组件中触发一个以 camelCase (驼峰式命名) 命名的事件,你将可以在父组件中添加一个 kebab-case (短横线分隔命名) 的监听器。

  • 可以通过 emits 选项在组件上定义发出的事件
//父组件中
<script setup>
  import {
    ref,
  } from 'vue';
  import son from './components/Son.vue'

  let msg = ref("父组件中的值")

  function fn(arg) {
    msg.value = arg
    console.log(arg);
  }
</script>

<template>
  <div>
    <h3>{{msg}}</h3>
    <son @myclick="fn"></son>
  </div>
</template>

<style>

</style>


//子组件中、
<script>
    export default {
        emits: ["myclick"],  
//以数组的形式接收多个自定义事件名:emits:["myEvent","myclick"],  用法和2.0一样
        methods: {
            fn() {
                this.$emit('myclick', '子组件中的数据')
            }
        }

    }
</script>

<template>
    <div class="box">
        <button @click="fn">点我</button>
    </div>
</template>

  • 省略this-------3.2之前使用defineEmits
    • 注意必须
//子组件Son2.vue中
<script setup>
    import {
        ref,
        watch,
        defineEmits   //导入注册,3.2之后版本可以直接省略
    } from 'vue'

    let emit = defineEmits()

    function fn() {
        emit('myclick2', '子组件2中的数据', 111)
    }
</script>

<template>
    <div class="box">
        <button @click="fn">defineEmits方法</button>
    </div>
</template>

//父组件中
//弹窗实现
<script setup>
  import {
    ref,
  } from 'vue';
  import son2 from './components/Son2.vue'

  let msg = ref("父组件中的值")


  function fn2(arg1, arg2) {
    console.log(arg1, arg2);
  }
</script>

<template>
  <div>
    <h3>{{msg}}</h3>
    <son2 @myclick2="fn2"></son2>
  </div>
</template>

  • v-model双向绑定

    //子组件接收
    
    <script setup>
        import {
            ref,
            defineEmits,
            defineProps
        } from 'vue'
    
    
    
        let emit = defineEmits()
        let obj = defineProps(["msg1", "msg2", "msg3"])
    
        function fn() {
            emit('update:msg1', 111)
            emit('update:msg2', 222)
            emit('update:msg3', 333)
        }
    </script>
    
    <template>
        <div class="box">
            <button @click="fn">v-model修改值</button>
            <p>{{obj.msg1}}---{{obj.msg2}}---{{obj.msg3}}</p>
    
        </div>
    </template>
    
    //父组件
    <script setup>
      import {
        ref,
      } from 'vue';
      import son3 from './components/Son3.vue'
    
      let msg1 = ref('hello1')
      let msg2 = ref('hello2')
      let msg3 = ref('hello3')
    
    </script>
    
    <template>
      <div>
        <son3 v-model:msg1="msg1" v-model:msg2="msg2" v-model:msg3="msg3"></son3>  //绑定多个变量
        <p>{{msg1}}---{{msg2}}---{{msg3}}</p>
      </div>
    </template>
    
    <style>
    
    </style>
    

11、属性传值

  1. 组件内部 还是可以用2.0 做项目时愿意用2.0的同学语法不变
<script>
export default {
   pops:["msg"],// pops:{msg:String}, 
   setup(){}    
}
< /script>
<template>
  <div>
      {{msg}}
  </div>
</template>
    

    父组件:
    <Box :msg="n"></Box>

2.vue3组件内部组合式API setup中取属性值

//子组件
<script>
    export default {
        props: ['msg', 'count'],  //readonly  不可修改
        setup(props) {
            let fn = () => {
                console.log(props.msg, props.count);
            }
            return {
                fn
            }
        }
    }
</script>

<template>
    <div class="box">
        <p>{{msg}}-------count={{count}}</p>
        <button @click="fn">点我</button>
    </div>
</template>



<style scoped lang="scss">
    .box {
        width: 500px;
        height: 300px;
        background-color: rgb(131, 124, 124);

    }
</style>
    

####父组件:
   <script setup>
  import {
    ref,
  } from 'vue';
  import Box1 from './components/Box1.vue'
  let msg = '父组件中的数据'
</script>

<template>
  <div>
    <Box1 :msg="msg" :count="200"></Box1>
  </div>
</template>

 

3.setup语法糖中获取属性值

//子组件
<script setup>
    //定义接收属性
    import {
        defineProps
    } from "vue";
    // 定义从父组件接收的属性
    const obj = defineProps({
        msg: String,
        count: Number,
    });
    let fn = () => {
        console.log(obj.msg, obj.count)
    }
</script>

<template>
    <div class="box">
        <p>{{msg}}-------count={{count}}</p>
        <button @click="fn">点我</button>
    </div>
</template>



<style scoped lang="scss">
    .box {
        width: 500px;
        height: 300px;
        background-color: rgb(161, 127, 127);

    }
</style>
    

//父组件:
<script setup>
  import {
    ref,
  } from 'vue';
  import Box2 from './components/Box2.vue'
  let msg = '父组件中的数据'
</script>

<template>
  <div>
    <Box2 :msg="msg" :count="200"></Box2>
  </div>
</template>

12、css变量

  • 案例:变色球
    • 关键代码:background-color: v-bind(color);
<script setup>
  import {
    ref
  } from "vue"

  let color = ref('red')

  let change = () => {
    var r = Math.floor(Math.random() * 256);
    var g = Math.floor(Math.random() * 256);
    var b = Math.floor(Math.random() * 256);
    color.value = "rgb(" + r + ',' + g + ',' + b + ")"

  }
</script>

<template>
  <div>
    <div class="box">变色球</div>
    <button @click="change">点击变色</button>
  </div>

</template>

<style scoped>
  .box {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: v-bind(color);
    line-height: 100px;
    color: aqua;
  }
</style>

13、占位组件Suspense和异步组件

等待异步组件时渲染一些额外的内容,让应用有更好的用户体验

<suspense> 组件有两个插槽。它们都只接收一个直接子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。

异步组件
当我们的项目达到一定的规模时,对于某些组件来说,我们并不希望一开始全部加载,而是需要的时候进行加载;这样的做得目的可以很好的提高用户体验。

为了实现这个功能,Vue3中为我们提供了一个方法,即defineAsyncComponent,这个方法可以传递两种类型的参数,分别是函数类型和对象类型。

14、注册组件(八种方式)

  1. 组件内部
<script>
    import Box1 from "./Box1.vue"
    export defult{
        components:{
            Box1
        },
        setup(){}
    }
</script>
<template>
    <Box1></Box1>	
< /template>

2.vue3组件内部组合式API setup语法糖中注册组件

<script setup>
    import Box1 from "./Box1.vue"  
    //只需要导入 不用写注册代码  会在打包的时候自动帮我们注册 
</script>
<template>
    <Box1></Box1>	
< /template>

3.注册全局组件

//main.js文件:
import { createApp} from 'vue'
import App from './App.vue'
const app=createApp(App)

import Box1 from "./Box5.vue"
app.component(Box1.name,Box1)

app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了

//App.vue文件:
<template>
    <Box1></Box1>	
< /template>

4.定义同步组件:

//Box1.vue文件:
<script>
  import {defineComponent} from "vue"
  export default defineComponent({
		data(){
			return {}
		},
		methods:{},		
		setup(){
		}		
	});
</script>

5.1定义局部异步组件:

组件内部

<script>
	import {defineAsyncComponent} from "vue"
	let Box1 = defineAsyncComponent(() => import("./Box1.vue")) //注意3.2之后不用引入defineAsyncComponent
	export default {
		components: {
			Box1
		},
		setup() {}
	}
</script>

setup语法糖:

<script setup>
		import 	Box1 from "./Box1.vue"
		import 	Box2 from "./Box2.vue"
		import {defineAsyncComponent} from "vue"
		let Box3=defineAsyncComponent(()=>import("./Box3.vue"))//注意3.2之后不用引入defineAsyncComponent,而且这个变量名直接就是注册的组件名(打包时自动注册的)
</script>

5.2定义全局异步组件:

//main.js文件:
import { createApp,defineAsyncComponent} from 'vue'
import App from './App.vue'
const app=createApp(App)
let Box1=defineAsyncComponent(()=>import("./Box4.vue"))
app.component("Box1",Box1)
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了

15、isRef toRef toRefs(响应式数据解构)

isref判断是否是变量,返回布尔值

<script setup>
  import {
    ref,
    isRef,
  } from 'vue'
  let a = ref(10)

  let fn = function () {
    console.log(isRef(a))  //判断a是否是响应式数据 返回true
  }
</script>

<template>
  <div>
    <button @click="fn">点我判断是否为变量</button>


  </div>
</template>

<style scoped>

</style>

toRef 将对象中的数据转换为单个的变量

<script setup>
  import {
    ref,
    reactive,
    isRef,
    toRef
  } from 'vue'
  let obj = reactive({
    name: 'merry',
    age: 24
  })

  let name = toRef(obj, "name");
//把obj中的name变为ref响应式数据
//隐式代码:let name=ref(obj.name) 并且响应到obj中

  let change = () => {
    name.value = 'jack'
  }
</script>

<template>
  <div>
    <h3>{{name}}</h3>
    <button @click="change">点我变名字</button>
  </div>
</template>

toRefs函数的作用是将响应式对象中的所有属性转换为单独的响应式数据,对象成为普通对象,并且值是关联的。在这个过程中toRefs会做以下两件事:

  1. 把一个响应式对象转换成普通对象

  2. 对该普通对象的每个属性都做一次ref操作,这样每个属性都是响应式的
    说明:

    • reactive 对象取出的所有属性值都是非响应式的,而利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性。
    • reactive的响应式功能是赋值给对象,如果展开对象,会让数丢失响应的能力
    • 使用toRefs可以保证对象展开的每个属性都是响应式的

    应用场景:

    • 展开响应式对象时,想使用响应式对象中的多个或者所有属性做为响应式数据。
    • 当函数返回响应式对象时,toRefs非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用。
    //响应式对象解构
    <script setup>
      import {
        ref,
        reactive,
        isRef,
        toRef,
        toRefs
      } from 'vue'
      
      let obj2 = reactive({
        name1: 'merry',
        age: 24
      })
      let {
        name1,
        age
      } = toRefs(obj2) //响应式数据解构
    
      let change2 = () => {
        name1.value = 'karen'
        age.value++
      }
    </script>
    
    <template>
      <div>
        
        <h3>{{name1}}----{{age}}</h3>
        <button @click="change2">点我变名字和年龄</button>
      </div>
    </template>
    
    

16、readonly

readonly 接收一个 ref 或者 reactive 包装对象,返回一个只读的响应式对象。

例如缓存文件,不能修改只能读取

<script setup>
    import {
        reactive,
        readonly
    } from 'vue'

    let food = reactive({
        title: '鱼香肉丝',
        price: 20,
        count: 2
    })
    let obj = readonly(food)

    let add = () => {
        count.value++;
    }
</script>

<template>
    <div class="box2">
        <h4>{{obj.title}}---{{obj.price}}----{{obj.count}}</h4>
        <button @click="add">+1</button>
        <p>tips:readonly只可以读取,不可以修改</p>
        <h4>总价:{{obj.price * obj.count}}</h4>
    </div>
</template>

配置

1.公共属性配置(globalProperties)

注意:在自己定义的函数下不能调用会得到空,只能在组件的钩子函数中使用

main.js里面配置

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)

app.config.globalProperties.$cqsx='xxxxxxxx111xxx'
//属性添加成员
app.config.globalProperties.rank = 'xxxxxx222xx'


app.mount('#app')

app.vue中使用

<script setup>
  import {
    getCurrentInstance
  } from 'vue'   //导入globalProperties

//使用变量接收
  let zujian = getCurrentInstance()
  //打印
  console.log(zujian, zujian.proxy.$cqsx, zujian.proxy.rank);
</script>

<template>
  <div>
  </div>
</template>


//3.0选项式API中
<script>
  import {
    getCurrentInstance
  } from 'vue'
  export default {
    mounted() {
      // mounted函数中this.$cqsx 使用
    },
    setup() {
      let {
        proxy
      } = getCurrentInstance();
      //setup中 proxy.$cqsx使用
      function fn() {
          //在setup中的函数下不能调用会得到空,只能在组件的钩子函数中使用
        getCurrentInstance() //null

      }
      return {
        fn
      }
    }
  }
</script>

2.网络配置

//1.vue.config.js中配置代理
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  proxy: {
    '/api': { 
        target: 'http://127.0.0.1:7001', // 代理的目标地址
        rewrite: (path) => path.replace(/^\/api/, '/'), // 路径重写
        changeOrigin:true,
        // secure: true, // target是否https接口
        // ws: true, // target是否代理websockets               
    }
}
})


//2.main.js中添加公共属性axios
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)  //声明app

axios.default.baseURL = 'http://127.0.0.1:7001/api'
app.config.globalProperties.$axios = axios

app.mount('#app') //挂载到页面


//3.Box.vue中使用axios
<template>
    <div>

    </div>
</template>

<script setup>
    import {
        onMounted,
        getCurrentInstance
    } from 'vue'
    let {
        Proxy
    } = getCurrentInstance()
    onMounted(async () => {
        // http://ip:5173/test  axios.defaults.baseURL='http://127.0.0.1:7001/api' 代理后的网址  http://127.0.0.1:7001/api/test

        let res = await Proxy.$axios('/test');
        console.log(res);
        
    })
</script>

use插件配置

//第一种在main.js中
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)

import axios from 'axios'
function $axios(app) {
    axios.default.baseURL = 'http://127.0.0.1:7001/api'
    app.config.globalProperties.$axios = axios;
}
app.use($axios)

app.mount('#app')


//第二种在main.js中
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)


import $axios from './http/$axios.js'
app.use($axios)

app.mount('#app')

//./http/$axios.js中配置然后导出$axios,在main.js中导入使用
import axios from 'axios'
function $axios(app) {
    axios.default.baseURL = 'http://127.0.0.1:7001/api'
    app.config.globalProperties.$axios = axios;
}

export default $axios;

3.路由配置

  • cli配置:vue create init 选择router vue3.0
  • vite中:npm init vite 项目名 (手动下载router:npm i vue-router)
  • 导入到项目中和vue2.0不同:
  • 注册路由(子路由,父路由)方式不变
  • 全局守卫,独享守卫,组件守卫逻辑不变,语法有稍微改变
//webpack 中  vue create init 
//1.router/index.js中注册路由
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/home',
    name: 'home',
    component: () => import( '../views/home.vue')
  },
  {
    path: '/car',
    name: 'car',
    component: () => import( '../views/car.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

router.beforeEach((to, from, next)=>{
  next()
})

router.beforeResolve()((to, from, next)=>{
  next()
})
export default router

//2.App.vue中使用
<template>
  <nav>
    <router-link to="/home">Home</router-link>
    <br>
    <router-link to="/car">car</router-link>
  </nav>
  <router-view />
</template>

2、vite中:npm init vite 项目名 (手动下载router:npm i vue-router)

//1、router/index.js  注册路由
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/home',
    name: 'home',
    component: () => import( '../views/home.vue')
  },
  {
    redirect:'/car/carView',
    path: '/car',
    name: 'car',
    component: () => import('../views/car.vue'),
    children: [
      {
        
        path: '/car/carView',
        name: 'car-view',
        component: () => import( '../views/carView.vue')
      },
      {
        
        path: '/car/carMoney',
        name: 'carMoney',
        component: () => import( '../views/carMoney.vue')
      },
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

router.beforeEach((to, from, next)=>{
  next()
})

router.beforeResolve()((to, from, next)=>{
  next()
})
export default router


//2、main.js中导入挂载路由
import { createApp } from 'vue'
import App from './App.vue'

import router from "./router/index.js"

createApp(App).use(router).mount('#app')


//3、App.vue中使用
<template>
  <nav>
    <router-link to="/home">Home</router-link>
    <br>
    <router-link to="/car">car</router-link>

  </nav>
  <router-view />
</template>

//子组件car.vue中使用
<template>
    <div class="box3">
        <H3>car</H3>
        <router-link to="/car/carView">carView</router-link> |||
        <router-link to="/car/carMoney">carMoney</router-link>

        <router-view></router-view>
    </div>
</template>

在这里插入图片描述

onBeforeRouteLeave需要引入

//离开当前页面打印2222
<script setup>
    import {
        onBeforeRouteLeave,
    } from 'vue-router'
    onBeforeRouteLeave((to, from, next) => {
        console.log('2222')
        next();
    })
</script>
//home里面传query参数  点击执行fn
<template>
    <div class="box2">
        <h3>home</h3>
        <button @click="fn">car</button>

    </div>
</template>

<script setup>
    import {
        useRouter
    } from "vue-router"
    let router = useRouter()
    let fn = () => {
        router.push({
            path: "/car/carView",
            query: {
                id: 123
            }
        })
    }
</script>

//car.vue 打印
<script setup>
	import {useRoute} from "vue-router"
	let route=useRoute()
	
	console.log(route.query)
</script>

4.仓库配置

vuex

vuex3.0适用于vue2.0

vuex4.0适用于vue3.0和2.0

Pinia 的优点

  • 简便,存储和组件变得很类似,你可以轻松写出优雅的存储。
  • 类型安全,通过类型推断,可以提供自动完成的功能。
  • vue devtools 支持,可以方便进行调试。
  • Pinia 支持扩展,可以非常方便地通过本地存储,事物等进行扩展。
  • 模块化设计,通过构建多个存储模块,可以让程序自动拆分它们。
  • 非常轻巧,只有大约 1kb 的大小。
  • 服务器端渲染支持。

Pinia 适用于3.0 为了兼容2.0也能使用 主要是给3.0的组合式API设计的

官方中文网: https://pinia.web3doc.top/

1.安装

npm install pinia

2.main.js中引入配置

import { createPinia } from 'pinia'
createApp(App).use(createPinia())

3.使用

在这里插入图片描述

==> state: () =>({msg: ‘我是仓库中的数据’}) //尽量不要省略return

  • 解决组件中的数据共享

4.修改仓库

  • 点语法修改
 let change1 = () => {
        store.msg = '修改了仓库中的数据'
        console.log(store.msg);
    }

如果不想用 . 语法,那么就需要解构,但是下面这种解构出来之后msg是一个普通变量,不是响应性的变量

let cardata=useCar()
let {msg}=cardata
//直接改变msg页面应用到的数据不会改变

要想解构变成响应式的,用toRefs

let cardata=useCar()
let {msg}=toRefs(cardata);
//这个时候改变msg页面应用到的数据会响应式改变
  • 重置:$reset :将修改后的仓库数据恢复到原始状态数据

     //重置
        let reset1 = () => {
            store.$reset()
        }
    
    
  • $patch 批量操作

    //修改仓库中的count和age
    store.$patch({
      msg:"666修改成功",
      count:store.count+100,
    })
    store.$patch((state)=>{
        state.msg="666修改成功";
        state.count= state.count+100,
    })
    

    问:$patch整体修改个单条修改的区别?

    答:状态刷新一次,可以批量修改

  • 订阅修改

    //可以通过 store 的 $subscribe() 方法查看状态及其变化,通过patch修改状态时就会触发一次
    store.$subscribe((mutation, state) => { 
      // 每当它发生变化时,将整个状态持久化到本地存储
      localStorage.setItem('test', JSON.stringify(state))
    })
    
  • getter

    Getter 完全等同于 Store 状态的 计算值。 它们可以用 defineStore() 中的 getters 属性定义。 他们接收“状态”作为第一个参数以鼓励箭头函数的使用:(ps:虽然鼓励但是依然提供了非es6玩家的使用方式 内部可以用this代表state)

    //home.js
    getters:{
            age: (state) => {
                return new Date().getFullYear()-new Date(state.birth).getFullYear()
            }
        },
            
            
    //页面中使用
     <h4>getter中的age:{{store.age}}</h4>
    
  • Actions

    在pinia中没有提供mutaion 因为Actions就够了(它可以异步同步的统一修改状态)

    之所以提供这个功能 就是为了项目中的公共修改状态的业务统一

    export const useStore = defineStore('main', {
      state: () => ({
        counter: 0,
      }),
      actions: {
        increment() {
          this.counter++//1.有this
        },
        randomizeCounter(state) {//2.有参数   想用哪个用哪个
          state.counter = Math.round(100 * Math.random())
        },
        randomizeCounter(state) {
            //异步函数
            axios("/test").then(res=>{
                 state.counter = res.data.length
            })
         
        }
      },
    })
    
    //组件中的使用:
      setup() {
        const store = useStore()
        store.increment()
        store.randomizeCounter()
      }
    
  • .模块化

    一个文件一个小仓库,仓库之间可以相互访问数据 不同组件也可以访问任意小仓库数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值