vue随笔(yang)

01 vue基础指令

一、认识VUE
二、MV* 模式
三、VUE基础

  1. 使用
    1.1. 引入
    1.2. 初始化VUE
    1.3. options解析
    1.4. el选项
    1.5. data选项 + 模板语法-插值
  2. 模板语法-指令
    2.1. vue提供的固有指令
    2.2. 自定义指令
  3. 插值语法
  4. 双向绑定
  5. 条件渲染
    4.1 基本条件渲染
    4.2 使用key值管理可复用元素
  6. 列表渲染
    5.1 v-for + key 属性的使用
    5.2 key的唯一性
    5.3 循环的嵌套
  7. class与style的绑定
    6.1 class 的绑定
    6.2 style 的绑定 (作业)

一、认识VUE

什么是VUE

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
VUE官网
vue开发者工具

Vue Devtools
下载版本介绍

下载源码
cdn下载
包管理器下载
体验VUE的强大

购物车案例的快速实现

<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <ul>
            <li v-for="(item,idx) in goods" :key="item.goodsId">
                {{item.goodsId}} -
                {{item.goodsName}} -
                {{item.stock}}
                <button @click="add(idx)" v-if="item.stock-item.sales != 0">+</button>
                {{item.sales}}
                <button @click="dec(idx)" v-if="item.sales != 0">-</button>
                - {{item.stock - item.sales}}
                <span v-if="item.stock - item.sales <= 0">卖完了</span>
            </li>
        </ul>
        <p>总销售量:{{sum}}</p>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            goods:[{
                goodsName:"手机",
                stock:90,
                sales:8,
                goodsId:0
            },{
                goodsName:"电脑",
                stock:20,
                sales:5,
                goodsId:1
            },{
                goodsName:"冰箱",
                stock:30,
                sales:26,
                goodsId:2
            }]
        },
        methods: {
            add(i){
                this.goods[i].sales++;
            },
            dec(i){
                this.goods[i].sales--;
            }
        },
        computed: {
            sum(){
                return this.goods.reduce((prev,now)=>{
                    return prev + now.sales;
                },0);
            }
        },
    })
</script>

读文档

二、MV* 模式

MVC模式

M(Model)模型,V(View)视图,C(controller)控制器
是使用一种将业务,数据,视图分离的方式组织架构代码
组件是架构开发,常常将视图,数据,业务逻辑等写在一个模块内,如果组件内容很多,常常造成层次的混乱,增加开发和日后维护的成本
MVC模式就是专门处理这种问题的。
MVP模式

传统的MVC模式虽然可以管理页面系统中的数据,视图,控制器,但是在视图层创建界面时常常会用到模型层内的数据,使模型层和视图层耦合在一起,降低了复用性和灵活性
MVP模式就是为了解决这方面的问题。
M(Model)模型,V(View)视图,P(Presenter)管理器
视图层不再直接引入模型层中的数据,而是通过管理层实现对模型层内的数据的访问。
即所有层次的交互都发生在管理层中,从而降低了模型层和视图层之间的耦合关系,提升了灵活性和复用性
MVVM模式

在MVP模式中,所有的操作都在管理层中,因此必须创建管理器才能实现需求,但是对于开发者来说,如果js学习的不深入,操作管理器的成本其实很大。
MVVM模式的出现就是为了进一步的减少这种操作成本,使开发者只需要通过操作html就能创建视图实现页面需求
M(Model)模型,V(View)视图,VM(ViewModel)视图模型层。
为视图层量身定做一套视图模型,并在视图模型中创建属性和方法,为视图层绑定数据并实现交互
MVVM模式是从MVC模式和MVP模式演化而来,因此它主要也是分离视图和模型。
不同的是MVVM模式使视图层更加灵活,可以独立于数据层和视图层,实现独立修改,自由创建。
MVVM模式是对视图模型层的高度抽象,因此当多个视图层对应一个视图模型层时,也使得视图模型层内的代码逻辑变得高度复用。
这样可以极大的方便不懂js的人,只要了解简单的html内容并按照视图层的规范格式创建视图即可,开发人员就可以专注于开发视图模型层内的业务逻辑。

三、VUE基础

  1. 使用
    1.1. 引入

1.2. 初始化VUE
const vm = new Vue(options);
1.3. options解析

  • Vue的配置信息,对象数据,键名由Vue指定,值由Vue约束
  • 内含多个选项,如:el,data,methods,computed等…

1.4. el选项
用于标记需要使用vue渲染的容器

<body>
    <div id="app">
        
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app"  // 需要使用vue渲染的容器
    })
</script>

1.5. data选项 + 模板语法-插值
用于设置vue实例中需要使用的数据

<body>
    <div id="app">
        <div v-bind:title="tit">{{msg}}</div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{  // 当前vue实例中需要使用的数据,以键值对的形式存在
            msg:"hello vue",
            tit:"鼠标悬停效果"
        }
    })
