vue3学习笔记

2024-05-28 1066阅读

原文:尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程_哔哩哔哩_bilibili

1.环境安装

    nodejs  验证命令node,有node 才有npm这个命令

npm 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。

npm 由三个独立的部分组成:

  • 网站
  • 注册表(registry)
  • 命令行工具 (CLI)
  • 01 - npm 是什么? | npm中文文档 | npm中文网

        >npm create vue@latest

    只是使用typescript

    vue3学习笔记

     通过vscode打开目录

      注意起重一定要有package.json文件,如果没有就另建项目重新npm create vue@latest

    工程介绍

    1).vscode-->extensions.json

    vue3学习笔记

    .vscode存放从商店中拿到的插件

    比如

    Vue - Official(代替了volar)

    vue3学习笔记

    2)env.d.ts

      从node modules引入

     如果没有则飘红

    npm i

    报错npm WARN saveError ENOENT: no such file or directory, open 'D:\src\ebook\package.json'

    原因是没有package.json,需要重新运行项目或者考过来pacakge.json

    之后会出现node_modules文件夹

    vue3学习笔记

    vite网址Vite中文网

    3)index.html

       入口文件

    入口文件不是main.js也不是main.ts

    vite.config.ts:工程配置文件

    3)main.ts

      

    import './assets/main.css'    //引入css文件

    import { createApp } from 'vue'       //类似于养花的花盆,创建应用

    import App from './App.vue'        //类似于养花的花根

    createApp(App).mount('#app'):

    createApp(App).把花插在花盆里 创建应用,每个应用都有个根组件

    mount: 成果摆在#app里面

    4)删掉src

       vue文件三个标签

            template   script style

    推荐使用

    2.setup

    新配置项,里面不能用this,this是undefined,vue3弱化this

    setup比beforecreate还在执行前面

    返回对象

    setup(){
            // console.log("@@@@@@@@@@@@@@"+this)   this是undefined
            // 数据,直接用let不是响应式的,也就是变化不会引发页面自动更新
            let name="杜甫";
            let age=21;
            let tel='123455'
            // 方法
            function changeName(){
                console.log(1)
               name='李白'
               console.log(name)
            }
            function changeAge(){
                console.log(2)
                age+=1;
                console.log(age)
            }
             
            function showTel(){
                console.log(3)
                  alert(tel)
            }
         return{
            name,age,changeAge,changeName,showTel
     
         }
        }

    上述返回的是对象

    返回函数

      return function(){
            return 'haha'
        }

       页面上直接出现

    vue3学习笔记

    因为绝不会有this,改造成箭头函数

      return ()=>{
          return 'haha2'
        }

    箭头函数在语法上比普通函数简洁多。箭头函数就是采用箭头=>来定义函数,省去关键字function。

    函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中

    ①如果箭头函数没有参数,写空括号

    ②如果箭头函数有一个参数,也可以省去包裹参数的括号

    ③如果箭头函数有多个参数,将参数依次用逗号(,)分隔,包裹在括号中。

     箭头函数的函数体

    ①如果箭头函数的函数体只有一句执行代码,简单返回某个变量或者返回一个简单的js表达式,可以省去函数体花括号{ }及return

     箭头函数没有原型prototype,因此箭头函数没有this指向

    箭头函数没有自己的this指向,它会捕获自己定义所处的外层执行环境,并且继承这个this值。箭头函数的this指向在被定义的时候就确定了,之后永远都不会改变。(!永远)

    箭头函数进一步

      return ()=>"haha3"

    语法糖

    可以不写setup  及里面的return

    let a='666'
    
    let a='666'
    

    一般vue3都有两个setup

    一个用来定义名字,不然就等同文件名

    另外一个写数据和方法

    配置插件

       为了支持把以上两个setup写在一起:

    let a='666'
    

    npm i vite-plugin-vue-setup-extend -D

    更改vite.config.ts

    vue3学习笔记

    之后重启就使用了

    vue3学习笔记

    从输出来看 右侧数据被单独放在一块,其余放在一块

    vue3学习笔记

    响应式数据

    不同于vue2用data(){return{}} 里面的数据自动是响应式

       ref:基本类型数据

    引入ref,在需要变成响应式的前面加ref

    import {ref} from 'vue'
    let name=ref("libai");
    let a='666';
    console.log(name);
    console.log(a);
    function change(){
    }
    

    从输出可以看到

    vue3学习笔记

    打开,带下划线的都是系统自身用的

    vue3学习笔记

     可见name是一个对象,使用时应该用name.value获取其值,但是   

     注意如下2个位置不同:template中不用写.value,自动添加

    vue3学习笔记

    reactive:只能对象类型数据

    let car=reactive({brand:'Benz',price:100})

    没包裹叫原对象

    包裹之后变为响应式对象

    vue3学习笔记

       ref2:对象类型数据

    {{ car.brand }}{{ car.city }}
    
    • {{item.id }} {{item.name }}
    更改品牌 更改游戏编号 import {ref} from 'vue' let car=ref({brand:'aodi',city:'shanghai'}) let games=ref([ {id:'a1',name:'王者'}, {id:'a2',name:'神探'}, {id:'a3',name:'日落'}]) function changeBrand(){ car.value.brand='宝马' } function changeName(){ games.value[0].id='a11' }

    vue3学习笔记

    reactive和Ref

    RefImpl都是由ref定义得来

    Proxy都是由reactive得来

    vue3学习笔记

     Ref遇到对象时用Reactive

    vue3学习笔记

    vue3学习笔记

    避免ref后面.value每次都要写的设置

      左下角齿轮-settings

    vue3学习笔记

    选中 AutoInsertDotvalue

    reactive所指元素被重新分配对象

      失去响应式

    以下function不能更改页面

    let car=reactive({brand:'aodi',city:'shanghai'})

    function changeCar(){

       car={brand:'保时捷',city:'tianjin'}

    }

    但是以下是可以的

    function changeCar(){

       car.brand='a'

    car.csity='gz'

    }

    解决方法1

    vue3学习笔记

    to refs结构对象

    左边相当于右面

    vue3学习笔记

     修改为  let {name,age}=toRefs(person)

    把reactive对象所定义的每一组key value变成ref对象

    vue3学习笔记

     vue3学习笔记

     并且name就是person.name  ,改变name同时也改变了person.name

    toRef:只改变某个元素

    let nl=toRef(person,'age')

    console.log("nl"+nl.value)

    3.计算属性

        { fullName }} { fullName2() }} 全名 {{ fullName2() }} -- import {ref,computed} from 'vue' let fName=ref('zhang') let lName=ref("big") // 下面定义的fullName只读,不可修改 // let fullName=computed(()=>{ // // slice(0,1)截取从0开始到1的1个数据 ,slice(1) 截取从1到最后的所有数据 // return (fName.value.slice(0,1).toUpperCase()+fName.value.slice(1)+lName.value) // }) // 下面定义的fullName 可读写 let fullName=computed( { get(){ return (fName.value.slice(0,1).toUpperCase()+fName.value.slice(1)+"-"+lName.value)}, set(val){ const [str1,str2]=val.split('-') fName.value=str1 lName.value=str2 } } ) console.log(fullName) function fullName2(){ console.log("function2") return (fName.value.slice(0,1).toUpperCase()+fName.value.slice(1)+lName.value) } function changeFullName(){ fullName.value="li-si" } .person{ background-color: cadetblue; }

    vue3学习笔记

    计算属性是有缓存的,发现其构成没变,则即使其它地方使用也不会再计算

    方法则没有缓存   {{方法名()}}

    如上定义计算属性是只读的

    vue3学习笔记

    要想该则需要设置get set

    set(val) 中的val就是fullname="li-si"中被赋予的值

       

    let fullName=computed(
        {
           get(){ return (fName.value.slice(0,1).toUpperCase()+fName.value.slice(1)+"-"+lName.value)},
           set(val){
            const  [str1,str2]=val.split('-')
            fName.value=str1
            lName.value=str2
           
           } 
        }
    )

    4.watch

    vue3学习笔记

     watch在vue3是一个函数

    watch(监视谁,回调函数)

      监视基本类型

         注意:监视sum不需要写.value

    let sum=ref(0);
    watch(sum,(newVal,oldVal)=>{
    console.log("sum变化了"+newVal+"旧值"+oldVal)
    })

    vue3学习笔记

    结束监视

    监视函数的返回值就是结束监视函数

    import {ref,computed,watch} from 'vue'
    let sum=ref(0);
    function addOne(){
        sum.value++
    }
    const stopWatch=watch(sum,(newVal,oldVal)=>{
    console.log("sum变化了"+newVal+"旧值"+oldVal)
    if(newVal>10){
        stopWatch();
    }
    })
    console.log(stopWatch)

    stopWatch就是

    vue3学习笔记

      监视Ref定义的对象类型数据

    监视对象地址值

    若需监视对象值,需开启深度监视,使用监视函数第三个值

    watch(person,(newVal,oldVal)=>{
       console.log("Person被修改了"+newVal+"旧值"+oldVal)
    },{deep:true})

    还可以加个immediate参数,一上来先执行,因为原来肯定是undefined

    watch(person,(newVal,oldVal)=>{
       console.log("Person被修改了"+newVal+"旧值"+oldVal)
    },{deep:true,immediate:true})

    另外(newVal,oldVal)如果里面只写一个参数代表的是新值

    vue3学习笔记

    vue3学习笔记

    监视Reatctive定义对象

    默认开启深度监视,不能关闭

    ref替换对象才是真正的替换,地址也变了,并且一直保持响应式

    vue3学习笔记

    如果用reactive person={},会失去响应式;object.assign:是修改人的属性而已

    监视对象类型的某个属性

    vue3学习笔记

    对象属性是基本类型

     vue3学习笔记

        
        姓名

    {{ person.name }}


    年龄

    {{ person.age }}


    {{ person.car.c1 }},{{person.car.c2 }}


    修改名字 修改年龄 修改第一台车 修改第二台车 修改车 import {reactive,watch} from 'vue' let person=reactive({ name:"白居易", age:18, car:{ c1:'奔驰', c2:'宝马' } }) function changeName(){ person.name="素食" } function changeAge(){ person.age++ } function changeFirstCar(){ person.car.c1="保时捷" } function changeSecondCar(){ person.car.c2="法拉利" } function changeCar(){ // Object.assign(person.car,{c1:'蓝宇',c2:'比亚迪'}) person.car={c1:'蓝宇',c2:'比亚迪'} } watch(()=>{ return person.name },(newVal,oldVal)=>{ console.log("发生变化了",newVal,oldVal) })

     上文中watch第一个值用了一个getter函数,简写为箭头函数,能返回一个值

    以实现只监视一个属性

    vue3学习笔记

    进一步简写为

    watch(()=>person.name,(newVal,oldVal)=>{
       console.log("发生变化了",newVal,oldVal)
    })

     vue3学习笔记

    对象属性是对象类型

    watch(person.car,(newVal,oldVal)=>{
       console.log("发生变化了",newVal,oldVal)
    })

    上面的有缺陷:就是整个car变了没有被监视到

    建议写全,监视的是地址值,属性变化监视不到

    watch(()=>person.car,(newVal,oldVal)=>{
       console.log("发生变化了",newVal,oldVal)
    })

    需要开启deep

    watch(()=>person.car,(newVal,oldVal)=>{
       console.log("发生变化了",newVal,oldVal)
    },{deep:true})
    

    监视多种值

    watch([()=>person.name,()=>person.car.c1],(newVal,oldVal)=>{
       console.log("发生变化了",newVal,oldVal)
    },{deep:true})

    watch Effect

        
       

    当前水温:{{ temp }}C

    当前水位:{{ height }}M

    点我水温+1 点我水位+1 import {ref,watch} from 'vue' let temp=ref(20) let height=ref(3) function changeTemp(){ temp.value++ } function changeHeight(){ height.value++ } watch([temp,height],(newVal,oldVal)=>{ // 从newVal中获取新的温度和水位 let [newTemp,newHeight]=newVal if(newTemp>=30||newHeight>=8){ console.log("alarm",newVal,oldVal) } })

    改用watch effect,有immediate效果

        因为被监视值就是定义的属性,

    watchEffect(()=>{
       if(temp.value>=30||height.value>=10){
        console.log("hello")
       }
    })

    vue3学习笔记

     标签REF

    用Ref的原因,是因为如果使用id属性,那么不同vue文件可能重复使用

         

    深圳

        console.log(document.getElementById('title2'))

    输出为

    vue3学习笔记

    对应改为ref2

        
          

    中国

    深圳

    龙华

    点我输出h2 import{ref} from 'vue' // 创建一个title2,用于存储ref2标记的内容 let title2=ref() function showLog(){ // console.log(document.getElementById('title2')) console.log(title2.value) }

    有的时候会输出,标签中会有 data-v-4cadc14e,这是局部样式导致的,去掉scoped就没有了

    vue3学习笔记

        
          

    中国

    深圳

    龙华

    点我输出h2 import{ref} from 'vue' // 创建一个title2,用于存储ref2标记的内容 let title2=ref() function showLog(){ // console.log(document.getElementById('title2')) console.log(title2.value) } .person{ background-color: cadetblue; }

    REF在组件

    Ref都是加在普通的html标签上而不是组件标签

    如果给组件加,比如

        

    ok

    点我输出 import Person from './components/Person.vue' import {ref} from 'vue' let title2=ref() let ren=ref() function outPrint(){ // console.log(title2.value) console.log(ren.value) } // export default{ // name:'App', // components:{Person} // }

    输出ref是组件的实例对象,但是什么具体值也看不到,原因是被本vue文件作为父级保护起来,这与Vue2不同,在Vue2中父级是可以看到所有子级的

       vue3学习笔记

    如果要显示,则需要去组件中添加

     import{ref,defineExpose} from 'vue

    defineExpose({a,b,c})

        
          

    中国

    深圳

    龙华

    点我输出h2 import{ref,defineExpose} from 'vue' // 创建一个title2,用于存储ref2标记的内容 let title2=ref() let a=ref(0) let b=ref(1) let c=ref(2) function showLog(){ // console.log(document.getElementById('title2')) console.log(title2.value) } defineExpose({a,b,c}) .person{ background-color: cadetblue; }

    vue3学习笔记

     总结

    ref可以定义在html普通标签,拿到的是元素

         也可以定义在组件上,拿到的是组件实例,能看到哪些元素取决于组件本身的expose

     TS

      约束定义

     1)在src下新建types文件夹,下面新建index.ts

    // 定义一个接口对象,用于限制person对象的具体数值
    export interface PersonInter{
        id:string,
        name:string,
        age:number
    }
    // export type Persons=Array
    export type Persons=PersonInter[]

    上面定义的是对象

    下面定义了数组,引用了对象,有两种定义方法,一种用泛型,另外一种用[]

      约束使用

       先引入

      然后用冒号

        
         ???
        
    
    
    // import的是个约束,不是具体值,所以无法打印
    import {type PersonInter,type Persons} from '@/types'
    //下文的意思是person要符合接口规范,包括变量名称
    let person:PersonInter={id:'001',name:'李白',age:22}
    //下文的意思是person数组泛型要符合接口规范,包括变量名称
    // let persons:Array=[
        let persons:Persons=[
    {id:'001',name:'李白',age:22},
    {id:'002',name:'杜甫',age:32}
    {id:'003',name:'白居易',age:36}
    ]
    
    
    .person{
        background-color: cadetblue;
    }
    
    

    Props

    1. 属性中冒号

        

          let x=99

    有了冒号代表取变量或者本身就是表达式,结果如下

      vue3学习笔记

    2.给子组件传值

       

      子组件接收:注意子组件里面的let x把所有的defineProps里面的值都保存了起来

    import {defineProps} from 'vue'
    // 接收a
    // defineProps(['a','b'])
    //接收a并且保存起来
    let x=defineProps(['a','b','list'])
    console.log(x.a)
    console.log(x)
    

    v-for

           如果写为 

    :list="5"   子组件显示会展示5次

    vue3学习笔记

         v-for的key主要是为了更新,作为索引;如果不指定key,则用数据下标0,1,2等为索引,容易混乱

      

  •   persons也可以用具体数字

    如果v-for对应list为空,则直接不显示 

    可有可无的属性

    参见x后的?

    export interface PersonInterface{
        id:string,
        name:string,
        age:number,
        x?: number
    }
    export type Persons=PersonInterface[]

    优雅泛型

    参加reactive后面的取代了PersonList:Persons

    let PersonList=reactive ([
        {id:'001',name:'李白',age:22},
        {id:'002',name:'杜甫',age:23},
        {id:'003',name:'白居易',age:24},
        {id:'004',name:'苏轼',age:25},
    ])
    

    接收并限制类型

    子组件接收时检查类型

        
           
    • {{item.id}}---{{item.name}}---{{ item.age }}
    import{defineProps,withDefaults} from 'vue' import {type Persons} from '../types' // 定义需要手动父组件传递过来的的属性名称,同时必须类型符合 // defineProps() // 接收list+限制类型 +限制必要性+默认值 withDefaults(defineProps(),{ // list:()=>[{id:'008',name:'王麻子',age:99}] } )

      宏函数

       define定义的函数比如defineProps等,vue3默认引入,不会报错

    生命周期

    也叫生命周期函数,钩子

    组件:创建       挂载            更新      销毁

              created   mounted 

    vue2声明周期

       全局安装 

    npm install -g @vue/cli

     查看vue版本   vue -V

    创建 vue create vue2test

    创建   前   before      后

    挂载

    更新

    销毁     

        
            

    当前求和为{{sum}}

    点我sum+1 export default{ /* eslint-disable */ name:'Person', data(){ return{ sum : 1, } }, methods:{ addSum(){ this.sum++; } }, // 顺序不重要,何时调vue决定 // 创建:比如人怀孕 beforeCreate() { console.log("创建前"); }, created(){ console.log("创建完毕") }, // 挂载:出生 beforeMount() { console.log("没有挂载") // debugger //停在这里 }, mounted(){ console.log("挂载完毕") }, //更新,多次 beforeUpdate(){ console.log("更新前") }, updated() { console.log("更新完毕") }, //销毁前 beforeDestroy(){ console.log("销毁前") }, destroyed(){ console.log("销毁完毕") } }

    vue3声明周期

    1.子先挂载

    vue3学习笔记

    1.子先挂载,App这个组件最后挂载

    2.vue2与vue3生命周期对比

    vue3学习笔记

    3.vue3

        
             

    {{ sum }}

    点我加1 import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'; let sum=ref(0) function addSum(){ sum.value++ } // setup 相当于befeforeCreate和created console.log("创建完了") //挂载需要引入onBeforeMounted onBeforeMount(()=>{ console.log("挂载前") }) //挂载完引入onMounted onMounted(()=>{ console.log("子---挂载完毕") }) //更新前 onUpdated(()=>{ console.log("更新前") }) //更新后 onUpdated(()=>{ console.log("更新后") }) //卸载前 onBeforeUnmount(()=>{ console.log("卸载前") }) //卸载后 onUnmounted(()=>{ console.log("卸载后") })

    axios

        npm i axios

    获取狗的图片

    https://dog.ceo/api/breed/pembroke/images/random

    后端服务器返回的一定是对象,里面有很多key value

    {data: {…}, status: 200, statusText: '', headers: AxiosHeaders, config: {…}, …}

    找到需要要的key

    hooks

        
            

    {{ sum }}

    点我加1
    再来一只狗 import { ref,reactive } from 'vue'; import axios from 'axios' let sum=ref(0); function addSum(){ sum.value++; } let dogList=reactive([ "https://images.dog.ceo/breeds/pembroke/n02113023_3927.jpg", "https://images.dog.ceo/breeds/pembroke/n02113023_1659.jpg" ]) async function moreDog(){ try{let result= await axios.get('https://dog.ceo/api/breed/pembroke/images/random') dogList.push(result.data.message)}catch(error){ console.log(error) } } img{ height: 200px; margin-right: 10px; }

    :把相关的数据和方法写在一起,有点像vue2 mixin

    step1:

      src下新建hooks文件夹

    本质就是xx.ts或xx.js

    命名规范 useXXX

    step2:

       定义bing暴露

    useDog.ts

    import { reactive } from 'vue';
    import axios from 'axios'
    //export default后面直接跟值比如export default '1',所以也可以跟一个匿名函数,如果没有default则必须命名
    export default function(){
        
    let dogList=reactive([
        "https://images.dog.ceo/breeds/pembroke/n02113023_3927.jpg",
        "https://images.dog.ceo/breeds/pembroke/n02113023_1659.jpg"
    ])
    async function getDog(){
        try{let result= await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
        dogList.push(result.data.message)}catch(error){
          console.log(error)
        }
    }
    // 向外部提供东西,可以提供对象
    return {dogList,getDog}
    }

    useSum.ts

    import { ref ,onMounted,computed} from 'vue';
    export default function(){
        let sum=ref(0);
        let bigSum=computed(()=>{
            return sum.value*10
        })
        function addSum(){
            sum.value++;
        }
        onMounted(()=>{
          addSum()
        })
        return {sum,addSum,bigSum}
    }
    

    stpe3

     引入   接收  使用

        
            

    {{ sum }} 放大10倍后{{ bigSum }}

    点我加1
    再来一只狗 import useDog from '@/hooks/useDog' import useSum from '@/hooks/useSum' const {dogList,getDog}=useDog() const {sum,addSum,bigSum}=useSum() img{ height: 200px; margin-right: 10px; }

    vue3学习笔记

    前端路由

    vue3学习笔记

    因为是单页面应用所以需要路由

    当点击左侧导航,触发路径变化

    路径变化被路由器捕获到,加载对应组件

    vue3学习笔记

    点击另外一个目录时,卸载原来的组件,挂载新的组件

    vue3学习笔记

     路由设置

       1)确定页面导航区,展示区

       2)请来路由器

               安装路由器   npm i vue-router

               src下建立router文件夹 建立 index.ts

                      vue3学习笔记

       3)制定路由的具体规则(什么路径,对应什么组件)

    // 创建一个路由器bing暴露出去
    // 1.1引入createRouter
    import { createRouter ,createWebHistory} from "vue-router";
    // 1.2引入要呈现的组件  ,一开始飘红需要关掉vscode重新打开,主要是不认vue3
    import Home from '@/components/Home.vue'
    import News from '@/components/News.vue'
    import About from '@/components/About.vue'
    // 2.创建路由器
    const router=createRouter({
        history:createWebHistory(),//确定工作模式
        routes:[ //一个一个的路由规则
            {
                path:'/home',component:Home
            },
            {
                path:'/news',component:News
            },
            {
                path:'/about',component:About
            }
        ]
    })
    // 3.暴露
    export default router
    //4.要去main.ts引入路由器

      4)main.ts中引入路由

    import { createApp } from "vue"
    import App from './App.vue'
    // 路由4:引入路由器
    import router from "./router"
    // 创建一个应用
    const app=createApp(App)
    // 使用路由器
    app.use(router)
    // 挂载整个应用
    app.mount('#app')
    

    当有个app.use(router)之后,就可以看到控制台多了Routes

    vue3学习笔记

    这时候在地址栏加比如/news  就会被router监控到,但是还不知道展示在哪里

    5)展示位置

     import { RouterView } from 'vue-router';

     

    6)增加切换支持

     import { RouterView,RouterLink} from 'vue-router';
               首页
               新闻
               关于
           

    7)选中高亮

        
            

    Vue 路由测试

    首页 新闻 关于 import { RouterView,RouterLink} from 'vue-router'; /* app */ .title{ text-align: center; word-spacing: 5px; margin: 30px 0; height: 70px; line-height: 70px; background-image: linear-gradient(45deg,gray,white); border-radius: 10px; box-shadow: 0 0 2px; font-size: 30px; } .navigate{ display: flex; justify-content: space-around; margin: 0 100px; } .navigate a{ display: block; text-align: center; width: 90px; height: 40px; line-height: 40px; border-radius: 10px; background-color: gray; text-decoration: none; color: white; font-size: 18px; letter-spacing: 5px; } .navigate a.active{ background-color: #64967E; color: #ffc268; font-weight: 900; text-shadow: 0 0 1px black; font-family: 微软雅黑; } .main-content{ margin: 0 auto; margin-top: 30px; border-radius: 10px; width: 90%; height: 400px; border: 1px solid; }

    注意

    vue3学习笔记

    路由组件:就是通过路由引入进来的

    一般组件:通过import进来的,页面上要写

    视觉小时了组件是被卸载了

    路由工作模式

    history

       vue2   mode:'history'

      vue3:    history:createWebHistory()

    vue3学习笔记

    vue3学习笔记

    hash

    vue3学习笔记

    路由to的两种写法

    1.字符串写法

    {{item.title}}

    2.对象写法1

      关于

    3.对象写法2

     新闻

       参见命名路由

    命名路由

    增加了name

     routes:[ //一个一个的路由规则
            {
                name:'zhueye',
                path:'/home',
                component:Home
            },
            {
                name:'xinwen',
                path:'/news',component:News
            },
            {   name:'guanyu',
                path:'/about',component:About
            }
        ]

    嵌套路由

    [Vue Router warn]: No match found for location with path "/"    没有/的路由http://localhost:5173/

     先引入

     routes:[ //一个一个的路由规则
            {
                name:'zhueye',
                path:'/home',
                component:Home
            },
            {
                name:'xinwen',
                path:'/news',component:News,
                children:[
                    {
                        path:'detail',
                        component:Details
                    }
                ]
            },
            {   name:'guanyu',
                path:'/about',component:About
            }
        ]
    })
        
            
            
    • {{item.title}}
    import { reactive } from 'vue'; import {RouterView,RouterLink} from 'vue-router' const news=reactive([ {id:'new001',title:'美国进攻伊朗',content:'2023000美国进攻伊朗,从波斯湾开始'}, {id:'new002',title:'一种新的学科',content:'数学化学作为一种新的学科'}, {id:'new003',title:'一种新的昆虫',content:'有点像龙和鱼'}, {id:'new003',title:'好消息',content:'北极熊到达了南极'}, ]) .news{ padding: 0 20px; display: flex; justify-content: space-between; height: 100%; } .news ul{ margin-top: 30px; list-style: none; padding-left: 10px; } .news li>a{ font-size: 18px; line-height: 40px; text-decoration: none; color: #64967E; text-shadow: 0 0 1px rgb(0,84,0); } .news-content{ width: 70%; height: 90%; border: 1px solid ; margin-top: 20px; border-radius: 10px; }

    路由参数

    query

      父给子      {{item.title}}

    a=哈哈相当于键值对   &连接多个

    子组件接收:

        引入route

       
    • 编号:{{route.query.id}}
    • 标题:{{route.query.title}}
    • 内容:{{route.query.content}}
    import {useRoute} from 'vue-router' let route=useRoute() console.log(route)

    传递的参数在route对象的Target里面的query参数

    vue3学习笔记

    第一种写法

      {{item.title}}

    第二种写法

        

    简化子处使用参数

    注意解构赋值会丢失响应式

    新的details.vue

        
      {route.query.id}}
  • 标题:{{route.query.title}}
  • 内容:{{route.query.content}}
  • -->
  • 编号:{{query.id}}
  • 标题:{{query.title}}
  • 内容:{{query.content}}
