vu3入门

vue3

注意:从P41开始记笔记

卖座电影 (maizuo.com)

1.p41 父传子prop沟通的重要性

image-20230816073915768

三种传递方式:

<!-- 1.写法1 -->
<Navbar :title="title" left="返回" right="首页"></Navbar>
<button @:click="handleClick()">改变标题的名称</button>

<!-- 2.写法2 -->
<!-- <Navbar v-bind="{
'my-title':'电影',
left: '返回',
right: '首页'
}"></Navbar> -->

<!-- 3.写法3:将写法2抽象为对象进行绑定 -->
<!-- <Navbar v-bind="propObj"></Navbar>
<button @:click="handleClick()">改变标题的名称</button> -->

<!-- <Navbar title="影院"></Navbar>
<Navbar title="我的"></Navbar> -->

2.p42属性验证与默认属性

image-20230816085517765

在Vue中,当组件的prop验证失败时,Vue不会自动阻止这个prop的绑定,而是会在控制台输出一条警告信息。这意味着即使leftshow的验证失败,Vue仍然会使用这个prop的值,并且这个值将会影响模板中的v-show指令。

在你的代码中,leftshow是一个布尔类型的prop,它有一个验证器,这个验证器检查传入的值是否是'true'或'false'。但是,当你传递给leftshow的值不是'true'或'false'时,验证器会返回false,Vue会在控制台输出一条警告,说明leftshow的值无效。

然而,由于v-show指令是基于JavaScript的布尔值来工作的,它期望的是一个布尔值(true或false),如果传入的值不是有效的布尔值,Vue会尝试将其转换为布尔值。例如,如果leftshow的值是空字符串、null、undefined、0、NaN或'',Vue会将其转换为false;如果值是非空字符串、非0的数字、对象、数组等,Vue会将其转换为true

3.P43属性的透传

单向数据绑定:

image-20230816091840287

image-20230816092719159

4.P44 子传父:自定义事件

image-20230816110245640

image-20230816110730466

once:

image-20230816110918552

5.P45-$refs_父组件的强权

image-20230816115628116

6.p46 $parent和$root

image-20230816192949263

7.p47跨级通信_provide和inject

image-20230817083940364

8.P48发布订阅模式

image-20230817092357539

9.p49组件的动态生成

image-20230817114417645

image-20230817114623914

10.p50异步组件加载

使用好处:懒加载,页面需要用到那个组件时才进行加载

image-20230817165021319

image-20230817165200944

11.插槽

11-1.p53

image-20230817181536356

11-2 具名插槽p54

image-20230818113423477

11-3 作用域插槽

说明:可以通过子组件暴露数据给父组件,父组件操作数据,然后再插槽到子组件中

下载axios:

npm install axios --save

image-20230818125040196

12 生命周期

12-1 生命周期创建

lifecycle.16e4c08e

12-2生命周期更新

补充:调用方法执行点击事件,然后需要更新DOM树,但是更新DOM树是一个异步过程

image-20230818155536255

代码参考:

<template>
    <div >
        <button @click="handleClick()">改变echarts宽度</button>
        <div id="main" :style="{width: mywith, height: '400px'}"></div>
    </div>
</template>
<script>


    import * as echarts from 'echarts';
    export default{
        data() {
            return{
                title:"标题",
                mywith: '600px',
                optinon:{}
            }
        },
        
        // 1.没啥用,也无法获取组件的属性
        beforeCreate() {
            console.log(this.title)
        },
        // 2.可以用于对组件属性的初始化
        created() {
            this.title="22222",
            this.optinon = {
                title: {
                    text: 'ECharts 入门示例'
                },
                tooltip: {},
                xAxis: {
                    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
                },
                yAxis: {},
                series: [
                    {
                        name: '销量',
                        type: 'bar',
                        data: [5, 20, 36, 10, 10, 20]
                    }
                ]
            }
        },
        // 3.使用少,生成Dom节点之前一刻,此时无法得到Dom节点
        beforeMount() {

        },
        // 4.Dom节点挂载完成,可以访问Dom节点
        mounted() {
            // 订阅发布
            // ajax
            // setInterval
            // 访问dom

            // 获取节点,送入echarts初始化,生成图表
            // 基于准备好的dom,初始化echarts实例
            // 将myChart挂载到当前对象中,提供method使用
            this.myCharts = echarts.init(document.getElementById('main'));
            // 绘制图表
            this.myCharts.setOption(this.optinon);
        },
        methods: {
            handleClick() {
                this.mywith = '800px'
                // with还是600px
                console.log(document.getElementById('main').style.width)

                // 生命周期update()更新完成之后,执行一个回调函数,可以只更新某一个节点,
                //不像update对所以属性都进行更新
                this.$nextTick(()=>{
                    console.log("nextTick")
                    this.myCharts.resize()
                })
            }
            
        },
        beforeUpdate() {
            console.log("beforeUpdate()方法")
            // with还是600px
            console.log(document.getElementById('main').style.width)
        },
        updated() {
            console.log("updated()方法")
            console.log(document.getElementById('main').style.width)
            // 重新改变echarts大小,我将改变大小放到$nextTick()函数中
            // this.myCharts.resize()
        }
    }