</script>
  1. 模板语法-指令
    2.1. vue提供的固有指令
    指令 功能
    v-text 模板语法-插值-不解析html
    v-html 模板语法-插值-解析html
    v-show 条件判断
    v-if 条件判断
    v-else 条件判断
    v-else-if 条件判断
    v-for 列表循环
    v-on 绑定事件
    v-bind 绑定属性
    v-model 双向绑定
    v-slot 插槽
    v-pre 跳过这个元素和他的子元素的编译过程
    v-cloak 这个指令保持在元素上直到关联实例结束编译
    v-once 只渲染元素和组件一次
    2.2. 自定义指令
  2. 插值语法
    {{}}, v-text, v-html
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <div>{{msg1}}</div>
        <div>{{msg2}}</div>
        <div v-text="msg1"></div>
        <div v-text="msg2"></div>
        <div v-html="msg1"></div>
        <div v-html="msg2"></div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg1:"hello vue",
            msg2:"<mark>hello vue</mark>"
        }
    })
</script>
  1. 双向绑定
    v-model
<body>
    <div id="app">
        <!-- 输入框绑定value -->
        <input type="text" placeholder="用户名" v-model="username" /><br data-tomark-pass>        
        <input type="password" placeholder="密码" v-model="password" /><br data-tomark-pass>        
        <!-- 单选框绑定选中的value -->
        <input type="radio" name="sex" v-model="sex" value="1" /><br data-tomark-pass>        
        <input type="radio" name="sex" v-model="sex" value="0" /><br data-tomark-pass>        
        <!-- 下拉列表绑定选中的项 -->
        <select v-model="city">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="广州">广州</option>
        </select>
        <!-- 复选框(多个)绑定选中的项 -->
        <input type="checkbox" name="like" value="篮球" v-model="like">篮球
        <input type="checkbox" name="like" value="足球" v-model="like">足球
        <input type="checkbox" name="like" value="排球" v-model="like">排球
        <!-- 文本域绑定内容 -->
        <textarea v-model="note"></textarea>
        <!-- 复选框(单个)绑定是否选中的布尔值 -->
        <input type="checkbox" v-model="flag">同意条款
        <input type="button" value="查看用户输入信息" v-on:click="getData">
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            username:"",
            password:"",
            sex:1,
            city:"",
            like:[],
            note:"",
            flag:false
        },
        methods: {
            getData(){
                console.log(this.$data);
            }
        }
    })
</script>
  1. 条件渲染
    v-show, v-if, v-else, v-else-if
    4.1 基本条件渲染
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <select v-model="state">
            <option value="0" selected>0</option>
            <option value="1">1</option>
            <option value="2">2</option>
        </select>
        <h1 v-if="state==0">吃饭</h1>
        <h1 v-else-if="state==1">睡觉</h1>
        <h1 v-else="state">学习</h1>
        <hr>
        <h1 v-show="state==0">吃饭</h1>
        <h1 v-show="state==1">睡觉</h1>
        <h1 v-show="state==2">学习</h1>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            state:0
        }
    })
</script>

if 和 show的区别.gif

v-if 是“真正”的条件渲染
v-show 不管初始条件是什么,都会渲染,只是基于 CSS 的 display 进行切换
4.2 使用key值管理可复用元素

<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <input v-if="loginType === 'username'" :key="0" placeholder="用户名">
        <input v-else :key="1" placeholder="密码">
        <br data-tomark-pass>        <input type="button" v-on:click="setData" v-bind:value="loginType">
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            loginType:"username"
        },
        methods: {
            setData(){
                this.loginType = this.loginType=="username" ? "password" : "username";
            }
        },
    })
</script>

参考文档

  1. 列表渲染
    v-for
    5.1 v-for + key 属性的使用
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <ul>
            <li v-for="item in list" :key="item">{{item}}</li>
        </ul>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            list:["花生","瓜子","火腿肠"]
        }
    })
</script>

5.2 key的唯一性
同一个父元素的子元素必须有不同的 key。重复的 key 会造成渲染错误。
如果数据自身具有唯一性,可以将数据设置为key值
如果没有唯一的数据,可以将数据的索引设置为key值,但不推荐
尽量让数据拥有唯一的值
5.3 循环的嵌套
循环支持嵌套使用

<div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <ul>
            <li v-for="val in list" :key="s">
                <span>{{val.title}}</span>
                <ul>
                    <li v-for="item in val.items">{{item}}</li>
                </ul>
            </li>
        </ul>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            list:[{
                title:"大菜",
                items:["红烧肉","糖醋排骨","麻辣鱼头"]
            },{
                title:"小菜",
                items:["酸辣土豆丝","干煸豆角","地三鲜"]
            },{
                title:"凉菜",
                items:["花生米","拌黄瓜","皮蛋豆腐"]
            },]
        }
    })
</script>
注意:
不推荐同时使用 v-if 和 v-for
v-if 与 v-for 一起使用时, v-for 比 v-if 的优先级更高。
6. class与style的绑定
6.1 class 的绑定
条件写法
<style>
    .box{width: 100px;height: 100px;border: solid 1px black;}
    .red{background: red;}
