day04

组件三大部分

在这里插入图片描述

1.style中的样式 默认是作用到全局的
2.加上scoped可以让样式变成局部样式
scoped原理:
1.给当前组件模板的所有元素,都会添加上一个自定义属性data-v-hash值 用于区分开不通的组件
2.css选择器后面,被自动处理,添加上了属性选择器 div[data-v-hash值]

一个组件的 data 选项必须是一个函数。→ 保证每个组件实例,维护独立的一份数据对象。

<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  // data() {
  //   console.log('函数执行了')
  //   return {
  //     count: 100,
  //   }
  // },
  data: function () {
    return {
      count: 100,
    }
  },
}
</script>

<style>
.base-count {
  margin: 20px;
}
</style>

组件通信

在这里插入图片描述
在这里插入图片描述

父组件向子组件传值

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是父组件
    <!-- 1.给组件标签,添加属性方式 赋值 -->
    <Son :title="myTitle"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '学前端',
    }
  },
  components: {
    Son,
  },
}
</script>

<style>
</style>
<template>
  <div class="son" style="border:3px solid #000;margin:10px">
    <!-- 3.直接使用props的值 -->
    我是子组件 {{title}}
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
  // 2.通过props来接受
  props:['title']
}
</script>

<style>

</style>

子组件向父组件传值

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是父组件
    <!-- 2.父组件对子组件的消息进行监听 -->
    <Son :title="myTitle" @changTitle="handleChange"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '学前端',
    }
  },
  components: {
    Son,
  },
  methods: {
    // 3.提供处理函数,提供逻辑
    handleChange(newTitle) {
      this.myTitle = newTitle
    },
  },
}
</script>

<style>
</style>
<template>
  <div class="son" style="border: 3px solid #000; margin: 10px">
    我是Son组件 {{ title }}
    <button @click="changeFn">修改title</button>
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
  props: ['title'],
  methods: {
    changeFn() {
      // 通过this.$emit() 向父组件发送通知
      this.$emit('changTitle','传智教育')
    },
  },
}
</script>

<style>
</style>

props

<template>
  <div class="app">
    我是父组件
    <BaseProgress :w="width"></BaseProgress>
  </div>
</template>

<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
  data() {
    return {
      width: 30,
    }
  },
  components: {
    BaseProgress,
  },
}
</script>

<style>
</style>
<template>
  <div class="base-progress">
    我是子组件
    <div class="inner" :style="{ width: w + '%' }">
      <span>{{ w }}%</span>
    </div>
  </div>
</template>

<script>
export default {
  // 1.基础写法(类型校验)
  // props: {
  //   w: Number,
  // },

  // 2.完整写法(类型、默认值、非空、自定义校验)
  props: {
    w: {
      type: Number,
      required: true,
      default: 0,
      validator(val) {
        // console.log(val)
        if (val >= 100 || val <= 0) {
          console.error('传入的范围必须是0-100之间')
          return false
        } else {
          return true
        }
      },
    },
  },
}
</script>

<style scoped>
.base-progress {
  height: 26px;
  width: 400px;
  border-radius: 15px;
  background-color: #272425;
  border: 3px solid #272425;
  box-sizing: border-box;
  margin-bottom: 30px;
}
.inner {
  position: relative;
  background: #379bff;
  border-radius: 15px;
  height: 25px;
  box-sizing: border-box;
  left: -3px;
  top: -2px;
}
.inner span {
  position: absolute;
  right: 0;
  top: 26px;
}
</style>

单向数据流

<template>
  <div class="app">
    <BaseCount :count="count" @changeCount="handleChange"></BaseCount>
  </div>
</template>

<script>
import BaseCount from './components/BaseCount.vue'
export default {
  components:{
    BaseCount
  },
  data(){
    return {
      count:100
    }
  },
  methods:{
    handleChange(newVal){
      // console.log(newVal);
      this.count = newVal
    }
  }
}
</script>

<style>

</style>
<template>
  <div class="base-count">
    <button @click="handleSub">-</button>
    <span>{{ count }}</span>
    <button @click="handleAdd">+</button>
  </div>
</template>

<script>
export default {
  // 1.自己的数据随便修改  (谁的数据 谁负责)
  // data () {
  //   return {
  //     count: 100,
  //   }
  // },
  // 2.外部传过来的数据 不能随便修改
  props: {
    count: {
      type: Number,
    },
  },
  methods: {
    handleSub() {
      this.$emit('changeCount', this.count - 1)
    },
    handleAdd() {
      this.$emit('changeCount', this.count + 1)
    },
  },
}
</script>

<style>
.base-count {
  margin: 20px;
}
</style>

综合案例

App.vue

<template>
  <section id="app">
    <!--3. 使用组件-->
    <Header @add="add"></Header>
    <Main :list="list" @del="del"></Main>
    <Footer :list="list" @clear="clear"></Footer>

  </section>
</template>

<script>

// 渲染功能:
// 1.提供数据: 提供在公共的父组件 App.vue
// 2.通过父传子,将数据传递给TodoMain
// 3.利用 v-for渲染

// 添加功能:
// 1.手机表单数据  v-model
// 2.监听事件(回车+点击都要添加)
// 3.子传父,讲任务名称传递给父组件 App.vue
// 4.进行添加 unshift(自己的数据自己负责)
// 5.清空文本框输入的内容
// 6.对输入的空数据 进行判断

// 删除功能
// 1.监听事件(监听删除的点击) 携带id
// 2.子传父,讲删除的id传递给父组件的App.vue
// 3.进行删除filter(自己的数据 自己负责)