</script>

12-3 生命周期销毁

销毁组件中调用了windows对应的函数不会随着组件销毁而销毁,需要手动在unmounted中销毁

image-20230818163002802

13.swapper

14.自定义组件

15 过度效果(内置组件)

15-1 vue内置组件

image-20230819071858023

15-2 使用css库实现过度效果

官网链接:Animate.css | A cross-browser library of CSS animations.

第一步:下载库

npm install animate.css --save

第二步:引入库

import 'animate.css';

第三步:使用

image-20230819073146955

源码参考:

<template>
    <div>
        
       
        <Transition enter-active-class="animate__animated animate__bounceIn" 
        leave-active-class="animate__animated animate__bounceOut">
            <div v-show="isShow">11111</div>
        </Transition>
        <button @click="handleClick()">显示or隐藏</button>
    </div>
</template>
<script>

    import 'animate.css';
    export default{
        data() {
            return{
                isShow: true
            }
        },
        methods: {
            handleClick() {
                this.isShow = !this.isShow
            }
        }
    }
</script>

<style>

</style>

15-3 列表过度

image-20230819080148062

16 .VCA(Vue Composition API)入门

image-20230819114811487

17.p68ref函数使用

<template>
    app
    <div>
        <!-- 1.如果使用ref封装基本类型值,模板解析时会自动执行myname.value,所以只需要写一个myname就可以了 -->
        {{ myname }}
        <input type="text" ref="myinput">
        <button @click="handleClick()">改变名字</button>
    </div>
    
    
</template>

<script>
    import {ref} from 'vue'
    export default {
        setup() {
            // reactive只能封装对象,ref功能更丰富,可以封装基本类型
            const myname = ref("xurong") // 底层:new Proxy({value: "xurong"})
            const myinput = ref(null)
            const handleClick = () => {
                // 2.使用ref封装基本类型值,事件处理的时候,需要调用value进行处理,使用ref外层都封装了value
                myname.value = "wang"
                
                // 3.可以拿到输入框的值
                // myinput.value拿到代理对象,myinput.value.value:通过代理对象获取输入框中的值
                console.log(myinput.value.value)
            }
            return{
                myname,
                handleClick,
                myinput
            }
        }
        
    }
</script>

18.toRef和toRefs使用

reactive转ref:

image-20230819162201439

ref转reactive:

image-20230819163205858

19.VCL中computed中的计算属性

image-20230819180648186

逻辑复用-函数封装:

第一步:封装

image-20230819190743401

第二步:直接调用即可

image-20230819190847220

19.p72 VAL中的watch和watchEffect

<template>
    <div>
        <input type="text" v-model="mytext">
        <select name="" id="" v-model="opt">
            <option value="aa">aa</option>
            <option value="bb">bb</option>
            <option value="cc">cc</option>
        </select>
        {{reactiveValueTest.testText}}
    </div>
