Vue2.0简讲!

news2024/9/29 9:35:59

Vue2.0简讲 @Draven

  • 入门
    • 1.1、响应式渲染
      • 1.1.0、Vue创建
      • 1.1.1、指令(7)
      • 1.1.2、class与style
        • 绑定HTML Class
          • 对象语法
          • 数组语法
        • 绑定内联样式
          • 对象语法
          • 数组语法
      • 1.1.3、条件渲染
        • v-if else-if
        • template v-if
      • 1.1.4、列表渲染
        • v-for
        • key
        • 数组更新检测
        • 列表渲染Vue2
        • 列表渲染Vue3
        • 过滤应用(模糊查询)
      • 1.1.5、事件处理器
        • 事件修饰符
        • 按键修饰符
        • 系统修饰键
        • 鼠标按钮修饰符
      • 1.1.6、计算属性
        • computed-同步
        • watch-异步
    • 1.2、fetch&axios
      • 1.2.0、fetch-get
      • 1.2.1、fetch-post
      • 1.2.2、后端要求传入json格式
      • 1.2.3、fetch-猫眼数据案例
      • 1.2.4、axios-get
      • 1.2.5、axios-post
      • 1.2.6、猫眼数据--案例
      • 过滤器
    • 1.3、组件
      • 1.3.1、组件
        • 1.1、全局与局部
          • 局部写法
        • 1.2、组件父传子
          • 属性验证&默认属性
        • 1.3、组件子传父
        • 1.4、中间人模式
        • 1.5、中央事件总线
        • 1.6、组件refs
        • 1.7、动态组件
      • 1.3.2、slot
        • 2.1、旧版slot
        • 2.2、新版slot
    • 1.4、过度
      • 1.4.1、过度效果
      • 1.4.2、多个元素过度
      • 1.4.3、多个组件过度
      • 1.4.4、多个列表过度
      • 1.4.5、可复用过度
    • 1.5、生命周期
      • 1.5.1、组件生命周期
        • 1.1、创建阶段
        • 1.2、更新阶段
      • 1.5.2、销毁
    • 1.6、Swiper
      • 1.6.1、Swiper静态
      • 1.6.2、Swiper动态
      • 1.6.3、vue-swiper
      • 1.6.4、vue-swiper-组件 ※
    • 1.7、Vue3
      • 1.7.1、组件定义
      • 1.7.2、生命周期
    • 1.8、指令
      • 1.8.1、自定义指令
      • 1.8.2、指令轮播
      • 1.8.3、指令函数简写
      • 1.8.4、Vue3-指令轮播
      • 1.8.5、轮播-nextTick
    • 1.9、Vue-Cli
      • 1.9.1、安装
      • 1.9.2、启动流程&入口文件
      • 1.9.3、eslint修复
      • 1.9.4、单文件组件
      • 1.9.5、WebStorm的Vue模板
      • 1.9.6、反向代理
    • spa&路由引入
      • SPA概念
      • vue-router
        • 开始
        • 重定向
        • 声明式导航
        • 嵌套路由
        • 编程式导航
        • 动态路由
        • 命名路由
        • 路由模式
      • 全局路由拦截
        • 守卫
        • 回到我们的目的页面
      • 局部路由拦截
      • 路由懒加载
      • rem
    • Swiper组件
    • 猫眼案例
      • axios封装
        • 1 - 对于数据请求的封装
        • 2- 对局数据请求的封装
        • 3 - 拦截器
      • 更好的滚动-betterScroll
      • ElementUi组件 - pc端框架
      • vant - 移动端框架
        • axios拦截,绑定加载框
        • 索引城市列表 组件
    • Vuex - 状态管理模式
      • state
      • mutations
      • actiones
    • Vuex新写法
      • 引入
      • mapState
      • mapActions
      • mapMutations
      • 注入
    • Vuex持久化
  • 常用方法
    • 变量
    • 数组
    • 事件
    • New系列
      • localStorage-本地储存
  • HTML属性
  • ajax方法
  • Vue
    • 基础方法
      • Vue外部
      • Vue内部
      • 方法区中
    • 组件方法
      • 标签
      • 使用
    • 过度方法
    • 生命周期

入门

1.1、响应式渲染

1.1.0、Vue创建

https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js

https://cdn.jsdelivr.net/npm/vue@2

下载Vue2.0.js

<script>
    new Vue(){
        el: "#选择器",".选择器"....,
        data:{
          数据{变量,数组,对象....}  
        },methods:{
            方法区
        }
    }
</script>

1.1.1、指令(7)

指令说明
v-bind动态绑定属性
v-if动态创建/删除
v-show动态显示/隐藏
v-on:click绑定事件
v-for遍历
  • {{item}}-{{index}}
v-model双向绑定表单
v-html可赋html代码
v-bind:src==> :src
v-on:click==> @click
<button @click=“list_addData()”>

1.1.2、class与style

绑定HTML Class

对象语法
<div :class="classObj">动态切换class-1-对象</div>
classObj:{
    aa:true,
    bb:true,
    cc:false,
},
// vue2 解决方案, Vue.set(对象,属性,值) 
==F12== Vue.set(vm.classObj,"dd",true) //新增 dd calss属性
数组语法
<div :class="classArr">动态切换class-2-数组</div>
classArr:["aa","bb"],
==F12== vm.classArr.push('dd') //新增 dd class属性
==F12== vm.classArr.shift()	   //删除第一个 calss 属性	
==F12== vm.classArr.splice(1,1)//删除下标为1的calss属性,删除一个

绑定内联样式

对象语法
<div :style="styleObj">动态切换style-1-对象</div>
styleObj:{
    backgroundColor:'red',
},
==F12== Vue.set(vm.styleObj,'fontSize','30px') 
//新增 fontSize样式
数组语法
<div :style="styleArr">动态切换style-2-数组</div>
styleArr:[{backgroundColor: 'yellow'}],
==F12== vm.styleArr.push({fontSize:'30px'}) 
//新增fontSize样式

1.1.3、条件渲染

v-if else-if

    <div id="box">
<!--        <div v-if="isCreated">111111111</div>-->
<!--        <div v-else>222222222</div>-->
        <h2>所有订单</h2>
        <ul>
            <li v-for="item in dataList">
               {{item.title}} <!-- -{{item.state}}-->
                <span v-if="item.state===0">-未支付</span>
                <span v-else-if="item.state===1">-代发货</span>
                <span v-else-if="item.state===2">-已发货</span>
                <span v-else>-已签收</span>
            </li>
        </ul>
    </div>
<script>
    var vm = new Vue({
        el: "#box",
        data:{
            isCreated:false,
            dataList:[
                {
                    title:'动感小苦茶',
                    state:0 ,//'未支付'
                },
                {
                    title:'动感大苦茶',
                    state:1 ,//'代发货'
                },
                {
                    title:'情趣小苦茶',
                    state:2 ,//'已发货'
                },
                {
                    title:'情趣大苦茶',
                    state:3 ,//'已完成'
                },
            ],
        },
    });
</script>

template v-if

<div id="box">
    <template v-if="isCreated">
        <div>111111</div>
        <div>222222</div>
        <div>333333</div>
    </template>
</div>
<script>
    new Vue({
        el: "#box",
        data:{
            isCreated : true
        }


        /*
        * template是一个包裹元素,不会真正创建在页面上.
        * */
    });
</script>

1.1.4、列表渲染

v-for

v-for
v-for=“temp in dataList”没有
v-for=“temp of dataList”区别

key

  • 跟踪每个节点的身份,从而重用和重新排序现有元素
  • 理想的 key 值是每项都有的且唯一的 id。data.id

Vue为什么要设置key值(底层)

数组更新检测

  • 使用以下方法操作数组,可以检测变动
    • push() pop() shift() unshift() splice() sort() reverse()
    • filter(), concat() 和 slice() ,map(),新数组替换旧数组
  • 不能检测以下变动的数组
    • vm.items[indexOfItem] = newValue
  • 解决
    1. Vue.set(example1.items, indexOfItem, newValue)
    2. splice

检测数组的变动(底层)

列表渲染Vue2

<div id="box">
    <!--数组遍历-->
    <ul>
        <li v-for="(temp,index) of dataList" :key="temp.id">
            {{temp}}-{{index}}
        </li>
    </ul>
    <!--对象遍历-->
    <ul>
        <li v-for="(temp,key) in obj ">
            {{key}}-{{temp}}
        </li>
    </ul>
    <!--数字遍历-->
    <ul>
        <li v-for="temp in 10">
            {{temp}}
        </li>
    </ul>
</div>
<script>
    new Vue({
        el: "#box",
        data:{
            dataList:["111","222","333"],
            obj:{
                name: "Draven",
                age: 18,
                location: "love"
            },
        },

    });
</script>

列表渲染Vue3

<div id="box">
    <!--数组遍历-->
    <ul>
        <li v-for="(temp,index) of dataList" :key="temp.id">
            {{temp}}-{{index}}
        </li>
    </ul>
    <!--对象遍历-->
    <ul>
        <li v-for="(temp,key) in obj ">
            {{key}}-{{temp}}
        </li>
    </ul>
    <!--数字遍历-->
    <ul>
        <li v-for="temp in 10">
            {{temp}}
        </li>
    </ul>
</div>
<script>
    Vue.createApp({
        data(){
            return {
                dataList:["111","222","333"],
                obj:{
                    name: "Draven",
                    age: 18,
                    location: "love"
                },
            }
        }
    }).mount("#box")
</script>

过滤应用(模糊查询)

<div id="box">
    <input type="text" @input="handleInput()" v-model="myText">
    <ul>
        <li v-for="temp in test()" :key="temp">
            {{temp}}
        </li>
    </ul>
<!--    {{ test() }}-->
</div>
<script>
    new Vue({
        el: "#box",
        data:{
            myText:"",
            dataList:["aaa","bbb","ccc","ddd","eee","ace","add","aee","dee","cbe","cde","dfa"]
        },
        methods:{
            test(){
                return this.dataList.filter(temp=> temp.includes(this.myText));
            },
        }
    });
</script>

1.1.5、事件处理器

  • 监听事件

    • 直接触发代码

    • <button @click="count++">add-3-表达式</button>
      
  • 方法事件处理器

    • 写函数名 handleClick

    • <button @click="handleAdd2">add-2-函数名</button>
      
      handleAdd2(evp){
          this.count++
          console.log(evp.target)
      },
      
  • 内联处理器方法

    • 执行函数表达式 handleClick( e v e n t ) = = ( event) ==( event)==(event 事件对象)==

    • <button @click="handleAdd1($event,1,2,3)">add-1-函数表达式</button>
      
      handleAdd1(evt,a,b,c){
          this.count++
          console.log(evt.target,a,b,c)
      },
      

事件修饰符

  • https://cn.vuejs.org/v2/guide/events.html
- `.stop`
- `.prevent`
- `.capture`
- `.self`
- `.once`
- `.passive`
  • <!-- 阻止单击事件继续传播(事件冒泡泡) -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 只有修饰符,阻止默认提交行为 -->
    <form v-on:submit.prevent></form>
    
    <!-- 添加事件监听器时使用事件捕获模式 -->
    <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
    <div v-on:click.capture="doThis">...</div>
    
    <!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
    <!-- 即事件不是从内部元素触发的 -->
    <div v-on:click.self="doThat">...</div>
    
  • <ul @click.self="handleUlClick">
        <!--禁止事件冒泡-->
        <li @click.stop="handleLiClick">1111</li>
        <li @click="handleLiClick">2222</li>
        <!--只可触发一次-->
        <li @click.once="handleLiClick">3333</li>
        <!--阻止a链接的默认行为-->
        <a href="http://www.baidu.com" @click.prevent>跳转</a>
    </ul>
    

按键修饰符

  • https://cn.vuejs.org/v2/guide/events.html#%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6
    • .enter
    • .tab
    • .delete (捕获“删除”和“退格”键)
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

系统修饰键

  • .ctrl
  • .alt
  • .shift
  • .meta
  • .exact

​ 注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

鼠标按钮修饰符

  • .left
  • .right
  • .middle

1.1.6、计算属性

computed-同步

  • 计算属性(防止模板过重,难以维护),负责逻辑放在计算属性中来写
  • 方法
    • 事件绑定,逻辑计算。可以不用return,没有缓存
  • 计算属性(重视结果)﹒
    • 解决模板过重问题,必须有return· ,只求结果缓存,同步。
//计算的属性
computed:{
    myComputedName(){ 
        console.log("计算属性")
        return this.myName.substring(0,1).toUpperCase() + this.myName.substring(1);
    }
}

watch-异步

  • watch(重视过程),监听一个值的改变。不用返回值﹐异步同步
		watch:{
    // es5 filter
    myText(newVal){
        console.log("改变了",newVal)
        //模拟ajax异步延时状态
        setTimeout(()=>{
            this.dataList = this.original.filter(temp=> temp.includes(newVal))
        },2000)
    }
},

1.2、fetch&axios

1.2.0、fetch-get

handleFetch(){
    //json文件路径,或数据来源
    fetch("./json/test.json")
    	//返回json数据
        .then(res=>res.json())
    	//输出json数据对象
        .then(res=>{
        console.log(res)
        })
    	//如果是psot请求,返回err报错信息
        .catch(err=>{
            console.log(err)
        })
}