import {toRefs} from 'vue' import {useRoute} from 'vue-router' let route=useRoute() console.log(route) // 可以从route上进行解构赋值,这是从响应对象上直接解构,丢失响应式,页面切换无效果 // let {query} =route // 可以从route上进行解构赋值,并用toRefs let {query} =toRefs(route) .news-list{ list-style: none; padding-left: 20px; } .news-list>li{ line-height: 30px; }

params

三个注意

   1)在路由注册了参数,才能使用,并且不能缺

      除非在路由参数加?   path:'detail/:id/:title/:content?',

   2)router link对象写法中必须使用路由name

   3)router link对象写法中参数值不能是数组,对象

在路由后面不写?直接继续写/,也不用键值对

下面红色是路由,绿色是参数

vue3学习笔记

  {{item.title}}

没占位前会报  Vue Router warn]: No match found for location with path "/news/detail/哈哈/呃呃"

2)router/index.ts占位

      path:'detail/:id/:title/:content',

有三个占位,则要求调用时必须是3个否则无法识别

  {{item.title}}

vue3学习笔记

    
    {route.query.id}}
  • 标题:{{route.query.title}}
  • 内容:{{route.query.content}}
  • --> {query.id}}
  • 标题:{{query.title}}
  • 内容:{{query.content}}
  • -->
  • 编号:{{route.params.id}}
  • 标题:{{route.params.title}}
  • 内容:{{route.params.content}}