</template>
<script>

    import {ref, watch,reactive} from 'vue'
    export default {
        setup() {
            const mytext = ref("")
            // 1.写法1
            // watch(mytext,(newValue,oldValue)=>{
            //     console.log("同步/异步","ajax",newValue)
            // })

            // 2.写法2
            // watch(()=>mytext.value,(newValue,oldValue)=>{
            //     console.log("同步/异步","ajax",newValue)
            // })

            // 3.写法3:可以监听多组数据源
            // const opt = ref("aa");
            // watch([mytext,opt],(newValue,oldValue)=>{
            //     console.log("同步/异步","ajax",newValue)
            // })   

            // 4.写法4:第一次数据渲染时也打印监听的数据
            const opt = ref("aa");
            watch([mytext,opt],(newValue,oldValue)=>{
                console.log("同步/异步","ajax",newValue)
            },{immediate:true})

            // 5.推荐使用watch使用ref,以下是对reactive实现监听
            const reactiveValueTest = reactive({
                testText: "reactive test111"
            })
            // 对reactive对象中的某个值进行监听
            watch(()=>reactiveValueTest.testText,(newValue,oldValue)=>{
                console.log("同步/异步","ajax",newValue)
            },{immediate:true})

            return {
                mytext,
                opt,
                reactiveValueTest
            }
            
        }
    }
</script>

watchEffect用法:

image-20230820075524362

20.props和emit

父传子:

image-20230820130835454

子传父:

特别要注意父组件监听的参数不要加括号

image-20230820131431939

21.provide和inject

image-20230820161522829

22.生命周期

<template>
    app--{{ v}}
    <button @click="handleClick">更新value</button>
</template>

<script>

    import { onBeforeMount,onMounted,onBeforeUpdate,onUpdated,ref,nextTick} from 'vue';
    export default {
        setup() {
            const v = ref("11111")
            onBeforeMount(()=>{
                console.log("DOM创建之前调用111")
            })
            onMounted(()=>{
                console.log("订阅,ajax,DOM创建后,swiper,echart初始化222")
            }) 
            onBeforeUpdate(()=>{
                console.log("DOM树更新之前调用")
            })
            onUpdated(()=>{
                console.log("DOM树更新之后调用")
            })
            const handleClick = () => {
                v.value = "22222"
                // DOM树更新完成之后调用
                nextTick(()=>{
                    console.log("nextTick")
                })
            }
            return {
                v,
                handleClick
            }
        }
        
    }
</script>

销毁组件时,终止异步函数setInterval

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

<script>

    import {onBeforeUnmount,onMounted,onUnmounted} from 'vue'
    export default{
        setup() {
            let clearId
            onMounted(()=>{
                clearId = setInterval(()=>{
                    console.log(11111)
                },1000)
            })
            onBeforeUnmount(()=>{

            })
            onUnmounted(()=>{
                clearInterval(clearId)
            })
        }
    }
</script>

23.setup语法糖,见代码

<template>
    app 
    <div>
        {{ msg }}
        <button @click="handleClick">button</button>
        <div>
            {{ myname }}--{{age }}
        </div>
        <div>
            {{computedName}}
        </div>
        <Child title="电影" @right="handleRightClick"></Child>
    </div>
</template>
<!-- 省略了setup,return -->
<script setup>
    import {ref,reactive,toRefs,computed} from 'vue'
    import Child from './Child.vue'

    const msg = ref("hello,world")
    const obj = reactive({
        myname: "xu",
        age:23
    })
    // 将obj属性值解构出去
    const {myname,age} = {...toRefs(obj)}
    const computedName = computed(()=>myname.value.substring(0,1).toUpperCase()+myname.value.substring(1))
    const handleClick= () => {
        msg.value = "xurong"
    }

    const handleRightClick = (value) => {
        console.log("接收到的子组件值为:",value)
    }
</script>
<template>
    <div>
        <button>返回</button>
        {{ title }}
        <button @click="handHome">首页</button>
    </div>
</template>
<script setup>
    import {defineProps,defineEmits} from 'vue'
    const props = defineProps({
        title: {
            type: String,
            default: "000000"
        }
    })

    // 向父组件传值
    const emit = defineEmits(["right"])
    const handHome = () => {
        emit("right","来自子组件的值")
    }
</script>

24.Vue Router

作用:一个html单页面开发

介绍 | Vue Router (vuejs.org)

24-1 使用

image-20230826221809363

image-20230826221605228

image-20230826221516676

image-20230826221947753

说明:router-view被全局注册了,无需进行引入注册操作

24-2 重定向

方式1:

image-20230826222402091

方式2:

image-20230826222520462

错误路径处理和给路径起别名:

image-20230915093921627

24-3 css嵌套语法:

scss:

image-20240827173703409

