菜鸟必读 网站被入侵后需做的检测 1,慈溪企业网站建设,延安网站建设费用,免费wordpress 模板一、组件的三大组成部分#xff08;结构/样式/逻辑#xff09;
1. scoped样式冲突 默认情况#xff1a;写在组件中的样式会全局生效 - 因此很容易造成多个组件之间的样式冲突问题。
1. 全局样式#xff1a;默认组件中的样式会作用到全局
2. 局部样式#xff1a;可以…一、组件的三大组成部分结构/样式/逻辑
1. scoped样式冲突 默认情况写在组件中的样式会全局生效 - 因此很容易造成多个组件之间的样式冲突问题。
1. 全局样式默认组件中的样式会作用到全局
2. 局部样式可以给组件加上scoped属性可以让样式只作用于当前组件
scoped的原理
1. 当前组件内部标签都被添加data-v-hash值 的属性
2. CSS选择器都被添加[data-v-hash值] 的属性选择器
最终效果必须是当前组件的元素才会有这个自定义属性才会被这个样式作用到 示例
components/BaseOne.vue components/BaseTwo.vue
templatediv classbase-oneBaseTwo/div
/templatescript
export default {}
/scriptstyle scoped
div {border: 3px solid red;margin: 30px;
}
/style
App.vue
templatediv idappBaseOne/BaseOneBaseTwo/BaseTwo/div
/templatescript
import BaseOne from ./components/BaseOne
import BaseTwo from ./components/BaseTwo
export default {name: App,components: {BaseOne,BaseTwo}
}
/script
效果 2. data是一个函数
一个组件的data选项必须是一个函数。 - 保证每个组件实例维护独立的一份数据对象。
每次创建新的组件实例都会执行一次data函数得到一个新对象
data() {return {count: 100}
},
示例
components/BaseCount.vue
templatediv classbase-countbutton clickcount---/buttonspan{{ count }}/spanbutton clickcount/button/div
/templatescript
export default {data() {return {count: 100,}},
}
/scriptstyle
.base-count {margin: 20px;
}
/style
App.vue
templatediv classappbaseCount/baseCountbaseCount/baseCountbaseCount/baseCount/div
/templatescript
import baseCount from ./components/BaseCount
export default {components: {baseCount,},
}
/scriptstyle
/style
效果 二、组件通信
组件通信就是指组件与组件之间的数据传递。
组件的数据是独立的无法直接访问其他组件的数据。想用其他组件的数据 - 组件通信。
不同的组件关系 和 组件通信方案分类
组件关系分类
1. 父子关系 2. 非父子关系 1. 组件通信语法 2. 父传子
父组件通过 props 将数据传递给子组件
①父组件给组件添加标签添加属性的方式传值②子组件通过props进行接收③子组件渲染使用 示例 效果 什么是 prop
Prop定义组件上 注册的一些自定义属性
Prop作用向子组件传递数据
特点
可以传递任意数量的prop可以传递任意类型的prop
示例
App.vue
templatediv classappUserInfo:usernameusername:ageage:isSingleisSingle:carcar:hobbyhobby/UserInfo/div
/templatescript
import UserInfo from ./components/UserInfo.vue
export default {data() {return {username: 小帅,age: 28,isSingle: true,car: {brand: 宝马,},hobby: [篮球, 足球, 羽毛球],}},components: {UserInfo,},
}
/scriptstyle
/stylecomponents/UserInfo.vue
templatediv classuserinfoh3我是个人信息组件/h3div姓名{{ username }}/divdiv年龄{{ age }}/divdiv是否单身{{ isSingle ? 是 : 否}}/divdiv座驾{{ car.brand }}/divdiv兴趣爱好 {{ hobby.join(、) }}/div/div
/templatescript
export default {props: [username, age, isSingle, car, hobby]
}
/scriptstyle
.userinfo {width: 300px;border: 3px solid #000;padding: 20px;
}
.userinfo div {margin: 20px 10px;
}
/style
效果 props校验
思考组件的prop可以乱传吗
作用为组件的prop指定验证要求不符合要求控制台就会有错误提示 - 帮助开发者快速发现错误
语法
①类型校验
props: {校验的属性名类型 // Number String Boolean···
},
②非空校验
③默认值
④自定义校验
props: {校验的属性名 {type: 类型, // Number String Boolean···required: true, // 是否必填default: 默认值, // 默认值validator (value) {// 自定义校验逻辑return 是否通过校验}}
},
示例
App.vue
templatediv classappBaseProgress :wwidth/BaseProgress/div
/templatescript
import BaseProgress from ./components/BaseProgress.vue
export default {data() {return {width: 30,}},components: {BaseProgress,},
}
/scriptstyle
/style
components/BaseProgress.vue
templatediv classbase-progressdiv classinner :style{ width: w % }span{{ w }}%/span/div/div
/templatescript
export default {// props: [w],// 1.基础写法类型校验// props: {// // 类型校验// w: Number// }// 2.完整写法类型、是否必填、默认值、自定义校验props: {w: {type: Number,required: true,default: 0,validator (value) {// console.log(value)if(value 0 value 100) {return true} else {console.error(传入的prop w必须是0-100的数字)return false}}}}}
/scriptstyle 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
效果 prop data 单向数据流
共同点 都可以给组件提供数据
区别
data的数据是自己的 - 随便改prop的数据是外部的 - 不能直接改要遵循单向数据流
单向数据流父组件的prop的数据更新会单向的向下流动影响到子组件。这个数据流动是单向的。
示例
App.vue
templatediv classappBaseCount :countcountchangeCounthandleChange/BaseCount/div
/templatescript
import BaseCount from ./components/BaseCount.vue
export default {components:{BaseCount},data(){return {count:100}},methods:{handleChange(newCount) {this.count newCount}}
}
/scriptstyle/style
components/BaseCount.vue
templatediv classbase-countbutton clickhandleSub-/buttonspan{{ count }}/spanbutton clickhandleAdd/button/div
/templatescript
export default {// 1.自己的数据随便修改 谁的数据 谁负责// data () {// return {// count: 100,// }// },// 2.外部传过来的数据 不能随便修改// 单向数据流父组件的prop更新会单向的向下流动影响到子组件props: {count: Number },methods: {handleAdd() {this.$emit(changeCount, this.count 1)},handleSub() {this.$emit(changeCount, this.count - 1)}}}
/scriptstyle
.base-count {margin: 20px;
}
/style 3. 子传父
子组件利用 $emit 通知父组件进行修改更新
①子组件$emit发送消息②父组件给子组件添加消息监听③父组件实现处理函数。
示例
components/Son.vue
templatediv classson styleborder:3px solid #000;margin:10px我是Son组件 {{ title }}button clickchangeFn修改title/button/div
/templatescript
export default {name: Son-Child,props: [title],methods: {changeFn() {// 1. 通过$emit向父组件发送消息通知this.$emit(changeTitle, 传智教育)}}
}
/scriptstyle/style
App.vue
templatediv classapp styleborder: 3px solid #000; margin: 10px我是APP组件!-- 2. 父组件对消息进行监听 --Son :titlemyTitle changeTitlehandleChange/Son/div
/templatescript
import Son from ./components/Son.vue
export default {name: App,components: {Son,},data() {return {myTitle: 学前端就来黑马程序员,}},methods: {// 提供对应的处理函数提供逻辑handleChange(newTitle) {// console.log(newTitle)this.myTitle newTitle}}
}
/scriptstyle
/style
效果 4. 非父子拓展— event bus 事件总线
作用非父子组件之间进行简易消息传递。复杂场景 - Vuex
1. 创建一个都能访问到的事件总线空Vue实例 - utils/EventBus.js
import Vue from vue
const Bus new Vue()
export default Bus
2. A组件接收方监听Bus实例的事件
created() {Bus.$on(sendMsg, (msg) {this.msg msg})
}
3. B组件发送方触发Bus实例的事件
Bus.$emit(sendMsg, 这是一个消息) 示例
创建一个都能访问到的事件总线空Vue示例 - src/utils/EventBus.js
import Vue from vue// 1. 创建一个都能访问到的事件总线空的Vue实例
const Bus new Vue()export default Bus
src/components/BaseA.vue接收方
templatediv classbase-a我是A组件接收方p{{msg}}/p /div
/templatescript
import Bus from ../utils/EventBus
export default {data() {return {msg: ,}},created() {// 在A组件接收方进行监听Bus的事件订阅消息Bus.$on(sendMsg, (msg) {// console.log(msg)this.msg msg})},
}
/scriptstyle scoped
.base-a {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
/style
src/components/BaseB.vue发送方
templatediv classbase-bdiv我是B组件发布方/divbutton clicksendMsgFn发送消息/button/div
/templatescript
import Bus from ../utils/EventBus
export default {methods: {sendMsgFn() {// 3. B组件发送方)触发事件的方式传递参数// 任何监听了这个消息的组件都可以接收到对应的消息Bus.$emit(sendMsg, 今天天气不错适合旅游)},},
}
/scriptstyle scoped
.base-b {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
/style
src/components/BaseC.vue接收方
templatediv classbase-c我是C组件接收方p{{msg}}/p /div
/templatescript
import Bus from ../utils/EventBus
export default {data() {return {msg: ,}},created() {Bus.$on(sendMsg, (msg) {// console.log(msg)this.msg msg})},
}
/scriptstyle scoped
.base-c {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
/style
App.vue
templatediv classappBaseA/BaseABaseB/BaseBBaseC/BaseC/div
/templatescript
import BaseA from ./components/BaseA.vue
import BaseB from ./components/BaseB.vue
import BaseC from ./components/BaseC.vue
export default {components:{BaseA,BaseB,BaseC}
}
/scriptstyle/style 5. 非父子通信拓展 — provide inject
provide inject作用跨层级共享数据。 1. 父组件provide提供数据
export default {provide() {return {// 普通类型【非响应式】color: this.color,// 复杂类型 【响应式】userInfo: this.userInfo,}}
}
2. 子 / 孙组件 inject 取值使用
export default {inject: [color, userInfo],created() {console.log(this.color, this.userInfo)}
}
示例
App.vue
templatediv classapp我是APP组件button clickchange修改数据/buttonSonA/SonASonB/SonB/div
/templatescript
import SonA from ./components/SonA.vue
import SonB from ./components/SonB.vue
export default {provide() {return {// 简单类型 是非响应式的color: this.color,// 复杂类型 是响应式的userInfo: this.userInfo,}},data() {return {color: pink,userInfo: {name: zs,age: 18,},}},methods: {change() {this.color redthis.userInfo.name ls},},components: {SonA,SonB,},
}
/scriptstyle
.app {border: 3px solid #000;border-radius: 6px;margin: 10px;
}
/style
components/SonA.vue
templatediv classSonA我是SonA组件GrandSon/GrandSon/div
/templatescript
import GrandSon from ../components/GrandSon.vue
export default {components:{GrandSon}
}
/scriptstyle
.SonA {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
/style components/SonB.vue
templatediv classSonB我是SonB组件/div
/templatescript
export default {}
/scriptstyle
.SonB {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
/style
components/GrandSon.vue
templatediv classgrandSon我是GrandSon{{ color }} -{{ userInfo.name }} -{{ userInfo.age }}/div
/templatescript
export default {inject: [color, userInfo],
}
/scriptstyle
.grandSon {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 100px;
}
/style
效果 三、综合案例小黑记事本组件版
需求说明
①拆分基础组件
新建组件 - 拆分存放结构 - 导入注册使用 ②渲染待办任务
提供数据公共父组件 - 父传子传递list - v-for渲染
③添加任务
收集数据 v-model - 监听事件 - 子传父传递任务 - 父组件unshift
④删除任务
监听删除携带id - 子传父传递id - 父组件filter删除
⑤底部合计 和 清空功能
底部合计父传子传递list - 合计展示
清空功能监听点击 - 子传父通知父组件 - 父组件清空
⑥持久化存储
watch监视数据变化持久化到本地 TodoHeader.vue
template!-- 输入框 --header classheaderh1小黑记事本/h1inputkeyup.enterhandleAdd placeholder请输入任务 classnew-todo v-modeltodoName/button classadd clickhandleAdd添加任务/button/header
/templatescript
export default {data() {return {todoName: }},methods: {handleAdd() {// console.log(this.todoName)// 非空判断if(this.todoName.trim() ) {alert(任务名称不能为空)return}this.$emit(add, this.todoName)this.todoName }}
};
/scriptstyle
/style
TodoMain.vue
template!-- 列表区域 --section classmainul classtodo-listli classtodo v-for(item, index) in list :keyitem.iddiv classviewspan classindex{{ index 1 }}./span label{{ item.name }}/labelbutton classdestroy clickhandleDel(item.id)/button/div/li/ul/section
/templatescript
export default {props: {list: Array,},methods: {handleDel(id) {this.$emit(delete, id)}}
};
/scriptstyle
/style
TodoFooter.vue
template!-- 统计和清空 --footer classfooter!-- 统计 --span classtodo-count合 计:strong {{ list.length }} /strong/span!-- 清空 --button classclear-completed clickclear清空任务/button/footer
/templatescript
export default {props: {list: Array},methods: {clear() {this.$emit(clear)}}
};
/scriptstyle
/style
App.vue
template!-- 主体区域 --section idappTodoHeader addhandleAdd/TodoHeaderTodoMain :listlist deletehandleDel/TodoMainTodoFooter :listlist clearhandleClear/TodoFooter/section
/templatescript
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自己的数据自己负责// 删除功能
// 1. 监听事件监听删除的点击 携带id
// 2. 子传父将任务id传递给父组件App.vue
// 3. 进行删除 filter(自己的数据自己负责)// 底部合计
// 1. 父传子传list
// 2. 数据统计// 清空功能
// 1. 子传父通知到父组件
// 2. 由父组件进行清空// 持久化存储
// 1. watch深度监视list的变化往本地存储进入页面优先读取本地export default {data() {return {list: JSON.parse(localStorage.getItem(list)) || [{ id: 1, name: 打篮球 },{ id: 2, name: 跳绳 },{ id: 3, name: 打羽毛球 },{ id: 4, name: 游泳 },],};},components: {TodoHeader,TodoMain,TodoFooter,},watch: {// 对list进行监视list: {deep: true,handler(newValue) {localStorage.setItem(list, JSON.stringify(newValue));}},},methods: {// 删除handleDel(id) {this.list this.list.filter((res) res.id ! id);},// 添加handleAdd(todoName) {// console.log(todoName)this.list.unshift({id: new Date(),name: todoName,});},// 清空handleClear() {this.list [];},},
};
/scriptstyle
/style效果 四、进阶语法
1. v-model原理
原理v-model本质上是一个语法糖。例如应用在输入框上就是value属性 和 input事件的合写。
作用提供数据的双向绑定
①数据变化视图跟着变 :value
②视图变数据跟着变 input
注意$event用于在模板中获取事件的形参
templatediv idappinput v-modelmsg typetextinput :valuemsg inputmsg $event.target.value typetext/div
/template
示例
App.vue
templatediv classappinput typetext v-modelmsg1/br /!-- 模板中获取事件的形参 - $event 获取 --input typetext :valuemsg2 inputmsg $event.target.value/div
/templatescript
export default {data() {return {msg1: ,msg2: }},
}
/scriptstyle
/style
效果 2. v-model应用于组件
表单类组件封装 v-model简化代码
1. 表单类组件 封装
①父传子数据应该是父组件 props 传递过来的v-model 拆解 绑定数据
②子传父监听输入子传父传值给父组件修改 App.vue
templatediv classappBaseSelect :cityIdselectIdchangeIdselectId $event/BaseSelect/div
/templatescript
import BaseSelect from ./components/BaseSelect.vue
export default {data() {return {selectId: 102,}},components: {BaseSelect,},methods: {handleChange(e) {console.log(e);this.selectId e}}
}
/scriptstyle
/style
components/BaseSelect.vue
templatedivselect :valuecityId changehandleChange option value101北京/optionoption value102上海/optionoption value103武汉/optionoption value104广州/optionoption value105深圳/option/select/div
/templatescript
export default {props: {cityId: String},methods: {handleChange(e) {// console.log(e.target.value)this.$emit(changeId, e.target.value)}}
}
/scriptstyle
/style
效果 2. 父组件v-model简化代码实现子组件和父组件数据双向绑定
①子组件中props通过value接收事件触发input
②父组件中v-model给组件直接绑定数据:value input 示例
BaseSelect.vue
templatedivselect :valuevalue changehandleChange option value101北京/optionoption value102上海/optionoption value103武汉/optionoption value104广州/optionoption value105深圳/option/select/div
/templatescript
export default {props: {value: String},methods: {handleChange(e) {// console.log(e.target.value)this.$emit(input, e.target.value)}}
}
/scriptstyle
/style
App.vue
templatediv classappBaseSelect v-modelselectId/BaseSelect/div
/templatescript
import BaseSelect from ./components/BaseSelect.vue
export default {data() {return {selectId: 103,}},components: {BaseSelect,},methods: {handleChange(e) {console.log(e);this.selectId e}}
}
/scriptstyle
/style
效果 3. .sync修饰符
作用可以实现子组件和父组件数据的双向绑定简化代码
特点prop属性名可以自定义非固定为value
场景封装弹框类的基础组件visible属性true显示false因此
本质就是 :属性名 和 update:属性名 合写 示例
App.vue
templatediv classappbutton classlogoutclickisShow true退出按钮/button!-- :visible.sync 等价于 :visible update:visible --BaseDialog :visible.syncisShow/BaseDialog/div
/templatescript
import BaseDialog from ./components/BaseDialog.vue
export default {data() {return {isShow: false}},methods: {},components: {BaseDialog,},
}
/scriptstyle
/style
BaseDialog.vue
templatediv classbase-dialog-wrap v-showvisiblediv classbase-dialogdiv classtitleh3温馨提示/h3button classclose clickclosex/button/divdiv classcontentp你确认要退出本系统么/p/divdiv classfooterbutton clickclose确认/buttonbutton clickclose取消/button/div/div/div
/templatescript
export default {props: {visible: Boolean},methods: {close() {this.$emit(update:visible, false)}}
}
/scriptstyle 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 / $nextTick
作用利用ref 和 $refs可以用于获取dom元素或 组件实例
特点查找范围 - 当前组件内更精确稳定 ①获取dom
1. 目标标签 — 添加ref属性
div refchartRef我是渲染图表的容器/div
2. 恰当时机通过this.$refs.xxx获取目标标签
mounted() {console.log(this.$refs.chartRef)
),
示例
App.vue
templatediv classappdiv classbase-chart-box这是一个捣乱的盒子/divBaseChart/BaseChart/div
/templatescript
import BaseChart from ./components/BaseChart.vue
export default {components:{BaseChart}
}
/scriptstyle
.base-chart-box {width: 200px;height: 100px;
}
/style
BaseChart.vue
templatediv refmychart classbase-chart-box子组件/div
/templatescript
import * as echarts from echartsexport default {mounted() {// 基于准备好的dom初始化echarts实例// const myChart echarts.init(document.querySelector(.base-chart-box))const myChart echarts.init(this.$refs.mychart)// console.log(this.$refs.mychart)// 指定图表的配置项和数据const option {title: {text: ECharts 入门示例,},tooltip: {},xAxis: {data: [衬衫, 羊毛衫, 雪纺衫, 裤子, 高跟鞋, 袜子],},yAxis: {},series: [{name: 销量,type: bar,data: [5, 20, 36, 10, 10, 20],},],}// 使用刚指定的配置项和数据显示图表myChart.setOption(option)},
}
/scriptstyle scoped
.base-chart-box {width: 400px;height: 300px;border: 3px solid #000;border-radius: 6px;
}
/style
效果 ②获取组件
1.目标组件 — 添加ref属性
BaseForm refbaseForm/BaseForm
2. 恰当时机通过this.$refs.xxx获取目标组件就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
示例
BaseForm.vue
templatediv classappdiv账号: input v-modelusername typetext/divdiv密码: input v-modelpassword typetext/div/div
/templatescript
export default {data() {return {username: admin,password: 123456,}},methods: {// 收集表单数据返回一个对象getValues() {return {username: this.username,password: this.password}},// 重置表单resetValues() {this.username this.password }}
}
/scriptstyle scoped
.app {border: 2px solid #ccc;padding: 10px;
}
.app div{margin: 10px 0;
}
.app div button{margin-right: 8px;
}
/style
App.vue
templatediv classappBaseForm refbaseForm/BaseFormbutton clickhandleGet获取数据/buttonbutton clickhandleReset重置数据/button/div
/templatescript
import BaseForm from ./components/BaseForm.vue
export default {data() {return {}},components: {BaseForm,},methods: {handleGet() {console.log(this.$refs.baseForm.getValues())},handleReset() {this.$refs.baseForm.resetValues()}}
}
/scriptstyle
/style
效果 5. Vue异步更新、$nextTick
$nextTick等DOM更新后才会触发执行此方法里的函数体
语法this.$nextTick(函数体)
this.$nextTick(() {// 业务逻辑
})
需求编辑标题编辑框自动聚焦 1. 点击编辑显示编辑框
2. 让编辑框立刻获取焦点
this.isShowEdit true // 显示输入框
this.$refs.inp.focus() // 获取焦点
问题”显示之后“立刻获取焦点是不能成功的
原因Vue是异步更新DOM提升性能
this.$nextTick(() {this.$refs.inp.focus()
})
示例代码
App.vue
templatediv classappdiv v-ifisShowEditinput typetext v-modeleditValue refinp /button确认/button/divdiv v-elsespan{{ title }}/spanbutton clickhandleEdit编辑/button/div/div
/templatescript
export default {data() {return {title: 大标题,isShowEdit: false,editValue: ,}},methods: {handleEdit() {// 1. 显示输入框(异步DOM更新)this.isShowEdit true// 2. 让输入框 获取焦点($nextTick等DOM更新完 立刻去执行指定的函数体)this.$nextTick(() {this.$refs.inp.focus()})}},
}
/scriptstyle
/style
效果点击编辑后显示输入框并获取焦点