1.2.1、fetch-post

handleFetch({
    //路径
    fetch("**")
    	//请求方式: post
    	method:'post',
        //设置请求头
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        },
        //form编码格式数据 key=value
        body: "name=kerwin&age=100"    
})
    .then(res=>res.json())
    .then(res=>{
    	log(res);
})

1.2.2、后端要求传入json格式

handleFetch({
    //路径
    fetch(url地址)
    	//请求方式: post
    	method:'post',
        //设置请求头
        headers: {
            "Content-Type": "application/json"
        },
        //固定格式 json字符串编码	 
        body: JSON.stringify({
            name:"kerin",
            age:100
        })    
})
    .then(res=>res.json())
    .then(res=>{
    	log(res);
})

1.2.3、fetch-猫眼数据案例

https://m.maizuo.com/v5/#/films/nowPlaying

  • F12->Network->筛选Fetch/XHR接口->Name选项卡中的数据接口->Response中的代码复制在json文件中
<button @click="handleFetch">ajax-fetch</button>
<ul>
    <li v-for="(data,index) in dataList" :key="data.filmId">
        <img :src="data.poster" alt="">
        {{data.name}}
        主演:
        <span v-for="item in data.actors ">{{item.name}} </span>
    </li>
</ul>
new Vue({
    el:"#box",
    data:{
        dataList:[],
    },
    methods:{
        handleFetch(){
            fetch("./json/maoyan.json")
                .then(res=>res.json())
                .then(res=>{
                // console.log(res.data.films)
                    this.dataList=res.data.films
                })
                .catch(err=>{
                    console.log(err)
                })
        },
    },
})

1.2.4、axios-get

  • axios数据第三方

  • 要使用必须引入js文件

<div id="box">
    <button @click="handleFetch">ajax-fetch</button>
    <ul>
        <li v-for="(data,index) in dataList" :key="data.filmId">
            <img :src="data.poster" alt="">
            {{data.name}}
            主演:
            <span v-for="item in data.actors ">{{item.name}} </span>
        </li>
    </ul>
</div>
new Vue({
    el:"#box",
    data:{
        dataList:[],
    },
    methods:{
        handleFetch(){
          axios.get("./json/maoyan.json").then(res=>{
              // console.log(res.data.data.films)
              this.dataList=res.data.data.films
          })
        },
    },
})

1.2.5、axios-post

  • axios自动检测传入的是json对象还是字符串
  • 从而选择传输方式
axios.post("****","name=Draven"&age="18")
axios.post("****",{name:"Draven",age:18})

1.2.6、猫眼数据–案例

json数据在1-沙漠风暴->03-fetch&axios->json->maizuo.json

  • 因为访问的img路径是个接口
  • 所以要进行处理使用
  • 这里可以借鉴学习,后端拦截img访问请求
  • 在前端进行宽高数条件处理后方可访问
<div id="box">
    <button @click="handleAjax">click-ajax</button>
    <ul>
        <li v-for="item in dataList" :key="item.id">
            <img :src="handleImg(item.img)" alt="">
            {{item.nm}}
        </li>
    </ul>
</div>
new Vue({
    el:"#box",
    data:{
        dataList:[]
    },
    methods:{
        handleAjax(){
            axios.get("./json/maizuo.json").then(res=>{
                console.log(res.data.movieList)
                this.dataList=res.data.movieList
            })
        },
        handleImg(src){
            // return src.replace("w.h/","")+"@1l_1e_1c_128w_128h"
            return src.replace("w.h","128.128")
        },
    },
})

过滤器

  • Vue3.0 不可以使用
<img :src="item.img | imgFilter" alt="">
//👆--------------------------------------------👇
//Vue 过滤器
Vue.filter("imgFilter",(url)=>{
    return url.replace("w.h","")+"@1l_1e_1c_128w_128h"
})

1.3、组件

  • 为什么组件化
    • 扩展HTML元素,封装可重用的代码
  • 组件注册方式
    • 全局组件
    • 局部组件

1.3.1、组件

1.1、全局与局部

  • 起名字: js驼峰 , html 链接符-
  • dom片段·没有代码提示· 没有高亮显示·-· vue单文件组件解决
  • css只能写成行内。- vue单文件组件解决
  • template包含一个根节点
  • 组件是孤岛,无法【直接】访问外面的组件的状态或者方法。-间接的组件交流。
  • 自定义的组件data必须是一个函数,
  • 所有的组件都在一起,太乱了 — vue单文件组件解决
方法说明
Vue.component(“navber”,{})定义一个全局标签
template:‘
aaaa
固定的
methods:{}方法
computed:{}计算属性
watch:{}请求
data(){return{}}数据
<div id="box">
    <navber></navber>
</div>
//👆-------------------------------------------👇
        //定义一个全局组件
        Vue.component("navber",{
            //dom ,js css
            template:'<div>' +
                '<button @click="handleLeft">left</button>' +'{{myName}}'+ '<button @click="handleRight">right</button>' +
                '</div>',
            methods: {
                handleLeft(){
                    console.log("left")
                },
                handleRight(){
                    console.log("right")
                }
            },
            computed:{},
            watch:{},
            //data在组件中必须是函数写法
            data(){
                return{
                    myName:"爱能克服遥远距离",
                }
            }
        })
		//根组件
        new Vue({
            el:"#box",
        })
局部写法
方法说明
components:{“draven-Child”:{template:‘
’}}
子组件
<div id="box">
    <navber></navber>

    <child></child>
</div>
//👆-------------------------------------------👇
//定义一个全局组件
        Vue.component("navber",{
            //dom ,js css
            template:'<section>' +
                '<button>left</button>' +'{{myName}}'+ '<button>right</button>' +
                '<child></child>'+

                '<draven-Child></draven-Child>'
                + '</section>',
            //局部,内部组件
            components:{
                "draven-Child":{
                    template:'<div>{{myName}}</div>',
                    data(){
                        return{
                            myName:"戒了烟我怎么办"
                        }
                    }
                },

            },
        })
        //全局
        Vue.component("child",{
            template:'<div style="background-color: red">"回忆化不开"</div>'
        })
        new Vue({
            el:"#box",
        })

1.2、组件父传子

  • 函数调用两次
  • 两次行为不一样
方法说明
props:{}接受属性
<div id="box">
    <div style="background-color: yellow">根组件标题</div>

    <navber my-Name="电影" :my-Right="false"></navber>
    <navber my-Name="影院"></navber>
</div>
//👆-------------------------------------------👇
 Vue.component("navber",{
            // props:["my-Name","my-Right"], //接受myName属性,    this.myName
            /*props:{
              "my-Name":String,
              "my-Right":Boolean,
            },//接受myName属性,  属性验证*/
            props: {
                "my-Name":{
                    type:String,
                    default:""
                },
                "my-Right": {
                    type:Boolean,
                    default:true
                },
            },//接受myName属性,  属性验证,默认属性
            template:'<div>' +
                '<button>left</button>' +
                '<span>{{myName}}</span>' +
                '<button v-show="myRight">right</button>' +
                '</div>'
        })
        new Vue({
            el:"#box",
        })//创建根组件
属性验证&默认属性
方法说明
props:{}接受属性
props: {
       "my-Name":{
       		type:String,
       		default:""
       },
       "my-Right": {
       		type:Boolean,
       		default:true
       },
},//接受myName属性,  属性验证,默认属性

1.3、组件子传父

方法说明
this.$emit(“myevent”)激活子组件事件
  • 步骤:

    1. 第一步, 在需要点击操作的按钮标签中 添加handleClick事件
    2. 第二步, 在子组件中实现handleClick方法 并使用this.$emit(“myevent”)激活
    3. 第三步, 在父组件中的子组件标签中 加入@myevent=“handleEvent”
    4. 第四步, 在父组件中实现handleEvent方法 方法区域的内容是点击后效果的具体实现
    5. 在这里插入图片描述
  • 父传子 - 属性

  • 子传父 - 事件

<div id="box">
    <navbar @myevent="handleEvent"></navbar>

    <sidebar v-show="isShow"></sidebar>
</div>
//👆-------------------------------------------👇
		Vue.component("sidebar",{
            template:'        <div style="background-color: #EF4A82" >\n' +
                '            <ul>\n' +
                '                <li>11111</li>\n' +
                '                <li>11111</li>\n' +
                '                <li>11111</li>\n' +
                '                <li>11111</li>\n' +
                '                <li>11111</li>\n' +
                '                <li>11111</li>\n' +
                '            </ul>\n' +
                '        </div>'
        })
        Vue.component("navbar",{
            template:'' +
                '        <div style="background-color: red">\n' +
                '            <button @click="handleClick()">点击</button>-导航栏\n' +
                '        </div>'
            ,methods:{
                handleClick(){
                    // console.log("子传父,告诉父组件,取反isShow的值")
                    // this.$emit("myevent",11)
                    this.$emit("myevent")
                }
            }
        })
        new Vue({
            el:"#box",
            data:{
                isShow: true
            },
            methods:{
                handleEvent(){
                    console.log("父组件定义的事情")
                    this.isShow= !this.isShow
                }
            }
        })

1.4、中间人模式

  • 中间人是 父
  • 子传父
  • 父传子
<div id="box">
    <button @click="handleAjax">ajax</button>

    <film-item v-for="item in dataList" :key="item.filmId" :mydata="item"  @myevent="handleEvent"></film-item>

    <!--{{filmData}}-->
    <film-datail :film-data="filmData"></film-datail>
</div>
//👆-------------------------------------------👇
 		//父传子
        Vue.component("filmDatail",{
            props:["filmData"],
            template:'' +
                '<div class="filminfo">' +
                '{{filmData}}' +
                '</div>'
        })
        //子传父
        Vue.component("filmItem",{
            props:["mydata"],
            template:'' +
                '<div class="item">' +
                    '<img :src="mydata.poster" alt="">' +
                    '{{mydata.name}}' +
                    '<div>' +
                        '<button @click="handleClick()">详情</button>' +
                    '</div>' +
                '</div>',
            methods:{
                handleClick(){
                    // console.log(this.mydata.synopsis)
                    this.$emit("myevent",this.mydata.synopsis)
                }
            }
        })

        new Vue({
            el:"#box",
            data:{
                dataList:[],
                filmData:"",
            },
            methods:{
                handleAjax(){
                    axios.get("./json/maoyan.json").then(res=>{
                        console.log(res.data.data.films)
                        this.dataList = res.data.data.films
                    })
                },
                //自定义的事件处理器
                handleEvent(data){
                    console.log("父组件定义",data)
                    this.filmData = data
                }
            }
        })



1.5、中央事件总线

  1. bus 中央时间总线 订阅发布模式
  2. vuex 状态管理
方法说明
bus.$on监听
bus.$emit发布
先发布后监听
<div id="box">
    <button @click="handleAjax">ajax</button>

    <film-item v-for="item in dataList" :key="item.filmId" :mydata="item"></film-item>

    <!--{{filmData}}-->
    <film-datail></film-datail>
</div>
//👆-------------------------------------------👇

var bus = new Vue()
    //bus.$on    监听
    //bus.$emit  发布

    //子传父
    Vue.component("filmItem", {
        props: ["mydata"],
        template: '' +
            '<div class="item">' +
                '<img :src="mydata.poster" alt="">' +
                '{{mydata.name}}' +
                    '<div>' +
                        '<button @click="handleClick">详情</button>' +
                    '</div>' +
            '</div>',
        methods: {
            handleClick() {
                console.log(this.mydata.synopsis)
                bus.$emit("yqy", this.mydata.synopsis)
            }
        }
    })

    //父传子
    Vue.component("filmDatail", {
        //组件刚刚创建好  就开始订阅
        data(){
            return{
                info:""
            }
        },
        //生命周期
        mounted(){
            // console.log("当前组件上树后触发")
            bus.$on("yqy", (data) => {
                console.log(11, data)
                this.info = data
            })
        },
        template: '' +
            '<div class="filminfo">' +
                '{{info}}' +
            '</div>'
    })

    new Vue({
        el: "#box",
        data: {
            dataList: [],
        },
        methods: {
            handleAjax() {
                axios.get("./json/maoyan.json").then(res => {
                    console.log(res.data.data.films)
                    this.dataList = res.data.data.films
                })
            },
        }
    })

1.6、组件refs

  • refs容易出问题

  • 因为可以随意修改子组件的状态值

  • this.$refs.      this.(属性)
    
<div id="box">
    <input type="text" ref="mytext">
    <input type="password" ref="mypassword">
    <button @click="handleAdd">add</button>
    <child ref="myChild"></child>
</div>
//👆-------------------------------------------👇

Vue.component("child",{
            data(){
                return{
                    myname:'child-11'
                }
            },
            template:'' +
                '<div>' +
                    'child---{{myname}}' +
                '</div>' +
                '',
        })
        new Vue({
            el:"#box",
            methods:{
                handleAdd(){
                    // console.log(this.$refs.mytext,this.$refs.mypassword)
                    // console.log(this.$refs.myChild.myname)
                    this.$refs.myChild.myname="child-22"
                }
            }
            /*
            * ref -绑定dom节点,拿到的就是 dom对象
            * ref -绑定组件,拿到的就是 组件对象
            * */
        })