</style>
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <input type="checkbox" v-model="flag">
        <div v-bind:class="flag ? 'box red' : 'box'"></div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            flag:false
        }
    })
</script>

对象写法

<style>
    .box{width: 100px;height: 100px;border: solid 1px black;}
    .red{background: red;}
    .green{background: green;}
</style>
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <div v-bind:class="{box:flag,red:!flag,green:flag}"></div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            flag:true
        }
    })
</script>

数组写法

<style>
    .box{width: 100px;height: 100px;border: solid 1px black;}
    .red{background: red;}
    .green{background: green;}
</style>
<body>
    <div id="app" style="border:solid 2px #aaa;padding: 20px;">
        <div v-bind:class="[style,color]"></div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            style:"box",
            color:"red"
        }
    })
</script>

6.2 style 的绑定 (作业)
对象写法
数组写法

02 vue 选项 生命周期

一、事件

  1. 事件绑定
  2. 事件对象
  3. 事件修饰符
  4. 按键修饰符
  5. 系统修饰键
  6. 表单修饰
    二、计算属性 和 侦听器
    三、生命周期 - 钩子函数
    四、数据请求
    五、作业

一、事件

  1. 事件绑定
    行内事件
    可以直接在事件属性中执行js语句,
    不推荐使用,因为在更多情况下,事件的处理可能会更复杂
<body>
    <div id="app">
        <button v-on:click="msg = 'hello ' + msg">
            点击
        </button>
        <span>{{msg}}</span>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"vue"
        }
    })
</script>

事件处理方法
在methods选项内,定义事件触发方法
在事件中调用方法名
无法传参

<body>
    <div id="app">
        <button v-on:click="say">
            点击
        </button>
        <span>{{msg}}</span>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"vue"
        },
        methods: {
            say(){
                this.msg = "你好 " + this.msg;
            }
        },
    })
</script>

行内调用事件处理方法
在methods选项内,定义事件触发方法
在事件中执行方法
可以传参

<body>
    <div id="app">
        <button v-on:click="say('萨瓦迪卡')">
            点击
        </button>
        <span>{{msg}}</span>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"vue"
        },
        methods: {
            say(m){
                this.msg = m + this.msg;
            }
        },
    })
</script>
  1. 事件对象
    事件处理方法
    在事件处理方法内,接收event参数
<body>
    <div id="app">
        <button v-on:click="fn">
            点击
        </button>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        methods: {
            fn(event){
                console.log(event);
            }
        },
    })
</script>

行内调用事件
需要主动将$event传参

<body>
    <div id="app">
        <button v-on:click="fn($event)">
            点击
        </button>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        methods: {
            fn(event){
                console.log(event);
            }
        },
    })
</script>
  1. 事件修饰符
    事件方法,为了更方便的处理数据逻辑,而不是DOM细节,vue提供了事件修饰符:
    .stop:阻止事件冒泡
    .prevent:阻止默认事件
    .capture:使用事件捕获
    .self:只有event.target是当前元素时才会触发
    .once:只会触发一次事件
    .passive:立即触发滚动事件,不会等待onscroll结束
<!-- 阻止单击事件继续传播 -->
<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>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
  1. 按键修饰符
    监听键盘事件时,方便查找具体的键
    .enter:查找回车键
    .tab:查找tab键
    .delete (捕获“删除”和“退格”键)
    .esc:查找esc键
    .space:查找空格键
    .up:查找上键
    .down:查找下键
    .lefe:查找左键
    .right:查找右键
    还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
    // 可以使用 v-on:keyup.f1
    Vue.config.keyCodes.f1 = 112
  2. 系统修饰键
    用来做组合键的系统按键

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

  1. 表单修饰
    .lazy
    在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。添加 lazy 修饰符,从而转为在 change 事件之后进行同步:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
.number
即使在 type="number" 时,HTML 输入元素的值也会返回字符串。
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
<input v-model.number="age" type="number">
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
<input v-model.trim="msg">

二、计算属性 和 侦听器

对于任何复杂逻辑,都应当使用计算属性
计算属性其实就是针对data中的选项,通过某种逻辑得到的新的数据

一般的书写形式为一个函数,返回一个值
使用计算属性时,和data中的数据使用方式保持一致
计算属性在vue实例的computed选项中

<body>
    <div id="app">
        <div>
            {{msg}} --- {{ msg.split("").reverse().join("") }}
        </div>
        <div>
            <!-- 使用计算属性时,和data中的数据使用方式保持一致 -->
            {{msg}} --- {{ reverseMsg }}
        </div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"hello vue"
        },
        // 计算属性在vue实例的computed选项中
        computed: {
            // 一般的书写形式为一个函数,返回一个值
            reverseMsg(){
                return this.msg.split("").reverse().join("");
            }
        }
    })
</script>

计算属性 VS 方法

同等条件下,计算属性具有依赖性,要优于方法
计算属性只有在原始数据发生改变时才会重新执行
方法会在每次使用时都会重新执行