24-4 p80 路由跳转

image-20240827175503894

结果:

默认会生成a链接

image-20240827175715855

自定义为li元素,也可以为div

image-20240827175606546

24-5 嵌套路由

效果:image-20230915161304644

image-20230915161114225

自动加载子组件:

image-20240827182352312

24-6 p82 编程导航

1.声明式导航-跳转路径拼接id

image-20240828155210141

2.编程式导航

image-20240828160329302

24-7 p83 动态路由

1.params传参:方式1:路径拼接参数 方式2:对象传参

image-20240828161733262

注意:对象传参需要给上述路径起一个名字

image-20240828163909888

得到携带的参数:

image-20240828165013113

2.query传参:在路径上拼接上?然后拼接上参数进行传参

image-20240828165520907

image-20240828165828244

得到传递的参数:

image-20240828165906269

效果图:

image-20240828165659008

3.返回上一个页面

image-20240828170903915

4.猜你喜欢电影页面跳转(同级别)

image-20240828172824835

结果:

image-20240828173006825

24-8 p84 路由模式

image-20240829073049507

结果:

image-20240829073120605

24-9 p85全局登录拦截

1.只放行登录页面,对其他页面都需要进行登录拦截

// 全局拦截-后台系统,除了登录页面,其他页面都必须授权才能放行访问
router.beforeEach((to,from,next) => {
    let isAuthenticated = localStorage.getItem("token");
    if(to.name !== 'Login' && !isAuthenticated) next({
        name: 'Login'
    })
    else next()
})

2.对特定页面进行登录拦截

对Centers页面进行拦截:设置标记meta表明访问该路径时需要登录

    {
        path:"/center",
        component:Centers,
        meta: {
            requiredAuth: true
        }
    }

逻辑实现:

// 对center页面进行登录拦截
router.beforeEach((to,from,next) => {
    let isAuthenticated = localStorage.getItem("token");
    console.log(to.fullPath)
    if(to.name !== 'Login' && !isAuthenticated && to.meta.requiredAuth) next({
        name: 'Login'
    })
    else next()
})

3.路径拦截通过之后,可以对点击量进行统计

router.afterEach((to,from) => {
    
    // 统计对该电影喜爱数据(可以在路径中获取对应的电影id,然后可以统计该用户对该id统计的次数)
    let filmId = to.params.myid;
    console.log("提交后端用户行为信息",filmId);
})

24-10 组件内的守卫

1.单个组件的拦截--beforeRouteEnter

image-20240829085628878

2.路由更新之前获取请求参数-- beforeRouteUpdate

image-20240829090315323

4.beforeRouteLeave

image-20240829091153621

24-11 路由懒加载--按需导入对应的组件

image-20240829092600836

image-20240829092803658

结果:

image-20240829093327719

24-12 VCL与路由

1.组件注册

image-20240829110849414

2.路由跳转--引入useRouter

image-20240829111955944

3.改造1

image-20240829113440728

4.onMounted,route,router,onBeforeRouteUpdate--setup模式

image-20240829120013454

5.beforeRouteEnter在组合式模式下没有对应的函数

image-20240829121414178

25 vuex

25- 1 简介

npm install vuex@next --save

1.页面中有多个需要共享的状态,引入vuex,便于维护(非父子通信)

2.缓存部分异步数据,减少后端服务的访问,增加用户体验

使用:

注册:

image-20240829151042690

使用vuex创建属性值:决定是否显示Tabbar组件

image-20240829152319938

实践:

image-20240829155224836

image-20240829155103016

结果:

image-20240829155348211

image-20240829155431719

上述为错误示例:isTabbarShow属性值不可跟踪

25-2 mutations

image-20240829180257362

image-20240829180324587

演示:

image-20240829181724897

改进:

image-20240829182350215

开发中常用写法:

image-20240829184205561

25-3 action--缓存数据

image-20240829195631793

image-20240830070731914

如果你希望在 Vuex 中缓存数据,通常的做法是:

在 action 中进行异步请求(例如,从 API 获取数据)。
在异步操作完成后,将数据提交到 mutation 中,mutation 会更新 store 的状态。
一旦数据被提交到 store,它就会被保存在 Vuex 的状态树中,直到你显式地修改它。
状态树中的数据在默认情况下是响应式的,这意味着当状态发生变化时,任何依赖于该状态的组件都会自动更新。如果你想要缓存数据,你可以选择将数据保存在状态树中,这样它就会在组件之间共享,并且可以在多个组件之间保持同步。

