<div id="app">
<child :content="message"></child>
</div>
let Child = Vue.extend({
template: `{{message}}
`,
props:{
content:{
type:String,
default:() => {return 'form child'}
}
}
})
new Vue({
el:'#app',
data:{
message:'form parent'
},
components:{
Child
}
})
// 结果
// form parent
数组形式
props:['data1','data2',……,'datan']
简单对象形式
props:{
data1: Number,
data1: String,
data1: Boolean,
data1: Function,
data1: Object,
data1: Array,
data1: Symbol,
}
复杂对象形式
props:{
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
<div id="app">
<my-button @receive="sayHi"></my-button>
</div>
let Child = Vue.extend({
template: ``,
data(){
return {
msg:'vue.js'
}
},
methods:{
send(){
this.$emit('receive',this.msg)
}
}
})
new Vue({
el:'#app',
components:{
MyButton
},
methods:{
sayHi(val){
console.log('Hi,'+val)
}
}
})
// 结果
// Hi,vue.js
Vue.js(/vjuː/,或简称为Vue)
是一个用于创建用户界面的开源JavaScript 渐进式框架,也是一个创建单页面应用的Web应用框架
同时也是一款流行的JavaScript前端框架,旨在更好的组织与简化Web开发。Vue所关注的核心是MVC模式中的视图层,同时,它也能方便地获取数据更新,并通过组件内部特定的方法实现视图与模型的交互
就是框架分层,最核心的是视图层渲染,然后往外是组件机制,在此基础上再加入路由机制,再加入状态管理,最外层是构建工具
优势:根据不同的需求选择不同的层级
代表含义:主张最少
理解:不做除自己职责之外的事
单页Web应用,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面完成,这一切都由JavaScript来控制。
良好的交互体验
用户不需要从新刷新页面,获取数据也是通过Ajax异步获取
良好的前后端工作分离模式
减轻服务器压力
服务器只用出数据就可以,不用管展示逻辑和页面合成
共用一套后端程序代码
MVVM表示的是 Model-View-ViewModel
把图形、非图形的各种逻辑抽象为一个统一的概念(组件)来实现开发模式,在vue中每一个 .vue 文件都可以视为一个组件
指令是带有 v- 前缀的特殊属性
作用:当表达式的值改变时,将其产生的连带影响响应式地作用于DOM
常用指令:
在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
页面显示的数据和data中的数据已经保持同步了,都是最新的
Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了
Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
会触发 下面这几个beforeCreate, created, beforeMount, mounted
DOM 渲染在 mounted 中就已经完成了
建议放在created里
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
如果在mounted钩子函数中请求数据可能导致页面闪屏问题
其实就是加载时机问题,放在created里会比mounted触发早一点,如果在页面挂载完之前请求完成的话就不会看到闪屏了
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
官方理解:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
个人理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,即数据更新了,在dom渲染完成后调用该函数
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">{{msg1}}</div>
<div v-if="msg2">{{msg2}}</div>
<button @click="changeMsg">点击修改……</button>
</div>
new Vue({
el: '.app',
data: {
msg: 'Hello world.',
msg1: '',
msg2: ''
},
methods: {
/*
* 通过 this.$refs.msgDiv.innerHTML 操作 ref="msgDiv" 指向的DOM
*/
changeMsg() {
this.msg = "Hello vue."
// 调用了 this.$nextTick() 将会在下一次 DOM 更新之后执行(此时,msg已修改为Hello vue.且页面已更新完成)
this.$nextTick(() => {
//this.$refs.msgDiv.innerHTML = 'Hello vue.'
this.msg1 = this.$refs.msgDiv.innerHTML
})
// 当点击时Dom还未更新,获取到的msgDiv.innerHTML仍然是未更新前页面上渲染的数据(Hello world.)
// this.$refs.msgDiv.innerHTML = 'Hello world.'
this.msg2 = this.$refs.msgDiv.innerHTML
}
}
})
/*
* 点击前:
* Hello world.
* 点击修改……
*
* 点击后:
* Hello world.
* Hello vue.
* Hello world.
* 点击修改……
*/
在Vue生命周期的created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中
在created()
钩子函数执行的时候DOM 其实并未进行任何渲染
在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()
的回调函数中。
为什么在vue组件中,我们的data属性必须是一个函数,new Vue()中的data除外,因为new Vue中只有一个data属性
因为我们能抽离出来的组件,肯定是具有复用性的,它在项目中会存在多个实例。如果data属性值是一个对象时,那么它所有的实例都会共享这些数据,这是很麻烦的事情,你不能确保你的所有实例中的属性值都不会重复,无论在哪个组件实例中修改data
,都会影响到所有的组件实例
期望:每个组件都能独立维护自己的数据
// 声明构造器函数
function Person() {}
Person.prototype.data = {
// 原型下挂载一对象,并有name属性
name: 'Hello World!',
};
var p1 = new Person();
var p2 = new Person();
p1.data.name = 'Hello Vue!';
console.log(p1.data.name); // Hello Vue!
console.log(p2.data.name); // Hello Vue!
此时原型下为一个对象,p1,p2均指向同一个实体
原型下的属性相当于公有的
修改一个实例对象下的属性也会造成另一个实例属性跟着改变,这样组件复用的时候,肯定是不行的,那么改成函数呢?
// 声明构造器函数
function Person() {
this.data = this.data()
}
Person.prototype.data = {
// 原型下挂载一对象,并有name属性
name: 'Hello World!',
};
var p1 = new Person();
var p2 = new Person();
p1.data.name = 'Hello Vue!';
console.log(p1.data.name); // Hello Vue!
console.log(p2.data.name); // Hello World!
由此得出:若原型下为一个函数,根据函数具有独立作用域快的特点,完美地达到了每个组件都能独立维护自己的数据
同理,在vue中要想组件能到达复用的效果,data必须是一个函数
是一个计算属性,类似于过滤器,对绑定到view的数据进行处理
computed:{ 属性名:function(){ return 返回值; } }
<div id="app">
<p>{{ list }}</p>
<p>{{ sumList }}</p>
</div>
var app = new Vue({
el: '#app',
data: {
list: [9,3,5,7]
},
computed: {
// 计算属性的 getter
sumList: function () {
var sum = 0
for(let i=0;i<=this.list;i++){
sum = sum + list[i]
}
return sum
}
}
})
计算属性包含两个操作:
getter,获取计算属性的结果,默认的function是获取结果(所以可以省略不写)。用get:function(){}实现。当依赖数据发生变化时会自动执行get函数。
setter,设置计算属性的结果。用set:function(){}实现。当计算属性通过v-model绑定在表单元素上,用户对表单元素的值进行了修改时执行set函数。
computed:{
sumList:{
get:function(){
return 返回值
},
set:function(参数){
}
}
}
// ...
computed: {
fullName: {
// getter
// 回调函数 当需要读取当前属性值是执行,根据相关数据计算并返回当前属性的值
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
// 监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据
// newValue :fullName的最新值
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
watch 是一个观察的动作
<div>
<p>FullName: {{Price}}</p>
<p>FirstName: <input type="text" v-model="price"></p>
</div>
new Vue({
el: '#root',
data: {
price: '2',
symbolUnit: '¥',
Price:''
},
watch: {
price(newName, oldName) {
this.Price = newName + this.symbolUnit;
}
}
})
watch: {
price: {
handler(newName, oldName) {
this.Price = newName + this.symbolUnit;
},
// 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法,如果设置了false,那么效果和上边例子一样
immediate: true
}
}
<div>
<p>obj.a: {{obj.a}}</p>
<p>obj.a: <input type="text" v-model="obj.a"></p>
</div>
new Vue({
el: '#root',
data: {
obj: {
a: 123
}
},
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true
}
}
})
当我们在输入框中修改a的值时,发现watch是无效的
默认情况下,handler只监听obj这个属性引起的变化,我们只有给obj赋值的时候它才会监听到,而obj.a的改变无法监听
当需要监听 obj.a 时,需要使用 deep 属性
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
而同时这种方法对性能影响很大,修改obj里面的任何一个方法都会触发这个监听器里的 handler
改进:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
是观察的动作
无缓存性,页面重新渲染时值不变化也会执行(值改变则直接触发)
支持异步
监听数据必须是 data 中声明过或者父组件传递过来的props中的数据
区别一:
计算属性是基于它的依赖进行实时更新的,方法则是调用才更新
区别二:
计算属性是有缓存的,只要相关依赖没有改变,多次访问计算属性得到的值是之前缓存的计算结果,不会多次执行。操作一个没有依赖项的值也是如此。
任何一个方法都会触发这个监听器里的 handler
改进:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
是观察的动作
无缓存性,页面重新渲染时值不变化也会执行(值改变则直接触发)
支持异步
监听数据必须是 data 中声明过或者父组件传递过来的props中的数据
区别一:
计算属性是基于它的依赖进行实时更新的,方法则是调用才更新
区别二:
计算属性是有缓存的,只要相关依赖没有改变,多次访问计算属性得到的值是之前缓存的计算结果,不会多次执行。操作一个没有依赖项的值也是如此。