<body>
    <div id="app">
        <div>
            {{msg}} --- {{ reverseMsgFn() }} --- {{ reverseMsgFn() }}
        </div>
        <div>
            {{msg}} --- {{ reverseMsg }} --- {{ reverseMsg }}
        </div>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"hello vue"
        },
        computed: {
            reverseMsg(){
                console.log("computed");
                return this.msg.split("").reverse().join("");
            }
        },
        methods: {
            reverseMsgFn(){
                console.log("methods");
                return this.msg.split("").reverse().join("");
            }
        },
    })
</script>

在这里插入图片描述

侦听器

当数据发生变化,执行异步或开销较大的操作时,可以使用侦听器
侦听器会在侦听数据发生变化时执行

<body>
    <div id="app">
        <input type="text" v-model="firstName"> + <input type="text" v-model="lastName"> = {{fullName}}
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            firstName:"",
            lastName:"",
            fullName:""
        },
        watch: {
            firstName( newVal, oldVal ){
                this.fullName = newVal + this.lastName;
            },
            lastName( newVal, oldVal ){
                this.fullName = this.firstName + newVal;
            }
        }
    })
</script>

计算属性 PK 侦听器

同等条件下,计算属性优于侦听器
计算属性可以更方便的解决某些问题

<body>
    <div id="app">
        <input type="text" v-model="firstName"> + <input type="text" v-model="lastName"> = {{fullName}}
        <br />
        <input type="text" v-model="firstName2"> + <input type="text" v-model="lastName2"> = {{fullName2}}
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            firstName:"",
            lastName:"",
            fullName:"",
            firstName2:"",
            lastName2:"",
        },
        watch: {
            firstName( newVal, oldVal ){
                this.fullName = newVal + this.lastName;
            },
            lastName( newVal, oldVal ){
                this.fullName = this.firstName + newVal;
            }
        },
        computed: {
            fullName2(){
                return this.firstName2 + this.lastName2;
            }
        },
    })
</script>

计算属性的setter

三、生命周期 - 钩子函数

每个 Vue 实例在被创建时都要经过一系列的初始化过程,这个过程中会运行一些叫做生命周期钩子的函数,让用户可以在不同阶段添加自己的代码。
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
在这里插入图片描述

声明周期钩子函数的分类

const vm = new Vue({
    el:"#app",
    beforeCreate() {
        /*备孕*/
        console.log("实例创建之前")
    },
    created() {
        /*怀上了*/
        // 数据请求
        console.log("实例创建之后")
    },
    beforeMount() {
        /*快生了*/
        console.log("挂载到页面之前")
    },
    mounted() {
        /*生了*/
        // 数据请求,DOM操作,监听滚动事件...
        console.log("挂载到页面之后")
    },
    beforeUpdate() {
        /*快长大了*/
        console.log("数据更新之前")
    },
    updated() {
        /*长大了*/
        // DOM操作,实例化的操作...
        console.log("数据更新之后")
    },
    beforeDestroy() {
        /*要挂了*/
        // 取消数据订阅
        console.log("实例销毁之前")
    },
    destroyed() {
        /*挂了*/
        console.log("实例销毁之后")
    }
})

四、数据请求

fetch

fetch是一种HTTP数据请求的方式,是XMLHttpRequest的一种替代方案。
fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。
fetch会返回一个Promise对象,这个Promise对象只是一个HTTP的响应,并不是一个JSON
如果要进行一个json请求,我们需要借助json方法,手动返回一个json,才能进行下一步处理

let url = "http://localhost:3000/pro";
fetch(url).then(function(res) {
    return response.json();
}).then(function(data) {
    console.log(data);
});

axios

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
需要引入第三方文件 axios.js

使用方式类似于jQuery的ajax
axios使用文档

let url = "http://localhost:3000/pro";
axios.get(url).then(function (res) {
    console.log(res);
}).catch(function (err) {
    console.log(err);
});

五、作业

TodoList的实现:增删改查

03 vue-组件

一、组件的概念

  1. 组件
  2. vue中的组件
    二、全局注册组件
    三、局部注册组件
    四、组件之间的关系
  3. 父子组件
    组件的嵌套
    父子组件的传值 - 非常重要
    (一) 父组件给子组件传值
    (二) 子组件给父组件传值
  4. 兄弟组件 - 传值
  5. 子组件使用父组件的方法和数据
  6. 父组件使用子组件的方法和数据
    五、provide + inject

一、组件的概念

  1. 组件
    组件,是对数据和方法的简单封装。组件可以有自己的属性和方法。属性是组件数据的简单访问者。方法则是组件的一些简单而可见的功能。
    使用组件可以实现拖放式编程、快速的属性处理以及真正的面向对象的设计。
  2. vue中的组件
    组件(Component)是 Vue.js 最强大的功能之一。
    组件可以扩展 HTML 元素,封装可重用的代码。
    组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
    在这里插入图片描述

