工程化开发和脚手架
后续只需进行第3、4步骤
第四步中的 “serve” 是来自于 package.json 中的
项目目录介绍和运行流程
index.js
main.js
//文件核心作用:导入 App.vue ,基于 App.vue 创建结构渲染 index.html
//1.导入 Vue 核心包
import Vue from 'vue'
//2.导入 App.vue 根组件
import App from './App.vue'
//提示:当前处于什么环境(生产环境 / 开发环境)
Vue.config.productionTip = false
// Vue 实例化,提供 render 方法 作用:基于 App.vue 创建结构渲染 index.html
new Vue({
// el:'#app',作用:和 $mount('#app') 作用一致,用于指定 Vue 所管理容器
//render: h => h(App), 简写
render:(createElement) => {
// 基于 App 创建元素结构 完整写法
return createElement(App)
}
}).$mount('#app')
小结
组件化开发和根组件
组件化
组件化:
- 含义:一个页面可以拆分成一个个组件,每个组件都有自己独立的结构、样式、行为。
- 好处:便于维护,利于复用 —— 提升开发效率。
- 组件分类:普通组件、根组件
根组件
根组件:整个应用最上层的组件,包括所有普通的小组件
一个根组件App.vue 文件(单文件组件)的三个组成部分
- 三部分组成:
- template:结构(有且只能一个根元素)
- script:行为(js 逻辑)
- style:样式(可支持 less)
- 让组件支持 less
- style标签,lang="less" 开启 less 功能
普通组件的注册使用
局部注册
<template>
<div class="App">
<!--头部组件-->
<IopHeader></IopHeader>
<!--主体组件-->
<!--底部组件-->
</div>
</template>
<script>
import IopHeader from './components/IopHeader.vue'
export default {
components:{
//组件名:组件对象
//IopHeader:IopHeader
//如果组件名和组件对象同名,可以直接写:
IopHeader
}
}
</script>
<style>
.App{
width: 600px;
height: 700px;
background-color: #8cbcf3;
margin: 0 auto;
padding: 20px;
}
</style>
<template>
<div class="iop-header">
我是iop-header
</div>
</template>
<script>
export default {
}
</script>
<style>
.iop-header{
height: 100px;
line-height: 100px;
text-align: center;
font-size: 30px;
background-color: bisque;
color: aquamarine;
}
</style>
全局注册
<template>
<button class="iop-button">通用按钮</button>
</template>
<script>
export default {
}
</script>
<style>
.iop-button{
height: 50px;
line-height: 50px;
padding: 0 20px;
background-color: #c27b7b;
border-radius: 5px;
color: white;
border: none;
vertical-align: middle;
cursor: pointer;
}
</style>
import Vue from 'vue'
import App from './App.vue'
//编写导入的代码,往代码的顶部去编写(规范)
import IopButton from './components/IopButton.vue'
Vue.config.productionTip = false
//进行全局注册 —— 在所有的组件范围内都能直接使用
//Vue.component(组件名,组件对象)
Vue.component('IopButton',IopButton)
new Vue({
render: h => h(App),
}).$mount('#app')
<div class="iop-header">
我是iop-header
<IopButton></IopButton>
</div>
</template>
<script>
export default {
}
</script>
<style>
.iop-header{
height: 100px;
line-height: 100px;
text-align: center;
font-size: 30px;
background-color: bisque;
color: rgb(112, 155, 141);
}
</style>
组件拆分的页面开发思路:
scoped 样式冲突
默认情况下:写在组件中的样式会全局生效 → 因此很容易造成多个组件之间的样式冲突问题。
- 全局样式:默认组件中的样式都会作用到全局
- 局部样式:可以给组件加上scoped属性,让样式只作用于当前组件
原理:
- 当前组件内标签都被添加 data-v-hash 值的属性,比如
data-v-5f6a9d56- css选择器都被添加 [data-v-hash值]的属性选择器
最终效果:必须是当前组件的元素,才有这个自定义属性,才会被这个样式作用
data 是一个函数
一个组件的data 选项必须是一个函数。→ 保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会新执行一次 data函数,得到一个新对象。
data: function () {
return {
count: 100,
}
}
<template>
<div class="base-count">
<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
</div>
</template>
<script>
export default {
data: function () {
return {
count: 100,
}
},
}
</script>
<style scoped>
.base-count {
margin: 20px;
}
</style>
组件通信
组件通信就是指 组件与组件 之间的 数据传递
- 组件的数据是独立的,无法直接访问其他组件的数据
- 想用其他组件的数据 → 组件通信
组件关系的分类:
- 父子关系
- 非父子关系
组件通信解决方案
父子关系
父 → 子
- 如果子组件中需要用到父组件的数据,那么就需要 父组件通过props将数据传递给子组件
- 子组件利用
$emit,通知父组件修改更新
如果想把父组件
‘学前端,来黑马!’这个 title 用在子组件中,目前理论上这样是实现不了的
正确的流程如下:
- 父组件中的子组件添加自定义属性并传值
- 在子组件的vue中通过props接受冒号的内容
- 直接在子组件上面模板中使用
子 → 父
如果想通过点击按钮来修改上面的 title
<div class="son" style="border: 3px solid #000; margin: 10px">
我是 Son 组件 {{ title }}
<button @click="changeFn">修改title</button>
正确的流程如下:
- 先在子组件的
template里写好你的按钮,并给它注册了一个点击事件,并在下面写了一个methods方法。但是你此时在methods里直接写是没有用的<button @click="changeFn">修改title</button>
- 通过this.$emit() 向父组件发送通知
methods: { changeFn() { // 通过this.$emit() 向父组件发送通知 this.$emit('changTitle','传智教育') }, },
- 父组件为了收到消息,需要在子组件上绑上监听
<Son :title="myTitle" @changTitle="handleChange"></Son>
- 在父组件中提供对应的处理函数,提供逻辑
props 详解
-
定义:组件上注册的一些自定义属性
-
作用:向子组件传递数据
-
特点:可以传递任意数量的prop
-
校验:
- 类型校验
- 非空校验
- 默认值
- 自定义
// 类型校验
props:{
校验的属性名:类型 // Number、String、Boolean、Function...
}
// 完整校验
props:{
校验的属性名:{
type: 类型, // Number String Boolean Function...
required:true, // 是否必填
default:默认值, //设置默认值
validator (value) {
//自定义校验逻辑
参数为传过来的值
return true/false
}
}
}
prop & data、单向数据流
- 共同点:都可以给组件提供数据。
- 区别:
data 的数据是自己的 → 随便改
prop 的数据是外部的 → 不能直接改,要遵循单向数据流
单向数据流:父级prop的数据更新,会向下流动,影响子组件。
v-model 详解
原理: v-model 本质上是一个语法糖。例如应用在输入框上,就是 value 属性和 input 事件的合写
作用:提供数据的双向绑定
- 数据变,视图跟着变
:value - 视图变,数据跟着变
@input
注意:$event用于在模板中,获取事件的形参
表单类组件封装
- 父传子:
:value数据应该是父组件props传递过来的,v-model拆解绑定数据 - 子传父:
@input监听输入,子传父传值给父组件修改
<template>
<div class="app">
<BaseSelect
:cityId="selectId"
@change="selectId=$event"
></BaseSelect>
</div>
</template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
data() {
return {
selectId: '102',
}
},
components: {
BaseSelect,
},
}
</script>
<style>
</style>
<template>
<div>
<select :value="cityId" @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: {
cityId: String,
},
methods: {
selectCity(e) {
this.$emit('changeId', e.target.value)
},
},
}
</script>
<style>
</style>
v-model简化代码
- 子组件中:
props通过value接收,事件触发input - 父组件中:
v-model给组件直接绑数据
sync修饰符
-
作用:可以实现子组件与父组件数据的双向绑定,简化代码
-
特点:
prop属性名,可以自定义,非固定为value -
场景:封装弹框类的基础组件,
visible属性 ——true显示false隐藏 -
本质:属性名 和
@update:属性名合写
<BaseDialog :visible.sync="isShow" />
// <BaseDialog :visible="isShow" @update:visible="isShow = $event"/>
ref 和 $refs 获取 dom 和组件
用于获取 dom元素 ,或 组件实例
-
获取dom
-
目标标签添加 ref属性
<div ref="chartRef"> </div> -
恰当事件,通过 this.$refs.xxx 获取目标标签
mounted () { console.log(this.$refs.chartRef) }
-
-
获取组件实例
-
目标组件添加ref属性
<BaseForm ref="baseForm"> </BaseForm> -
适当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
-
Vue 异步更新和 $nextTick
需求:点击编辑,编辑标题,编辑框自动聚焦
$nextTick :等dom更新后,才会触发执行此方法里的函数体
- 语法:
this.$nextTick(函数体)
this.$nextTick(() => {
代码
})
需求:
- 点击编辑,显示编辑框
- 让编辑框,立刻获取焦点
this.$nextTick(() => {
this.$refs.inp.focus ()
})