// 1.使用query // import {toRefs} from 'vue' // import {useRoute} from 'vue-router' // let route=useRoute() // console.log(route) // // 可以从route上进行解构赋值,这是从响应对象上直接解构,丢失响应式,页面切换无效果 // // let {query} =route // // 可以从route上进行解构赋值,并用toRefs // let {query} =toRefs(route) // 2.使用Params import {useRoute} from 'vue-router' let route=useRoute() console.log(route) .news-list{ list-style: none; padding-left: 20px; } .news-list>li{ line-height: 30px; }

父组件

     如下写法必须使用name

    
        
        
  • {item.title}} --> {item.title}} --> {item.title}} --> {item.title}} --> {item.title}} --> {{item.title}}
import { reactive } from 'vue'; import {RouterView,RouterLink} from 'vue-router' const news=reactive([ {id:'new001',title:'美国进攻伊朗',content:'2023000美国进攻伊朗,从波斯湾开始'}, {id:'new002',title:'一种新的学科',content:'数学化学作为一种新的学科'}, {id:'new003',title:'一种新的昆虫',content:'有点像龙和鱼'}, {id:'new003',title:'好消息',content:'北极熊到达了南极'}, ]) .news{ padding: 0 20px; display: flex; justify-content: space-between; height: 100%; } .news ul{ margin-top: 30px; /* list-style: none; */ padding-left: 10px; } .news li::marker{ color: #64967E; } .news li>a{ font-size: 18px; line-height: 40px; text-decoration: none; color: #64967E; text-shadow: 0 0 1px rgb(0,84,0); } .news-content{ width: 70%; height: 90%; border: 1px solid ; margin-top: 20px; border-radius: 10px; }

路由的props配置

为了简化页面引用的层级,可以在路由参数中配置:  props:true

第一种写法

   将路由收到所有params参数作为props传给路由组件

  routes:[ //一个一个的路由规则
        {
            name:'zhueye',
            path:'/home',
            component:Home
        },
        {
            name:'xinwen',
            path:'/news',component:News,
            children:[
                {   
                    name:'xiangqing',
                    path:'detail/:id/:title/:content?',
                    component:Details,
                    props:true
                }
            ]
        },
        {   name:'guanyu',
            path:'/about',component:About
        }
    ]

加了props相当于在使用Detail时顺便传三个参数

vue3学习笔记

    
    {route.query.id}}
  • 标题:{{route.query.title}}
  • 内容:{{route.query.content}}
  • --> {query.id}}
  • 标题:{{query.title}}
  • 内容:{{query.content}}
  • --> {route.params.id}}
  • 标题:{{route.params.title}}
  • 内容:{{route.params.content}}
  • -->
  • 编号:{{id}}
  • 标题:{{title}}
  • 内容:{{content}}
// 1.使用query // import {toRefs} from 'vue' // import {useRoute} from 'vue-router' // let route=useRoute() // console.log(route) // // 可以从route上进行解构赋值,这是从响应对象上直接解构,丢失响应式,页面切换无效果 // // let {query} =route // // 可以从route上进行解构赋值,并用toRefs // let {query} =toRefs(route) // // 2.使用Params // import {useRoute} from 'vue-router' // let route=useRoute() // console.log(route) //3使用路由props defineProps(['id','title','content']) .news-list{ list-style: none; padding-left: 20px; } .news-list>li{ line-height: 30px; }

第二种写法

可以用于query

   自己决定将什么作为路由组件传给props