组件化开发,类似于模块化开发,但并不等同于模块化开发。
组件化开发,包括页面,样式,行为(业务逻辑)
模块化开发,只包括行为(业务逻辑)
vue中的组件分为全局注册组件和局部注册组件
组件是特殊的实例
组件选项和实例选项基本保持一致
组件没有el选项,有template选项
template可以是一段html代码,也可以直接指向页面模板,类似于el选项
组件的data选项必须是一个函数,返回一个对象
组件渲染时,数据会有作用域,如果自身没有定义,则不会渲染(数据也可以由上级传递 - 组件之间的传值)

二、全局注册组件

vue实例创建之前

<body>
    <div id="app">
        {{msg}}
        <hello-temp></hello-temp>
    </div>
</body>
<script src="libs/vue.js"></script>
<script>
    Vue.component('hello-temp', {
        template: `<div>
                     <h1>{{msg}}</h1>
                  </div>`,
        data: function () {
            return {
                msg: "hello vue temp"
            }
        }
    });
    const vm = new Vue({
        el: "#app",
        data:{
            msg:"hello vue"
        }
    })
</script>

但是以上案例并不推荐使用,因为在js的字符中书写html代码,不会自动进行语法检查,不方便书写,也不方便调试
建议使用vue提供的template标签,创建模板,然后再注册到组件

<body>
    <div id="app">
        {{msg}}
        <!-- 3.使用组件 -->
        <hello-temp></hello-temp>
    </div>
</body>
<script src="libs/vue.js"></script>
<!-- 1.创建组件模板 -->
<template id="temp">
    <div>
        <h1>{{msg}}</h1>
    </div>
</template>
<script>
// 2.注册组件
 Vue.component('hello-temp', {
        template: "#temp",
        data: function () {
            return {
                msg: "hello vue temp 2"
            }
        }
    });
    const vm = new Vue({
        el: "#app",
        data: {
            msg: "hello vue"
        }
    })
</script>

三、局部注册组件

vue实例内部选项

<body>
    <div id="app">
        {{msg}}
        <!-- 4.使用组件 -->
        <my-com></my-com>
    </div>
</body>
<script src="libs/vue.js"></script>
<!-- 1.创建组件模板 -->
<template id="temp">
    <div>
        <h1>{{msg}}</h1>
    </div>
</template>
<script>
    // 2.定义组件
    const com = {
        template: "#temp",
        data() {
            return {
                msg: "helle temp 3"
            }
        }
    }

    const vm = new Vue({
        el: "#app",
        data: {
            msg: "hello vue"
        },
        components: {
            // 3.注册局部组件,谁注册谁使用!
            "my-com": com,
        }
    })
</script>

四、组件之间的关系

  1. 父子组件
    组件的嵌套
<body>
    <div id="app">
        {{msg}}
        <!-- 7.使用父组件 -->
        <my-com></my-com>
    </div>
</body>
<script src="libs/vue.js"></script>
<!-- 1.创建父组件模板 -->
<template id="temp">
    <div>
        <h1>{{msg}}</h1>
        <!-- 8.使用子组件 -->
        <child-com></child-com>
    </div>
</template>
<!-- 2.创建子组件模板 -->
<template id="child">
    <div>
        <h2>{{msg}}</h2>
    </div>
</template>

<script>

 // 3.定义子组件
    const child = {
        template: "#child",
        data() {
            return {
                msg: "hello child-temp"
            }
        }
    }
    // 4.定义父组件
    const com = {
        template: "#temp",
        data() {
            return {
                msg: "helle temp 3"
            }
        },
        components: {
            // 6.注册局部组件(子组件),谁注册谁使用!
            "child-com": child
        }
    }

    const vm = new Vue({
        el: "#app",
        data: {
            msg: "hello vue"
        },
        components: {
            // 5.注册局部组件(父组件),谁注册谁使用!
            "my-com": com,
        }
    })
</script>

父子组件的传值 - 非常重要
(一) 父组件给子组件传值
父组件传递
父组件在调用子组件的位置,添加一个自定义属性,属性的值就是父组件传入子组件的值。
如果属性值是一个变量,或布尔类型的数据,或数值类型的数据,该自定义属性需要使用绑定属性

<template id="temp">
    <div>
        <h1>{{msg}}</h1>
        <!-- 父组件调用子组件的位置 -->
        <!-- 父组件在调用子组件的位置,添加一个自定义属性,属性的值就是父组件传入子组件的值。 -->
        <!-- 如果属性值是一个变量,或布尔类型的数据,或数值类型的数据,该自定义属性需要使用绑定属性 -->

        <!-- a的值为:字符串aaa -->
        <!-- b的值为:字符串10 -->
        <!-- c的值为:数值10 ---- 绑定属性-->
        <!-- d的值为:字符false -->
        <!-- flag的值为:布尔true ---- 绑定属性-->
        <!-- pd的值为:变量 ---- 绑定属性-->
        <child-com a="aaa" b="10" :c="10" d="false" :flag="true" :pd="parentData"></child-com>
    </div>
</template>

子组件接收
在子组件定义的位置,添加一个props选项
选项的值默认是一个数组,数组的元素就是:父组件在调用子组件的位置定义的属性名