1.7、动态组件

标签说明
活着,用来保留数据
动态组件
Vue提供的组件标签
<div id="box">
    <!--
    	<home v-show="which==='home'">1</home>
    	<list v-show="which==='list'">2</list>
    	<shopcar v-show="which==='shopcar'">3</shopcar>
    -->
    <keep-alive>
        <component :is="which"></component>
    </keep-alive>

    <footer>
        <ul>
            <li @click="which='home'">首页</li>
            <li @click="which='list'">列表</li>
            <li @click="which='shopcar'">购物车</li>
        </ul>
    </footer>
</div>
//👆-------------------------------------------👇
Vue.component("home",{
            template:
                '<div>' +
                    'home' +
                    '<input type="search">' +
                '</div>'
        })
        Vue.component("list",{
            template:
                '<div>' +
                    'list' +
                '</div>'
        })
        Vue.component("shopcar",{
            template:
                '<div>' +
                    'shopcar' +
                '</div>'
        })

        new Vue({
            el:'#box',
            data:{
                which:"home"
            }
        })

1.3.2、slot

2.1、旧版slot

标签说明
插槽,在组件dom中加入
属性
slot=“right”使用插槽
  • 插槽会根据模板dom中的slot属性来找组件dom中的插槽位置

  • 模板dom

  • 组件dom

  • 人和房间的值是一样的,形成一人一间房,一个盒子对应一个插槽操作

  • 插槽的意义:

    • 扩展组件能力
    • 提高组件的复用性
  • 单个插槽

    • <slot></slot>
      
  • 具有名字的插槽

    • <slot name="a"></slot>
      
<div id="box">
    <!--当前组件或者节点 在那个模板中,就能访问那个模板状态-->
    <child>
        <div slot="a">111</div>
        <div slot="b">222</div>
        <div slot="c">333</div>
        <div slot="a">444</div>
    </child>
    <navbar>
        <button slot="left">aaa</button>
        <i class="iconfont icon-all" slot="right">字体图标</i>
    </navbar>
</div>
//👆-------------------------------------------👇
//插槽的意义: 扩展组件能力,  提高组件的复用性
        Vue.component("navbar",{
            template:
                '<div>' +
                    '<slot name="left"></slot>' +
                    // '<button>left</button>' +
                    '<span>navbar</span>' +
                    // '<button>right</button>' +
                    '<slot name="right"></slot>' +
                '</div>'
        })

        //单个插槽, <slot></slot>
        //具有名字的插槽 <slot name="a"></slot>
        Vue.component("child",{
            template:
                '<div>' +
                    'child' +
                    '<slot name="a"></slot>' +
                    '<slot name="b"></slot>' +
                    '<slot name="c"></slot>' +
                    '<slot></slot>' +
                '</div>'
        })
        new Vue({
            el:"#box",
        })

2.2、新版slot

指令说明
代替 slot 属性
<template #b>简写
<div id="box">
    <!--当前组件或者节点 在那个模板中,就能访问那个模板状态-->
    <child>
        <template v-slot:a>
            <div>111</div>
        </template>
        <template #b>
            <div>222</div>
        </template>
        <div slot="c">333</div>
        <div slot="a">444</div>
    </child>
    <navbar>
        <template #left>
            <button>aaa</button>
        </template>
        <template #right>
            <i class="iconfont icon-all">字体图标</i>
        </template>
    </navbar>
</div>
//👆-------------------------------------------👇
    //插槽的意义: 扩展组件能力,  提高组件的复用性
    Vue.component("navbar",{
        template:
            '<div>' +
            '<slot name="left"></slot>' +
            // '<button>left</button>' +
            '<span>navbar</span>' +
            // '<button>right</button>' +
            '<slot name="right"></slot>' +
            '</div>'
    })

    //单个插槽, <slot></slot>
    //具有名字的插槽 <slot name="a"></slot>
    Vue.component("child",{
        template:
            '<div>' +
            'child' +
            '<slot name="a"></slot>' +
            '<slot name="b"></slot>' +
            '<slot name="c"></slot>' +
            '<slot></slot>' +
            '</div>'
    })
    new Vue({
        el:"#box",
    })

1.4、过度

1.4.1、过度效果

标签说明
固定过度标签
属性说明
enter-active-class=“”动画开始时切换的class
leave-active-class=“”动画结束时切换的class
name=“yqy”根据calss名动态查找
yqy-enter-active查找后根据enter和leave自动选择calss
yqy-leave-active
appear出现,当进入页面的时候自动执行出现动画
<style>
  /* 进场动画 */
  .yqy-enter-active {
    animation: aaa 1.5s;
  }

  /* 出场动画 */
  .yqy-leave-active {
    animation: aaa 1.5s reverse;
  }

  @keyframes aaa {
    0% {
      opacity: 0;
      transform: translateX(100px);
    }

    100% {
      opacity: 1;
      transform: translateX(0px);
    }
  }
</style>
//👆-------------------------------------------👇
<div id="box">
        <button @click="isShow = !isShow">change</button>
        <transition enter-active-class="yqy-enter-active" leave-active-class="yqy-leave-active">
            <div v-show="isShow">1111</div>
        </transition>
        <transition name="yqy">
            <div v-show="isShow">2222</div>
        </transition>
    </div>
//👆-------------------------------------------👇new Vue({
            el:"#box",
            data:{
                isShow:false
            }
        })
new Vue({
            el:"#box",
            data:{
                isShow:false
            }
        })

1.4.2、多个元素过度

  • Key值一样的会去对比

  • 如果标签一样

    • 为了性能,选择复用
    • 只会修改里面的内容
  • 如果标签不一样,

    • 即便设置了key值,也会自动干掉一个
    • 会根据v-if v-else 删除创建dom.
  • 标签一样,key值不一样

    • 就是为了不让相同的标签复用
    • 实现 标签不一样 时的效果
<style>
  /* 进场动画 */
  .yqy-enter-active {
    animation: aaa 1.5s;
  }

  /* 出场动画 */
  .yqy-leave-active {
    animation: aaa 1.5s reverse;
  }

  @keyframes aaa {
    0% {
      opacity: 0;
      transform: translateX(100px);
    }

    100% {
      opacity: 1;
      transform: translateX(0px);
    }
  }
</style>
//👆-------------------------------------------👇
<!-- <div class="kerwin-enter-active">333333333333333</div> -->
  
  <div id="box">
    <button @click="isShow = !isShow">change</button>
    <transition  name="yqy">
        <div v-if="isShow" key="1">11</div>
        <div v-else  key="2">22</div>
    </transition>
  </div>
//👆-------------------------------------------👇
  <script type="text/javascript">
    var vm = new Vue({
      el: "#box",
      data: {
        isShow: true
      }
    })

    /*
      diff算法,
       1. 同层级对比
       2. 同标签, 组件 对比
       3. 同key对比
    */
  </script>

1.4.3、多个组件过度

<div id="box">

    <!--<keep-alive>
        <component :is="which"></component>
    </keep-alive>-->

    <keep-alive>
        <transition name="yqy" mode="out-in">
            <component :is="which"></component>
        </transition>
    </keep-alive>

    <footer>
        <ul>
            <li @click="which='home'">首页</li>
            <li @click="which='list'">列表</li>
            <li @click="which='shopcar'">购物车</li>
        </ul>
    </footer>
</div>
//👆-------------------------------------------👇
Vue.component("home", {
        template:
            '<div>' +
            'home' +
            '<input type="search">' +
            '</div>'
    })
    Vue.component("list", {
        template:
            '<div>' +
            'list' +
            '</div>'
    })
    Vue.component("shopcar", {
        template:
            '<div>' +
            'shopcar' +
            '</div>'
    })

    new Vue({
        el: '#box',
        data: {
            which: "home"
        }
    })

1.4.4、多个列表过度

标签说明
组件中包含多个要执行动画的标签
:key=“item.id”使用key值的id做比较
提供唯一的key值属性
<transition-group name="yqy">
    <li v-for="(item,index) in dataList" :key="item">
        {{item}}
        <button @click="list_delData(index)">del</button>
    </li>
</transition-group>
//👆-------------------------------------------👇
new Vue({
        el: "#box",
        data: {
            dataList: ["11", "22", "33"],
            myText: "",
        },
        methods: {
            list_addData() {
                if (this.myText !== "") {
                    // console.log("获取value",this.myText)
                    this.dataList.push(this.myText);
                    //清空
                    this.myText = "";
                } else {
                    alert("请勿为空");
                }

            },
            list_delData(index) {
                // console.log("del",index)
                this.dataList.splice(index, 1);
            },
        }
    })

1.4.5、可复用过度

<style>
    .left{

    }
    .right{
        position: fixed;
        right: 0px;
        top: 0px;

    }
    /* 进场动画 */
    .left-enter-active {
        animation: aaa 1.5s;
    }

    /* 出场动画 */
    .left-leave-active {
        animation: aaa 1.5s reverse;
    }

    @keyframes aaa {
        0% {
            opacity: 0;
            transform: translateX(-100%);
        }

        100% {
            opacity: 1;
            transform: translateX(0px);
        }
    }
    /* 进场动画 */
    .right-enter-active {
        animation: aaa 1.5s;
    }

    /* 出场动画 */
    .right-leave-active {
        animation: aaa 1.5s reverse;
    }

    @keyframes aaa {
        0% {
            opacity: 0;
            transform: translateX(100px);
        }

        100% {
            opacity: 1;
            transform: translateX(0px);
        }
    }
</style>
//👆-------------------------------------------👇
<div id="box">
    <navbar  @myevent="handleEvent"></navbar>
<!--    <transition name="yqy">-->
        <sidebar v-show="isShow"  mode="left"></sidebar>
<!--    </transition>-->
</div>
//👆-------------------------------------------👇
Vue.component("navbar",{

        template:`
          <div>
          nabbar-
            <button @click="handleClick">click</button>
          </div>
        `,
        methods:{
            handleClick(){
                // 通知父组件 取反 isShow - 子传父 依靠 事件
                this.$emit("myevent")
            }
        }
    })

    Vue.component("sidebar",{
        props:["mode"],
        template:`
            <transition :name="mode">
            <ul style="background-color: yellow;width: 200px;height: 500px;" :class="mode">
              <li>首页</li>
              <li>钱包</li>
              <li>设置</li>
            </ul>
            </transition>
          `
    })

    new Vue({
        el:"#box",
        data:{
            isShow:false
        },
        methods:{
            handleEvent(){
                console.log("父组件","1111111")
                this.isShow = !this.isShow
            }
        }
    })

1.5、生命周期

1.5.1、组件生命周期

1.1、创建阶段

方法说明
beforeCreate()创建前
created()初始化 重要
beforeMount()载入前
模板解析前,最后一次修改模板的节点
mounted ()载入后
依赖于dom创建之后,才进行初始化工作的插件 (轮播插件)创建完dom后的初始化 重要
订阅 bus.$on初始化ajax数据
    <div id="box">
        {{name}}
        {{globalName}}
    </div>
//👆-------------------------------------------👇
//根组件
new Vue({
    // el:"#box",
    data:{
        name:"baby"
    },
    beforeCreate(){
        console.log("beforeCreate",this.name)
    },
    /*初始化 *-重要*/
    created(){
        console.log("created","初始化状态或者挂载到当前实例的一些属性工作")
        this.name+="1111" //被拦截的状态
        //this下面的值
        this.user=localStorage.getItem("user")
        this.globalName = "this可以直接访问的属性值,"
    },
    beforeMount(){
        console.log(document.getElementById("box").innerHTML)
        //模板解析之前最后一次修改模板的节点
        console.log(this.$el)
    },
    /*创建完dom后的初始化 *重要*/
    mounted(){
        console.log("拿到真实的dom节点",document.getElementById("box").innerHTML)
        //依赖于dom创建之后,才进行初始化工作的插件 (轮播插件)
        //订阅 bus.$on
        //ajax
    }
}).$mount("#box")

1.2、更新阶段

方法说明
beforeUpdate()更新前
updated()更新后
//更新阶段---------------
beforeUpdate(){
    console.log("beforeUpdate","更新之前,记录老的mon状态,比如滚动条位置的记录")
},
updated(){
    console.log("updated","更新完成,获取更新后的dom节点,才进行swiper工作的插件")
    console.log(document.getElementsByTagName("li"))
}

1.5.2、销毁

方法说明
beforeDestroy()销毁前
destroyed()销毁后
Vue.component("child",{
    data(){
        return{
            time:1000
        }
    },
    mounted(){
        this.id = setInterval(()=>{
            console.log("姚小煜")
            this.time--;
        },1000)
    },
    template:
        '<div>' +
            '抢购倒计时组件' +
            '<div>{{time}}</div>' +
        '</div>',
    created(){
          this.id = null;
    },
    beforeDestroy(){
        console.log("beforeDestroy","清除定时器,时间解绑,,,,")

        clearInterval(this.id)
    },
    destroy(){
        console.log("destroy","清除定时器,时间解绑,,,,")
    }

})
var vm = new Vue({
    el:"#box",
    data:{
        isCreated:true
    }
})

1.6、Swiper