// 底部合计:父传子  传list 渲染
// 清空功能:子传父  通知父组件 → 父组件进行更新
// 持久化存储:watch深度监视list的变化 -> 往本地存储 ->进入页面优先读取本地数据


// 1. 导入组件
import Header from "@/components/Header";
import Main from "@/components/Main";
import Footer from "@/components/Footer";


export default {
  // 2. 引用组件
  components: {Footer, Main, Header},
  data() {
    return {
      list: JSON.parse(localStorage.getItem('list')) || [
        {id: 1, name: '打篮球'},
        {id: 2, name: '看电影'},
        {id: 3, name: '逛街'},
      ]
    }
  },
  methods: {
    add(name) {
      if (name.trim() === '') {
        alert("请输入名称")
        return
      }
      this.list.unshift({
        id: +new Date(),
        name: name
      })
    },
    del(id) {
      this.list = this.list.filter((item) => item.id !== id)
    },
    clear(list) {
      this.list = list
    }
  },
  // 持久化到本地需要
  watch: {
    list: {
      deep: true, //深度监视
      handler(newVal) {
        localStorage.setItem('list', JSON.stringify(newVal))
      }
    }
  }
}
</script>

<style>

</style>

Header.vue

<template>
  <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</h1>
    <input v-model="name" @keyup.enter="add" placeholder="请输入任务" class="new-todo"/>
    <button class="add" @click="add">添加任务</button>
  </header>
</template>

<script>
export default {
  name: "Header",
  data() {
    return {
      name: ''
    }
  },
  methods: {
    add() {
      this.$emit("add",this.name)
      this.name = ''
    }
  }
}
</script>

<style scoped>

</style>

Main.vue

<template>
  <!-- 列表区域 -->
  <section class="main">
    <ul class="todo-list">
      <li class="todo" v-for="(item,index) in list" :key="item.id">
        <div class="view">
          <span class="index">{{ index + 1 }}.</span> <label>{{ item.name }}</label>
          <button @click="del(item.id)" class="destroy"></button>
        </div>
      </li>
    </ul>
  </section>
</template>

<script>
export default {
  name: "Main",
  props: {
    list: {
      type: Array
    }
  },
  methods: {
    del(id) {
      this.$emit("del",id)
    }
  }
}
</script>

<style scoped>

</style>

Footer.vue

<template>
  <!-- 统计和清空 -->
  <footer class="footer">
    <!-- 统计 -->
    <span class="todo-count">合 计:<strong> {{ list.length }} </strong></span>
    <!-- 清空 -->
    <button @click="clear" class="clear-completed">
      清空任务
    </button>
  </footer>
</template>

<script>
export default {
  name: "Footer",
  props: {
    list: {
      type: Array
    }
  },
  methods: {
    clear() {
      this.$emit("clear",[])
    }
  }
}
</script>

<style scoped>

</style>

由于我的子组件命名不规范(没有写成大驼峰的形式)所以需要在vue.config.js中添加一行代码 lintOnSave: false // 关闭语法检查错误

非父子通信

在这里插入图片描述
在这里插入图片描述

指令进阶

v-model

<template>
  <div class="app">
    <input type="text" v-model="msg1" />
    <br />
    <!-- v-model的底层其实就是:value和 @input的简写 
		$event 用于在模板中,获取事件的形参
	-->
    <input type="text" :value="msg2" @input="msg2 = $event.target.value" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg1: '',
      msg2: '',
    }
  },
}
</script>

<style>
</style>

sync修饰符

在这里插入图片描述

ref 和 $refs

<template>
  <div class="app">
    <h4>父组件 -- <button>获取组件实例</button></h4>
    <BaseForm ref="baseForm"></BaseForm>
  </div>
</template>

<script>
import BaseForm from './components/BaseForm.vue'
export default {
  components: {
    BaseForm,
  },
  methods: {
   getMsg() {
     this.$refs.baseForm.getFormData()
   },
    resetMsg() {
      this.$refs.baseForm.resetFormData()
    }
  }
}
</script>

<style>
</style>
<template>
  <div class="app">
    <div>
      账号: <input v-model="username" type="text">
    </div>
     <div>
      密码: <input v-model="password" type="text">
    </div>
    <div>
      <button @click="getFormData">获取数据</button>
      <button @click="resetFormData">重置数据</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: 'admin',
      password: '123456',
    }
  },
  methods: {
    getFormData() {
      console.log('获取表单数据', this.username, this.password);
    },
    resetFormData() {
      this.username = ''
      this.password = ''
      console.log('重置表单数据成功');
    },
  }
}
</script>

<style scoped>
.app {
  border: 2px solid #ccc;
  padding: 10px;
}
.app div{
  margin: 10px 0;
}
.app div button{
  margin-right: 8px;
}
</style>

$nextTick

<template>
  <div class="app">
    <div v-if="isShowEdit">
      <input type="text" v-model="editValue" ref="inp" />
      <button>确认</button>
    </div>
    <div v-else>
      <span>{{ title }}</span>
      <button>编辑</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '大标题',
      isShowEdit: false,
      editValue: '',
    }
  },
  methods: {
    handleEdit() {
      // 1. 显示输入框(异步dom更新)
      this.isShowEdit = true

      // 2. 让 输入框 获取焦点   $nextTick 异步dom更新后立刻执行
      this.$nextTick(() => {
        this.$refs.inp.focus()
      })
    }
  },
}
</script>

<style>
</style>
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值