文章目录
发现宝藏
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。
前言
为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。
一、组件的三大组成部分(结构/样式/逻辑)
1. scoped样式冲突
2. 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>
二、组件通信
1. 组件通信语法
2. 父传子
3. 子传父
4. props详解
1. 什么是 props
2. props 校验
- 子组件
<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">
<BaseProgress :w="width"></BaseProgress>
</div>
</template>
<script>
import BaseProgress from './components/BaseProgress.vue'
export default {
data() {
return {
width: 30,
}
},
components: {
BaseProgress,
},
}
</script>
<style>
</style>
3. prop & data、单向数据流
- 子组件
<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>
- 父组件
<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>
5. 非父子通信 - event bus 总线
- EventBus.js
import Vue from 'vue'
const Bus = new Vue()
export default Bus
- BaseA.vue
<template>
<div class="base-a">
我是A组件(接受方)
<p>{
{
msg}}</p>
</div>
</template>
<script>
import Bus from '../utils/EventBus'
export default {
data() {
return {
msg: '',
}
},
created() {
Bus.$on('sendMsg', (msg) => {
// console.log(msg)
this.msg = msg
})
},
}
</script>
<style scoped>
.base-a {
width: 200px;
height: 200px;
border: 3px solid #000;
border-radius: 3px;
margin: 10px;
}
</style>
- BsaeB.vue
<template>
<div class="base-b">
<div>我是B组件(发布方)</div>
<button @click="sendMsgFn">发送消息</button>
</div>
</template>
<script>
import Bus from '../utils/EventBus'
export default {
methods: {
sendMsgFn() {
Bus.$emit('sendMsg', '今天天气不错,适合旅游')
},
},
}
</script>
<style scoped>
.base-b {
width: 200px;
height: 200px;
border: 3px solid #000;
border-radius: 3px;
margin: 10px;
}
</style>
- BaseC.vue
<template>
<div class="base-c">
我是C组件(接受方)
<p>{
{
msg}}</p>
</div>
</template>
<script>
import Bus from '../utils/EventBus'
export default {
data() {
return {
msg: '',
}
},
created() {
Bus.$on('sendMsg', (msg) => {
// console.log(msg)
this.msg = msg
})
},
}
</script>
<style scoped>
.base-c {
width: 200px;
height: 200px;
border: 3px solid #000;
border-radius: 3px;
margin: 10px;
}
</style>
6. 非父子通信 - provide & inject
三、 综合案例:小黑记事本(组件版)
1. 拆分组件
2. 渲染
3. 添加
4. 删除
5. 统计&清空&持久化
6. 完整代码
- TodoHeader.vue
<template>
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input placeholder="请输入任务" class="new-todo" v-model="todoName" @keyup.enter="handleAdd"/>
<button class="add" @click="handleAdd">添加任务</button>
</header>
</template>
<script>
export default {
data(){
return {
todoName:''
}
},
methods:{
handleAdd(){
// console.log(this.todoName)
this.$emit('add',this.todoName)
this.todoName = ''
}
}
}
</script>
<style>
</style>
- TodoMain.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 class="destroy" @click="handleDel(item.id)"></button>
</div>
</li>
</ul>
</section>
</template>
<script>
export default {
props: {
list: {
type: Array,
},
},
methods: {
handleDel(id) {
this.$emit('del', id)
},
},
}
</script>
<style>
</style>
- TodoFooter.vue
<template>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count"
>合 计:<strong> {
{
list.length }} </strong></span
>
<!-- 清空 -->
<button class="clear-completed" @click="clear">清空任务</button>
</footer>
</template>
<script>
export default {
props: {
list: {
type: Array,
},
},
methods:{
clear(){
this.$emit('clear')
}
}
}
</script>
<style>
</style>
- App.vue
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @add="handleAdd"></TodoHeader>
<TodoMain :list="list" @del="handelDel"></TodoMain>
<TodoFooter :list="list" @clear="clear"></TodoFooter>
</section>
</template>
<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'
// 渲染功能:
// 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的变化 -> 往本地存储 ->进入页面优先读取本地数据
export default {
data() {
return {
list: JSON.parse(localStorage.getItem('list')) || [
{
id: 1, name: '打篮球' },
{
id: 2, name: '看电影' },
{
id: 3, name: '逛街' },
],
}
},
components: {
TodoHeader,
TodoMain,
TodoFooter,
},
watch: {
list: {
deep: true,
handler(newVal) {
localStorage.setItem('list', JSON.stringify(newVal))
},
},
},
methods: {
handleAdd(todoName) {
// console.log(todoName)
this.list.unshift({
id: +new Date(),
name: todoName,
})
},
handelDel(id) {
// console.log(id);
this.list = this.list.filter((item) => item.id !== id)
},
clear() {
this.list = []
},
},
}
</script>
<style>
</style>
四、进阶语法
1. V-model原理
2. v-model应用于组件
- 父组件 App.vue
<template>
<div class="app">
<BaseSelect
v-model="selectId"
></BaseSelect>
</div>
</template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
data() {
return {
selectId: '102',
}
},
components: {
BaseSelect,
},
}
</script>
<style>
</style>
- 子组件 BaseSelect
<template>
<div>
<select :value="value" @change="selectCity">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">武汉</option>
<option value="104">广州</option>
<option value="105">深圳</option>
</select>
</div>
</template>
<script>
export default {
props: {
value: String,
},
methods: {
selectCity(e) {
this.$emit('input', e.target.value)
},
},
}
</script>
<style>
</style>
3. sync修饰符
- 父组件 App.vue
<template>
<div class="app">
<button @click="openDialog">退出按钮</button>
<!-- isShow.sync => :isShow="isShow" @update:isShow="isShow=$event" -->
<BaseDialog :isShow.sync="isShow"></BaseDialog>
</div>
</template>
<script>
import BaseDialog from './components/BaseDialog.vue'
export default {
data() {
return {
isShow: false,
}
},
methods: {
openDialog() {
this.isShow = true
// console.log(document.querySelectorAll('.box'));
},
},
components: {
BaseDialog,
},
}
</script>
<style>
</style>
- 子组件 BaseDialog
<template>
<div class="base-dialog-wrap" v-show="isShow">
<div class="base-dialog">
<div class="title">
<h3>温馨提示:</h3>
<button class="close" @click="closeDialog">x</button>
</div>
<div class="content">
<p>你确认要退出本系统么?</p>
</div>
<div class="footer">
<button>确认</button>
<button>取消</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isShow: Boolean,
},
methods:{
closeDialog(){
this.$emit('update:isShow',false)
}
}
}
</script>
<style scoped>
.base-dialog-wrap {
width: 300px;
height: 200px;
box-shadow: 2px 2px 2px 2px #ccc;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 0 10px;
}
.base-dialog .title {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #000;
}
.base-dialog .content {
margin-top: 38px;
}
.base-dialog .title .close {
width: 20px;
height: 20px;
cursor: pointer;
line-height: 10px;
}
.footer {
display: flex;
justify-content: flex-end;
margin-top: 26px;
}
.footer button {
width: 80px;
height: 40px;
}
.footer button:nth-child(1) {
margin-right: 10px;
cursor: pointer;
}
</style>
4. ref和 $refs
- 父组件 App.vue
<template>
<div class="app">
<div class="base-chart-box">
这是一个捣乱的盒子
</div>
<BaseChart></BaseChart>
</div>
</template>
<script>
import BaseChart from './components/BaseChart.vue'
export default {
components:{
BaseChart
}
}
</script>
<style>
.base-chart-box {
width: 300px;
height: 200px;
}
</style>
- 子组件 BaseChart.vue
<template>
<div class="base-chart-box" ref="baseChartBox">子组件</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
mounted() {
// 基于准备好的dom,初始化echarts实例
// document.querySelector 会查找项目中所有的元素
// $refs只会在当前组件查找盒子
// var myChart = echarts.init(document.querySelector('.base-chart-box'))
var myChart = echarts.init(this.$refs.baseChartBox)
// 绘制图表
myChart.setOption({
title: {
text: 'ECharts 入门示例',
},
tooltip: {
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
},
yAxis: {
},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
})
},
}
</script>
<style scoped>
.base-chart-box {
width: 400px;
height: 300px;
border: 3px solid #000;
border-radius: 6px;
}
</style>
5. $nextTick
总结
欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)