文章目录
Vue总结(一)
1、脚手架
1.1 脚手架文件结构
├── node_modules: 存放依赖文件
├── public: 公共资源文件夹
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src: 源码目录
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
1.2 关于不同版本的Vue
- vue.js与vue.runtime.xxx.js的区别:
- vue.js是完整版的Vue,包含:核心功能 + 模板解析器。
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template这个配置项,需要使用render函数接收到的createElement函数去指定具体内容。
1.3 vue.config.js配置文件
- 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
- 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
2、组件
组件化编码流程(通用)
实现静态组件:
- 页面区域划分,抽取组件,使用组件实现静态页面效果
展示动态数据:
- 数据的类型、名称是什么?
- 数据保存在哪个组件?
- 交互:
- 从绑定事件监听开始
2.1 ref属性
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
- 使用方式:
- 打标识:
<h1 ref="xxx">.....</h1>
或<School ref="xxx"></School>
- 获取:
this.$refs.xxx
- 打标识:
示例代码
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<School ref="sch"/> <!-- 给School标签元素打标识,相当于id -->
</div>
</template>
<script>
//引入School组件
import School from './components/School'
export default {
name:'App',
components:{School},
data() {
return {
msg:'欢迎学习Vue!'
}
},
methods: {
showDOM(){
//获取元素或对象
console.log(this.$refs.title) //真实DOM元素
console.log(this.$refs.btn) //真实DOM元素
console.log(this.$refs.sch) //School组件的实例对象(vc)
}
},
}
</script>
2.2 props配置项
-
功能:让组件接收外部传过来的数据
-
传递数据:
<Demo name="xxx"/>
-
接收数据:
-
第一种方式(只接收):
props:['name']
-
第二种方式(限制类型):
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值):
-
示例代码
APP.vue
<template>
<div>
<!-- 传值,要使用props进行接收 -->
<Student name="李四" sex="女" :age="18"/>
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{Student}
}
</script>
School.vue
<template>
<div>
<h1>{{msg}}</h1>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>学生年龄:{{myAge+1}}</h2>
<button @click="updateAge">尝试修改收到的年龄</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
console.log(this)
return {
msg:'哈哈哈',
myAge:this.age
}
},
methods: {
updateAge(){
this.myAge++ //修改复制后的数据,不修改原始数据
}
},
//props简单声明接收
// props:['name','age','sex']
//接收的同时对数据进行类型限制
/* props:{
name:String,
age:Number,
sex:String
} */
//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, //name是必要的
},
age:{
type:Number,
default:99 //默认值
},
sex:{
type:String,
required:true
}
}
}
</script>
再如:
//接收方式
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
2.3 组件的自定义事件
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:
- A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
-
绑定自定义事件:
-
第一种方式,在父组件中:
<Demo @shijian="test"/>
或<Demo v-on:shijian="test"/>
-
第二种方式,在父组件中:
<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on('shijian',this.test) }
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。
-
-
触发自定义事件:
this.$emit('shijian',数据)
-
解绑自定义事件
this.$off('shijian')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
this.$refs.xxx.$on('shijian',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
功能需求:
子组件给父组件传数据,将子组件中的数据,展示到父组件中。
props配置项实现
App.vue
<template>
<div class="app">
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getXueXiaoName="getSchoolName"/>
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components:{School},
data() {
return {
msg:'你好啊!',
}
},
methods: {
getSchoolName(name) {
console.log('App收到了学校名:',name)
}
},
}
</script>
<style scoped>
.app{
background-color: gray;
padding: 5px;
}
</style>
School.vue
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
name:'School',
props:['getXueXiaoName'], //声明接收父组件传过来的getXueXiaoName函数
data() {
return {
name:'北京大学',
address:'北京',
}
},
methods: {
sendSchoolName() {
this.getSchoolName(this.name)
}
},
}
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
组件自定义事件实现
App.vue
<template>
<div class="app">
<h1>{{msg}},学生姓名是:{{studentName}}</h1>
<!--通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on)-->
<!-- <Student @demo="m1"/> -->
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref,打标识)-->
<Student ref="student" @click.native="show"/>
</div>
</template>
<script>
import Student from './components/Student'
import School from './components/School'
export default {
name:'App',
components:{School,Student},
data() {
return {
msg:'你好啊!',
studentName:''
}
},
methods: {
getStudentName(name,...params) { //接收一个或多个参数
console.log('App收到了学生名:', name, params)
this.studentName = name
},
m1() {
console.log('demo事件被触发了!')
},
show(){
alert(123)
}
},
mounted() {
this.$refs.student.$on('shijian01',this.getStudentName) //绑定自定义事件(绑定事件监听)
// this.$refs.student.$once('demo',this.m1)
//绑定自定义事件(一次性),m1是demo事件触发时对应的处理函数(demo事件触发时,会回调m1函数)
},
}
</script>
<style scoped>
.app{
background-color: gray;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>当前求和为:{{number}}</h2>
<button @click="add">点我number++</button>
<button @click="sendStudentlName">把学生名给App</button>
<button @click="unbind">解绑shijian01事件</button>
<button @click="death">销毁当前Student组件的实例(vc)</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'张三',
sex:'男',
number:0
}
},
methods: {
add(){
console.log('add回调被调用了')
this.number++
},
sendStudentlName() {
//触发Student组件实例身上的shijian01事件
this.$emit('shijian01',this.name,666,888,900) //自定义事件,传递多个参数
// this.$emit('demo')
// this.$emit('click')
},
unbind(){
this.$off('shijian01') //解绑一个自定义事件
// this.$off(['shijian01','demo']) //解绑多个自定义事件
// this.$off() //解绑所有的自定义事件
},
death(){
this.$destroy() //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效。
}
},
}
</script>
<style lang="less" scoped>
.student{
background-color: pink;
padding: 5px;
margin-top: 30px;
}
</style>
3、插槽
父组件向子组件传递带数据的标签,当一个组件有不确定的结构时, 就需要使用
slot 技术。注意:插槽内容是在父组件(插槽使用者)中编译后, 再传递给子组件的。
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
分类:默认插槽、具名插槽、作用域插槽
3.1 默认插槽
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- slot标签,定义一个插槽(占个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'] //声明接收组件使用者传过来的title属性值
}
</script>
<style scoped>
.category {
background-color: skyblue;
width: 200px;
height: 300px;
}
h3 {
text-align: center;
background-color: orange;
}
video {
width: 100%;
}
img {
width: 100%;
}
</style>
App.vue(Category组件的使用者)
<template>
<div class="container">
<!-- title使用props接收 -->
<Category title="美食" >
<!-- img的内容是要放在插槽位置的东西 -->
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏" >
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
}
},
}
</script>
3.2 具名插槽
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
App.vue
<template>
<div class="container">
<Category title="美食" >
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.meishi.com">更多美食</a>
</Category>
<Category title="游戏" >
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<div class="foot" slot="footer">
<a href="http://www.yx.com">单机游戏</a>
<a href="http://www.yx.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.dianying.com">经典</a>
<a href="http://www.dianying.com">热门</a>
<a href="http://www.dianying.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
}
},
}
</script>
3.3 作用域插槽
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 将组件自身的一些东西(如:games),传给组件的使用者 -->
<slot :games="games" msg="hello">我是默认的一些内容</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
}
},
}
</script>
<style scoped>
.category {
background-color: skyblue;
width: 200px;
height: 300px;
}
h3 {
text-align: center;
background-color: orange;
}
video {
width: 100%;
}
img{
width: 100%;
}
</style>
App.vue
<template>
<div class="container">
<Category title="游戏">
<!-- duixiang接收子组件传过来的东西,组件使用者(父组件)利用这些东西做不同的实现 -->
<template scope="duixiang">
<ul>
<li v-for="(g,index) in duixiang.games" :key="index">{{g}}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<!-- 接收子组件传过来的东西,组件使用者(父组件)利用这些东西做不同的实现 -->
<template scope="{games}"> <!-- 解构赋值 -->
<ol>
<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<!-- 接收子组件传过来的东西,组件使用者(父组件)利用这些东西做不同的实现 -->
<template slot-scope="{games}"> <!-- 解构赋值 -->
<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
}
</script>
<style scoped>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)。