1.6.1、Swiper静态

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="lib/swiper/js/swiper.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <style>
        .draven {
            height: 500px;
        }
    </style>
</head>

<body>
<header>导航</header>
<div class="swiper draven">
    <div class="swiper-wrapper">
        <div class="swiper-slide">11111111</div>
        <div class="swiper-slide">22222222</div>
        <div class="swiper-slide">33333333</div>
    </div>
    <!-- 如果需要分页器 -->
    <div class="swiper-pagination"></div>

    <!-- 如果需要导航按钮 -->
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>

    <!--    &lt;!&ndash; 如果需要滚动条 &ndash;&gt;-->
    <!--    <div class="swiper-scrollbar"></div>-->
</div>
<footer>底部内容</footer>
//👆-------------------------------------------👇
<script>
    //初始化 swiper
    new Swiper(".draven", {
        direction: "vertical", //垂直
        // 如果需要分页器
        pagination: {
            el: '.swiper-pagination',
        },
        loop: true, // 循环
        //自动
        autoplay: {
            delay: 2500,
            disableOnInteraction: false,
        },
        // 如果需要前进后退按钮
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },
    })
</script>
</body>

</html>

1.6.2、Swiper动态

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="lib/swiper/js/swiper.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <style>
        .draven {
            height: 500px;
        }
    </style>
</head>

<body>
<header>导航</header>
<div class="swiper draven">
    <div class="swiper-wrapper">

    </div>
    <!-- 如果需要分页器 -->
    <div class="swiper-pagination"></div>

    <!-- 如果需要导航按钮 -->
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>

    <!--    &lt;!&ndash; 如果需要滚动条 &ndash;&gt;-->
    <!--    <div class="swiper-scrollbar"></div>-->
</div>
<footer>底部内容</footer>
//👆-------------------------------------------👇
<script>
    //swiper 初始化过早
    setTimeout(()=>{
        var list = ["cccc","dddd","eeee"]
        var newlist = list.map(item=>`<div class="swiper-slide">${item}</div>`)
        console.log(newlist)

        var oweapper = document.querySelector(".swiper-wrapper")
        oweapper.innerHTML = newlist.join("")
        //dom插入完了之后,再new swiper
        init()
    },2000)
    //初始化 swiper
    function init(){
        new Swiper(".draven", {
            direction: "vertical", //垂直
            // 如果需要分页器
            pagination: {
                el: '.swiper-pagination',
            },
            loop: true, // 循环
            //自动
            autoplay: {
                delay: 2500,
                disableOnInteraction: false,
            },
            // 如果需要前进后退按钮
            navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
            },
        })
    }

</script>
</body>

</html>

1.6.3、vue-swiper

  • 无法复用
  • 如果当前页面 状态不止datalist一个,其他状态更新, update重新运行,new Swiper 执行多次, 出bug
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="lib/swiper/js/swiper.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/vue.js"></script>
    <style>
    </style>
    <style>
      *{
        margin: 0px;
        padding: 0px;
      }
      #box{
        width: 1519px;
        height: 630px;
        overflow: hidden;
      }
    </style>
</head>
<body>
<div id="box">
    <header>导航-{{myname}}</header>
    <div class="swiper draven">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(item,index) in datalist" :key="index">
                <img :src="item" alt="">
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>

    </div>
    <footer>底部内容</footer>
</div>
//👆-------------------------------------------👇
<script>
    //初始化 swiper --初始化过早

    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
            myname: "driven"
        },
        mounted() {
            //ajax
            setTimeout(() => {
                this.datalist = [
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221031/aae09131279f92db243a308c1c8bf9b8.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221101/adfde1ffdc3b53024ef7917e8f530839.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221023/8a9fc13d6622b84f936dd526f7002a93.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/3a799afc366751ccd940bc8ff47d8593.jpeg"
                ]
              //过早
            }, 2000)
        },
        updated(){
          new Swiper(".draven", {
            // direction: "vertical", //垂直
            // 如果需要分页器
            pagination: {
              el: '.swiper-pagination',
            },
            loop: true, // 循环
            //自动
            autoplay: {
              delay: 2500,
              disableOnInteraction: false,
            },
            // 如果需要前进后退按钮
            navigation: {
              nextEl: '.swiper-button-next',
              prevEl: '.swiper-button-prev',
            },
          })
        }
    })
/*    //初始化过早
    new Swiper(".draven", {
        // direction: "vertical", //垂直
        // 如果需要分页器
        pagination: {
            el: '.swiper-pagination',
        },
        loop: true, // 循环
        //自动
        autoplay: {
            delay: 2500,
            disableOnInteraction: false,
        },
        // 如果需要前进后退按钮
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },
    })*/

    /*
      1. 无法复用
      2. 如果当前页面 状态不止datalist一个,其他状态更新, update重新运行,new Swiper 执行多次, 出bug
    
    */
</script>
</body>

</html>

1.6.4、vue-swiper-组件 ※

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="lib/swiper/js/swiper.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/vue.js"></script>
    <script src="./module/vueswiper.js"></script>
</head>
<style>
    #box{
        width: 820px;
        height: 340px;
        overflow: hidden;
        margin: 0 auto;
        background-color: #EF4A82;
    }
</style>
<body>
<div id="box">
    <swiper v-if="datalist.length"  :loop="false">
        <swiper-item v-for="(item,index) in datalist" :key="index">
            <img :src="item" alt="">
        </swiper-item>
    </swiper>
<!--    <swiper :key="datalist.length"><swiper>-->
</div>
//👆-------------------------------------------👇
<script>
    Vue.component("swiperItem",{
        template:
            `<div class="swiper-slide">
                <slot></slot>
            </div>`,
    })
    Vue.component("swiper", {
        props:{
            loop:{
                type:Boolean,
                default:true,
            }
        }
        template: `
        <div class="swiper draven">
            <div class="swiper-wrapper">
                <slot></slot>
            </div>
            <!-- 如果需要分页器 -->
            <div class="swiper-pagination"></div>

            <!-- 如果需要导航按钮 -->
            <div class="swiper-button-prev"></div>
            <div class="swiper-button-next"></div>
        </div>`,
        mounted(){
            new Swiper(".draven", {
                // direction: "vertical", //垂直
                // 如果需要分页器
                pagination: {
                    el: '.swiper-pagination',
                },
                loop: true, // 循环
                //自动
                autoplay: {
                    delay: 2500,
                    disableOnInteraction: false,
                },
                // 如果需要前进后退按钮
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev',
                },
            })
        }
    })
    new Vue({
        el: "#box",
        data:{
            datalist:[]
        },
        mounted() {
            //ajax
            setTimeout(() => {
                this.datalist = [
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221031/aae09131279f92db243a308c1c8bf9b8.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221101/adfde1ffdc3b53024ef7917e8f530839.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221023/8a9fc13d6622b84f936dd526f7002a93.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/3a799afc366751ccd940bc8ff47d8593.jpeg"
                ]
                //过早
            }, 2000)
        }
    })
</script>
</body>
</html>

1.7、Vue3

1.7.1、组件定义

  • 先把主对象app new出来
  • 使用app去调用需要使用的函数方法
  • 最后通过.mount("#box")进行上树
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="lib/vue.next.js"></script>
</head>
<body>
<div id="box">
    {{myname}}

    <navbar myname="aaa">
        <div>11</div>
    </navbar>
</div>
//👆-------------------------------------------👇
<script>
    var obj = {
        data() {
            return {
                myname: "kzc"
            }
        },
        methods() {
        },
        computed() {

        }
    }
    let app = Vue.createApp(obj)
    app.component("navbar", {
        props: ["myname"],
        template:
            '<div>' +
            'navbar-{{myname}}' +
            '<slot></slot>' +
            '</div>'
    })
    app.mount("#box")
</script>
</body>
</html>

1.7.2、生命周期

  • vue3的生命周期中
  • beforeDestroy 替换成 beforeUnmount
  • destroy 替换成 unmounted
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="lib/vue.next.js"></script>
</head>
<body>
<div id="box">
    {{myname}}

    <navbar myname="aaa">
        <div>11</div>
    </navbar>
</div>
//👆-------------------------------------------👇
<script>
    var obj = {
        data() {
            return {
                myname: "kzc"
            }
        },
        methods() {
        },
        beforeCreate() {
            console.log("beforeCreate")
        },
        created(){
            console.log("created")
        },

        beforeMount(){
            console.log("beforeMount")
        },
        mounted(){
            console.log("mounted")
        },

        beforeUnmount(){
            console.log("beforeUnmount")
        },
        unmounted(){
            console.log("unmounted")
        }
        /*生命周期替换
        beforeDestroy(){
            console.log("beforeDestroy")
        },
        destroy(){
            console.log("destroy")
        },*/
    }
    let app = Vue.createApp(obj)
    app.component("navbar", {
        props: ["myname"],
        template:
            '<div>' +
            'navbar-{{myname}}' +
            '<slot></slot>' +
            '</div>'
    })
    app.mount("#box")
</script>
</body>
</html>

1.8、指令

1.8.1、自定义指令

  • 指令:
    • 为了操作底层dom 作者流的方案
  • 实际应用:
    • 可以通过指令知道什么时候dom创建完成,从而进行 依赖dom的库的初始化工作
  • 指令的生命周期
方法说明
inserted(el,binding)第一次插入到父节点中触发
update(el,binding)每次更新的时候触发
Vue.directive(“hello”,{})自定义组件.组件名"hello"
<div id="box">
    <div v-hello="'red'">11111111</div>
    <div v-hello="'yellow'">22222222</div>
    <div v-hello="whichColor">33333333</div>
</div>
//👆-------------------------------------------👇
<script type="text/javascript">
    //指令: 为了操作底层dom 作者流的方案
    //
    Vue.directive("hello",{
        //指令的生命周期函数
        //第一次插入到父节点中触发
        inserted(el,binding){
            console.log("inserted",binding)
            el.style.backgroundColor=binding.value
        },
        //每次更新的时候触发
        update(el,binding){
            console.log("update")
            el.style.backgroundColor=binding.value
        }
    })
    var vm = new Vue({
        el:"#box",
        data:{
            whichColor:"blue"
        }
    })
</script>

1.8.2、指令轮播

  • 指令轮播是一只解决方案
  • 而并不是最优的使用方案
<div id="box">
    <header>导航-{{myname}}</header>
    <div class="swiper draven">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(item,index) in datalist" :key="index" v-swiper="{
                index:index,
                length:datalist.length
            }">
                <img :src="item" alt="">
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>
    </div>
    <footer>底部内容</footer>
</div>
//👆-------------------------------------------👇
<script>
    Vue.directive("swiper",{
        inserted(el,binding){
            console.log(el,binding.value.index,binding.value.length)
            console.log(el,binding.value)
            //最后一个节点插入都到父节点了,再去new
            let{length,index} = binding.value
            if (index===length-1){
                console.log("swiper")
                new Swiper(".draven", {
                    // direction: "vertical", //垂直
                    // 如果需要分页器
                    pagination: {
                        el: '.swiper-pagination',
                    },
                    //自动
                    autoplay: {
                        delay: 2500,
                        disableOnInteraction: false,
                    },
                    // 如果需要前进后退按钮
                    navigation: {
                        nextEl: '.swiper-button-next',
                        prevEl: '.swiper-button-prev',
                    },
                })
            }
        },
    })
    //初始化 swiper --初始化过早
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
            myname: "driven"
        },
        mounted() {
            //ajax
            setTimeout(() => {
                this.datalist = [
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221031/aae09131279f92db243a308c1c8bf9b8.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221101/adfde1ffdc3b53024ef7917e8f530839.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221023/8a9fc13d6622b84f936dd526f7002a93.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/3a799afc366751ccd940bc8ff47d8593.jpeg"
                ]
                //过早
            }, 2000)
        },
    })
</script>

1.8.3、指令函数简写

<div id="box">
    <div v-hello="'red'">11111111</div>
    <div v-hello="'yellow'">22222222</div>
    <div v-hello="whichColor">33333333</div>
</div>
//👆-------------------------------------------👇
<script type="text/javascript">
    //指令: 为了操作底层dom 作者流的方案
    //
    Vue.directive("hello",(el,binding)=>{
        console.log("更新和创建都会执行")
        el.style.backgroundColor=binding.value
    })
    var vm = new Vue({
        el:"#box",
        data:{
            whichColor:"blue"
        }
    })
</script>

1.8.4、Vue3-指令轮播

<div id="box">
    <header>导航-{{myname}}</header>
    <div class="swiper draven">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(item,index) in datalist" :key="index" v-swiper="{
                index:index,
                length:datalist.length
            }">
                <img :src="item" alt="">
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>
    </div>
    <footer>底部内容</footer>