25-4 getter

image-20240830120147896

25-5 辅助函数--mapState,mapAction,mapGetters 鸡肋

image-20240830142954145

25-6 vux--module开发

注意:涉及到辅助函数,命名空间,可以结合上一节重复看,我直接没有使用辅助函数

每一类数据对应一个js模块,然后合并到index.js中

index.js:将下面的切分不同module,然后合并:

/**
 * 1.页面中有多个需要共享的状态,引入vuex,便于维护(非父子通信)
   2.缓存部分异步数据,减少后端服务的访问,增加用户体验
 */
import {createStore} from 'vuex'
import {CHANGE_TABBAR,CHANGE_CINEMALIST} from './type.js'

import axios from 'axios'

const store = createStore({
  state() {
    return {
      isTabbarShow: true,
      cinemaList: []
    }
  },
  // 唯一修改状态的位置--同步
  mutations: {
    // showTabbar(state) {
    //   state.isTabbarShow = true;
    // },
    // hiddenTabbar(state) {
    //   state.isTabbarShow = false;
    // }

    [CHANGE_TABBAR](state,playload) {
      state.isTabbarShow = playload
    },
    [CHANGE_CINEMALIST](state,playload) {
      state.cinemaList = playload
    }
  },
  // 同步+异步
  actions: {
    async getCinemaList(store) {
      const res = await axios({
        url: "https://m.maizuo.com/gateway?cityId=440300&ticketFlag=1&k=1628079",
        headers: {
          'x-client-info':'{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777"}',
          'x-host':'mall.film-ticket.cinema.list'
        }
      });
      console.log(res.data.data.cinemas);
      // 提交mutation
      store.commit(CHANGE_CINEMALIST,res.data.data.cinemas);
    }
  },

  // getter store的计算属性
  getters: {
    filterCinemaList(state) {
      return (playload) => {
        return state.cinemaList.filter(item => 
          item.eTicketFlag === playload)
      }
    }
  }
})
export default store

改造:

/**
 * 1.页面中有多个需要共享的状态,引入vuex,便于维护(非父子通信)
   2.缓存部分异步数据,减少后端服务的访问,增加用户体验
 */
import { CHANGE_CINEMALIST } from '../type.js'

import axios from 'axios'

const CinemaModule = {
    state() {
        return {
            cinemaList: []
        }
    },
    // 唯一修改状态的位置--同步
    mutations: {
        // showTabbar(state) {
        //   state.isTabbarShow = true;
        // },
        // hiddenTabbar(state) {
        //   state.isTabbarShow = false;
        // }
        [CHANGE_CINEMALIST](state, playload) {
            state.cinemaList = playload
        }
    },
    // 同步+异步
    actions: {
        async getCinemaList(store) {
            const res = await axios({
                url: "https://m.maizuo.com/gateway?cityId=440300&ticketFlag=1&k=1628079",
                headers: {
                    'x-client-info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777"}',
                    'x-host': 'mall.film-ticket.cinema.list'
                }
            });
            console.log(res.data.data.cinemas);
            // 提交mutation
            store.commit(CHANGE_CINEMALIST, res.data.data.cinemas);
        }
    },

    // getter store的计算属性
    getters: {
        filterCinemaList(state) {
            return (playload) => {
                return state.cinemaList.filter(item =>
                    item.eTicketFlag === playload)
            }
        }
    }
}
export default CinemaModule
/**
 * 1.页面中有多个需要共享的状态,引入vuex,便于维护(非父子通信)
   2.缓存部分异步数据,减少后端服务的访问,增加用户体验
 */
import { CHANGE_TABBAR } from '../type.js'

const TabbarModule = {
    state() {
        return {
            isTabbarShow: true
        }
    },
    // 唯一修改状态的位置--同步
    mutations: {
        // showTabbar(state) {
        //   state.isTabbarShow = true;
        // },
        // hiddenTabbar(state) {
        //   state.isTabbarShow = false;
        // }

        [CHANGE_TABBAR](state, playload) {
            state.isTabbarShow = playload
        }
    }
}
export default TabbarModule
/**
 * 1.页面中有多个需要共享的状态,引入vuex,便于维护(非父子通信)
   2.缓存部分异步数据,减少后端服务的访问,增加用户体验
 */
