设计模式
MVC
代表:jsp+severlet+Javabean
模型(Model)-视图(View)-控制器(Controller)
- 模型(Model):业务规则/数据模型
- 视图(View):用户交互页面即页面显示逻辑
- 控制器(Controller):响应用户与页面的操作
MVVM
可以说是MVC的改进版。Vue的设计并没有完全遵循MVVM的思想
模型(Model)-视图(View)-ViewModel
- 模型(Model):业务规则/数据模型
- 视图(View):用户界面
- View-Model:监听Model中数据的变化并且控制图层(View)更新
优点:
- 低耦合
- 独立开发
- 可重用性
- 可测试
生命周期
生命周期是Vue十分重要的一个点,上图:
| 钩子 | 生效 |
|---|---|
| beforeCreate | 创建实例前 |
| create | 创建实例后 |
| beforeMount | 挂载前 |
| mount | 挂载后 |
| beforeUpdate | 更新前 |
| update | 更新后 |
| beforeDestroy | 销毁前 |
| Destroyed | 销毁后 |
| activated | 被keep-alive缓存的组件激活时 |
| deactivated | 被keep-alive缓存的组件失活时 |
| errorCaptured | 捕获后代组件的错误 |
生命周期中的this指向vm或组件实例对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta n="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>
<div id="app">
<h2>{{n}}</h2>
<button @click="add">+</button>
<button @click="des">销毁</button>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
n:1
},
methods:{
add(){
this.n++;
},
des(){
this.$destroy()
}
},
beforeCreate() {
console.log('----beforeCreate----')
console.log(this.n)
console.log(this.$el)
},
created() {
console.log('----created----')
console.log(this.n)
console.log(this.$el)
},
beforeMount() {
console.log('----beforeMount----')
console.log(this.n)
console.log(this.$el)
},
mounted() {
console.log('----mounted----')
console.log(this.n)
console.log(this.$el)
},
beforeUpdate(){
console.log('----beforeUpdate----')
console.log(this.n)
console.log(this.$el)
},
updated(){
console.log('----updated----')
console.log(this.n)
console.log(this.$el)
},
beforeDestroy(){
console.log('----beforeDestroy----')
console.log(this.n)
console.log(this.$el)
},
destroyed(){
console.log('----destoryed----')
console.log(this.n)
console.log(this.$el)
}
})
</script>
</body>
</html>
数据与资源
数据与资源在Vue实例声明
| 数据 | 作用 |
|---|---|
| data | Vue实例的数据对象 |
| methods | Vue实例的方法,方法中的this自动绑定为Vue实例 |
| watch | 监听:监听的属性变化时,调用回调函数 |
| computed | 计算属性:要用的属性不存在,通过计算得来 |
| props | 用于接收来自父组件的数据 |
| propsData | 创建实例时传递props |
| 资源 | 作用 |
|---|---|
| directives | 包含Vue实例可用指令的哈希表(自定义指令) |
| filters | 包含Vue实例可用过滤器的哈希表(只应用在双花号插值或v-bind) |
| components | 包含Vue实例可用组件的哈希表 |
data
- 形式可以是对象也可以是方法
<div id="app">
{{name}}
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
/*对象或函数均可
data:{
name:'WDY',
}, */
data:function(){
return{
name:"WDY"
}
}
})
</script>
- 组件的data必须是一个函数:防止两个组件的数据产生污染。如果都是对象的话,会在合并的时候,指向同一个地址。如果是函数的时候,合并的时候调用,会产生两个空间。
methods
- 不要使用箭头函数来定义methods函数,会改变this指向
<div id="app">
{{n}}
<button @click="add">+</button>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'WDY',
n:1
},
methods:{
add(){
this.n++
}
}
})
</script>
watch与vm.$watch
| 选项 | 作用 |
|---|---|
| immediate | 是否立即触发回调 |
| deep | 监听对象内部值的变化:number{a:10}监听number,当a发生变化时,也会被监听到 |
-
当被观察的属性变化时,回调函数自动调用
-
必须存在被观察的对象
-
不支持缓存
-
支持异步监听
-
两种写法
<div id="app"> {{n}} <button @click="addN">n+</button> <br> {{number.a}} <button @click="addA">a+</button> </div> <script type="text/javascript" > Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 const app = new Vue({ el:'#app', data:{ name:'WDY', n:1, number:{ a:10 } }, methods:{ addN(){ this.n++ }, addA(){ this.number.a++ } }, watch:{ n:{ //刚进页面就触发 immediate:true, handler(newValue,oldValue){ console.log(`n从${oldValue}变为${newValue}`) } }, 'number.a':{ handler(newValue,oldValue){ console.log(`a从${oldValue}变为${newValue}`) } } } }) </script> ``` - ```html <div id="app"> {{n}} <button @click="addN">n+</button> <br> {{number.a}} <button @click="addA">a+</button> </div> <script type="text/javascript" > Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 const app = new Vue({ el:'#app', data:{ name:'WDY', n:1, number:{ a:10 } }, methods:{ addN(){ this.n++ }, addA(){ this.number.a++ } }, }) app.$watch('n',{ //刚进入页面就触发 immediate:true, handler(newValue,oldValue){ console.log(`n的值从${oldValue}变为${newValue}`) } }) app.$watch('number.a',{ handler(newValue,oldValue){ console.log(`a的值从${oldValue}变为${newValue}`) } }) </script> ```
computed
- 要用的属性不存在
- 有缓存,只有在响应式依赖发生改变时,它们才会重新求值
- 不支持异步,在Computed中有异步操作时,无法监听数据变化
<div id="app">
<h3>{{name}}</h3>
<h3>computed {{reverseName}}</h3>
<h3>{{changeName}}</h3>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'WDY',
n:1
},
computed:{
reverseName:function(){
return this.name.split('').reverse().join('')
},
changeName:{
get(){
console.log('调用get')
return `${this.n} --- ${this.name}`
},
set(value){
console.log('调用set')
const newName = value.split(' ')
this.name = newName[0]
this.n = newName[newName.length - 1]
}
}
}
})
</script>
props
- 用于父组件给子组件传递数据
<script type="text/javascript" >
const app = new Vue({
el:'#app',
props:['xxx','yyy','zzz'],
props:{
xxx:String,
yyy:Number,
zzz:Boolean,
}
})
</script>
directives
- 指令定义时不加v-,使用时要加v-
- 不要使用驼峰命名,使用-命名
<div id="app">
<div v-add="age"></div>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
age:10
},
directives:{
add:function(el,binding,vode){
console.log(binding)
el.innerHTML = binding.value+1
}
}
})
</script>
filters
与computed,methods不同,filters不会对数据进行修改而只是过滤。其余两者会修改数据
以下栗子为局部过滤器,全局过滤器请见:常用全局API - Vue.fliter
div id="app">
<div v-for="per in person">
我是:{{per.name | pickPerson}}
</div>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
person:[
{name:'WDY',age:10000},
{name:"wdy",age:1000}
]
},
filters:{
pickPerson(value,str='WDY'){
if(value === 'WDY')
return value
}
}
})
</script>
components
更详细内容请见:组件
<div id="app">
<my-component></my-component>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const myComponent = Vue.extend({
template:`<div><h3>My name is {{name}}</h3></div>`,
data(){
return {
name:"WDY"
}
},
})
Vue.component('myComponent',myComponent)
const app = new Vue({
el:'#app',
data:{
},
components:{
myComponent
}
})
</script>
常用指令
| 指令 | 作用 |
|---|---|
| v-text | 解析文本 |
| v-html | 解析HTML |
| v-show | 根据条件展示元素 |
| v-if | 根据条件展示元素 |
| v-else-if | 根据条件展示元素 |
| v-else | 根据条件展示元素 |
| v-for | 多次渲染元素 |
| v-on /@ | 绑定事件监听器 |
| v-bind /: | 数据绑定 单向 |
| v-model | 数据绑定 双向 |
| v-slot /# | 提供具名插槽或需要接收props的插槽 |
| v-once | 只渲染元素和组件一次,之后元素/组件及其所有子节点将视为静态内容并跳过 |
| v-cloak | 隐藏未编译的标签直至编译完成,与CSS搭配使用 |
| v-pre | 跳过元素和子元素的编译过程 |
v-text
无论内容是什么,只会解析为文本:
- v-text会替换节点中内容,而{{}}不会:
可以很清楚地看到,span中的“我要被替换了”变成了WDY。
<div id="app">
{{name}}
<br>
<span v-text = "name">我要被替换了</span>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
name:'WDY'
}
})
</script>
v-html
<div id="app">
{{name}}
<br>
<span v-html = "name"></span>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
name:"<span style='color:pink'>我是粉色</span>"
}
})
</script>
v-show
- 不支持template和v-else。
- 不展示时,DOM元素未被移除,仅是隐藏。
- 适用于切换频率较高的一些场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>
<div id="app">
<h1 v-show="show">{{name}}</h1>
//也可以是表达式
<h1 v-show="1 === 1">{{name}}</h1>
<h1 v-show="noShow">{{name}}</h1>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
name:"WDY",
show:true,
noShow:false
}
})
</script>
</body>
</html>
v-if,v-else-if,v-else
- 不展示DOM的元素直接被移除。
- 适用于切换频率较低的场景。
- 调用addIfCondition方法(源码),生成vnode的时候会忽略节点,render不会渲染。
<div id="app">
<div v-if="n < 20">我是:v-if</div>
<div v-else-if="n < 30">我是:v-else-if</div>
<div v-else-if="n < 40">我是:v-else-if</div>
<div v-else>我是:v-else</div>
//template中使用
<template v-if="n === 10">
{{name}}
</template>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
name:"WDY",
n:10
}
})
</script>
v-for
- 遍历展示
<div id="app">
<h3 v-for="item in items">
{{item.name}}
</h3>
<!-- <h3 v-for="(item,index) in items"> -->
<h3 v-for="(item,index) of items">
{{index}} --- {{item.name}}
</h3>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
name:"WDY",
items:[
{id:001,name:"WDY"},
{id:002,name:"wdy"},
{id:003,name:"WdY"}
]
}
})
</script>
v-on
监听DOM事件
| 修饰符 | 作用 | |
|---|---|---|
| .stop | 调用event.stopPropagation() 停止冒泡/传播 | |
| .prevent | 调用event.preventDefault() 阻止默认行为:阻止默认事件(超链接跳转) | |
| .capture | 添加事件侦听器,开启捕获模式 | |
| .self | 只当事件是从侦听器绑定的元素本身触发时,才触发回调 | |
| .{keyCode | keyAlias} | 只当事件从特定建触发时才触发回调 |
| .native | 监听组件根元素的原生事件 | |
| .once | 只触发一次回调 | |
| .left | 只当点击鼠标左键时触发 | |
| .right | 只当点击鼠标右键时触发 | |
| .middle | 只当点击鼠标中键时触发 | |
| .passive | 以{passive:true}模式添加侦听器 |
<div id="app">
<a href="www.baidu.com" @click.prevent="noJump">点我我也不跳转</a>
<div @click="nobuble">
<button @click.stop="nobuble">阻止冒泡</button>
</div>
<button @click.once="justOnce">只会触发一次</button>
<div style="background-color: pink;width: 50px;height: 20px;" @click.capture="capture('pink')">
<div style="background-color: purple;width: 20px;height: 20px;" @click="capture('purple')"></div>
</div>
</div>
<script type="text/javascript" >
const app = new Vue({
el:'#app',
data:{
n:1
},
methods:{
noJump(){
console.log('我被点击了,但我不能跳转')
},
nobuble(){
console.log('阻止冒泡即不重复')
},
justOnce(){
console.log('不管点我多少次,我只执行一次')
},
capture(color){
console.log(color)
}
},
})
</script>
-
当点击超链接时,正常超链接会跳转到链接页面,而加了.stop,链接不会跳转:阻止默认事件
-
当点击阻止冒泡时,普通情况应该输出两次“阻止冒泡而不重复”,而加了.prevent,不会冒泡:阻止冒泡事件
-
当多次点击只会触发一次时,只有第一次会触发输出,加了.once:只触发一次
-
当点击紫色块时:
- 事件冒泡:由子元素冒泡向父元素 自底向上 先输出purple再输出pink
- 事件捕获:由父元素捕获至子元素 先输出pink在输出purple
v-bind
动态更新HTML元素上的属性
- 单向数据绑定
- 动态地绑定一个或多个attribute或prop
<div id="app">
<input type="text" :value="name">
<!--两种绑定样式的方法均可-->
<h3 :style="myStyle">WDY</h3>
<h3 :style="{color:'pink'}">WDY</h3>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'WDY',
myStyle:{
color:'pink',
}
},
methods:{
},
})
</script>
v-model
- 只能在表单类上使用:input select textarea components
- 双向数据绑定
- 下面栗子一开始并未指定变量值,而是双向绑定的结果
<div id="app">
<input type="text" v-model="name">
我的名字是:{{name}}
<br>
<input type="checkbox" :value="isChecked" @click="changeChecked">
我的选定状态是:{{isChecked}}
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'',
isChecked:''
},
methods:{
changeChecked(){
this.isChecked = !this.isChecked
}
},
})
</script>
v-slot
v-slot 只能添加到 template 或自定义组件上
插槽就是子组件中的提供给父组件使用的一个占位符,插槽显不显示,怎样显示由父组件控制,在哪里显示由子组件控制:
默认(匿名)插槽
-
vue2.6.0以前: 我将以非单文件组件的形式展示
- 如果不在child中添加,我是插槽这句话将不会显示,组件只会展示其template部分,如果要展示别的内容,则需开辟空间/预留位置,即插槽。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta n="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script type="text/javascript" src="../vue.js"></script> </head> <body> <div id="app"> <father style="background-color:skyblue;width: 200px; height: 200px;"> </father> </div> <script> const child = Vue.extend({ template:`<div> 我是cDiv <slot></slot> </div>`, data(){ return{ } } }) const father = Vue.extend({ template:`<div> 我是fDiv <child style="background-color: pink;width: 150px; height: 150px;"> 我是插槽 </child> </div>`, data(){ return{ } }, components:{ child } }) Vue.component('father',father) Vue.component('child',child) const app = new Vue({ el:'#app', components:{ father, child } }) </script> </body> </html>
- vue2.6.0以后:v-slot提供具名插槽或需要接受prop的插槽。
具名插槽
-
vue2.6.0以前: 我将以非单文件组件的形式展示:
- 一般用于搭建一个通用模板
<div id="app">
<father style="background-color:skyblue;width: 200px; height: 200px;">
</father>
</div>
<script>
const child = Vue.extend({
template:`<div>
我是cDiv
<slot name='purple'></slot>
<slot></slot>
<slot name='orange'></slot>
</div>`,
data(){
return{
}
}
})
const father = Vue.extend({
template:`<div>
我是fDiv
<child style="background-color: pink;width: 150px; height: 150px;" >
<template slot='purple'>我是紫色</template>
我是默认
<template slot='orange'>我是橙色</template>
</child>
</div>`,
data(){
return{
}
},
components:{
child
}
})
Vue.component('father',father)
Vue.component('child',child)
const app = new Vue({
el:'#app',
components:{
father,
child
}
})
</script>
-
vue2.6.0以前:v-slot 我将以非单文件组件的形式展示:
<div id="app"> <father style="background-color:skyblue;width: 200px; height: 200px;"> </father> </div> <script> const child = Vue.extend({ template:`<div> 我是cDiv <slot name='purple'></slot> <slot></slot> <slot name='orange'></slot> </div>`, data(){ return{ } } }) const father = Vue.extend({ template:`<div> 我是fDiv <child style="background-color: pink;width: 150px; height: 150px;"> <template v-slot:purple>我是紫色</template> <template v-slot:default>我是默认</template> <template #orange>我是橙色</template> </child> </div>`, data(){ return{ } }, components:{ child } }) Vue.component('father',father) Vue.component('child',child) const app = new Vue({ el:'#app', components:{ father, child } }) </script>
作用域插槽
-
vue2.6.0以前: 我将以非单文件组件的形式展示:
- 下面栗子中:在子组件中绑定了name,在父组件中的插槽中可以通过子组件传递的数据进行插槽中数据的修改
<div id="app"> <father style="background-color:skyblue;width: 200px; height: 200px;"> </father> </div> <script> const child = Vue.extend({ template:`<div> 我是cDiv <slot :person='person'>{{name}}</slot> </div>`, data(){ return{ person:[ {name:'WDY',age:1000}, {name:'wdy',age:999} ] } } }) const father = Vue.extend({ template:`<div> 我是fDiv <child style="background-color: pink;width: 150px; height: 150px;"> <template slot-scope='person'> <div v-for='per in person'>{{per}}</div> </template> </child> </div>`, data(){ return{ } }, components:{ child } }) Vue.component('father',father) Vue.component('child',child) const app = new Vue({ el:'#app', components:{ father, child } }) </script>
-
vue2.6.0以后:v-slot 我将以非单文件组件的形式展示:
<div id="app"> <father style="background-color:skyblue;width: 200px; height: 200px;"> </father> </div> <script> const child = Vue.extend({ template:`<div> 我是cDiv <slot :person='person'>{{name}}</slot> </div>`, data(){ return{ person:[ {name:'WDY',age:1000}, {name:'wdy',age:999} ] } } }) const father = Vue.extend({ template:`<div> 我是fDiv <child style="background-color: pink;width: 150px; height: 150px;"> <template v-slot='person'> <div v-for='per in person'>{{per}}</div> </template> </child> </div>`, data(){ return{ } }, components:{ child } }) Vue.component('father',father) Vue.component('child',child) const app = new Vue({ el:'#app', components:{ father, child } }) </script>
v-once
- 用于优化更新性能
<div id="app">
<!--只有第二个n会改变-->
<h3 v-once>{{n}}</h3>
<h3>{{n}}</h3>
<button @click="n++">加一</button>
<!--父类的子节点也会被视为静态-->
<div v-once>
<h3>{{n}}</h3>
<button @click="n++">加一</button>
</div>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
n:1
},
})
</script>
v-cloak
- 当编译过程较慢时,下面代码会出现页面直接呈现{{name}}的情况,而v-cloak可以隐藏未编译完成的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta n="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="../vue.js"></script>
<style>
[v-clock]{
display: none !important;
}
</style>
</head>
<body>
<div id="app" v-cloak>
{{name}}
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'WDY'
},
})
</script>
</body>
</html>
v-pre
-
会跳过编译过程:
- 当不含指令节点时会加快编译速度
- 当含指令节点时不会实现指令
<div id="app" v-pre>
{{name}}
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const app = new Vue({
el:'#app',
data:{
name:'WDY'
},
})
</script>
常用全局API
| API | 作用 |
|---|---|
| Vue.extend | 创建一个“子类”(组件) |
| Vue.nextTick | 在下次DOM更新循环结束之后执行延迟回调 |
| Vue.set | 向响应式对象中添加一个property |
| Vue.delete | 删除对象的property |
| Vue.directive | 注册或获取全局指令(自定义指令) |
| Vue.filter | 注册或获取全局过滤器 |
| Vue.component | 注册或获取全局组件 |
| Vue.use | 安装Vue.js插件(引入库并使用) |
| Vue.mixin | 全局注册一个混入 |
| Vue.compile | 将一个模板字符串编译成render函数 |
| Vue.observable | 让一个对象可响应 |
| Vue.verson | 提供字符串形式的Vue安装版本号 |
Vue.extend
<div id="app">
<my-component></my-component>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const myComponent = Vue.extend({
template:`<div><h3>My name is {{name}}</h3></div>`,
data(){
return {
name:"WDY"
}
},
})
Vue.component('myComponent',myComponent)
const app = new Vue({
el:'#app',
data:{
},
components:{
myComponent
}
})
</script>
Vue.set/vm.$set
- 不能给vm添加属性
<div id="app">
{{my}}
</div>
<script>
const app = new Vue({
el:'#app',
data:{
my:{
name:"WDY"
}
},
})
Vue.set(app.my,"age",10000)
app.$set(app.my,'sex','guess')
</script>
Vue.delete/vm.$delete
<div id="app">
{{my}}
</div>
<script>
const app = new Vue({
el:'#app',
data:{
my:{
name:"WDY"
}
},
})
Vue.set(app.my,"age",10000)
app.$set(app.my,'sex','guess')
Vue.delete(app.my,'age')
app.$delete(app.my,'sex')
</script>
Vue.directive
钩子函数:
| 函数 | 作用 |
|---|---|
| bind | 只调用一次,指令第一次绑定到元素时调用 |
| inserted | 被绑定元素插入父节点时调用(仅保证父节点存在) |
| update | 所在组件的VNode更新时调用 |
| componentUpdated | 指令所在组件的VNode及其子VNode其全部更新后调用 |
| unbind | 只调用一次,指令解绑时调用 |
全局自定义指令
<div id="app">
<div v-add="age"></div>
</div>
<script>
Vue.directive('add',{
bind(el,binding,vnode){
console.log(binding)
el.innerHTML = binding.value+1
}
})
const app = new Vue({
el:'#app',
data:{
age:10
},
/* directives:{
add:function(el,binding,vnode){
console.log(binding)
el.innerHTML = binding.value+1
}
} */
})
</script>
Vue.filter
全局过滤器
<div id="app">
<div v-for="per in person">
我是:{{per.name | pickPerson}}
</div>
</div>
<script>
Vue.filter('pickPerson',function(value){
if(value === 'WDY'){
return value
}
})
const app = new Vue({
el:'#app',
data:{
person:[
{name:'WDY',age:10000},
{name:"wdy",age:1000}
]
},
/* 局部过滤器
filters:{
pickPerson(value,str='WDY'){
if(value === 'WDY')
return value
}
} */
})
</script>
Vue.component
<div id="app">
<my-component></my-component>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const myComponent = Vue.extend({
template:`<div><h3>My name is {{name}}</h3></div>`,
data(){
return {
name:"WDY"
}
},
})
Vue.component('myComponent',myComponent)
const app = new Vue({
el:'#app',
data:{
},
components:{
myComponent
}
})
</script>
常用特殊属性
| 属性 | 作用 |
|---|---|
| key | 判断两个虚拟节点是否是相同节点 |
| ref | 给元素或子元素注册引用信息 |
| is | 用于动态组件且基于DOM内模板的限制工作 |
| slot | 具名插槽中插入子组件内容(已废弃,现为v-slot) |
| slot-scope | 作用域插槽中数据传递(已废弃,现为v-slot) |
key
-
key的作用是为了在diff算法执行时更快的找到对应节点,提高diff速度,更快更新虚拟DOM。
-
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后将新旧虚拟DOM进行比较:
-
旧的虚拟DOM中的key与新的虚拟DOM中的key相同
- 有变化:生成新的真实DOM随后替换之前的真实DOM
- 无变化:使用之前的真实DOM
-
新虚拟DOM的key不存在
- 创建新的真实DOM随后渲染
-
-
如果单纯为了展示数据可以将index作为key展示,但是如果要对数据进行操作,index作为key就不合适了:
-
只展示信息
<div id="app"> <ul> <li v-for="(per,index) in person" :key="index"> {{per.id}}---{{per.name}}---{{per.age}} <!-- <input type="text"/> --> </li> </ul> <button @click="add">加人</button> </div> <script> const app = new Vue({ el:'#app', data:{ person:[ {id:'001',name:'A',age:1}, {id:'002',name:'B',age:2}, {id:'003',name:'C',age:3} ] }, methods:{ add(){ this.person.unshift({id:'004',name:'D',age:4}) } } }) </script>从:
-
变为:
- 对数据进行操作:
<div id="app">
<ul>
<li v-for="(per,index) in person" :key="index">
{{per.id}}---{{per.name}}---{{per.age}}
<input type="text"/>
</li>
</ul>
<button @click="add">加人</button>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
person:[
{id:'001',name:'A',age:1},
{id:'002',name:'B',age:2},
{id:'003',name:'C',age:3}
]
},
methods:{
add(){
this.person.unshift({id:'004',name:'D',age:4})
}
}
})
</script>
在各自的输入框输入信息后,从:
变为:
所以当我们要对key值有一个唯一标识,可以自定义id,使用学号等等,也可以使用nanoid或uuid:
<div id="app">
<ul>
<li v-for="(per) in person" :key="per.id">
{{per.id}}---{{per.name}}---{{per.age}}
<input type="text"/>
</li>
</ul>
<button @click="add">加人</button>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
person:[
{id:'001',name:'A',age:1},
{id:'002',name:'B',age:2},
{id:'003',name:'C',age:3}
]
},
methods:{
add(){
this.person.unshift({id:'004',name:'D',age:4})
}
}
})
</script>
在各自的输入框输入信息后,从:
变为:
ref
更详细内容请见:组件- 嵌套组件 -数据传递- ref
slot
更详细内容请见:常用指令- v-slot - 具名插槽
<div id="app">
<father style="background-color:skyblue;width: 200px; height: 200px;">
</father>
</div>
<script>
const child = Vue.extend({
template:`<div>
我是cDiv
<slot name='purple'></slot>
<slot></slot>
<slot name='orange'></slot>
</div>`,
data(){
return{
}
}
})
const father = Vue.extend({
template:`<div>
我是fDiv
<child style="background-color: pink;width: 150px; height: 150px;" >
<template slot='purple'>我是紫色</template>
我是默认
<template slot='orange'>我是橙色</template>
</child>
</div>`,
data(){
return{
}
},
components:{
child
}
})
Vue.component('father',father)
Vue.component('child',child)
const app = new Vue({
el:'#app',
components:{
father,
child
}
})
</script>
slot-scope
更详细内容请见:常用指令- v-slot - 作用域插槽
<div id="app">
<father style="background-color:skyblue;width: 200px; height: 200px;">
</father>
</div>
<script>
const child = Vue.extend({
template:`<div>
我是cDiv
<slot :person='person'>{{name}}</slot>
</div>`,
data(){
return{
person:[
{name:'WDY',age:1000},
{name:'wdy',age:999}
]
}
}
})
const father = Vue.extend({
template:`<div>
我是fDiv
<child style="background-color: pink;width: 150px; height: 150px;">
<template slot-scope='person'>
<div v-for='per in person'>{{per}}</div>
</template>
</child>
</div>`,
data(){
return{
}
},
components:{
child
}
})
Vue.component('father',father)
Vue.component('child',child)
const app = new Vue({
el:'#app',
components:{
father,
child
}
})
</script>
混入
Vue中分发可复用功能的方式
| 选项 | 作用 |
|---|---|
| mixins | 接收一个混入对象数组。 |
| Vue.mixin | 全局注册一个混入,影响注册之后所有创建的每个Vue实例 |
局部混入(mixins)
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const mixin = {
created(){
console.log(`My name is ${this.name}`)
},
data(){
this.age = 10000
}
}
const child = Vue.extend({
template:`<div>
{{name}}--{{age}}
</div>`,
data(){
return{
name:'WDY'
}
},
mixins:[mixin]
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
{{name}}---{{age}}
<child></child>
</div>`,
data(){
return{
name:'wdy'
}
},
mixins:[mixin],
components:{
child,
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
全局混入(Vue.mixin)
慎用,甚至不用
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
Vue.mixin({
created:function(){
console.log(`My name is ${this.name}`)
},
})
Vue.mixin({
data:function(){
return{
age:10000
}
}
})
const child = Vue.extend({
template:`<div>
{{name}}--{{age}}
</div>`,
data(){
return{
name:'WDY'
}
},
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
{{name}}---{{age}}
<child></child>
</div>`,
data(){
return{
name:'wdy'
}
},
components:{
child,
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
全局混入会作用到每一个组件,而app组件没有定义name,因此输出了undefined。
组件
- 组件化开发可以提高:开发效率,复用性
普通组件
-
组件中的data必须是函数式。
-
非单文件组件(后缀名为.html):创建了一个名为my的组件。
-
使用多个组件时要使用双标签,如果使用单标签,只会显示第一个组件。
-
非普通组件在使用时不能用驼峰命名法!! 千万注意
(别问我是怎么知道的)- 可以注册,但在使用时将驼峰化为 -
<div id="app"> <my-component></my-component> </div> <script type="text/javascript" > Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 const myComponent = Vue.extend({ template:`<div><h3>My name is {{name}}</h3></div>`, data(){ return { name:"WDY" } }, }) Vue.component('myComponent',myComponent) const app = new Vue({ el:'#app', data:{ }, components:{ myComponent } }) </script>
-
单文件组件(后缀名为.vue):一下是一个简单的模板
- 无论是浏览器还是ES6语法都无法识别vue文件,所以需要通过脚手架或webpack打包后才能正常运行
<template> <div> My name is {{name}} </div> </template> <script> export default { data(){ return{ name:'WDY' } } } </script> <style> </style>
嵌套组件
数据传递
嵌套组件间的数据传递:(组件间的数据传递是非常重要的一个点)
-
父子组件间数据传递:
- 父传子:子组件只能使用父组件传来的数据
props
-
使用props:
- props可以定义一个及以上的数据,可以是各种数据类型及函数:
<div id="app"> <father></father> </div> <script> const child = Vue.extend({ template:`<div> {{name}}---{{age}} </div>`, data(){ return{ } }, props:['name','age'] }) Vue.component('child',child) const father = Vue.extend({ template:`<div> <child :name='name' :age='age'></child> </div>`, data(){ return{ name:"WDY", age:"10000" } }, components:{ child } }) Vue.component('father',father) const app = new Vue({ el:'#app', components:{ father, child } }) </script>
- 子传父:子组件不能直接给父组件传值,应使用$emit
$emit
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const child = Vue.extend({
template:`<div>
<div v-for="per in pers" :key='per.id' @click=send(per.id)>
{{per.name}}---{{per.age}}
</div>
</div>`,
data(){
return{
}
},
methods:{
send(index){
console.log('子组件的方法')
this.$emit('receive',index)
}
},
props:['pers']
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
<child :pers="persons" @receive='receive'></child>
{{childId}}
</div>`,
data(){
return{
persons:[
{id:'001',name:'A',age:1},
{id:'002',name:'B',age:2},
{id:'003',name:'C',age:3}
],
childId:'000'
}
},
methods:{
receive(idx){
console.log('父组件的方法')
this.childId = idx
}
},
components:{
child
}
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father,
child
}
})
</script>
点击第二行后:
- 父子间通信
ref
| 方法 | 作用 |
|---|---|
| ref | 给元素或子元素注册引用信息 |
| $ref | 一个对象,注册持有过ref属性的所有DOM元素和组件实例 |
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const child = Vue.extend({
template:`<div>
{{name}}
</div>`,
data(){
return{
name:'WDY'
}
},
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
<child ref="child"></child>
</div>`,
data(){
return{
}
},
mounted(){
console.log(this.$refs.child.name)
},
components:{
child,
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
- 父子间通信
parent
| 实例 | 作用 |
|---|---|
| $children | 当前实例的直接子组件 |
| $parent | 当前实例的父实例 |
$children是一个数组且它是无序的,$parent是一个对象
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const child = Vue.extend({
template:`<div>
{{name}}
</div>`,
data(){
return{
name:'WDY'
}
},
computed:{
receive(){
return this.$parent.name
}
}
})
Vue.component('child',child)
const anotherChild = Vue.extend({
template:`<div>{{name}}</div>`,
data(){
return{
name:''
}
},
computed:{
receive(){
return this.$parent.name
}
}
})
Vue.component('anotherChild',anotherChild)
const father = Vue.extend({
template:`<div @click=send>
<child></child>
<another-child></another-child>
</div>`,
data(){
return{
name:'wdy'
}
},
methods:{
send(){
this.$children[0].name = 'wdy'
this.$children[1].name = 'another-wdy'
console.log(this.$children)
}
},
components:{
child,
anotherChild
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
- 父子间/祖孙间数据传递:
依赖注入(provide/inject)
provide与inject需要一起使用,以允许一个祖先组件向其子孙后代注入一个依赖。
依赖注入所提供的属性时非响应式的即父类传递的变量变化之后,后代接收到的值是不会改变的,同时会带来代码重构困难的问题。
| 选项 | 作用 |
|---|---|
| provide | 一个对象或返回一个对象的函数,该对象包含可注入子孙的property |
| inject | 一个对象或一个字符串数组 |
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const grandChild = Vue.extend({
template:`<div>{{name}}</div>`,
/*template:`<div>{{all.name}}</div>`,*/
inject:['name']
/*
inject:['all']
*/
})
Vue.component('grandChild',grandChild)
const child = Vue.extend({
template:`<div>
{{name}}
<grand-child></grand-child>
</div>`,
/*
template:`<div>
{{all.name}}
<grand-child></grand-child>
</div>`,
*/
data(){
return{
}
},
inject:['name'],
/*
inject:['all']
*/
components:{
grandChild
}
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
<child></child>
</div>`,
data(){
return{
name:'WDY',
}
},
provide(){
return{
name:this.name
/*
将全部属性都传递下去
all:this
*/
}
},
components:{
child,
grandChild
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
- 兄弟组件间数据传递,全局通信:
事件总线(EventBus)
| 方法 | 作用 |
|---|---|
| vm.$on | 监听当前实例的自定义事件 |
| vm.$once | 监听一个自定义事件,但只触发一次 |
| vm.$off | 移除自定义事件监听器 |
| vm.$emit | 触发当前实例上的事件,附加参数会传给监听器回调 |
事件总线是一种强大的通信方式,不仅是父子间,兄弟间,甚至是跨组件,通过事件总线都可以进行数据的传递。在组件通信的过程中,事件总线(EventBus)充当沟通桥梁的角色,是所有组件的事件中心,组件可以向该中心注册发送时间或者接收事件。
如果项目中不需要使用Vuex,那么事件总线是一个很好地选择,但是事件总线过于简便,所以我们在使用时要注意,避免出现难以维护的情况。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta n="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="../vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//Vue.prototype.$EventBus = new Vue();
//const EventBus = new Vue()
const child = Vue.extend({
template:`<div>
<button @click='send()'>
发送
</button>
</div>`,
data(){
return{
name:'WDY',
age:10000
}
},
methods:{
send(){
this.$bus.$emit('receive',{
name:this.name,
age:this.age
})
}
},
})
Vue.component('child',child)
const anotherChild = Vue.extend({
template:`<div>
{{name}}
</div>`,
data(){
return{
name:'brother'
}
},
mounted(){
this.$bus.$on('receive',param =>{
this.name = param.name;
})
},
beforeDestory(){
this.$bus.$off('receive')
}
})
Vue.component('anotherChild',anotherChild)
const father = Vue.extend({
template:`<div>
<child></child>
<another-child></another-child>
{{name}}
</div>`,
data(){
return{
name:'father'
}
},
methods:{
},
components:{
child,
anotherChild
},
mounted(){
this.$bus.$on('receive',(param)=>{
this.name = param.name
})
},
beforeDestory(){
this.$bus.$off('receive')
}
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
beforeCreate(){
Vue.prototype.$bus = this
}
})
</script>
</body>
</html>
点击前(数据传输前):
点击后(数据传输完成):
- 隔代传输
$attrs/$listeners
| 实例 | 作用 |
|---|---|
| $attrs | 包含了父作用域中不作为prop被识别的属性 |
| $listeners | 包含了父作用域中的事件监听器 |
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const grandChild = Vue.extend({
template:`<div>
{{$listeners.send()}}---{{$attrs.name}}
</div>`,
data(){
return{
}
},
})
Vue.component('grandChild',grandChild)
const child = Vue.extend({
template:`<div>
<grand-child v-bind="$attrs" v-on="$listeners"></grand-child>
</div>`,
data(){
return{
name:'WDY'
}
},
computed:{
},
components:{
grandChild
}
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
<child :name='name' @send='send'></child>
</div>`,
data(){
return{
name:'wdy'
}
},
methods:{
send(){
return '你叫什么名字?'
}
},
components:{
child,
},
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
- 全局通信
Vuex
子组件和父组件的执行顺序
<div id="app">
<father></father>
</div>
<script>
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const child = Vue.extend({
template:`<div @click='change'>
{{name}}
</div>`,
data(){
return{
name:'WDY'
}
},
methods:{
change(){
this.name = '儿子---变'
}
},
beforeCreate() {
console.log('----child---beforeCreate----')
},
created() {
console.log('----child---created----')
},
beforeMount() {
console.log('----child---beforeMount----')
},
mounted() {
console.log('----child---mounted----')
},
beforeUpdate(){
console.log('----child---beforeUpdate----')
},
updated(){
console.log('----child---updated----')
},
beforeDestroy(){
console.log('----child---beforeDestroy----')
},
destroyed(){
console.log('----child---destoryed----')
}
})
Vue.component('child',child)
const father = Vue.extend({
template:`<div>
<child></child>
{{name}}
<button @click='change'>change</button>
<button @click="des">销毁</button>
</div>`,
data(){
return{
name:'wdy'
}
},
methods:{
change(){
this.name = 'father---变'
},
des(){
this.$destroy()
}
},
components:{
child,
},
beforeCreate() {
console.log('----father---beforeCreate----')
},
created() {
console.log('----father---created----')
},
beforeMount() {
console.log('----father---beforeMount----')
},
mounted() {
console.log('----father---mounted----')
},
beforeUpdate(){
console.log('----father---beforeUpdate----')
},
updated(){
console.log('----father---updated----')
},
beforeDestroy(){
console.log('----father---beforeDestroy----')
},
destroyed(){
console.log('----father---destoryed----')
}
})
Vue.component('father',father)
const app = new Vue({
el:'#app',
components:{
father
},
})
</script>
在脚手架中组件名首字母大写。
放不下了兄弟萌,后续请见:[Vue2从入门到出门(续)](Vue2从入门到出门(续) - 掘金 (juejin.cn))