{{item.title}}
  children:[
                {   
                    name:'xiangqing',
                    // path:'detail/:id/:title/:content?',
                    path:'detail',
                    component:Details,
                    // 第一种写法将路由收到所有params参数作为props传给路由组件
                    // props:true
                    //第二种写法    自己决定将什么作为路由组件传给props
                    props(qwe){
               console.log(qwe)
                        return{
                            x:100,
                            y:200,
                            z:300
                        }
                    }
                }
            ]
        },

可以看到qwe就是route

vue3学习笔记

 router文件修改为

       children:[
                {   
                    name:'xiangqing',
                    // path:'detail/:id/:title/:content?',
                    path:'detail',
                    component:Details,
                    // 第一种写法将路由收到所有params参数作为props传给路由组件
                    // props:true
                    //第二种写法    自己决定将什么作为路由组件传给props
                    // props收到的参数本质就是route,并且因为在query查询时query也是对象,所以可以返回
                    props(route){
                        return route.query
                    }
                }
            ]
    
    {route.query.id}}
  • 标题:{{route.query.title}}
  • 内容:{{route.query.content}}
  • --> {query.id}}
  • 标题:{{query.title}}
  • 内容:{{query.content}}
  • --> {route.params.id}}
  • 标题:{{route.params.title}}
  • 内容:{{route.params.content}}
  • -->
  • 编号:{{id}}
  • 标题:{{title}}
  • 内容:{{content}}
// 1.使用query // import {toRefs} from 'vue' // import {useRoute} from 'vue-router' // let route=useRoute() // console.log(route) // // 可以从route上进行解构赋值,这是从响应对象上直接解构,丢失响应式,页面切换无效果 // // let {query} =route // // 可以从route上进行解构赋值,并用toRefs // let {query} =toRefs(route) // // 2.使用Params // import {useRoute} from 'vue-router' // let route=useRoute() // console.log(route) //3使用路由props defineProps(['id','title','content']) .news-list{ list-style: none; padding-left: 20px; } .news-list>li{ line-height: 30px; }

第三种写法

对象写法,但是是写死了,很少用

vue3学习笔记

replace

路由跳转时,会操作浏览器历史记录,默认是push

push:

   相当于浏览器历史记录是个栈,有个指针

vue3学习笔记

replace

    替换

在导航区routelink上加replace

     首页
           新闻
           关于

编程式导航

脱离实现跳转

1.设置自动跳转

import { useRouter } from 'vue-router';

    
          vue3学习笔记
    


import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
const router=useRouter()
onMounted(()=>{
    setTimeout(()=>{
    // 在此处编写一段代码,让路由实现跳转
      router.push('/news')
    },3000)
})


.home{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
}

2.设置点击跳转

vue3学习笔记

    
        
        
  • {item.title}} --> {item.title}} --> {item.title}} --> {item.title}} --> {item.title}} --> 查看新闻 {{item.title}}
import { isTemplateExpression } from 'typescript'; import { reactive } from 'vue'; import {RouterView,RouterLink,useRouter} from 'vue-router' const news=reactive([ {id:'new001',title:'美国进攻伊朗',content:'2023000美国进攻伊朗,从波斯湾开始'}, {id:'new002',title:'一种新的学科',content:'数学化学作为一种新的学科'}, {id:'new003',title:'一种新的昆虫',content:'有点像龙和鱼'}, {id:'new003',title:'好消息',content:'北极熊到达了南极'}, ]) const router=useRouter() interface ItemInter{ id:string, title:string, content:string } // function showNewsDetail(item:any){ function showNewsDetail(item:ItemInter){ // push()方法支持类似to=""中双引号所包围内容 的写法 // router.push("/news/detail/哈哈/呃呃/嘿嘿") router.push({ name:'xiangqing', query:{ id:item.id, title:item.title, content:item.content } }) } .news{ padding: 0 20px; display: flex; justify-content: space-between; height: 100%; } .news ul{ margin-top: 30px; /* list-style: none; */ padding-left: 10px; } .news li::marker{ color: #64967E; } .news li>a{ font-size: 18px; line-height: 40px; text-decoration: none; color: #64967E; text-shadow: 0 0 1px rgb(0,84,0); } .news-content{ width: 70%; height: 90%; border: 1px solid ; margin-top: 20px; border-radius: 10px; }

 重定向

主要是解决刚进入网站初始页面路由报错的问题

、让指定的路径重新定位到另一个路径

vue3学习笔记

// 创建一个路由器bing暴露出去
// 1.1引入createRouter
import { createRouter ,createWebHistory} from "vue-router";
// 1.2引入要呈现的组件  ,一开始飘红需要关掉vscode重新打开,主要是不认vue3
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import About from '@/pages/About.vue'
import Details from "@/pages/Details.vue";
// 2.创建路由器,自己路由不用写斜杠
const router=createRouter({
    history:createWebHistory(),//确定工作模式
    routes:[ //一个一个的路由规则
        {
            name:'zhueye',
            path:'/home',
            component:Home
        },
        {
            name:'xinwen',
            path:'/news',component:News,
            children:[
                {   
                    name:'xiangqing',
                    // path:'detail/:id/:title/:content?',
                    path:'detail',
                    component:Details,
                    // 第一种写法将路由收到所有params参数作为props传给路由组件
                    // props:true
                    //第二种写法    自己决定将什么作为路由组件传给props
                    // props收到的参数本质就是route,并且因为在query查询时query也是对象,所以可以返回
                    // props(route){
                    //     return route.query
                    // }
                    // 第三种写法
                    props:{
                        a:100,
                        b:200,
                        c:300
                    }
                }
            ]
        },
        {   name:'guanyu',
            path:'/about',component:About
        },
        {
            path:'/',
            redirect:'/home'
        }
    ]
})
// 3.暴露
export default router
//4.要去main.ts引入路由器

pinia:共用数据的处理

Pinia | The intuitive store for Vue.js

Pinia 是 Vue 的存储库,它允许跨组件/页面共享状态。实际上,pinia就是Vuex的升级版,官网也说过,为了尊重原作者,所以取名pinia,而没有取名Vuex,所以大家可以直接将pinia比作为Vue3的Vuex。

vue2用的是vueX 

vue3用的是pinia

   集中式状态(也就是数据)管理

把各个组件需要共享的数据交给pinia

https://api.uomg.com/api/rand.qinghua?format=json

生成id  npm i nanoid   npm i uuid

准备

1.sum.app  注意把下值转换为数字的两种方法

    v-model.number=‘n’  这样更优雅

或者:value=

    
       

当前求和为{{ sum }}

1 2 3 加 减 import { ref } from 'vue'; let sum=ref(0) let n=ref(0) function add(){ sum.value+=n.value } function minus(){ sum.value-=n.value } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

2.Talk.vue

注意axios获取接口数据后的两次结构,一次重命名

还有取随机数

    
       获取一句诗歌
       
  • {{ item.title }}
import { reactive } from 'vue'; // axios是默认暴露,直接引入即可 import axios from 'axios'; import {nanoid} from 'nanoid' let talkList=reactive([ {id:'0001',title:'秦时明月汉时关'}, {id:'0002',title:'床前明月光'}, {id:'0003',title:'北风卷地百草折'}, {id:'0004',title:'东边不亮西边亮'}, {id:'0005',title:'遥看瀑布挂前川'} ]) async function getPoem(){ // 发请求 // let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // console.log(result.data.content) // 把请求回来的字符串包装成一个对象 // let obj={id:nanoid(),title:result.data.content} // let obj={id:nanoid(),title:title} let obj={id:nanoid(),title} talkList.unshift(obj) } .talk{ background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; }

安装

npm i pinia

main.ts引入  创建 安装三步

import { createApp } from "vue"
import App from './App.vue'
// pinia1 :引入pinia
import { createPinia } from "pinia"
// 路由4:引入路由器
// import router from "./router"
// 创建一个应用
const app=createApp(App)
// 使用路由器
// app.use(router)
// pinia2 app创建完后创建pinia
const pinia=createPinia()
// pinia3 :安装pinia
app.use(pinia)
// 挂载整个应用
app.mount('#app')

之后浏览器会出现

vue3学习笔记

建立仓库

 pinia强调分类

src下应该建立store

在其下建立比如count.ts

import { defineStore } from "pinia";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useCountStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'count',
    {
        // 真正存储数据的地方是state
        state(){
        return{
            sum:6
        }}
    }
)

这样就有了

vue3学习笔记

把cout.ts想象成一个仓库,只要跟统计相关的都放入

组件使用

// pinar1:引入
import {useCountStore} from '@/store/count'
// 
const countStore=useCountStore();
console.log("@@@@@",countStore)

countStore实际上是reactive定义的响应式对象

vue3学习笔记

关注sum和$state

vue3学习笔记

进一步展开

vue3学习笔记

 sum是个REF对象,其有value,但是contStore是个Reactive对象

怎么样读出reactive里面的ref值呢?不用.value即可

let obj=reactive({
    a:1,
    b:2,
    c:ref(3)
})
// 在reactove里面的ref不用再拆value出来
// console.log(obj.c.value)
console.log(obj.c)

countStore.sum

也可以从state下面拿到,麻烦一点

countStore.$state.sum

再查看控制台,注意 就是count.ts被组件使用了才会出现在这里

vue3学习笔记

另外一个例子

talk.ts

import { defineStore } from "pinia";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useTalkStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'talk',
    {
        // 真正存储数据的地方是state
        state(){
        return{
            talkList:[
                {id:'0001',title:'秦时明月汉时关'},
                {id:'0002',title:'床前明月光'},
                {id:'0003',title:'北风卷地百草折'},
                {id:'0004',title:'东边不亮西边亮'},
                {id:'0005',title:'遥看瀑布挂前川'}
            ]
        }}
    }
)

被使用后

talk.vue

    
       获取一句诗歌
       
  • {{ item.title }}
import { reactive } from 'vue'; // axios是默认暴露,直接引入即可 import axios from 'axios'; import {nanoid} from 'nanoid' import {useTalkStore} from '@/store/talk' // let talkList=reactive([ // {id:'0001',title:'秦时明月汉时关'}, // {id:'0002',title:'床前明月光'}, // {id:'0003',title:'北风卷地百草折'}, // {id:'0004',title:'东边不亮西边亮'}, // {id:'0005',title:'遥看瀑布挂前川'} // ]) const talkStore=useTalkStore(); console.log(talkStore) async function getPoem(){ // 发请求 // let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // // console.log(result.data.content) // // 把请求回来的字符串包装成一个对象 // // let obj={id:nanoid(),title:result.data.content} // // let obj={id:nanoid(),title:title} // let obj={id:nanoid(),title} // talkList.unshift(obj) } .talk{ background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; }

talkList是reactive下的reactive,

vue3学习笔记 修改数据

三种修改方法-1,2

    
       

当前求和为{{ countStore.sum }}

欢迎来到{{ countStore.school }}坐落于{{ countStore.address }}