const child = {
    template: "#child",
    props: ["a", "b", "c", "d", "flag", "pd"],
    data() {
        return {
            msg: "hello child-temp"
        }
    }
}

这样就可以在子组件中通过自定义属性名访问父组件传入的数据了

<template id="child">
    <div>
        <h2>{{msg}}</h2>
        <p>a的值为:{{a}},a的类型为{{typeof a}}</p>
        <p>b的值为:{{b}},b的类型为{{typeof b}}</p>
        <p>c的值为:{{c}},c的类型为{{typeof c}}</p>
        <p>d的值为:{{d}},d的类型为{{typeof d}}</p>
        <p>flag的值为:{{flag}},flag的类型为{{typeof flag}}</p>
        <p>pd的值为:{{pd}},pd的类型为{{typeof pd}}</p>
    </div>
</template>

props选项内接收到的父组件传过来的数据,可以进行一些自定义配置。
首先将props的值改为对象,对象的key:自定义的属性名,对象的value:对象
这个对象就是当前接收到数据的配置对象
配置选项有:

type:数据类型
default:默认数据
required:当前数据为必传数据
props: {
    num: {
        type: Number,
        default: 123,
        required: true
    },
}

(二) 子组件给父组件传值
父组件在调用子组件的位置,绑定自定义事件。事件的名字不能是关键字,保留字及固有的事件名称…
该事件的处理方法,由父组件定义。方法接收的参数,为子组件发送的数据
在子组件的某个事件或行为中,使用this.$emit触发自定义事件,并且传递参数(数据)

<body>
    <div id="app">
        {{msg}}
        <my-com></my-com>
    </div>
</body>
<script src="libs/vue.js"></script>

<template id="temp">
    <div>
        <h1>{{msg}}</h1>
        <!-- 1.父组件在调用子组件的位置,绑定自定义事件 -->
        <child-com v-on:myeve="getData"></child-com>
    </div>
</template>

<template id="child">
    <div>
        <h2>{{msg}}</h2>
    </div>
</template>

<script>
    const child = {
        template: "#child",
        data() {
            return {
                msg: "hello child-temp"
            }
        },
        created() {
            // 3. 相应的子组件触发对应的自定义事件
            this.$emit('myeve', this.msg);
        }
    }
    const com = {
        template: "#temp",
        data() {
            return {
                msg: "helle temp",
                parentData: "this is parent data"
            }
        },
        components: {
            "child-com": child
        },
        methods: {
            // 2.父组件中定义 自定义事件的处理函数,参数为子组件传过来的数据
            getData(res){
                console.log("子组件发过来的数据为:", res);
            }
        },
    }

    const vm = new Vue({
        el: "#app",
        data: {
            msg: "hello vue"
        },
        components: {
            "my-com": com,
        }
    })
</script>
  1. 兄弟组件 - 传值
    非父子组件,就是兄弟组件,兄弟组件之间并无任何关系,如果需要传值,需要借助中间方,如:
    租客 ==> 中介 ==> 房东
    使用 中央事件总线 ,其实就是一个新的vue实例传值
    接收数据的一方,通过 $on 先监听自定义事件,并接收值
    发送数据的一方,通过 $emit 触发事件,并传值
<body>
    <div id="app">
        <my-nav></my-nav>
        <my-cont></my-cont>
    </div>
</body>
<script src="libs/vue.js"></script>
<template id="nav">
    <div>
        <ul>
            <li @click="sendData('手机')">手机</li>
            <li @click="sendData('电脑')">电脑</li>
            <li @click="sendData('音箱')">音箱</li>
        </ul>
    </div>
</template>
<template id="cont">
    <div>
        <div>这里是{{tit}}主场</div>
    </div>
</template>
<script>

  // 1.使用 中央事件总线 ,其实就是一个新的vue实例传值
    const bus = new Vue();
    const Nav = {
        template: "#nav",
        methods: {
            sendData(i) {
                // 3. 发送数据的一方,触发事件,并传值
                bus.$emit("titData", i);
            }
        },
    }
    const Cont = {
        template: "#cont",
        data() {
            return {
                tit: "手机"
            }
        },
        created() {
            // 2.接收数据的一方,监听自定义事件,并接收值
            bus.$on("titData", (res) => {
                this.tit = res;
            })
        },
    }

    const vm = new Vue({
        el: "#app",
        components: {
            "my-nav": Nav,
            "my-cont": Cont,
        }
    })
</script>
  1. 子组件使用父组件的方法和数据
    在子组件中可以直接通过 $parent 拿到父组件的实例,从而访问父组件的方法或数据
<template >
    <div>
        <h2>子组件中的数据:{{msg}}</h2>
        <p>子组件拿到的父组件的数据:{{$parent.msg}}</p
    </div>
</template>
  1. 父组件使用子组件的方法和数据
    在子组件的调用处,使用自定义属性ref添加子组件别名
<template id="temp">
    <div>
        <h1>{{msg}}</h1>
        <input type="button" value="获取子组件数据" @click="getData">
        <p>父组件拿到的子组件的数据:{{ childMsg }}</p>
        <!-- 1.在子组件的调用处,使用自定义属性`ref`添加子组件别名 -->
        <child-com ref="test"></child-com>
    </div>