import {createStore} from 'vuex'
import CinemaModule from './module/CinemaModule'
import TabbarModule from './module/TabbarModule'

const store = createStore({
  modules: {
    CinemaModule,
    TabbarModule
  }
  
})
export default store

代码变动:

老师使用了辅助函数,命名空间,我是直接调用方法和计算属性,我觉得这和普通的方法和计算属性可以区别开来

image-20240830150249225

image-20240830150405778

25-7 vca与vuex

vca模式下,不支持 (辅助函数--mapState,mapAction,mapGetters,底层基于this)所有改造的项目中没有使用辅助函数

25-7 vuex持久化插件

将保存在内存中的数据保存到localStore中

npm install --save vuex-persistedstate

image-20240830164447216

结果;

image-20240830164524032

26.pinia

定义 Store | Pinia (vuejs.org)

26-1 简介

Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态.

由于组合式API中vuex不能够很好使用,所以出现了pinia

npm install pinia

26-2 optionStore

1.aciton

image-20240831125428474

image-20240831125738760

2.getters

image-20240831130843995

26-3 setupStore

tabbarStore.js:

import { defineStore } from 'pinia'
import { ref } from 'vue';

// 第一个参数是唯一的storeId
export const useTabbarStore = defineStore("tabbar", () => {
    // state: () => ({
    //     isTabbarShow: true
    // }),
    const isTabbarShow = ref(true);

    // actions: {
    //     change(value) {
    //         // this可以直接获取state对象
    //         this.isTabbarShow = value;
    //     }
    // },

    const change = (value) => {
        isTabbarShow.value = value;
    }
    return {
        isTabbarShow,
        change
    }
})

cinemaStore.js:

import { defineStore } from "pinia";

import axios from 'axios'
import { computed, ref } from "vue";
export const useCinemaStore = defineStore("cinema",() => {
    // state: () => ({
    //     cinemaList: []
    // }),
    const cinemaList = ref([]);

    // actions: {
    //     // 方法
    //     async getCinemaList() {
    //         const res = await axios({
    //             url: "https://m.maizuo.com/gateway?cityId=440300&ticketFlag=1&k=1628079",
    //             headers: {
    //                 'x-client-info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777"}',
    //                 'x-host': 'mall.film-ticket.cinema.list'
    //             }
    //         });
    //         console.log(res.data.data.cinemas);
    //         this.cinemaList = res.data.data.cinemas;
    //     }
    // },
    const getCinemaList = async() => {
        const res = await axios({
            url: "https://m.maizuo.com/gateway?cityId=440300&ticketFlag=1&k=1628079",
            headers: {
                'x-client-info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777"}',
                'x-host': 'mall.film-ticket.cinema.list'
            }
        });
        console.log(res.data.data.cinemas);
        cinemaList.value = res.data.data.cinemas;
    }
    
    // getters: {
    //     // 根据选择的App订票和前台兑换过滤cinemaList,注意该计算属性返回值是一个参数为value的函数
    //     filterCinema(state) {
    //         return (value) => {
    //             return state.cinemaList.filter(item =>
    //                 item.eTicketFlag === value)
    //         }
    //     }
    // }

    // 老师写法:
    // const filterCinema = computed(() => 
    //         // 一个参数为value的函数
    //         // 注意cinemaList是ref包装的
    //         (value) => {return cinemaList.value.filter(item =>
    //                 item.eTicketFlag === value)
    //         } 
    // )
    
    // gpt写法
    // ue 3 中,computed 函数应该返回一个值或者一个函数,而不是一个函数的声明
    const filterCinema = computed(() => {
        // 注意cinemaList是ref包装的
        return (value) => {
          return cinemaList.value.filter(item => item.eTicketFlag === value);
        }
      });
      
    return {
        cinemaList,
        getCinemaList,
        filterCinema
    }
})

27.vant

快速上手 - Vant 4 (vant-ui.github.io)

移动组件库:

npm i vant

27.1 导入vant中的组件作为全局组件和局部组件

1.全局组件:

image-20240831163058819

2.局部组件:

image-20240831163631738