1 2 3 加 减 import { reactive, ref } from 'vue'; // pinar1:引入 import {useCountStore} from '@/store/count' // const countStore=useCountStore(); // console.log("@@@@@",countStore.sum) console.log("@@@@@",countStore) let n=ref(0) function add(){ // 第一种修改数据方法:直接改 // countStore.sum+=n.value // countStore.school="南京大学" // countStore.address="南京" // 第二种修改方法:适用于数据较多 countStore.$patch( { sum:999, school:'苏州大学', address:'苏州' } ) } function minus(){ } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

其它知识:

时间线的介绍

vue3学习笔记

Mouse:看鼠标事件

常用的Componet events:组件事件

当用上述第一种直接修改,时间线pinia出现了3次,因为确实直接修改了三次

vue3学习笔记

当使用上述第二种方法时,只发生了1次

vue3学习笔记

 第三种修改方法

   在store/count.ts中使用action

import { defineStore } from "pinia";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useCountStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'count',
 
    {
    //action里面放置的是一个一个方法,用于响应组件中的“动作"
    actions:{
       increment(value:any){
        console.log("increment被调用了",value,this)
        // 通过this操作
        this.sum+=value
        // 或者
        
       }
    },
        // 真正存储数据的地方是state
        state(){
        return{
            sum:6,
            school:'btj',
            address:'beijing'
        }}
    }
)

组件

    
       

当前求和为{{ countStore.sum }}

欢迎来到{{ countStore.school }}坐落于{{ countStore.address }}

1 2 3 加 减 import { reactive, ref } from 'vue'; // pinar1:引入 import {useCountStore} from '@/store/count' // const countStore=useCountStore(); // console.log("@@@@@",countStore.sum) console.log("@@@@@",countStore) let n=ref(0) function add(){ // 第一种修改数据方法:直接改 // countStore.sum+=n.value // countStore.school="南京大学" // countStore.address="南京" // 第二种修改方法:适用于数据较多 // countStore.$patch( // { // sum:999, // school:'苏州大学', // address:'苏州' // } // ) // 第三种修改方法: countStore.increment(n.value); } function minus(){ } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

注意this是当前的store,就是countStore可以直接使用sum

vue3学习笔记

还有一个常识:$开始的都是给程序员用的,也可以用里面的$state

vue3学习笔记

第二个案例
import { defineStore } from "pinia";
import axios from "axios";
import { nanoid } from "nanoid";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useTalkStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'talk',
    {
        actions:{
         async  getPoem(){
                // 发请求
                let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
                // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title
                // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
                // // console.log(result.data.content)
                // // 把请求回来的字符串包装成一个对象
                 let obj={id:nanoid(),title:result.data.content}
                // // let obj={id:nanoid(),title:title}
                // let obj={id:nanoid(),title}
                 this.talkList.unshift(obj)
             }
        },
        // 真正存储数据的地方是state
        state(){
        return{
            talkList:[
                {id:'0001',title:'秦时明月汉时关'},
                {id:'0002',title:'床前明月光'},
                {id:'0003',title:'北风卷地百草折'},
                {id:'0004',title:'东边不亮西边亮'},
                {id:'0005',title:'遥看瀑布挂前川'}
            ]
        }}
    }
)
    
       获取一句诗歌
       
  • {{ item.title }}
import { reactive } from 'vue'; // axios是默认暴露,直接引入即可 // import axios from 'axios'; // import {nanoid} from 'nanoid' import {useTalkStore} from '@/store/talk' // let talkList=reactive([ // {id:'0001',title:'秦时明月汉时关'}, // {id:'0002',title:'床前明月光'}, // {id:'0003',title:'北风卷地百草折'}, // {id:'0004',title:'东边不亮西边亮'}, // {id:'0005',title:'遥看瀑布挂前川'} // ]) const talkStore=useTalkStore(); // console.log(talkStore) // async function getPoem(){ // 发请求 // let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // // console.log(result.data.content) // // 把请求回来的字符串包装成一个对象 // // let obj={id:nanoid(),title:result.data.content} // // let obj={id:nanoid(),title:title} // let obj={id:nanoid(),title} // talkList.unshift(obj) function getPoem(){ talkStore.getPoem(); } .talk{ background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; }

优雅化

目的是让vue文件插值中的{{counterStore.sum]]中的counterStorm能被去掉

方法1  使用toRefs解构

    代价:太多东西被toRefs

count.ts

import { defineStore } from "pinia";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useCountStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'count',
 
    {
    //action里面放置的是一个一个方法,用于响应组件中的“动作"
    actions:{
       increment(value:number){
        console.log("increment被调用了",value,this)
        // 通过this操作
        this.sum+=value
        // 或者
        
       }
    },
        // 真正存储数据的地方是state
        state(){
        return{
            sum:6,
            school:'btj',
            address:'beijing'
        }}
    }
)

Count.vue

    
       

当前求和为{{ sum }}

欢迎来到{{ countStore.school }}坐落于{{ countStore.address }}

1 2 3 加 减 import { reactive, ref,toRefs } from 'vue'; // pinar1:引入 import {useCountStore} from '@/store/count' // const countStore=useCountStore(); // console.log("@@@@@",countStore.sum) // console.log("@@@@@",countStore) // 解构 const {sum,school,address} =toRefs(countStore) console.log(toRefs(countStore)) let n=ref(0) function add(){ // 第一种修改数据方法:直接改 // countStore.sum+=n.value // countStore.school="南京大学" // countStore.address="南京" // 第二种修改方法:适用于数据较多 // countStore.$patch( // { // sum:999, // school:'苏州大学', // address:'苏州' // } // ) // 第三种修改方法: countStore.increment(n.value); } function minus(){ countStore.sum-=n.value } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

上文中用了toRefs,但是显示把countStores所有属性都变成了ref,没有这个必要

vue3学习笔记

 方法2  使用storeToRefs 解构

pinia替你想到了

import { storeToRefs } from 'pinia';

// 解构

const {sum,school,address} =storeToRefs(countStore)

console.log("aaaaa",storeToRefs(countStore))

可以看到storeToRefs只关注数据,不会对方法进行包裹

vue3学习笔记

    
       

当前求和为{{ sum }}

欢迎来到{{ school }}坐落于{{ address }}

1 2 3 加 减 import { reactive, ref,toRefs } from 'vue'; import { storeToRefs } from 'pinia'; // pinar1:引入 import {useCountStore} from '@/store/count' // const countStore=useCountStore(); // console.log("@@@@@",countStore.sum) // console.log("@@@@@",countStore) // 解构 const {sum,school,address} =storeToRefs(countStore) console.log("aaaaa",storeToRefs(countStore)) let n=ref(0) function add(){ // 第一种修改数据方法:直接改 // countStore.sum+=n.value // countStore.school="南京大学" // countStore.address="南京" // 第二种修改方法:适用于数据较多 // countStore.$patch( // { // sum:999, // school:'苏州大学', // address:'苏州' // } // ) // 第三种修改方法: countStore.increment(n.value); } function minus(){ countStore.sum-=n.value } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

getters

对数据不满意,可以加工下

写法1

import { defineStore } from "pinia";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useCountStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'count',
 
    {
    //action里面放置的是一个一个方法,用于响应组件中的“动作"
    actions:{
       increment(value:number){
        console.log("increment被调用了",value,this)
        // 通过this操作
        this.sum+=value
        // 或者
        
       }
    },
        // 真正存储数据的地方是state
        state(){
        return{
            sum:6,
            school:'btj',
            address:'beijing'
        }},
        getters:{
            // bigSum(){
            //     return 999
            // }
// 默认被传递进来state
              bigSum(state){
                return state.sum*10
            }
        }
    }
)

vue3学习笔记

写法2

    this起始也是state

vue3学习笔记

import { defineStore } from "pinia";
import { compileScript } from "vue/compiler-sfc";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useCountStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'count',
 
    {
    //action里面放置的是一个一个方法,用于响应组件中的“动作"
    actions:{
       increment(value:number){
        console.log("increment被调用了",value,this)
        // 通过this操作
        this.sum+=value
        // 或者
        
       }
    },
        // 真正存储数据的地方是state
        state(){
        return{
            sum:6,
            school:'btj',
            address:'beijing'
        }},
        getters:{
            // bigSum(){
            //     return 999
            // }
// 默认被传递进来state
            //   bigSum(state){
            //     return state.sum*10
            // },
//简写
      bigSum:state=>state.sum*10,
   //   也可以用this
            // upperSchool(state){
                // :String是表示返回值是字符串
                upperSchool():String{
                console.log("upperschool",this)
                return this.school.toUpperCase()
            }
        }
    }
)

Count.vue

    
       

当前求和为{{ sum }},放大10倍后{{ bigSum }}

欢迎来到{{ school }},坐落于{{ address }} 大写{{ upperSchool }}

1 2 3 加 减 import { reactive, ref,toRefs } from 'vue'; import { storeToRefs } from 'pinia'; // pinar1:引入 import {useCountStore} from '@/store/count' // const countStore=useCountStore(); // console.log("@@@@@",countStore.sum) // console.log("@@@@@",countStore) // 解构 const {sum,school,address,bigSum,upperSchool} =storeToRefs(countStore) console.log("aaaaa",storeToRefs(countStore)) let n=ref(0) function add(){ // 第一种修改数据方法:直接改 // countStore.sum+=n.value // countStore.school="南京大学" // countStore.address="南京" // 第二种修改方法:适用于数据较多 // countStore.$patch( // { // sum:999, // school:'苏州大学', // address:'苏州' // } // ) // 第三种修改方法: countStore.increment(n.value); } function minus(){ countStore.sum-=n.value } .count{ background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } select,button{ margin: 0 5px; height: 30px; }

订阅

  相当于监视store数据变化,可以利用来保存到storage,实现持久化

    
       获取一句诗歌
       
  • {{ item.title }}
import { reactive } from 'vue'; // axios是默认暴露,直接引入即可 // import axios from 'axios'; // import {nanoid} from 'nanoid' import {useTalkStore} from '@/store/talk' import { storeToRefs } from 'pinia'; // let talkList=reactive([ // {id:'0001',title:'秦时明月汉时关'}, // {id:'0002',title:'床前明月光'}, // {id:'0003',title:'北风卷地百草折'}, // {id:'0004',title:'东边不亮西边亮'}, // {id:'0005',title:'遥看瀑布挂前川'} // ]) const talkStore=useTalkStore(); const {talkList}=storeToRefs(talkStore) // talkStore发生变化,会传递mutate即变化信息 state talkStore.$subscribe((mutate,state)=>{ console.log("talkStore里面保存的数据发生了变化",mutate,state) // 可以做的事举例 localStorage.setItem('talkList',state.talkList) }) // console.log(talkStore) // async function getPoem(){ // 发请求 // let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // // console.log(result.data.content) // // 把请求回来的字符串包装成一个对象 // // let obj={id:nanoid(),title:result.data.content} // // let obj={id:nanoid(),title:title} // let obj={id:nanoid(),title} // talkList.unshift(obj) function getPoem(){ talkStore.getPoem(); } .talk{ background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; }