</div>
//👆-------------------------------------------👇
<script>
    //初始化 swiper --初始化过早
    let obj = {
        data(){
            return{
                datalist:[]
            }
        },
        mounted() {
            //ajax
            setTimeout(() => {
                this.datalist = [
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221031/aae09131279f92db243a308c1c8bf9b8.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221101/adfde1ffdc3b53024ef7917e8f530839.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221023/8a9fc13d6622b84f936dd526f7002a93.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/3a799afc366751ccd940bc8ff47d8593.jpeg"
                ]
                //过早
            }, 2000)
        },
    }
    let app = Vue.createApp(obj);
    app.directive("swiper",{
        mounted(el,binding){
            console.log(el,binding.value.index,binding.value.length)
            console.log(el,binding.value)
            //最后一个节点插入都到父节点了,再去new
            let{length,index} = binding.value
            if (index===length-1){
                console.log("swiper")
                new Swiper(".draven", {
                    // direction: "vertical", //垂直
                    // 如果需要分页器
                    pagination: {
                        el: '.swiper-pagination',
                    },
                    //自动
                    autoplay: {
                        delay: 2500,
                        disableOnInteraction: false,
                    },
                    // 如果需要前进后退按钮
                    navigation: {
                        nextEl: '.swiper-button-next',
                        prevEl: '.swiper-button-prev',
                    },
                })
            }
        },
    })
    app.mount("#box")
    /*
      1. 无法复用
      2. 如果当前页面 状态不止datalist一个,其他状态更新, update重新运行,new Swiper 执行多次, 出bug
    */

    /*
        //vue3 指令生命周器 约== 组件生命周期

    */
</script>

1.8.5、轮播-nextTick

  • 无法复用
<div id="box">
    <header>导航-{{myname}}</header>
    <div class="swiper draven">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(item,index) in datalist" :key="index">
                <img :src="item" alt="">
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>

    </div>
    <footer>底部内容</footer>
</div>
//👆-------------------------------------------👇
<script>
    //初始化 swiper --初始化过早
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
            myname: "driven"
        },
        mounted() {
            //ajax
            setTimeout(() => {
                this.datalist = [
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221031/aae09131279f92db243a308c1c8bf9b8.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221101/adfde1ffdc3b53024ef7917e8f530839.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221023/8a9fc13d6622b84f936dd526f7002a93.jpeg",
                    "https://ossweb-img.qq.com/upload/adw/image/977/20221028/3a799afc366751ccd940bc8ff47d8593.jpeg"
                ]
                //过早
                this.$nextTick(()=>{
                    console.log("我比updated执行的都晚,而且只执行一次")
                    new Swiper(".draven", {
                        // direction: "vertical", //垂直
                        // 如果需要分页器
                        pagination: {
                            el: '.swiper-pagination',
                        },
                        loop: true, // 循环
                        //自动
                        autoplay: {
                            delay: 2500,
                            disableOnInteraction: false,
                        },
                        // 如果需要前进后退按钮
                        navigation: {
                            nextEl: '.swiper-button-next',
                            prevEl: '.swiper-button-prev',
                        },
                    })
                })
            }, 2000)
        },
        /*updated(){
            new Swiper(".draven", {
                // direction: "vertical", //垂直
                // 如果需要分页器
                pagination: {
                    el: '.swiper-pagination',
                },
                loop: true, // 循环
                //自动
                autoplay: {
                    delay: 2500,
                    disableOnInteraction: false,
                },
                // 如果需要前进后退按钮
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev',
                },
            })
        }*/
    })
    /*    //初始化过早
        new Swiper(".draven", {
            // direction: "vertical", //垂直
            // 如果需要分页器
            pagination: {
                el: '.swiper-pagination',
            },
            loop: true, // 循环
            //自动
            autoplay: {
                delay: 2500,
                disableOnInteraction: false,
            },
            // 如果需要前进后退按钮
            navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
            },
        })*/

    /*
      1. 无法复用
      2. 如果当前页面 状态不止datalist一个,其他状态更新, update重新运行,new Swiper 执行多次, 出bug

    */
</script>

1.9、Vue-Cli

1.9.1、安装

选项说明
Bable可以把ES6转换为ES5代码(兼容性问题) 必选
Router路由必选
VuexVue全家桶,状态管理必选
CSS Pre-processorsCSS预处理器
Linter / Formatter保证按照国际默认代码风格规范化必选
  • 流程图
  1. 在需要创建项目的文件夹中右键–>打开Powershell窗口

    • 在这里插入图片描述
  2. 输入vue --version查看vue -Cli版本

    • 在这里插入图片描述

    • PS: 如果没有安装过Cli工具 输入一下命令进行安装

    • npm install -g @vue/cli
      
  3. 输入 vue create hello-world 来创建一个新的项目

    • 在这里插入图片描述

    • 选择需要的版本

    • 自带的两个版本为:

      • 默认的Vue2
      • 默认的Vue3
      • 我们需要自定义,所以选择Manually select features
  4. 使用空格和上下键来选择我们需要的组件工具

    • 其中Bable、Router、Vuex、CSS Pro-processors、Linter / Formatter为必选
    • 在这里插入图片描述
  5. 选择项目Vue版本

    • 在这里插入图片描述
  6. 选择路由状态,此处按回车默认

    • 在这里插入图片描述
  7. 选择dart-sass编译器(同比node编译器效率更高)

    • 在这里插入图片描述
  8. 选择ESLint + Standard config版本,检查并修复代码错误的标准配置

    • 在这里插入图片描述
  9. 每次保存自动Lint,每次提交后自动修复

    • 在这里插入图片描述
  10. 这么多单独的东西,是对每一个都分出来一个配置文件还是将全部组建配置到package中的选择

    • 在这里插入图片描述
  11. 是否为你的选择做出预设,以便下次使用时,多个为这些配置的选项

    • 在这里插入图片描述
  12. 回车,等待安装完成

    • 在这里插入图片描述
  13. 安装完成提示

    • 在这里插入图片描述
  14. 文件夹中查看项目结构

    • 在这里插入图片描述

    • 在这里插入图片描述

  • 项目结构解析

1.9.2、启动流程&入口文件

  1. 看项目结构中的package.json文件,scripts下的第一个为启动项目
    • 在这里插入图片描述

    • npm run serve 自动启动本地服务器,来运行项目 – 开发阶段用的

    • npm run buld 把所有的代码压缩,合并. – 发布阶段用的

    • npm run lint 能帮我们检查出一百多个格式错误,进行修复,修复我们所写的所有错误的格式

    • 当然,这三个名字都不是固定的,可以通过修改,来改变npm run ""的版本

  2. 启动,测试项目
    1. 在集成工具(WebStrome)中,进入集成终端

      • 在这里插入图片描述
    2. 输入npm run serve启动项目

      • 在这里插入图片描述

      • 在浏览器地址栏中输入 Local 或 Network:下的地址可以访问服务器入口主页

      • 在这里插入图片描述

    3. 项目执行周期

      1. 项目下public文件夹里面的index.html文件是入口文件
      2. index.html文件中有个div id=app的标签,是vue的父组件
      3. 父组件通过src目录下的main.js文件创建
      4. 组件具体代码位置在App.vue中实现

1.9.3、eslint修复

  • npm run link修复

  • 下载ESLint插件

    • 在这里插入图片描述
  • 配置插件

    • 在这里插入图片描述
  • 暂时关闭代码检测 .eslintrc.js文件中注释以下代码

    • 在这里插入图片描述

    • 以后再想用可以取消注释,然后勾选ESLint设置中的Run....或终端执行npm run lint

1.9.4、单文件组件

属性说明
scoped局部作用域
<style lang=“scss” scoped
  • 父组件

    • <template>
        <div>
          hello {{myname}}
          <img src="/y5.jpg" alt="">
          <input type="text" v-model="mytext">
          <button @click="handleClick">add</button>
          <ul>
            <li v-for="(item,index) in datalist" :key="index">
                {{item}}
            </li>
          </ul>
          <!--子组件-->
          <navbar myname="home" :myright="false"  @myevent="handleEvent">
            <div>插槽 壹壹</div>
          </navbar>
          <sidebar v-show="isShow"></sidebar>
          <!--组件-->
          <div v-hello>我是组件1</div>
          <div v-hello>我是组件2</div>
          <!--过滤器-->
          <img :src="imgUrl | imgFilter" alt="">
        </div>
      </template>
      
      <script>
      import navbar from "@/components/Navbar";
      import sidebar from "@/components/Sidebar";
      import Vue from "vue";
      // 全局注册
      // Vue.component("navbar",navbar)
      Vue.directive("hello",{
        inserted(el,binding){
          console.log(el)
          el.style.border="1px solid red"
          console.log(binding)
        }
      })
      Vue.filter("imgFilter",(path)=>{
          return "/y5.jpg"
      })
      export default {
        data(){
          return{
            myname:"杨清壹",
            datalist:["田洪源","孔智超","杨清壹"],
            mytext:"",
            isShow: true,
            imgUrl: "https://ossweb-img.qq.com/upload/adw/image/977/20221028/07b4fa4c953cc2e6efae2541389a1299.jpeg"
          }
        },
        // 局部注册
        components:{
          navbar,
          sidebar
        },
        methods:{
          handleClick(){
            this.datalist.push(this.mytext);
            this.mytext="";
          },
          handleEvent(){
              this.isShow= !this.isShow
          },
        },
        computed:{
      
        },
        watch:{
      
        }
      }
      </script>
      
      <style lang="scss">
        $width:200px;
        ul{
          li{
            background: pink;
            width: $width;
          }
        }
        img{
          width: 108px;
          height: 170px;
        }
      </style>
      
      
      
  • 子组件 navbar

    • <template>
          <div>
              <button @click="handleLeft">left</button>
            {{ myname }}
              <button v-show="myright">right</button>
            <slot></slot>
          </div>
      </template>
      
      <script>
      export default {
        props:{
          "myname":{
              type:String,
              default:"杨清壹"
          },
          "myright":{
              type:Boolean,
              default: true
          }
        },
        methods:{
          handleLeft(){
              this.$emit("myevent")
          }
        }
      }
      </script>
      <style scoped>
      
      </style>
      
      
  • 子组件 Sidebar

    • <template>
        <ul>
          <li v-for="(item) in datalist" :key="item.filmId">
            {{ item.name }}
          </li>
        </ul>
      </template>
      
      <script>
      import axios from 'axios'
      export default {
        data(){
          return{
              datalist:[]
          }
        },
        mounted() {
          /*fetch("/test.json").then(res=>res.json())
          .then(res=>{
            console.log(res.data.films)
            this.datalist=res.data.films
          })*/
          axios.get('/test.json').then(res=>{
            console.log(res.data.data.films)
            this.datalist=res.data.data.films
          })
        },
      }
      </script>
      <!--scoped 局部作用域-->
      <style lang="scss" scoped>
      $width: 200px;
      ul {
        li {
          background: greenyellow;
          width: $width;
        }
      }
      </style>
      
      

1.9.5、WebStorm的Vue模板

  • **File—Setting—Editor—File and Code Templates **

  • 点击添加,依次输入 Name 和 Extension,OK 即可。

  • 在这里插入图片描述

  • <!--
        @author 玫瑰到了花期
        @data ${DATE} ${TIME}
        @love 又偷偷看你了
        @V: CBWR-K
    -->
    <template>
      <div class="">
    
      </div>
    </template>
    
    <script>
    // 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
    // 例如:import 《组件名称》 from '《组件路径》';
    // 例如:import uploadFile from '@/components/uploadFile/uploadFile'
    
    export default {
      name: '${COMPONENT_NAME}',
      // import引入的组件需要注入到对象中才能使用
      components: {},
        props: {
        // patientIn: {
        //   type: Object,
        //   default () {
        //     return {}
        //   }
        // }
      },
      data () {
        // 这里存放数据
        return {
    
        }
      },
      // 监听属性 类似于data概念
      computed: {},
      // 方法集合
      methods: {},
      // 监控data中的数据变化
      watch: {},
      // 生命周期 - 创建完成(可以访问当前this实例)
      created () {
    
      },
      // 生命周期 - 挂载完成(可以访问DOM元素)
      mounted () {
    
      },
      beforeCreate () {
      }, // 生命周期 - 创建之前
      beforeMount () {
      }, // 生命周期 - 挂载之前
      beforeUpdate () {
      }, // 生命周期 - 更新之前
      updated () {
      }, // 生命周期 - 更新之后
      beforeDestroy () {
      }, // 生命周期 - 销毁之前
      destroyed () {
      }, // 生命周期 - 销毁完成
      activated () {
      } // 如果页面有keep-alive缓存功能,这个函数会触发
    }
    </script>
    
    <style scoped lang="scss">
      //@import url(); 引入公共css类
    </style>
    

1.9.6、反向代理

  • 反向代理前

    • mounted() {
        axios.get('https://api.bilibili.com/x/web-interface/nav').then(res=>{
          console.log(res.data)
        })
      }
      
  • 反向代理后

    • App.vue

    • mounted() {
        axios.get('/draven/x/web-interface/nav').then(res=>{
          console.log(res.data)
        })
      }
      
    • vue.config.js

    •   // 配置反向代理
        devServer: {
          proxy: {
      /*        '/ajax':{
                target:'https://api.bilibili.com',
                changeOrigin:true,
              }*/
            '/draven':{
              target: 'https://api.bilibili.com/',
              //替换
              changeOrigin:true,
              //路径重启
              pathRewrite:{
                '^/draven':''
              }
            }
          }
        }
      

spa&路由引入