27-2 axios结合Toast组件使用

image-20240831173104736

实现效果:

image-20240831173339926

27-3 List组件

<template>
    <div>
        <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" 
        @load="onLoad" offset="10" :immediate-check="false">
            <van-cell v-for="item in datalist" :key="item.filmId" @click="handleClick(item.filmId)">
                <img :src="item.poster" alt="电影图片展示" style="width: 100px;float: left;">
                <div>
                    {{ item.name }}
                </div>
            </van-cell>
        </van-list>

        <!-- <ul>
            <li v-for="(item, index) in datalist" :key="item.filmId" @click="handleClick(item.filmId)">
                {{ item.name }}
            </li>
        </ul> -->
    </div>
</template>

<script setup>
import axios from 'axios';
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { List as vanList, Cell as vanCell } from 'vant';

const datalist = ref([]);
const router = useRouter();

const loading = ref(false);
const finished = ref(false);
const pageNumber = ref(1);
const totalSize = ref(0);


const handleClick = (id) => {
    router.push(`/detail/${id}`);
}

onMounted(async () => {
    const res = await axios({
        url: "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=7994369",
        headers: {
            'x-client-info':
                '{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777","bc":"110100"}',
            'x-host':
                'mall.film-ticket.film.list'
        }
    });
    console.log(res.data)
    datalist.value = res.data.data.films;
    console.log(datalist.value[1]);
    totalSize.value = res.data.data.total;
    console.log(totalSize.value)
})


const onLoad = async () => {
    console.log(datalist.value.length)
    if(totalSize.value === datalist.value.length) {
        finished.value = true;
        return
    }
    console.log("到底了");
    pageNumber.value++;
    const res = await axios({
        url: `https://m.maizuo.com/gateway?cityId=110100&pageNum=${pageNumber.value}&pageSize=10&type=1&k=7994369`,
        headers: {
            'x-client-info':
                '{"a":"3000","ch":"1002","v":"5.2.1","e":"172475767310819585259339777","bc":"110100"}',
            'x-host':
                'mall.film-ticket.film.list'
        }
    });
    console.log(res.data.data.films)
    datalist.value = [...datalist.value,...res.data.data.films];
    loading.value = false;
}
</script>
<style scoped lang="scss">

// 外部组件:van-cell为van-list的子节点,无法直接使用类名进行样式绑定,以下为解决方案
// van-cell__value可以借助浏览器的devtool找到
// 深度选择器:表示只要是父节点下有class名为van-cell__value节点,文本都居中显示
:deep(.van-cell__value) {
    text-align: left;
}
ul {
    li {
        padding: 10px;
    }
}
</style>

注意点1:样式渲染

// 外部组件:van-cell为van-list的子节点,无法直接使用类名进行样式绑定,以下为解决方案
// van-cell__value可以借助浏览器的devtool找到
// 深度选择器:表示只要是父节点下有class名为van-cell__value节点,文本都居中显示
:deep(.van-cell__value) {
    text-align: left;
}

注意点2:

image-20240901101052309

28.element-plus

见代码:

29.TypeScript

29-1:组合式API

1.reactive定义自定义类型的对象

import {reactive,ref} from 'vue'
interface IState {
    name:string,
    age: number
}
const state:IState = reactive({
    name: "xiao3",
    age:23
})

2.ref定义变量

法1:

import {ref} from 'vue'
import type {Ref} from 'vue'
const state:Ref<IState> = ref({
    name: "aa",
    age:23
})

法2:

import {ref} from 'vue'
const state = ref<IState>({
    name:"xiao1",
    age: 22
})

3.计算属性使用

onMounted(()=>{
    console.log(mydiv.value?.textContent+"abc")
})
// 指明该计算属性返回为string类型
const computedName = computed<string>(() => {
    return state.value.name.substring(0,1).toUpperCase()+state.value.name.substring(1)
})

4.父传子,子传父

父传子:

image-20240902222211986

子传父:

image-20240902222729425

29-2:ts与路由

image-20240903063656628

image-20240903063334104

image-20240903063417172

image-20240903063528148

image-20240903063605585

29-3 TypeScript与Pinia

image-20240903070049776

image-20240903070137387

posted @ 2024-09-08 15:57  远道而重任  阅读(19)  评论(0编辑  收藏  举报