</template>

在父组件中,使用 this.$refs.子组件别名 获取到子组件实例,从而访问子组件的数据和方法
const com = {
    template: "#temp",
    data() {
        return {
            msg: "hello parent-temp",
            childMsg:""
        }
    },
    components: {
        "child-com": child
    },
    methods: {
        getData(){
            // 2. 在父组件中,使用 `this.$refs.子组件别名` 获取到子组件实例,从而访问子组件的数据和方法
            this.childMsg = this.$refs.test.msg;
        }
    }
}

五、provide + inject

provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
用于一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
provide 选项是一个对象
inject 选项是一个字符串数组

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

04 vue-脚手架

一、脚手架介绍

1.文档介绍

  • 快速,自动,创建模板项目
  • 自动安装环境,配置环境

2.安装

  • 在拥有npm工具的前提下
  • npm install -g @vue/cli

3.查看版本

  • vue --version

4.升级

  • npm update -g @vue/cli

二、项目创建


1.创建项目

  • vue create myapp

2.根据提示做各项选择

  • 步骤一:选择安装方式 - 自定义安装

    • Default ([Vue 2] babel, eslint)
    • Default (Vue 3 Preview) ([Vue 3] babel, eslint)
    • Manually select features
  • 步骤二:选择要安装的模块

    • ◉ Choose Vue version — 选择视图版本
    • ◉ Babel — 解析js
    • ◯ TypeScript — 解析ts
    • ◉ Progressive Web App (PWA) Support — 渐进式支持
    • ◉ Router — 路由
    • ◉ Vuex — 状态管理
    • ◉ CSS Pre-processors — 样式处理
    • ◉ Linter / Formatter — 代码格式化
    • ◉ Unit Testing — 测试插件
    • ◉ E2E Testing — 测试插件
  • 步骤三:选择路由模式(任选后期可调)

    • history — http://localhost:81/ 后端支持

    • hash — http://localhost:81/#/ 不需要后端支持
      -步骤四:选择什么方式处理css(node环境的scss)

    • sass/scss - node

  • 步骤五:代码校验规则 - 标准规则:Standard

    • ESLint with error prevention only

    • ESLint + Airbnb config

    • ESLint + Standard config

    • ESLint + Prettier
      -步骤六:什么时候校验:保存时

    • ◉ Lint on save

    • ◯ Lint and fix on commit

  • 步骤七:选择测试模块:任选

  • 步骤八:端对端测试方案:默认

  • 步骤九:项目的配置文件

    • package.json
  • 步骤十:是否保存当前配置:不保存

三、项目目录介绍

根目录

+ node_modules --- 项目的依赖包
+ public --- 基本的页面结构及静态资源
+ src --- 工作区,开发环境
+ tests --- 测试
    .browserslistrc --- 浏览器配置
    .editorconfig --- 编辑器配置
    .eslintrc --- 代码格式化配置
    .gitignore --- git忽略的文件配置
    babel.config.js --- babel的配置
    cypress.json --- 测试相关插件
    package.json --- 项目依赖的描述
    README.md --- 说明文件

src目录

+ assets --- 静态资源
+ components --- 组件
+ router --- 路由
+ store --- 状态管理器
+ views --- 页面
    App.vue --- 主页面结构
    main.js --- 程序的入口

四、单文件组件

1.文档

2.以vue结尾的文件

  • 结构 + 行为 + 样式
  • template + script + style

3.css作用域

  • 样式只在当前组件中使用
<style scoped></style>

4.指定css语言

  • 可以通过scss语言编写css
<style lang="scss"></style>

5.单文件组件中,定义组件无需template选项

<template>
</template>

<script>
export default {}
</script>

<style scoped></style>

6.入口文件解析(main.js)

// 引入vue文件
import Vue from 'vue'
// 引入app组件
import App from './App.vue'
// 引入注册服务模块
import './registerServiceWorker'
// 引入路由模块
import router from './router'
// 引入状态管理器模块
import store from './store'

// vue配置,生产提示
Vue.config.productionTip = true

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
// 渲染app组件,并写入id为app的标签内

7.路由的映射和渲染

  • 路由的映射
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义 (路由) 组件
import Home from '../views/Home.vue'

// 2. 定义路由
const routes = [
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // 路由的懒加载,用时再加载,用时再映射
    component: () => import(/* webpackChunkName: "base" */ '../views/About.vue')
  }
]

// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
  routes
})
  • 路由的渲染
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签。可以通过 `tag` 属性指定渲染标签 -->
    <router-link to="/home" tag="span">Go to home</router-link>
    <router-link to="/about" tag="span">Go to about</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