mutate  和 state分别打印

vue3学习笔记

应用1:修改locaiStorage

比如

locaiStorage里面都是字符串,如果你传的不是字符串会调用toString,如果都是对象就会变成如下:

vue3学习笔记

修改为

    localStorage.setItem('talkList',JSON.stringify(state.talkList)) 

talk.ts

import { defineStore } from "pinia";
import axios from "axios";
import { nanoid } from "nanoid";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
export const useTalkStore=defineStore(
    // 第一个参数一般建议与文件名一致
    'talk',
    {
        actions:{
         async  getPoem(){
                // 发请求
                let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
                // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title
                // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
                // // console.log(result.data.content)
                // // 把请求回来的字符串包装成一个对象
                 let obj={id:nanoid(),title:result.data.content}
                // // let obj={id:nanoid(),title:title}
                // let obj={id:nanoid(),title}
                 this.talkList.unshift(obj)
             }
        },
        // 真正存储数据的地方是state
        state(){
        return{
            // 可以从localStorage中获取
            // talkList:[
            //     {id:'0001',title:'秦时明月汉时关'},
            //     {id:'0002',title:'床前明月光'},
            //     {id:'0003',title:'北风卷地百草折'},
            //     {id:'0004',title:'东边不亮西边亮'},
            //     {id:'0005',title:'遥看瀑布挂前川'}
            // ]
            // talkList:JSON.parse(localStorage.getItem('talkList'))
            // 可能去除null,解决方法1:用断言,但是这样初始化就是null,二要添加的时候就会报不能在null上添加unshift,就是null.unshift
            // talkList:JSON.parse(localStorage.getItem('talkList') as string) 
            //如下解决null的问题
            talkList:JSON.parse(localStorage.getItem('talkList') as string)||[]
        }}
    }
)

talk.vue

    
       获取一句诗歌
       
  • {{ item.title }}
import { reactive } from 'vue'; // axios是默认暴露,直接引入即可 // import axios from 'axios'; // import {nanoid} from 'nanoid' import {useTalkStore} from '@/store/talk' import { storeToRefs } from 'pinia'; // let talkList=reactive([ // {id:'0001',title:'秦时明月汉时关'}, // {id:'0002',title:'床前明月光'}, // {id:'0003',title:'北风卷地百草折'}, // {id:'0004',title:'东边不亮西边亮'}, // {id:'0005',title:'遥看瀑布挂前川'} // ]) const talkStore=useTalkStore(); const {talkList}=storeToRefs(talkStore) // talkStore发生变化,会传递mutate即变化信息 state talkStore.$subscribe((mutate,state)=>{ console.log("talkStore里面保存的数据发生了变化",mutate,state) // 可以做的事举例 localStorage.setItem('talkList',JSON.stringify(state.talkList)) }) // console.log(talkStore) // async function getPoem(){ // 发请求 // let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // // console.log(result.data.content) // // 把请求回来的字符串包装成一个对象 // // let obj={id:nanoid(),title:result.data.content} // // let obj={id:nanoid(),title:title} // let obj={id:nanoid(),title} // talkList.unshift(obj) function getPoem(){ talkStore.getPoem(); } .talk{ background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; }

 store组合式写法

缺点是return多

import { defineStore } from "pinia";
import axios from "axios";
import { nanoid } from "nanoid";
// const命名规范:以use开始
// 下面的export使用的是分离暴露,也可以使用统一暴露,分离暴露引用时只能用import {}
// 1.选项式写法
// export const useTalkStore=defineStore(
//     // 第一个参数一般建议与文件名一致
//     'talk',
//     {
//         actions:{
//          async  getPoem(){
//                 // 发请求
//                 let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
//                 // 以下是从返回中先结构data,再从data中结构content,并且把content重命名为title
//                 // let {data:{content:title}}= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
//                 // // console.log(result.data.content)
//                 // // 把请求回来的字符串包装成一个对象
//                  let obj={id:nanoid(),title:result.data.content}
//                 // // let obj={id:nanoid(),title:title}
//                 // let obj={id:nanoid(),title}
//                  this.talkList.unshift(obj)
//              }
//         },
//         // 真正存储数据的地方是state
//         state(){
//         return{
//             // 可以从localStorage中获取
//             // talkList:[
//             //     {id:'0001',title:'秦时明月汉时关'},
//             //     {id:'0002',title:'床前明月光'},
//             //     {id:'0003',title:'北风卷地百草折'},
//             //     {id:'0004',title:'东边不亮西边亮'},
//             //     {id:'0005',title:'遥看瀑布挂前川'}
//             // ]
//             // talkList:JSON.parse(localStorage.getItem('talkList'))
//             // 可能去除null,解决方法1:用断言,但是这样初始化就是null,二要添加的时候就会报不能在null上添加unshift,就是null.unshift
//             // talkList:JSON.parse(localStorage.getItem('talkList') as string) 
//             //如下解决null的问题
//             talkList:JSON.parse(localStorage.getItem('talkList') as string)||[]
//         }}
//     }
// )
// 1.组合式写法
// 组合式数据直接用reactive定义
import { reactive } from "vue";
export const useTalkStore=defineStore('talk',()=>{
    // talklist就是state
    const talkList=reactive(
        JSON.parse(localStorage.getItem('talkList') as string)||[]
    )
    // getPoem相当于action
    async function  getPoem(){
                        let result= await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
                         let obj={id:nanoid(),title:result.data.content}
                        talkList.unshift(obj)
                     }
return {talkList,getPoem}
})

组件通信

vue3学习笔记

1.props

   可以实现父子双向传递

vue3学习笔记

子传父要调用父的函数vue3学习笔记

vue3学习笔记

父:

    
    

父组件

汽车{{ car }}

子给的 {{ toy }}

import Child from './Child.vue'; import {ref} from 'vue' // 数据 let car=ref('奔驰') let toy=ref('') // 方法 function getToy(value:string){ console.log('父',value) toy.value=value } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

    
    

子组件

{{ toy }}

父亲给的车:{{ car }}

把玩具给父亲 import {ref} from 'vue' // 数据 let toy=ref('奥特曼') // 声明接收props defineProps(['car','sendToy']) // let props=defineProps(['car','sendToy']) // // 方法 // function fasong(){ // console.log(props.sendToy) // } .child{ background-color: skyblue; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

尽可能不要给孙子传,虽然能实现

vue3学习笔记

 2.自定义事件

  典型的用于子传父

   事件名是多个单词是推荐肉串形式  a-b-c, 使用驼峰可能导致见听不到

vue3学习笔记

$event

  点我

function test(value){

    console.log('test',value)

}

1)调用时如果什么都不传,发现value是事件对象

vue3学习笔记

2)有参调用时需要调用时写$event

    
    

父组件

点我 import Child from './Child.vue'; function test(a:number,b:number,c:Event){ console.log('test',c) } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

3)直接使用 @click="str=$event"

    
    

父组件

{{ str }}

点我 import Child from './Child.vue'; import { ref } from 'vue'; let str=ref("你好") function test(){ str.value="哈哈" } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

点击输出:

vue3学习笔记

 自定义事件

  父亲绑定:

   

   
    
    
    

父组件

{ str }} --> import Child from './Child.vue'; // import { ref } from 'vue'; // let str=ref("你好") // function test(){ // str.value="哈哈" // } function xyz(value:number){ console.log('xyz',value) } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

  子声明   

并且调用时可以传值

   

    
    

子组件

玩具 {{ toy }}

测试调用父给子绑定的自定义事件并且传递值 import {ref,onMounted} from 'vue' let toy=ref("奥特曼") // 声明事件,并赋值给变量,这样模版就可以用了 const emit=defineEmits(['abc']) // 加载3秒后触发abc事件,触发结果就是调用Father的xyz onMounted(()=>{ setTimeout(()=>{ emit('abc') },3000) }) .child{ background-color: skyblue; margin-top: 10px; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

 自定义事件例子2

Father.vue

    
    

父组件

{ str }} -->

子给的玩具 {{ toy }}

import Child from './Child.vue'; import {ref} from 'vue' let toy=ref("") // import { ref } from 'vue'; // let str=ref("你好") // function test(){ // str.value="哈哈" // } // function xyz(value:number){ // console.log('xyz',value) // } // function saveToy(value:number){ // console.log('xyz',value) function saveToy(value:string){ console.log('sendToy',value) toy.value=value } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

Child.vue

    
    

子组件

玩具 {{ toy }}

测试调用父给子绑定的自定义事件并且传递值 import {ref,onMounted} from 'vue' let toy=ref("奥特曼") // 声明事件,并赋值给变量,这样模版就可以用了 // const emit=defineEmits(['abc']) const emit=defineEmits(['sent-toy']) // 加载3秒后触发abc事件,触发结果就是调用Father的xyz // onMounted(()=>{ // setTimeout(()=>{ // emit('abc') // },3000) // }) .child{ background-color: skyblue; margin-top: 10px; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

3.mitt

任意组件通信

pubsub 订阅

$bus

mitt

收数据的:提前绑定好事件(pubsub叫提前订阅好消息)

提供数据的:在合适的时候触发事件(pubsub叫发布消息)

本质是定义一个公共区域

vue3学习笔记

1.安装 npm i mitt

2.src下建立utils文件夹

     emitter.ts

// 引入mitt
import mitt from 'mitt'
// 调用mitt 获得傀儡emitter emitter可以绑定事件,出发事件
const emitter=mitt()
// 暴露emitter
export default emitter

3.main.ts中引入一行

  

import { createApp } from "vue";
import App from './App.vue'
import { createPinia } from "pinia";
import router from "./router";
import emitter from '@/utils/emitter'
const app=createApp(App)
const pinia=createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')

4.使用

   emitter.ts中

vue3学习笔记

all:拿到所有事件

emit:触发事件

off:解绑事件

on:绑定事件

简单使用

// 引入mitt
import mitt from 'mitt'
// 调用mitt 获得傀儡emitter emitter可以绑定事件,出发事件
const emitter=mitt()
// 绑定事件
emitter.on('test1',()=>{
    console.log("test1被调用了")
})
emitter.on('test2',()=>{
    console.log("test2被调用了")
})
// 触发事件
// setTimeout(()=>{
//     emitter.emit('test1')
//     emitter.emit('test2')
    
// },5000)
setInterval(
    ()=>{
        emitter.emit('test1')
          emitter.emit('test2') 
    },2000
)
// 解绑事件,3s后解绑test1
setTimeout(()=>{
emitter.off('test1')
},4000)
// 清空事件
setTimeout(()=>{
    emitter.all.clear()
    },8000)
    