SPA概念

  • 单页面应用多页面应用
    组成一个外壳页面和多个页面片段组成多个完整页面构成
    资源共用(css,js)共用,只需在外壳部分加载不共用,每个页面都需要加载
    刷新方式页面局部刷新或更改整页刷新
    url模式a.com/#/pageone
    a.com/#/pagetwo
    a.com/pageone.html
    a.com/pagetwo.html
    用户体验正面片段间的切换快,用户体验好页面切换加载缓慢,流畅度不够
    数据传递容易依赖url传参、或者cookie,localStorage等
    搜索引擎优化(SEO)需要单独方案、实现较为困难、不利于SEO检索、可利用服务器端渲染(SSR)优化实现方法简易
    试用范围高要求的体验度、追求界面流畅的应用适用于追求高度支持搜索引擎的应用
    开发成本较高,需要借助专业的框架较低,但页面重复代码多
    维护成本相对容易相对复杂
  • 在这里插入图片描述

vue-router

开始

标签说明
全局路由组件 路由导航
to=“/films”跳转到指定组件
active-class=“draven”自定义class样式
tag=“li”表示此组件会渲染成Li标签
路由容器,放在最后(所有路由导航后面)
  • 路由所在的文件夹 – router->index.js

  • **components**文件夹属于公共组件

  • **views**文件夹属于主视图组件

  • views文件夹中有cinemas,center,films三个组件

  • import Vue from 'vue'
    // 引入路由
    import VueRouter from 'vue-router'
    // 引入组件
    import Films from "@/views/Films";
    import Center from "@/views/Center";
    import Cinemas from "@/views/Cinemas";
    
    Vue.use(VueRouter) // 注册路由插件  两个全局组件 router-view router-link
    
    // 配置表
    const routes = [
      {
        path:"/films",
        component:Films,
      },
      {
        path:"/center",
        component:Center,
      },
      {
        path:"/cinemas",
        component:Cinemas,
      },
    ]
    
    const router = new VueRouter({
      routes
    })
    
    export default router
    
  • App.vue父组件中使用路由容器

    •   <!--路由容器-->
      <router-view></router-view>
      
  • 在浏览器地址栏中输入配置表(routes)的path进行访问

    • 在这里插入图片描述

重定向

  • 在输入localhost:8080不指定路由的情况下,让其默认进入主路由组件

    • 在配置表中加入如下代码

    • // 重定向
      {
        path: "/",
        redirect:'/films'
      }
      
    • 这样在地址栏中输入localhost:8080就会自动重定向与films组件

  • 在输入localhost:8080:后指定的路由地址为错的时候,让其默认进入主路由组件

    • 在配置表中修改路径后如下代码

    • // 重定向
      {
        path: "*",
        redirect:'/films'
      }
      
  • 点击按钮使用路由切换页面组件

    • <template>
        <div>
          <ul>
            <li>
              <a href="/#/films">电影</a>
            </li>
            <li>
              <a href="/#/cinemas">影院</a>
            </li>
            <li>
              <a href="/#/films">我的</a>
            </li>
          </ul>
            hello 小杨
            <!--路由容器-->
          <router-view></router-view>
        </div>
      </template>
      

声明式导航

<template>
  <div>
    <ul>
      <!--vue router(vue4版本之前) -1- 声明式导航-->
    <!--<router-link to="/films" active-class="draven" tag="li">电影</router-link>
        <router-link to="/cinemas" active-class="draven" tag="li">影院</router-link>
        <router-link to="/center" active-class="draven" tag="li">我的</router-link>-->
      <!--vue router(vue4版本) -2- 声明式导航-->
        <router-link to="/films" custom v-slot="{navigate,isActive}">
            <li @click="navigate" :class="isActive? 'draven':''">电影 -- {{isActive}}</li>
        </router-link>
        <router-link to="/cinemas" custom v-slot="{navigate,isActive}">
            <li @click="navigate" :class="isActive? 'draven':''">影院 -- {{isActive}}</li>
        </router-link>
        <router-link to="/center" custom v-slot="{navigate,isActive}">
            <li @click="navigate" :class="isActive? 'draven':''">我的 -- {{isActive}}</li>
        </router-link>
    </ul>
      hello 小杨
  </div>
</template>

嵌套路由

  • 在多个组件之间来回横跳

  • 项目结构:

    • 在这里插入图片描述
  • App.vue

  • import Vue from 'vue'
    // 引入路由
    import VueRouter from 'vue-router'
    // 引入组件
    import Films from "@/views/Films";
    import Center from "@/views/Center";
    import Cinemas from "@/views/Cinemas";
    import Comingsoon from "@/views/films/Comingsoon";
    import Nowplaying from "@/views/films/Nowplaying";
    import Search from "@/views/Search";
    Vue.use(VueRouter) // 注册路由插件  两个全局组件 router-view router-link
    
    // 配置表
    const routes = [
      {
        path:"/films",
        component:Films,
        // 嵌套路由
        children:[
          {
            path:"/films/nowplaying",
            component:Nowplaying,
          },
          {
            path:"/films/comingsoon",
            component:Comingsoon,
          },
          //重定向 在进入films过程中,自动进入nowplaying子组件
          {
            path: "/films",
            redirect: "/films/nowplaying"
          },
        ]
      },
      {
        path: "/cinemas/search",
        component: Search
      },
      {
        path:"/center",
        component:Center,
      },
      {
        path:"/cinemas",
        component:Cinemas,
      },
      // 重定向
      {
        path: "*",
        redirect:'/films'
      },
    ]
    const router = new VueRouter({
      routes
    })
    
    export default router
    
  • films.vue

  • <template>
      <div class="">
        films
        <div style="height: 200px;background: yellow">大轮播</div>
        <div>
          二级的声明式导航
        </div>
        <router-view></router-view>
      </div>
    </template>
    

编程式导航

  • Nowplaying.vue films下级目录,模仿列表

  • <!--
        @author 玫瑰到了花期
        @data 2022/11/9 13:54
        @love 又偷偷看你了
        @V: CBWR-K
    -->
    <template>
      <div class="">
          nowplaying
          <!--列表跳详情-->
          <ul>
            <li v-for="(item,index) in datalist" :key="index" @click="handleChangePage()">
                {{item}}
            </li>
          </ul>
      </div>
    </template>
    
    <script>
    // 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
    // 例如:import 《组件名称》 from '《组件路径》';
    // 例如:import uploadFile from '@/components/uploadFile/uploadFile'
    
    export default {
      name: 'Nowplaying',
      // import引入的组件需要注入到对象中才能使用
      components: {},
      props: {
        // patientIn: {
        //   type: Object,
        //   default () {
        //     return {}
        //   }
        // }
      },
      data() {
        // 这里存放数据
        return {
          datalist:['1111','2222','3333']
        }
      },
      // 监听属性 类似于data概念
      computed: {},
      // 方法集合
      methods: {
        handleChangePage(){
          // 编程式导航
          // location.href="#/detail"
          this.$router.push('/detail')
        },
      },
    </script>
    
    <style scoped lang="scss">
    //@import url(); 引入公共css类
    </style>
    

动态路由

在这里插入图片描述

  • 第一步: url跳转

    • nowplaying

    • <div class="">
          nowplaying
          <!--列表跳详情-->
          <ul>
            <li v-for="(item,index) in datalist" :key="index" @click="handleChangePage(item.filmId)">
                {{item}}
            </li>
          </ul>
      </div>
      
  • 第二步: 发送请求

    • nowplaying

    • methods: {
        handleChangePage(id){
          console.log(id)
          // 编程式导航
          // location.href="#/detail"
          // this.$router.push('/detail/'+id)
          this.$router.push(`/detail/${id}`)
        },
      },
      
    • router -> index.js

    •   {
          path: "/detail/:id", // 动态二级路由
          component: Detail
        },
      
  • 第三步: 获取id

    • detail.vue

    • // 生命周期 - 创建完成(可以访问当前this实例)
      created() {
        // 当前匹配的路由
        console.log('created',this.$route.params.id)
      
        // axios 利用id 发请求到详情接口 , 获取详情数据 , 布局页面
      },
      
  • 第四步: 发送请求

  • 第五步: 布局页面

命名路由

//1-通过路径跳转
// this.$router.push('/detail/'+id)
// this.$router.push(`/detail/${id}`)
//2-通过命名路由跳转
this.$router.push({
  name: "dravenDetail",
  params: {
    id
  }
})
//👆-------------------------------------------👇
  created() {
    // 当前匹配的路由
    console.log('created',this.$route.params.id)

    // axios 利用id 发请求到详情接口 , 获取详情数据 , 布局页面
  },

路由模式

const router = new VueRouter({
  mode:"history",
  routes
})
  • 由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就尴尬了。
  • 要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!
// 打包工具  生产环境构建
npm run build
  • hash模式,其原理是onhashchange事件,可以在window对象上监听这个事件

    • #后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。
    • 每次 hash 值的变化,会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。
    • 然后我们便可以监听hashchange来实现更新页面部分内容的操作
  • history模式,可利用“history.pushState”的API来完成URL跳转

    • 因为HTML5标准发布,多了两个 API,pushState()replaceState()。
    • 通过这两个 API
      • 可以改变 url 地址且不会发送请求
      • 不仅可以读取历史记录栈,还可以对浏览器历史记录栈进行修改
  • 当你选择mode类型之后,程序会根据你选择的mode 类型创建不同的history对象(hash:HashHistory 或 history:HTML5History 或 abstract:AbstractHistory)

  • 区别:

    • 前面的hashchange,你只能改变#后面的url片段。而pushState设置的新URL可以是与当前URL同源的任意URL。
    • history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误
  • 有限的选项卡:

    • 声明式导航
  • 长列表中,点击后往同一个组件跳转

    • 编程式跳转
  • r o u t e r 与 router与 routerroute的区别

    • r o u t e 从 当 前 r o u t e r 跳 转 对 象 里 面 可 以 获 取 n a m e 、 p a t h 、 q u e r y 、 p a r a m s 等 ( < r o u t e r − l i n k > 传 的 参 数 由 t h i s . route从当前router跳转对象里面可以获取name、path、query、params等(<router-link>传的参数由 this. routerouternamepathqueryparams<routerlink>this.route.query或者 this.$route.params 接收)
    • r o u t e r 为 V u e R o u t e r 实 例 。 想 要 导 航 到 不 同 U R L , 则 使 用 router为VueRouter实例。想要导航到不同URL,则使用 routerVueRouterURL使router.push方法;返回上一个history也是使用$router.go方法

全局路由拦截

守卫

  • 顾名思义,路由拦截即路由使用的拦截器

    • 方法说明
      beforeEach((to,form,next)=>{})在路由页面进入之前
      to从哪里来
      form到哪里去
      next自己人
      next在守卫代码块最后一行通过调用的方式放行next() 括号内可以为路由路径
  • 自定义路由状态来控制是否放行条件

    • 在这里插入图片描述

    • 在这里插入图片描述

  • 整个拦截和转载的过程是这样的

    1. 点击我的 {center}后,路由跳转到center组件

    2. 在跳转的过程中,通过拦截器进行拦截

    3. router.beforeEach((to,form,next)=>{
      })
      
    4. 拦截后,进行判断,判断本地存储中,是否有token字段,如果有,放行,如果没有,转发登陆 login页面

    5. // 判断本地存储中 是否有token字段
      if (localStorage.getItem('token')){
         next()
      }else {
         next("/login")
      }
      
    6. 转发至登陆页面后,进行身份验证,验证通过后,通过localStorage.setItem进行本地存储

    7.     handleLogin(){
            setTimeout(()=>{
              localStorage.setItem("token","后端返回的token字段")
              // this.$router.back() //返回上一个页面
              this.$router.push("/center")
            },0)
          }
      
    8. 本地存储过后,通过路由重新转发至来时的页面

    9. 然后路由会自动的全局拦截,这时判断的本地存储就有了相应的数据

    10. 自动放行

回到我们的目的页面

  • 登录后,跳转到我点击后给我拦截的页面

    • index.js

    • // 判断本地存储中 是否有token字段
      if (localStorage.getItem('杨清壹')){
        next()
      }else {
        next({
          path:"/login",
          query:{ redirect : to.fullPath}
        })
      }
      
    • Login.vue

    • handleLogin(){
        setTimeout(()=>{
          localStorage.setItem("杨清壹","后端返回的token字段")
          // this.$router.back() //返回上一个页面
      
          // 1. 获取 query字段
          // console.log(this.$route.query.redirect)
          // 2. 跳转到当时想要跳的页面去
          this.$router.push(this.$route.query.redirect)
        },0)
      }
      
  • 在拿到授权后,可以转身回到我们想要去的页面

局部路由拦截

  • 在你的子组件中,定义beforeRouteEnter(to,form,next)生命周期

  • 在渲染该组件的对应路由被 confirm 前调用

  • 不能获取组件实例 this

  • 因为当守卫执行前,组件实例还没被创建

  • Center.vue

    •   beforeRouteEnter(to,form,next){
          if (localStorage.getItem("杨清壹")){
            next()
          }else {
            next({
              path:"/login",
              query:{redirect: to.fullPath}
            })
          }
        },
      

路由懒加载

  • 懒加载,可以这么理解
  • 即,用的时候加载,不用的时候趟尸待命,不会占用浏览器首次启动资源
  • 我们npm run build后,会生成dist文件夹,文件夹中的js文件夹是我们上线时部署在浏览器上的核心代码
  • js文件夹中一般会有两个文件
    • app.js 我们的原生组件
    • chunk-vendors vue源码
  • 懒加载,加载的是子组件的创建
  • 我们在路由的index.js文件中,修改组件引入方式
  • 由原先: import Search from "@/views/Center";
  • 修改为: component: () => import("@/views/Center"),
  • 当然,修改的地方是在你即将==重定向页面当时,注册组件的时候,==使用此方法进行懒加载处理
  • 也不是所有的子组件都需要懒加载处理
  • 一般用于页面启动速度的优化

rem

方法说明
window.devicePixelRatio查看1px对应多少物理像素比
document.documentElement.clientWidth可布局的宽度
  • 最基本的适配方案之一

  • 也就是说每当切换手机型号的时候,我们的宽高大小都是固定的px

  • 我们可以将px转换成rem

  • 我们再public静态资源文件夹下的index.html页面中,加入适配计算

  • <script>
        //fontsize 计算
        document.documentElement.style.fontSize=document.documentElement.clientWidth/750 * 100 + "px";
    </script>
    
    .box{
      width: 10.00rem;
      height: 200px;
      background: pink;
    }
    
  • 当然,rem,计算方式为屏幕允许设置的大小/给予我们的大小*100/给予我们的大小

  • WebStrome中,要口头转换px与rem无非是一件痛苦的事情,我们可以下载插件

  • 插件的名字是:idea px2rem

  • 下载好之后重启webstrome

  • 重启之后就可以使用啦,选中我们输入好的xxxpx,按快捷键Alt+D,就会自动转成rem单位啦

Swiper组件

  • cnpm i --save swiper下载swiper资源

  • film

  • 轮播图主组件 声明 布局

  • <template>
      <div class="">
        <film-swiper :key="datalist.length">
          <film-swiper-item v-for="data in datalist" :key="data.id" class="filmswiperitem">
            <img :src="data.imgUrl" alt="">
          </film-swiper-item>
        </film-swiper>
        <div>
          二级的声明式导航
        </div>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    // webpack
    import filmSwiper from "@/components/films/FilmSwiper";
    import filmSwiperItem from "@/components/films/FilmSwiperItem";
    import axios from "axios";
    
    export default {
      name: 'Films',
      // import引入的组件需要注入到对象中才能使用
      components: {
        // eslint-disable-next-line vue/no-unused-components
        filmSwiper,
        filmSwiperItem
      },
      data() {
        // 这里存放数据
        return {
          datalist:[]
        }
      },
      // 生命周期 - 挂载完成(可以访问DOM元素)
      mounted() {
        setTimeout(()=>{
          axios.get("/banner.json").then(res=>{
            console.log(res.data.banner)
            this.datalist=res.data.banner
            console.log(this.datalist)
          })
        },1000)
      },
    }
    </script>
    
    <style scoped lang="scss">
    .filmswiperitem{
      img{
        width: 100%;
      }
    }
    </style>
    
  • filmSwiper

  • film-swiper子组件 主要用来控制组件的运动方式

  • <template>
      <div class="swiper draven">
        <div class="swiper-wrapper">
          <slot></slot>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>
        <!-- 如果需要导航按钮 -->
        <div class="swiper-button-prev"></div>
        <div class="swiper-button-next"></div>
      </div>
    </template>
    
    // webpack
    import {Swiper} from "swiper";
    import 'swiper/swiper.min.css'
    
    export default {
      name: 'filmSwiper',
      // import引入的组件需要注入到对象中才能使用
      components: {},
      props: {
        loop:{
          type:Boolean,
          default:true,
        }
      },
      // 生命周期 - 挂载完成(可以访问DOM元素)
      mounted() {
        new Swiper('.draven', {
          // direction: "vertical", //垂直
          // 如果需要分页器
          pagination: {
            el: '.swiper-pagination'
          },
          loop: this.loop, // 循环
          // 自动
          autoplay: {
            delay: 2500,
            disableOnInteraction: false
          },
          // 如果需要前进后退按钮
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev'
          }
        })
      },
    </script>
    
  • filmSwiperItem.vue

  • 用来for遍历数据的组件,主要用来存储数据的组件

  • <template>
      <div class="swiper-slide">
        <slot></slot>
      </div>
    </template>
    

猫眼案例

axios封装

  • 在src下新建util包,在util包下新建http.js文件

1 - 对于数据请求的封装

import axios from "axios";

function httpForList(){
  return axios({
    url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=5238343",
    headers:{
      'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16687528065266390114107393","bc":"110100"}',
      'X-Host': 'mall.film-ticket.film.list'
    }
  })
}
function httpForDetail(params){
  return axios({
    url:`https://m.maizuo.com/gateway?filmId=${params}&k=4819055`,
    headers:{
      "X-Host": "mall.film-ticket.film.info"
    }
  })
}

export default {
  httpForList,httpForDetail
}
  • 使用

Nowplaying.vue

import http from "@/util/http";

http.httpForList().then(res=>{
  console.log("Nowplaying==>",res.data.data.films)
  this.datalist=res.data.data.films
})

Detail.vue

import http from "@/util/http";

// 当前匹配的路由
console.log('created', this.$route.params.id)
http.httpForDetail().then(res=>{
  console.log(res)
})
// axios 利用id 发请求到详情接口 , 获取详情数据 , 布局页面

2- 对局数据请求的封装

http.js

import axios from 'axios'

const http = axios.create({
  baseURL: 'https://m.maizuo.com',
  timeout: 10000,
  headers: {
    "X-Client-Info": '{"a":"3000","ch":"1002","v":"5.2.1","e":"16687528065266390114107393"}'
  },
})

export default http

Nowplaying.vue

import http from '@/util/http';

http({
  url:"/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=5238343",
  headers:{
    "X-Host": "mall.film-ticket.film.list"
  }
}).then(res=>{
  console.log("res")
  console.log(res)
  console.log("Nowplaying==>",res.data.data.films)
  this.datalist=res.data.data.films
})

Detail.vue

import http from '@/util/http'

http({
  url:`/gateway?filmId=${this.$route.params.id}&k=4819055`,
  headers:{
    "X-Host": "mall.film-ticket.film.info"
  }
}).then(res=>{
  console.log(res)
})

3 - 拦截器

//在发请求之前拦截  --  showLoading
http.interceptors.request.use(function (config) {
  return config;
}, function (error) {
  return Promise.reject(error);
});

//在成功后拦截  --  hideLoading
http.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  return Promise.reject(error);
});