8.设计页面结构

  • 参考京东移动商城布局效果

  • 移动端的基准尺寸设置:

  • 假设设计稿以iPhone6为基准:

    • 物理像素:750*1334
    • dpr倍数:2
    • 独立像素(css):375*667
    • 1px对应(100/375)vw,即100px对应(100/375*100)vw
    • html{font-size:26.666666666666668vw}
    • 此时:1rem = 100px
  • 假设设计稿以iPhoneX为基准:

    • 物理像素:1080*1920
    • dpr倍数:3
    • 独立像素(css):414*736
    • 1px对应(100/414)vw,即100px对应(100/414*100)vw
    • html{font-size:24.154589371980677vw}
    • 此时:1rem = 100px

9.设计页面底部

  • 首页(home),分类(kind),购物车(cart),我的(my)

10.创建底部选项对应页面
在这里插入图片描述

11.底部的跳转

<ul>
  <router-link to="/index" tag="li">
    <span class="iconfont icon-ziyuan"></span>
    <h5>首页</h5>
  </router-link>
  <router-link to="/kind" tag="li">
    <span class="iconfont icon-fenlei"></span>
    <h5>分类</h5>
  </router-link>
  <router-link to="/find" tag="li">
    <span class="iconfont icon-faxian"></span>
    <h5>发现</h5>
  </router-link>
  <router-link to="/cart" tag="li">
    <span class="iconfont icon-gouwuche"></span>
    <h5>购物车</h5>
  </router-link>
  <router-link to="/my" tag="li">
    <span class="iconfont icon-wode"></span>
    <h5></h5>
  </router-link>
</ul>

12.当前项的设置

  • 当 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active。
    • 可以通过在页面审查元素得知
  • 只需要在css中添加对应样式即可

13.路由重定向和别名

  • 重定向
    • 如果路由 / 的重定向是 /home 。当用户访问 / 时,URL 将会被替换成 /home,然后匹配路由为 /home。
const routes = [{
  path: '/',
  redirect: '/home'
}, {
  path: '/home',
  component: Home
}]
  • 别名
    • 如果路由 / 的别名是 /home,意味着,当用户访问 /home 时,URL 会保持为 /home,但路由匹配则为 / ,就像用户访问 / 一样。
const routes = [{
  path: '/',
  alias: '/home'
}]

05 vue项目-组件库+首页

一、VUE的组件库介绍

PC端

  • element
  • iview
  • antd

    mobile
  • vant
  • mint-ui

二、移动端项目的页面优化

1.横屏处理

@media all and (orientation:landscape){
    html{font-size:100px;}
}

2.屏幕的安全距离

三、配置UI库并测试 - vant

1.安装

  • npm i vant -S

2.引入 - 自动按需引入组件 (推荐)

  • 下载babel-plugin-import插件,负责在编译过程中将 import 的写法自动转换为按需引入的方式
    • npm i babel-plugin-import -D
  • 在 babel.config.js 中配置
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};

3.使用 - button组件示例

import Vue from 'vue';
import { Button } from 'vant';

Vue.use(Button);
<van-button type="primary">主要按钮</van-button>
  • 根据 文档说明 ,按需使用

四、使用vant库完善首页布局

1.添加轮播图组件

2.封装axios接口请求

// src -> api -> index.js
import axios from 'axios'

export function getBanner () {
  return axios.get('http://localhost:3000/api/banner')
}

// ...

3.请求分类数据,添加宫格组件,实现分类导航

4.请求商品数据,添加卡片组件,实现商品列表

五、上拉加载数据

1.使用 List组件 实现上拉加载数据

  • 通过loading和finished两个变量控制加载状态,当组件滚动到底部时,会触发load事件并将loading设置成true。

  • 此时可以发起异步操作并更新数据,数据更新完毕后,将loading设置成false即可。

  • 若数据已全部加载完毕,则直接将finished设置成true即可。

<van-list
  v-model="loading"
  :finished="finished"
  finished-text="没有更多了"
  @load="onLoad"
>
  列表内容
</van-list>
methods: {
  onLoad () {
    setTimeout(()=>{
      // 开始异步请求数据
      // ...
      // 本次数据更新成功后,将loading设置为false
      this.loading = false

      // 如果所有数据已加载完毕,将finished设置为true
      this.finished = true
    },1000)
  }
}

六、下拉刷新数据

1.使用 PullRefresh组件 完成下拉刷新数据

  • 下拉刷新时会触发 refresh 事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将 v-model 设置为 false,表示加载完成。
<van-pull-refresh v-model="isLoading" @refresh="onRefresh">
  需要下拉的结构
</van-pull-refresh>
methods: {
  onRefresh() {
    setTimeout(() => {
      // 此处可以重新请求数据
      // ...
      // 数据更新成功后,将isLoading设置为false
      this.isLoading = false;
    }, 1000);
  },
},

七、回到顶部

1.回到顶部按钮,初始不显示
2.找到有滚动条的元素,添加滚动事件
3.在滚动事件中获取当前元素的滚动距离
4.当滚动到一定位置显示回到顶部按钮
5.给回到顶部按钮添加点击事件
6.点击回到顶部,将滚动元素的滚动距离设置为0
提示:可使用ref属性给元素添加别名,方便在vue实例中获取元素

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值