// 暴露emitter
export default emitter

例子1

   哥哥提供玩具给弟弟

哥哥提供数据:触发事件/发布消息

弟弟接收数据:绑定事件/接收消息

vue3学习笔记

关键是两个组件都要import emitter from '@/utils/emitter'

弟弟

    
    

子组件2

电脑{{ computer }}

哥哥给的玩具{{ toy }}

import {ref,onUnmounted} from 'vue' let computer=ref('ThinkPad') let toy=ref('') //一开始玩具是空 import emitter from '@/utils/emitter' // 要拿到文具信息,就要gei emiiter绑定事件 emitter.on('send-toy',(value:any)=>{ console.log("send-toy",value) toy.value=value }) // 注意组件卸载时,解绑事件,有点类似总线,不卸载会被发消息的组件一直惦记,占用内存 onUnmounted(()=>{ emitter.off('send-toy') }) .child{ margin-top: 50px; background-color: orange; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

哥哥:

    
    

子组件1

玩具{{toy }}

玩具给弟弟 import {ref} from 'vue' import emitter from '@/utils/emitter' let toy=ref('奥特曼') .child{ margin-top: 50px; background-color: skyblue; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

emitter.ts

// // 引入mitt
import mitt from 'mitt'
// // 调用mitt 获得傀儡emitter emitter可以绑定事件,出发事件
const emitter=mitt()
// 暴露emitter
export default emitter

main.ts

   不必引入emitter,因为传递消息双发已经独立引入了

4. v-model

很少用,可以父子双向传递

比如 hello,这里的大写的Button不是小写的button标签,而是组件

这里的a是传给组件的

UI组件库底层大量使用v-model进行通信

例子1:HTML标签之v-model

    
    

父组件

import {ref} from 'vue' let userName=ref("李白") .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

在控制台可以看到

vue3学习笔记

验证双向可修改

vue3学习笔记

用在html标签上的v-model的本质

    
    

父组件

import {ref} from 'vue' let userName=ref("李白") .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

例子2 组件之v-model

   自制一个ui组件


    




input{
    border: 2px solid;
    background-image: linear-gradient(45deg,red,yellow,blue);
    height: 30px;
 font-size:20px;
 color: white;
}

这个本质上input,但是input自身是可以使用v-model,这个不行

ui组件库绑定事件


    


import {defineProps,defineEmits} from 'vue'
defineProps(['modelValue'])
const emit=defineEmits(['update:modelValue'])


input{
    border: 2px solid;
    background-image: linear-gradient(45deg,red,yellow,blue);
    height: 30px;
 font-size:20px;
 color: white;
}

使用ui组件

    
    

父组件

import {ref} from 'vue' import SonghuiInput from './SonghuiInput.vue' let username=ref("李白") .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

vue3学习笔记

 综合效果就是

vue3学习笔记

补充

 ui中的

 

event.target中的event是html元素,所以用.target

而下面是组件

 

vue3学习笔记

注意

  vue2中还是   :value和@input     

vue3学习笔记

vue3改为:modelValue和@update

vue3学习笔记

改名

v-model 后面加qwe

右边3处也改为qwe

实现i多次使用v-model

 vue3学习笔记

5.attrs

   当前组件的父组件给当前组件的子组件传数据,祖给孙

1.父传的多于子收的

  父:   

 子:  defineProps(['a',‘b’])

虽然c d没有收,但是记在了attrs里面,所以没声明接收的都在这里

vue3学习笔记

使用 

{{ $attrs }}

vue3学习笔记

进一步引入v-bind

   
    
    

接收时在attrs里面被拆成x y

vue3学习笔记

 向孙传递

如果儿子一个都不接收,那么就可以完整的向孙传递

 

可见把响应数据和写死的xy都传给了孙子

vue3学习笔记

孙子给爷爷

爷爷有方法一样可以传递

 
function updateA(value:number){
    a.value+=value
}

孙子接收,并可更新爷爷的数据

    
    

孙组件

爷爷a{{ a }}

爷爷a{{ b}}

爷爷a{{ c }}

爷爷a{{ d }}

爷爷a{{ x }}

爷爷a{{ y }}

点位更新爷爷a import { defineProps } from 'vue'; defineProps(['a','b','c','d','x','y','updateA']) .child{ margin-top: 20px; background-color: orange; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

6.$refs $parent

一父多子

$refs:父传子

$parent: 子传父

vue3学习笔记

 父亲动儿子的-1对1

子组件首先要释放权限defineExpose

    
    

子组件1

儿子1玩具{{ toy }}

儿子1书籍{{ book }}本书

import {ref} from 'vue' let toy=ref("奥特曼") let book=ref(4) // 把数据交给外部 defineExpose({toy,book}) .child{ background-color: skyblue; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

父组件调用子组件时给唯一标记ref,因为c1是ref()就可以通过c1.value访问数据

    
    

父组件

父亲房产数{{ house }}

点我修改儿子1玩具 import Child1 from './Child1.vue'; import Child2 from './Child2.vue'; import {ref} from 'vue' let c1=ref() let house=ref('4') function changeToy(){ console.log(c1.value) c1.value.toy="变形金刚" } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

访问结果

vue3学习笔记

  父亲动儿子的-1对多$refs

    
    

父组件

父亲房产数{{ house }}

点我修改儿子1玩具 点我修改儿子2电脑 获取所有子组件 import Child1 from './Child1.vue'; import Child2 from './Child2.vue'; import {ref} from 'vue' let c1=ref() let c2=ref() let house=ref('4') function changeToy(){ console.log(c1.value) c1.value.toy="变形金刚" } function changeComputer(){ c2.value.computer="Dell" } function getAllChild(e){ console.log(e) } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

$refs

获取了父组件中标记了ref的子组件,没标记则不获取

vue3学习笔记

警告:

 Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Object'.

No index signature with a parameter of type 'string' was found on type 'Object'.ts(7053)

因为index 可能是c1或 c2

vue3学习笔记

// function getAllChild(refs:Object){
    // 写成any也可以
    // function getAllChild(refs:any){
    function getAllChild(refs:{[key:string]:any}){
    console.log(refs)
    //更改两个儿子的书籍数目,在对象中遍历用key,在数组中遍历用index
    for (let key in refs) {
     console.log(key)   //key就是字符串c1 c2
     console.log(refs[key])    //获取对象中每一个key对应的值
     refs[key].book+=3
    }
    
    

父组件

父亲房产数{{ house }}

点我修改儿子1玩具 点我修改儿子2电脑 让所有孩子的书变多 import Child1 from './Child1.vue'; import Child2 from './Child2.vue'; import {ref} from 'vue' let c1=ref() let c2=ref() let house=ref('4') function changeToy(){ console.log(c1.value) c1.value.toy="变形金刚" } function changeComputer(){ c2.value.computer="Dell" } // function getAllChild(refs:Object){ // 写成any也可以 // function getAllChild(refs:any){ function getAllChild(refs:{[key:string]:any}){ console.log(refs) //更改两个儿子的书籍数目,在对象中遍历用key,在数组中遍历用index for (let key in refs) { console.log(key) //key就是字符串c1 c2 console.log(refs[key]) //获取对象中每一个key对应的值 refs[key].book+=3 } } .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

$parent

$parent获取父亲的实例对象

一样,父亲要先允许  defineExpose({house})

    
    

子组件1

儿子1玩具{{ toy }}

儿子1书籍{{ book }}本书

去掉父亲的1套房产 import {ref} from 'vue' let toy=ref("奥特曼") let book=ref(4) // 把数据交给外部 defineExpose({toy,book}) function minusHouse(parent:any){ console.log(parent) parent.house-=1 } .child{ background-color: skyblue; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

补充

reactive下的ref不需要使用.value读取,自动解包

let obj=reactive({
    a:1,
    b:2,
    c:ref(3)
})
// 当访问obj.c的时候,底层会自动读取value属性,因为c是在这个响应式对象中
console.log(obj.a)
console.log(obj.b)
console.log(obj.c)

7.provide-inject

祖给后代

    
    

父组件

银子{{ money }}

一辆{{ car.brand }}车,价格{{ car.price }}

import Child from './Child.vue'; import {ref,reactive,provide} from 'vue' let money=ref(5000) let car=reactive({ brand:'奔驰', price:200 }) // 向后代提供数据 provide('qian',money) provide('che',car) .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

Grandson

    
    

孙组件

爷爷的钱{{ x }}

爷爷的车品牌是{{ y.brand }} 价格是{{ y.price }}

import {inject} from 'vue' // 80000是找不到qian的注入的备用值 let x=inject('qian',80000) // 使用第二个参数 缺省值可以相当于起到了推断断言的作用 let y=inject('che',{brand:'未知',price:'未知'}) .child{ margin-top: 20px; background-color: orange; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

后代给祖

 要求祖要有方法,一并传给后代

// 向后代提供数据及方法
provide('qianContext',{money,updateMoney})
    
    

父组件

银子{{ money }}

一辆{{ car.brand }}车,价格{{ car.price }}

import Child from './Child.vue'; import {ref,reactive,provide} from 'vue' let money=ref(5000) let car=reactive({ brand:'奔驰', price:200 }) function updateGrandMoney(value:number){ money.value-=value } // 向后代提供数据 // provide('qian',money) provide('che',car) // 向后代提供数据及方法,注意:千万不能写为money:money.value,这样会让后代接收时失去响应式 provide('qianContext',{money,updateGrandMoney}) //provide('qianContext',{money:money.value,updateGrandMoney}) .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; }

    
    

孙组件

爷爷的钱{{ money}}

爷爷的车品牌是{{ y.brand }} 价格是{{ y.price }}

花爷爷的钱 import {inject} from 'vue' // 80000是找不到qian的注入的备用值 // let x=inject('qian',80000) // 解构 // let {money,updateGrandMoney}=inject('qianContext',"我是缺省值") let {money,updateGrandMoney}=inject('qianContext',{money:0,updateGrandMoney:(param:number)=>{}}) // 使用第二个参数 缺省值可以相当于起到了推断断言的作用 let y=inject('che',{brand:'未知',price:'未知'}) .child{ margin-top: 20px; background-color: orange; padding: 10px; box-shadow: 0 0 10px black; border-radius: 10px; }

插槽

三种

需求

vue3学习笔记

 因为子组件被调用了3次,需要把三个变量都放在右侧,这样就没法在子组件接收参数

重新分析

   如下span不不会显示在页面上,因为vue见到了组件标签就去解析组件了

vue3学习笔记

在子组件加入slotE

vue3学习笔记

默认插槽

  挖个坑,一般只挖一个,如果挖多个就会每个坑都放

vue3学习笔记

    
    

父组件

  • {{ item.id }}--{{item.name}}
import Category from './Category.vue'; import {ref,reactive} from 'vue' let games=reactive([ {id:'001',name:'王者荣耀'}, {id:'002',name:'奇迹'}, {id:'003',name:'传奇'}, {id:'004',name:'原神'}, ]) let imgUrl=ref("https://t12.baidu.com/it/app=25&f=JPG&fm=175&fmt=auto&u=517088237%2C2849401642?w=402&h=300&s=501C76966449434754337E740300E078") let videoUrl=ref("https://media.w3.org/2010/05/sintel/trailer.mp4") .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; } .content{ display: flex; justify-content: space-evenly; } img,video{ width: 100%; }
    
       

{{title}}

defineProps(['title']) .category{ background-color: skyblue; border-radius: 10px; box-shadow: 0 0 10px; padding: 10px; width:200px; height: 300px; } h2{ background-color: orange; text-align: center; font-size: 20px; font-weight: 800; }

具名插槽

默认插槽也是有名字,只不过省略了

vue3学习笔记

v-slot只能用在组件名称和template上

vue3学习笔记

    
       {title}} -->
       默认标题
       默认内容
    


// defineProps(['title'])


.category{
    background-color: skyblue;
    border-radius: 10px;
    box-shadow: 0 0 10px;
    padding: 10px;
    width:200px;
    height: 300px;
}
    
    

父组件

热门游戏列表

  • {{ item.id }}--{{item.name}}

今日美食城市

今日影视推荐

import Category from './Category.vue'; import {ref,reactive} from 'vue' let games=reactive([ {id:'001',name:'王者荣耀'}, {id:'002',name:'奇迹'}, {id:'003',name:'传奇'}, {id:'004',name:'原神'}, ]) let imgUrl=ref("https://t12.baidu.com/it/app=25&f=JPG&fm=175&fmt=auto&u=517088237%2C2849401642?w=402&h=300&s=501C76966449434754337E740300E078") let videoUrl=ref("https://media.w3.org/2010/05/sintel/trailer.mp4") .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; } .content{ display: flex; justify-content: space-evenly; } img,video{ width: 100%; } h2{ background-color: orange; text-align: center; font-size: 20px; font-weight: 800; }

