侦听器-watch
介绍:想要侦听一个属性变化, 可使用侦听属性watch,可以侦听data/computed属性值改变
语法:
watch: {
"被侦听的属性名" (newVal, oldVal){}
}
完整例子代码:
<template>
<div>
<p>num: <input type="text" v-model="num" /></p>
<p>obj.name: <input type="text" v-model="obj.name" /></p>
<p>obj.age: <input type="text" v-model="obj.age" /></p>
<p>number: <input type="text" v-model="number" /></p>
<h1>{{ number }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
num: "",
obj: [
{
name: "悟空",
age: "22",
},
],
number: "10",
number2: 0,
};
},
// 开始监听属性的变化
watch: {
num(newVal, oldVal) {
// 以键值对的形式指定被监听字段:触发回调函数
console.log(newVal, oldVal);
},
// 复杂类型无法直接接听
obj: {
// deep 可以监听到复杂数据内部的改变
deep: true,
handler(newV, oldV) {
console.log(newV.name, oldV.age);
},
},
// immediate 可以在页面进来时马上触发一次当前的监听回调函数
number: {
immediate: true,
handler(aa) {
this.number2 += aa;
},
},
},
};
</script>
组件
1.为什么要用组件?
解决代码的冗余和重复,利于维护
2.概念
- 组件是可复用的 Vue 实例, 封装标签, 样式和JS代码
- 组件化 :封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护
- 一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的结构,样式和行为(html, css和js)
3. 基础使用
-
每个组件都是一个独立的个体(.vue结尾的文件)
-
口诀: 哪部分标签复用, 就把哪部分封装到组件内
-
注意:组件内template只能有一个根标签,且data必须是一个函数, 独立作用域
-
使用步骤
创建组件 components/ProItem.vue
<template> <div> <ProItem :xo="item" v-for="(item, index) in list" :key="index" /> </div> </template> <script> export default { components: { ProItem, }, data() { return { list: ["悟空", "八戒", "唐曾", "沙和尚"], }; }, }; </script>局部注册(局部和全部注册,按实际需求二选一即可,不可同时使用两种方式调用同一个vue文件)
<script> // 局部注册引入方法 // import(引入) ProItem(自定义组件名) from "@/components/ProItem.vue" //(@是指在 src 文件夹下开始找文件 相当于一个定点,@永远指向src/创建的组件) import ProItem from "@/components/ProItem.vue"; export default {}; </script>使用---局部注册
<template> <div> <!-- 使用组件 --> <ProItem :xo="item" v-for="(item, index) in list" :key="index" /> </div> </template> <script> // 局部注册 import ProItem from "@/components/ProItem.vue"; export default { components: { ProItem, }, data() { return { list: ["悟空", "八戒", "唐曾", "沙和尚"], }; }, }; </script>全局注册(全局入口在main.js, 在new Vue之上注册)
// 全局注册 (一处定义到处使用) import Vue from 'vue' // 引入组件 import ProItem from @/components/ProItem.vue' // 语法 Vue.component("ProItem", ProItem) // 为了避免不必要的错误,一般命名都会跟随创建的 .vue文件名 来 命名使用---全局注册
<!-- 单双标签都可以或者小写加-形式, 运行后, 会把这个自定义标签当做组件解析 --> <PannelG></PannelG> <PannelG/> <!-- 常用方式 --> <pannel-g></pannel-g>
4. scoped作用
- 作用:解决多个组件样式名相同, 冲突问题
- 使用:在style上加入scoped属性, 就会在此组件的标签上加上一个随机生成的data-v开头的属性,而且必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到
- 完整代码
<!-- scoped 可以将当前的样式限定在这个文件内,最多可以穿透到下一层的根元素,其他不受影响 -->
<style scoped> </style>
组件通信
父传子-props
- 父(App.vue) -> 子(ProItem.vue) 传值进入
<template>
<div>
<!-- 父传子第一步,父组件作为属性将数据交给子组件标签,属性名随便起 -->
<!-- 子组件引入后,怎么渲染父组件的数据则由子组件决定 -->
<!-- 如果我希望将一些数据传给子组件,以属性的形式传递即可,就好像以前class -->
<!-- 父组件可以传任意数据,子组件不一定接收,需要接收的时候声明props -->
<!-- 使用v-bind的方式 (:Prod="item):''/自定义变量名='要传过去的参数'-->
<ProItem :ProItem="item" v-for="(item, index) in list" :key="index" />
</div>
</template>
<script>
// 局部注册
import ProItem from "@/components/ProItem.vue";
export default {
// 组件使用
components: {
ProItem,
},
data() {
return {
list: ["悟空", "八戒", "唐曾", "沙和尚"],
};
},
};
</script>
- 子组件声明一个可以接收的配置---props
<script>
export default {
// 父传子第二步,子组件声明一个可以接收的配置 props
// 父组件可以传数据,但是没有权限控制子组件怎么渲染
// 如果子组件要接收父组件的数据,就要以字符串数组的形式声明props
props: ["Prod"],
};
</script>
- 使用父组件传过来的数据
<template>
<div>
<!-- 第三步 使用父组件传过来的数据 -->
<p>{{Prod}}</p>
</div>
</template>
子传父
- 子组件出发自定义事件,传递参数(可选)
<template>
<div>
<p class="box">{{ title }} {{ price }} {{ msg }}</p>
<button @click="change">砍价</button>
</div>
</template>
<script>
export default {
props: ["title", "price", "msg", "index"],
methods: {
// 子传父第一步 子组件出发自定义事件,传递参数(可选)
change() {
this.$emit("bargain", this.index, 2);
},
},
};
</script>
- 父组件监听子组件的自定义事件
<template>
<div>
<ProItem
v-for="(obj, index) in list"
:key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:msg="obj.info"
:index="index"
@bargain="check"
/>
<!-- @bargain="check" bargain为子组件传过来的组件名 check则是用来声明执行并接收的自定义命名-->
<!-- 子传父第二步,父组件监听子组件的自定义事件 监听事件 -->
<!-- 监听的自定义事件触发后,指定对应后续处理回调 -->
</div>
</template>
- 开始执行子组件传过来值
<template>
<div>
<ProItem
v-for="(obj, index) in list"
:key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:msg="obj.info"
:index="index"
@bargain="check"
/>
<!-- @bargain="check" bargain为子组件传过来的组件名 check则是用来声明执行并接收的自定义命名-->
<!-- 子传父第二步,父组件监听子组件的自定义事件 监听事件 -->
<!-- 监听的自定义事件触发后,指定对应后续处理回调 -->
</div>
</template>
<script>
// 局部注册
import ProItem from "@/components/ProItem.vue";
export default {
components: {
ProItem,
},
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 第三步 开始执行子组件传过来值 this.$emit("bargain", this.index, 2)
// index=this.index price=2
methods: {
check(index, price) {
if (this.list[index].proprice > price) {
this.list[index].proprice = (this.list[index].proprice - price).toFixed(2);
}
},
},
};
</script>
单向数据流
从父到子的数据流向,就叫单向数据流。而在vue中,我们需要遵循的单向数据流原则
- 父组件的数据发生了改变,子组件会自动跟着变
- 子组件不能直接修改父组件传递过来的props,props是只读的
- 父组件传给子组件的是一个对象,子组件修改对象的属性,是不会报错的,对象是引用类型, 互相更新