更好的滚动-betterScroll

  • 主要完成的功能需要包含Better-Scroll实现页面中拖动滚动、拉动属性等功能
  1. 给予父盒子宽高及calss属性

  2. 动态绑定父盒子的style属性(height值)

  3. 下载betterScoll

    • cnpm i --save betterScoll
  4. 使用betterScoll

    • this.$nextTick() 在挂载完dom之后执行,防止初始化过早

    • this.$nextTick(()=>{
        new BetterScroll('.boxed',{
          scrollbar:{
            fade:true
          }
        })
      })
      
      
  5. 动态计算高度–解决各设备不兼容问题

    • this.height = document.documentElement.clientHeight - (document.querySelector('div').children[0]).offsetHeight+'px'
      

ElementUi组件 - pc端框架

  1. 在Src目录下新建PcApp.vue文件

  2. 使用cnpm安装elementUi

    • cnpm i --save element-ui
  3. 使用

    1. 引入坐标

      • import Vue from 'vue';
        import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        
        Vue.use(ElementUI);
        
    2. 引入组件

    3. 改造组件

vant - 移动端框架

  1. 通过cnpm安装

    • cnpm i --save vant@latest-v2 Vue2.0
    • cnpm i --save vant Vue3.0
  2. 引入组件库

    • import Vant from 'vant';
      import 'vant/lib/index.css';
      
      Vue.use(Vant);
      
  3. 使用

    • 引入组件
    • 改造组件

axios拦截,绑定加载框

  • 封装到axioshttp.js文件中,这样就可以在每次请求数据之前使用加载框,请求到数据之后删除加载框
import { Toast } from 'vant';

//在发请求之前拦截  --  showLoading
http.interceptors.request.use(function (config) {
  Toast.loading({
    message: '加载中...',
    forbidClick: true,
    duration: 0
  });
  return config;
}, function (error) {
  return Promise.reject(error);
});

//在成功后拦截  --  hideLoading
http.interceptors.response.use(function (response) {
  // 隐藏加载框
  Toast.clear()
  return response;
}, function (error) {
  // 隐藏加载框
  Toast.clear()
  return Promise.reject(error);
});

索引城市列表 组件

  • vant2官网中找到自定义索引列表,并引用至.vue组件中

  • <template>
      <div>
        <van-index-bar :index-list="computedList">
          <div v-for="(item) in citieList" :key="item.type">
          <van-index-anchor :index="item.type" />
          <van-cell v-for="(data) in item.address" :key="data.cityId" :title="data.name" />
          </div>
        </van-index-bar>
      </div>
    </template>
    <script>
    import http from '@/util/http'
    import Vue from "vue";
    export default {
      name: "caty",
      data(){
        return{
          citieList:[],
        }
      },
      computed:{
        computedList(){
          return this.citieList.map(item=>item.type)
        }
      },
      mounted() {
        http({
          url:"/gateway?k=7323589",
          headers:{
            "X-Host":"mall.film-ticket.city.list"
          }
        }).then(res=>{
          console.log("1",this.renderCity(res.data.data.cities))
          this.citieList = this.renderCity(res.data.data.cities)
        })
      },
      methods:{
        renderCity(cities){
          // console.log(cities)
          //存储36个英文字符的临时数组
          var arrListABC = []
          //遍历向数组添加字符
          for (let i = 65; i < 91; i++) {
            arrListABC.push(String.fromCharCode(i))
          }
          //存储最后总数据整理
          var citiesList = []
          //存储首字母对应城市的数据
          var newList = []
          //筛选
          arrListABC.forEach(letter=>{
            var newList = cities.filter(
              item=>item.pinyin.substring(0,1).toUpperCase() === letter
            )
            //如果某个首字母的城市数据为空,则跳过本次执行,如果不为空,则向新数组添加一组json数据,type的值为letter(英文字母),address的值为newList(城市条数据)
            newList.length > 0 && citiesList.push({
              type:letter,
              address:newList
            })
          })
          // console.log("2",citiesList)
          return citiesList
        }
      }
    }
    </script>
    

Vuex - 状态管理模式

  • 作用:

    • vuex 管理保存公共状态

    • 分散在各组件内的状,统一管理

    • 实现组件与状态之间的互联

    • 非斧子的通信

    • 后端数据的缓存快照,减少重复数据请求,减轻服务器压力,提高用户体验

  • 注意:

    • vuex 默认是管理在内存,一刷新页面,公共状态也就丢失了
  • 原理图

  • 在这里插入图片描述

  • store.js文件属性分析

  • 属性作用
    state公共状态
    mutations数据接收器
    actions
  • 使用

  • 方法说明
    this.$store.commit(key(方法名),value(形参))提交数据完成更新
    this.$store.dispatch(key(方法名))

state

  • 声明

    • // state公共状态
      state:{
        cityId:"310100",
        cityName:"上海"
      }
      
  • 使用

    • 获取数据

      • {{ $store.state.cityName }}
        
    • 更新数据(no 不安全 无法跟踪修改数据源)

      • this.$store.state.cityName=item.name
        
    • 更新数据(正确写法 key,value传递方法)

      • this.$store.commit("changeCityName",item.name)
        

mutations

  • 只能支持同步

  • 统一管理

    • devtools 记录状态的修改
    • devtools在chrome中有可视化插件
  • commit方法内提供的key值声明成方法,里面带有两个形参 statevalue

    • state属性对象
    • value参数
  • 声明出的方法内使用state调用状态传值

    • mutations:{
        changeCityName(state,cityName){
          state.cityName=cityName
          console.log(cityName)
        },
      }
      

actiones

  • 异步(vuex持久化)

  • dispatch提供的方法名在actiones中声明出来,形参为

    • sotre代表store对象
    • vaule参数
  • 使用

    • // cinemas.vue
      // 先分发请求
      this.$store.dispatch('getCinemasData',this.$store.state.cityId)
      //👆-------------------------------------------👇
      // store -> index.js
      // 再在store.js文件中请求数据
      getCinemasData(store,cityId){
        return http({
          url: `/gateway?cityId=${cityId}&ticketFlag=1&k=7641159`,
          headers: {
            "X-Host": "mall.film-ticket.cinema.list"
          }
        }).then(res => {
          console.log(res)
      //把请求到的数据提交给state 
      store.commit('changeCinemasData',res.data.data.cinemas)
        })
      },
      

Vuex新写法

方法说明
…mapState([key,…,key])映射Store状态

引入

import {mapState,mapMutations,mapActions} from "vuex";