简写#s1

  
        
            

今日影视推荐

作用域插槽

     子组件slot上定义的属性,被传递给了使用者

vue3学习笔记

父组件通过如下v-slot='a' 中的a接收

vue3学习笔记

 可以看到a是对象{},有三组key value

vue3学习笔记

 vue3学习笔记

最后Father

    
    

父组件

{ a }} -->
  • {{ item.id }} {{ item.name }}
  1. {{ item.id }} {{ item.name }}

{{ item.id }} {{ item.name }}

import Game from './Game.vue'; .father{ background-color: rgb(165,164,164); padding: 20px; border-radius: 10px; } .content{ display: flex; justify-content: space-evenly; } img,video{ width: 100%; }

最后Game

    
        

游戏列表

import {reactive} from 'vue' let games=reactive([ {id:'001',name:'王者荣耀'}, {id:'002',name:'奇迹'}, {id:'003',name:'传奇'}, {id:'004',name:'原神'}, ]) .game{ width: 200px; height: 300px; background-color: skyblue; border-radius: 10px; box-shadow: 0 0 10px; } h2{ background-color: orange; text-align: center; font-size: 20px; font-weight: 800; }

其它API

ShallowRef:

浅层次ref,只处理第一层

如下可修改

    
      

求和为{{ sum }}

名字为{{ person.name }}

年龄为{{ person.age}}

sum++ 修改名字 修改年龄 修改整个人 import {ref,shallowRef} from 'vue' let sum=shallowRef(0) let person=shallowRef({ name:'张三', age:12 }) function addSum(){ sum.value++ } function changeName(){ person.value.name='李白' } function changeAge(){ person.value.age=22 } function changePerson(){ person.value={name:'王维',age:55} } .app{ background-color: #ddd; border-radius: 10px; box-shadow: 0 0 10px; padding: 10px; }

可修改sum和整个人,但是名字和年龄不能改

vue3学习笔记

第一层就是person.value

vue3学习笔记

shallowReactive

   reactive定义的数据不能整体替换,要用Objective.

shallowReactive:如下只能改brand这个第一层

    
      

求和为{{ sum }}

名字为{{ person.name }}

年龄为{{ person.age}}

汽车为{{ car}}

sum++ 修改名字 修改年龄 修改整个人
修改品牌 修改颜色 修改发动机 修改整个车 import {ref,shallowRef,reactive,shallowReactive} from 'vue' let car=shallowReactive({ brand:'奔驰', options:{ color:'红色', engine:'v8' } }) let sum=shallowRef(0) let person=shallowRef({ name:'张三', age:12 }) function addSum(){ sum.value++ } function changeName(){ person.value.name='李白' } function changeAge(){ person.value.age=22 } function changePerson(){ person.value={name:'王维',age:55} } // 修改车 function changeCarBrand(){ car.brand='宝马' } function changeCarColor(){ car.options.color='黄色' } function changeCarEngine(){ car.options.engine='V9' } function changeCarWhole(){ car={ brand:'奥迪', options:{ color:'紫色', engine:'A100' } } } .app{ background-color: #ddd; border-radius: 10px; box-shadow: 0 0 10px; padding: 10px; } button{ margin: 0 10px; }

vue3学习笔记

readOnly&shallowReadOnly

readOnly:复制一份数据,避免原数据被其他组件更改;原数据变化也会跟着变化

               复制出来的这个数据不能修改自己

shallowReadOnly:复制出来的这分数据第一层不能修改,但其它层可以修改

vue3学习笔记

    
        

sum为{{ sum }}

sum2为{{ sum2 }}

当前(car1)汽车为{{ car }}

当前(car2)汽车为{{ car2 }}

点我sum+1 点我sum2+1
修改品牌(car1) 修改颜色(car1) 修改价格(car1)
修改品牌(car2) 修改颜色(car2) 修改价格(car2) import {ref,reactive,readonly,shallowReadonly} from 'vue' let sum=ref(0) // 根据可变的sum缔造出一个不可变sum let sum2=readonly(sum) let car=reactive( { brand:'宝马', options:{ color:'red', price:100 } } ) let car2=readonly(car) function addSum(){ sum.value++ } function addSu2m(){ sum2.value++ //只读无法使用 } // 汽车 function changeCarBrand(){ car.brand='奥迪' } function changeCarColor(){ car.options.color='紫色' } function changeCarPrice(){ car.options.price=400 } // car2 function changeCar2Brand(){ car2.brand='奥迪' } function changeCar2Color(){ car2.options.color='紫色' } function changeCar2Price(){ car2.options.price=400 } button{ margin: 0 10px; }

vue3学习笔记

 toRaw和MarkRaw

toRaw

  let person2=toRaw(person)

lodash

MarkRaw

vue3学习笔记

customRef

ref立即变化

customRef用于实现比如6秒后变化

    
       

{{ msg }}

import { TIMEOUT } from 'dns' import {customRef, ref} from 'vue' // 自带ref // let msg=ref('你好') // customRef要求必须有get set,用customRef来定义响应式数据 let initValue="你好" let timer // 接收底层所传track 跟踪,trigger触发 let msg=customRef((track,trigger)=>{ return{ // ,sg被读取时调用 因为其在{{ msg }} ,v-model="msg"两处被读取,所以初始化页面打印出现两次 get(){ track() //告诉vue 要对msg关注,一旦msg变化就去更新,如果没有管制就算有了triggger页面也不会更新 console.log('get') return initValue }, //msg修改时 调入修改值value set(value){ clearTimeout(timer) //清除之前的定时器,因为页面每改一次就会调用setTimmeout //msg改变后,要求等1秒页面再变化 timer=setTimeout(() => { console.log('修改',value) initValue=value trigger() //通知vue一下数据变了,如果没有trigger vue不知道数据变化,很无聊 }, 1000) } } }) button{ margin: 0 10px; }

//hooks改造

import {customRef} from 'vue'
// initValue 初始值   delayTime:延迟时间
export default function(initValue:string,delayTime:number){
// customRef要求必须有get set,用customRef来定义响应式数据
// let initValue="你好"
let timer:number
// 接收底层所传track 跟踪,trigger触发
let msg=customRef((track,trigger)=>{
    return{
        // ,sg被读取时调用   因为其在{{ msg }} ,v-model="msg"两处被读取,所以初始化页面打印出现两次
        get(){
            track()  //告诉vue 要对msg关注,一旦msg变化就去更新,如果没有管制就算有了triggger页面也不会更新
            console.log('get')
            return initValue
        },
        //msg修改时  调入修改值value
        set(value){
            clearTimeout(timer) //清除之前的定时器,因为页面每改一次就会调用setTimmeout
                //msg改变后,要求等1秒页面再变化
        timer=setTimeout(() => {
           console.log('修改',value)
            initValue=value
            trigger()  //通知vue一下数据变了,如果没有trigger vue不知道数据变化,很无聊
        }, delayTime)
    }
    
    }
})
return {msg}
}
    
       

{{ msg }}

import useMsgRef from './useMsgRef' // 使用useMsgRef定义响应式数据 let {msg}=useMsgRef('你好',2000) button{ margin: 0 10px; }

TelePort

传送:默认子组件在父组件里,但是子组件弹窗需要以视口定位

父组件

    
        

我是APP组件

vue3学习笔记
import Model from './Model.vue'; .outer{ background-color: #ddd; border-radius: 10px; padding:5px; box-shadow: 0 0 10px; width: 400px; height: 800px; filter: saturate(200%); } img{ width: 270px; }

弹窗组件

    展示弹窗
    
    
    
        展示弹窗
   

我是弹窗标题

我是弹窗内容

关闭弹窗 import {ref} from 'vue' let isShow=ref(false) .model{ width: 200px; height: 150px; background-color: skyblue; border-radius: 10px; padding: 5px; box-shadow: 0 0 5px; text-align: center; /* 参考视口定位而不是父组件 */ position: fixed; top:20px; left: 50%; margin-left: -100px; }

加了传送 弹窗跑到了body下

vue3学习笔记

Supense

vue3学习笔记

异步任务导致子组件消失

改为

    
        

我是App

加载中

import { Suspense } from 'vue'; import Child from './Child.vue'; .app{ background-color: #ddd; border-radius: 10px; padding:10px; box-shadow: 0 0 10px; }
    
     

我是Child

当前求和为{{ sum }}

import {ref} from 'vue' import acxios from 'axios' import axios from 'axios' let sum=ref(0) let {data:{content}}=await axios.get("https://api.uomg.com/api/rand.qinghua?format=json") console.log(content) .child{ background-color: skyblue; border-radius: 10px; padding:10px; box-shadow: 0 0 10px; }

全局API转移到应用

vue3学习笔记

其它

 vue3学习笔记

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]