mapState

  • 映射store的state公共状态

  • 写在computed监听属性中

  • 语法:

    • ...mapState(["cinemasList"])
    • 多个状态可以再数组括号内用逗号隔开
    • ...mapState(["cinemasList","cityId","cityName"])
    • 然后当我们使用cinemasList的时候可以不用从this.$store对象中引用,可以直接把cinemasList当成本组件的data数据使用
  • 使用

    • // 正确的写法
      computed: {
        ...mapState(["cinemasList"])
      },
      // 等同于
      computed: mapState(["cinemasList"])
      // 等同于
      computed: {
          mapState: function (){
            return this.$store.state.cinemasList
          }
      },
      
    • //老写法
      if (this.$store.state.cinemasList.length===0)
      
    • //引入maoState后的写法
      if (this.cinemasList.length===0)
      

mapActions

  • 映射store的actions 异步请求

  • 写在methods方法集合中

  • 语法

    • ...mapActions(["getCinemasData"])
    • 多个状态可以再数组括号内用逗号隔开
    • ...mapActions(["getCinemasData","..."])
    • 然后当我们使用getCinemasData的时候可以不用从this.$store对象中引用,可以直接把getCinemasData当成本组件的mounted方法使用
  • 使用

    • // methods引入
      ...mapActions(["getCinemasData"]),
      
    • //正确写法
      this.getCinemasData(this.cityId)
      
    • //老写法
      this.$store.dispatch('getCinemasData',this.cityId)
      

mapMutations

  • 映射store的mutations统一管理

  • 写在methods方法集合中

  • 语法

    • ...mapMutations(["changeCityName"]),
    • 多个状态可以再数组括号内用逗号隔开
    • ...mapMutations(["changeCityName",...]),
    • 然后当我们使用changeCityName的时候可以不用从this.$store对象中引用,可以直接把changeCityName当成本组件的mounted方法使用
  • 使用

    • // methods引入
      ...mapMutations(["handleDeleteData"])
      
    • //正确写法
      this.handleDeleteData()
      
    • //老写法
      this.$store.commit("handleDeleteData")
      

注入

方法说明
mixins:[obj]注入obj的生命周期…
  • util包中新建mixinObj.js文件

  • 编写

    • var obj = {
        mounted() {
          // console.log("创建完成")
          this.$store.commit("hide")
        },
        destroyed(){
          this.$store.commit("show")
        }
      }
      export default obj
      
  • 使用

    • 在每个需要用到hide,show方法的组件中使用

    • import obj from "@/util/mixinObj";
      
      // 混入(注入)
      mixins: [obj],
      

Vuex持久化

  • 下载安装

    • cnpm i --save vuex-persistedstate
  • 引入

    • 打开我们的store文件
    • 在导包处写入以下代码
    • import createPersistedState from "vuex-persistedstate"
  • 使用

    • export...内第一行引用

    • plugins:[createPersistedState()],
      
  • 效果

    • 数据会存在浏览器的Storage
    • image-20221202183031079
  • 限制

    • plugins:[createPersistedState({
        reducer:(state)=>{
          // 规定要存入的数据
          return{
            cityId:state.cityId,
            cityName:state.cityName
          }
        }
      })],
      

常用方法

变量

变量说明
.substring(index,index)截取字符串
.toUpperCase()将字符串转换为大写

数组

方法说明
.splice(index1,index2,[str])删除从index1开始删除,删除index2个,替换成str的值
.push(data)向数组追加一条数据
.shift()把数组的第一个元素从其中删除,返回第一个元素的值。
.includes(str)检测是否包含字符串str
replace(“oldStr”,“newStr”)字符串中用一些字符替换另一些字符

事件

事件说明
arr.filter()监听器
v-model.lazy=“myText”懒惰的,失去焦点后同步数据
v-model.number=“myAge”将字符串类型转换为数字类型
v-model.trim=“myUserNmae”去首尾空格

New系列

localStorage-本地储存

方法说明
.setItem(str,value)设置Application->Local Storage->http…key中的str值
.getItem(str)获取Application->Local Storage->http…key中的str值

HTML属性

属性说明
disabled=true表单只读

ajax方法

方法说明
then()异步执行
就是当.then()前的方法执行完后再执行then()内部的程序,这样就避免了,数据没获取到等的问题。
text()返回json字符串

Vue

基础方法

Vue外部

方法说明
Vue.filter(“imgFilter”,(url)=>{return…})过滤器
:src=“item.img | imgFilter”Vue3不支持
Vue.component(“navber”,{})定义一个全局组件

Vue内部

new Vue({…})说明
el:绑定的选择器
el:“#box”
data:{}数据中心
methods:{}方法区
computed:{}计算属性
watch:{}监听器
当一个值发生改变时触发

方法区中

方法说明
fetch(url)发出 HTTP 请求
axios(url)Web数据交互方式 fetchPlus
https://cdn.jsdelivr.net/npm/axios@1.1.2/dist/axios.min.js

组件方法

标签

标签说明
名称
侧边栏
动态组件,Vue提供的标签
is属性固定的 "="后面跟的是使用的组件
活着->被它包裹的组件,输入框内留下的内容不会因切换组件而消失
代替 slot 属性
插槽,在组件dom中加入

使用

方法说明
Vue.component(“navber”,{})定义一个全局标签
template:‘
aaaa
固定的
methods:{}方法
computed:{}计算属性
watch:{}请求
data(){return{}}数据
props:{}接受属性
components:{“draven-Child”:{template:‘
’}}
子组件
插槽👇
插槽,在组件dom中加入
属性
slot=“right”使用插槽,在dom标签中使用
代替 slot 属性,在模板(dom)标签中使用
<template #a>代替 slot 属性.Plus在模板(dom)标签中使用
插槽,在组件dom中加入

过度方法

标签说明
固定过度标签
组件中包含多个要执行动画的标签
不同于transition,它会以一个真实元素呈现默认为一个span,也可以通过tag属性更换为其他元素
:key=“item.id”使用key值的id做比较,.提供唯一的key值属性
属性说明
enter-active-class=“”动画开始时切换的class
leave-active-class=“”动画结束时切换的class
name=“yqy”根据calss名动态查找
yqy-enter-active查找后根据enter和leave自动选择calss
yqy-leave-active
mode设置动画先后顺序
out-in先走再来
in-out先来再走

生命周期

方法说明
创建阶段👇
beforeCreate()创建前
created()初始化 重要
beforeMount()载入前
模板解析前,最后一次修改模板的节点
mounted ()载入后
依赖于dom创建之后,才进行初始化工作的插件 (轮播插件)创建完dom后的初始化 重要
订阅 bus.$on初始化ajax数据
更新阶段👇
beforeUpdate()更新前
updated()更新后
销毁👇
beforeDestroy()销毁前
destroyed()销毁后

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/55482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习与数据挖掘——前言

如果有兴趣了解更多相关内容&#xff0c;欢迎来我的个人网站看看&#xff1a;瞳孔空间 这是从老师的PPT里面提取出来的&#xff0c;知识点分布比较零散&#xff0c;可能他做PPT的时候也没想那么多。 一&#xff1a;机器学习 机器学习的定义&#xff1a;一个计算机程序被称为…

前端岗位初入职场后的最初一段时间需要做什么

文章题目有点长&#xff0c;叫 《前端岗位初入职场后的最初一段时间需要做什么》&#xff0c;说下写这篇文的初衷&#xff0c;在前端自学团里有很多刚毕业或者是刚从培训班出来的同学&#xff0c;在群里天天讨论着找工作和面试的事情&#xff0c;面试的题在很多app或者小程序可…

四、【React-Router6】高亮 NavLink

项目修改自 上一节 的 Demo 这里需要注意的变化 默认高亮样式类名如果依然是 active 则依然默认有效 6 里移除了 activeClassName &#xff0c;如果我的高亮样式类名是 peiqi&#xff0c;需要配置 className 为一个函数 函数接收两个参数 isActive&#xff1a;是否激活isPendi…

Arduino开发实例-DIY双向访客计数器和自动灯光控制

双向访客计数器和自动灯光控制 1、应用介绍 本应用将使用 Arduino 创建双向访客柜台和自动灯光控制系统。该应用基于一对 IR(红外)传感器,可在出现中断时检测障碍物。实际上,该系统可以检测来自两个方向的访客。从而对进入的访客数量和离开的访客数量进行计数。 该双向访…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校心理健康咨询平台vknhv

毕业设计其实不难&#xff0c;主要毕业的时候任务太紧了&#xff0c;所以大家都非常忙没有时间去做&#xff0c;毕业设计还是早做准备比较好&#xff0c;多花点时间也可以做出来的&#xff0c;建议还是自己动手去做&#xff0c;比如先选一个题&#xff0c;这样就有方向&#xf…

GIS工具maptalks开发手册(三)01——绘制工具

GIS工具maptalks开发手册(三)01——绘制工具 效果 代码 1、html官方版 <!DOCTYPE html> <html> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1"> <title>交互 - 绘制…

vite + vue3.0 + ts 项目搭建

Welcome to vue3-elementplus-admin &#x1f44b; vite vue3 Pinia elementUi-plus 无限级菜单 权限管理 后台模板 &#x1f3e0; Homepage Author &#x1f464; xuxuxu-ni Github: xuxuxu-niQQ: 595485548QQ群: 157216616email: 595485548qq.com Prerequisites node…

安卓使用动画启动Acitvity

1.检查系统版本 动画过渡Activity适用于*Android5.0&#xff08;API21&#xff09;*及以上&#xff0c;在代码增加中检查版本增强代码健壮性。 2.指定自定义过渡动画。 过渡可以在xml文件中指定&#xff0c;也可以直接在代码中指定。使用Window.requestFeature()声明启动窗口…

C语言:结构体

1、结构体&#xff1a; 定义&#xff1a;结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量 结构的声明&#xff1a; struct tag {member-list;}variable-list;举例&#xff1a;声明一个学生类型&#xff0c;想通过学生类型来创建学生变…

【Matplotlib绘制图像大全】(二十七):Matplotlib将数组array保存为图像

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

SpringBoot+Vue实现前后端分离的大学生志愿者管理系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

【C++修炼之路】10. vector类

每一个不曾起舞的日子都是对生命的辜负 vector本节目标1. vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义&#xff08;构造函数&#xff09;1.2.2 vector iterator的使用1.2.3 vector的空间增长问题1.2.4 vector增删查改2. vector的模拟实现2.1 构造函…

Mellanox CX4 offload 卸载功能介绍

无状态功能卸载 cx4支持多种类型的无状态卸载&#xff0c;如下面列表所示。 Checksum OffloadLarge Send OffloadsReceive Side ScalingTransmit Side ScalingInterrupt ModerationLarge Receive OffloadsVLAN insertion and strippingFlow Steering at layers 2, 3 and 4Pac…

使用MotionLayout实现模拟启动页动画和轮播图

目录效果图展示启动页效果轮播图效果MotionLayout详解准备工作正题轮播图效果实现ConstraintSetTransitionCarousel浅述启动页的实现插入gif图源码在这里&#xff1a; 源码链接本文是用java写的效果图展示 下面是本博客我使用真机所实现的功能展现&#xff0c;方便大家根据自身…

化妆品行业的数字进化论:S2B2B电商网站如何助力化妆品企业打造增长新动能

近年来&#xff0c;伴随着国家经济的快速发展和消费者对“美”的追求日益强烈&#xff0c;大大推动了化妆品行业的蓬勃发展&#xff0c;根据公开数据显示&#xff0c;2021年我国化妆品零售总额达4026亿元&#xff0c;较2020年同比增长18.40%&#xff0c;我国目前已成为全球第二…

一篇文章让你全方位掌握git版本控制管理

注&#xff1a;侵权请联系作者删除 目录 1.引入&#xff1a; 2.Git 的工作区域和流程 3.stash区域 4.git基本操作 A.git add B.git commit c.git pull D.git fetch E.git branch F.git init 5.在项目中实际运用案例&#xff1a; A.在github上创建一个新仓库 B.复制刚创建…

matlab使用hampel滤波,去除异常值

此示例显示了Hampel用于检测和删除异常值的过程的 实现。 最近我们被客户要求撰写关于hampel滤波的研究报告&#xff0c;包括一些图形和统计输出。 产生一个包含24个样本的随机信号x。 重置随机数生成器以获得可重复的结果。 rng defaultlx 24; x randn(1,lx); 围绕x的每…

网页前端知识汇总(六)——如何让网页全部内容显示成灰色

最近很多做网站前端的技术员是不是都接到了老板的任务&#xff0c;让网站的网页显示效果都变成灰色&#xff0c;这个也是随某些事件的发生或者某些专题内容觉得需要这样做的&#xff0c;大部分用于大家都不愿意看到的专题事件如某某烈士&#xff0c;逝去的伟人等&#xff1b;大…

Scala013--Scala中的方法

因为Scala是一种函数式编程语言&#xff0c;因此在Scala中基本上都是方法和函数&#xff0c;但是需要注意的是&#xff0c;在Java中方法和函数是同一个意思&#xff0c;但是在Scala中函数和方法的含义不同&#xff1a; 方法&#xff1a;是类和对象的成员函数&#xff1a;是对象…

jeecgboot-前端组件封装代码示例

首先我们要知道 jeecgboot他前台的组件代码封装文件夹的位置在src-components中&#xff0c;这时我们其实可以观察他们代码的写法(个人感觉学习代码的最好的途径就是临摹他人高质量的代码、多看、多写)路径如图&#xff1a; 接下来我们会在标注3下实现一个简单的自定义组件 代码…