Vue 使用基于 HTML 的方式来声明页面的样式,同时可以把我们在 Vue 中声明的数据 data 动态的展示在界面中,这就是模版语法。
数据绑定最常见的形式就是使用 “Mustache” 语法 (双大括号) 的文本插值。
HTML 特性不能用 Mustache 语法,应该使用 v-bind 指令。
{{}} v-bind: v-once v-text v-html
v-for
v-if v-else v-else-if v-show
你可以用 v-model 指令在表单 、
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
事件修饰符。
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组,对象的方式更常用。
模版语法是如何实现的。
我们编写的模版,看起来像 HTML 代码的内容它到底是什么呢。
实际上,将来 Vue 会将我们编写的这些模版转换成渲染函数,渲染函数最终的目标是要为了能够生成虚拟 DOM,再结合着 Vue 的响应式系统,Vue 就能够根据将来值发生变化之后,再次执行渲染函数,得到一个全新的虚拟 DOM,在新旧虚拟 DOM 之间经过比对,就可以知道我们真正要做的 DOM 操作。
这个渲染函数将来执行之后,它能够 return 一个结果,return 的这个结果就是虚拟 DOM。
// 输出vue替我们生成的渲染函数一窥究竟
console.log(app.$options.render)
// 它长这个样子
(function anonymous( ) { with(this){return _c(‘div‘,{attrs:{"id":"app"}},[_c(‘h2‘,{attrs: {"title":title}},[_v("\n "+_s(title)+"\n ")]),_v(" "),_c(‘input‘,{directives:[{name:"model",rawName:"v-model",value: (course),expression:"course"}],attrs:{"type":"text"},domProps:{"value": (course)},on:{"keydown":function($event) {if(!$event.type.indexOf(‘key‘)&&_k($event.keyCode,"enter",13,$event.key,"Enter" ))return null;return addCourse($event)},"input":function($event) {if($event.target.composing)return;course=$event.target.value}}}),_v(" "),_c(‘button‘,{on:{"click":addCourse}},[_v("新增课程")]),_v(" "),(courses.length == 0)?_c(‘p‘,[_v("没有任何课程信息")]):_e(),_v(" "),_c(‘ul‘,_l((courses),function(c){return _c(‘li‘,{class:{active: (selectedCourse === c)},on:{"click":function($event){selectedCourse = c}}}, [_v(_s(c))])}),0)])} })
改写为渲染函数版本:
const app = new Vue({
// 引入上面的render函数
render() {
with (this) { return ... }
}
})
所以实际上我们在编写模版的时候其实我们不是在写 HTML,在 Vue 的内部其实是把它转换成了一个 JS 的函数,这个才是真正的模版技术的工作机制。
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,此时就可以考虑计算属性和监听器。
计算属性具有缓存性:计算所得的值如果没有变化不会重复执行,页面不会重新渲染。其实计算属性里的逻辑挪到methods 里面也是可以实现的,但是不管这个值有没有变化,每次都会重新计算。
默认情况下 watch 在初始化时不执行,监听的值变了才执行,想要初始化时执行,要使用带选项的 watcher。
区别:
每个 Vue 实例在被创建时都要经过一系列的过程。例如,初始化、更新过程、销毁过程、需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,这一系列的过程统称为 Vue 实例的生命周期。
既然组件它将来要实例化,它会有生命周期,那么为了我们方便去写程序,Vue 为我们准备了一些叫做生命周期钩子的函数。可以在组件的选项中按照它规定的名字直接去写就可以了。
created 到 mounted 的这个过程是很快的,所以 ated 和 mounted 本质上是没有差别的。
生命周期分为三阶段:初始化、更新、销毁。
除了上面的生命周期图示的理解,我们还要完整的掌握生命周期列表。在 API 中,关于生命周期会有一个详细的列表展示。生命周期图示
一般情况下,这些带 before 的,我们在实际开发中用的都会少。因为在这些 before 的状态中,要么数据还没准备好,要么元素还没准备好,总是少了那么点东西。
{
beforeCreate(){} // 执行时组件实例还未创建,这个时间点非常之早,各种各样的数据还不齐全,所以大家在做操作的时候,不要随意的访问各种各样的数据,可能根本就访问不到,通常用于插件开发中执行一些初始化任务
created(){} // 组件初始化完毕,各种数据可以使用,常用于异步数据获取
beforeMounted(){} // 未执行渲染、更新,dom未创建
mounted(){} // 初始化结束,dom已创建,可用于获取访问数据和dom元素
beforeUpdate(){} // 更新前,可用于获取更新前各种状态,比如有一个列表更新了,你要记录它更新之前滚动条的位置,显然是在这个时候去保存是最合理的。
updated(){} // 更新后,所有状态已是最新,得到上列滚动条最新的高度,位置可能都有变化,这个时候可以做一些更新之后的操作。
beforeDestroy(){} // 销毁前,可用于一些定时器或订阅的取消,防止内存泄露,这个时候组件实例还在
destroyed(){} // 组件已销毁,作用同上,组件实例已经不在了
}
组件化就是把我们应用程序中的一些比较独立的功能的模块或者单元,把它抽取出来,然后把它封装成组件。就像工程化过程中的一些零件。搭积木时候积木的零件。以后在开发后续程序的过程中,维护,复用都会得到进一步的提升。
官方对组件的定义:组件是可复用的 Vue 实例,带有一个名字,我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。
Vue.component(name, options) 可用于注册组件。
props 用于接收数据。
template 用于编写组件模版。
当子组件需要和父级组件进行通信,可以派发并监听自定义事件。
公用的状态放在父组件。
v-model 是一个语法糖,v-model 最终会转换成一个属性的传递 :value="values" 和一个事件的监听 @input="onInput"。
onInput 回调函数里具体的逻辑是,将内部派发事件传递的那个最新的要变更的值赋值给 values,就是 @input = "values=$event",$event 是固定的名字,表示传递出来的参数,把它赋值给当前的 values 变量。
<course-add v-model="values"></course-add>
// 相当于:
<course-add :value="values" @input="values=$event"></course-add>
在组件的内部,要做的事情是,接受一个属性叫 value 和派发一个默认的 input 事件,把最新的值作为参数传递出来。
Vue.component(‘course-add‘, {
props: [‘values‘],
template: `
<div>
<!-- 需要实现input的:value和@input -->
<input :value="values" @input="onInput"/>
</div>
`,
methods: {
onInput(e) {
this.$emit(‘input‘, e.target.value)
}
}
})
非自定义组件和表单元素的 v-model 做的事是一样的,只不过组件内部做的事 Vue 已经在绑定了 v-model 的表单元素上做好了。所以在表单元素上使用数据双向绑定只需写一个 v-model 就行了。
在开发的过程中,我们经常会遇到一些这样的需求,比如说一个对话框要去组件化,这个对话框的内容在用的时候是在父组件中去指定的,但是这块内容将来是要把它显示在子组件内部的,这个就是内容分发。在外面去提供内容,将来分发到里面的指定位置,这就是内容分发技术。
Vue 中组件的内容分发,我们用的是插槽,这个名字非常的形象,我留出这个位置,内容实际上是不确定的,通过父组件传递过来,我用插槽来接收。
普通插槽
<message :show.sync="show">新增课程成功!</message>
// slot作为占位符
Vue.component(‘message‘, {
props: [‘show‘],
template: `
<div class="message-box" v-if="show">
<slot></slot>
<span class="message-box-close" @click="$emit(‘update:show‘, false)">X</span>
</div>
`
})
sync 修饰符:为了写法更简洁。:show.sync="isShow" 将来会展开成 :show="isShow" @update:show="show=$event" 的形式。所以将来子组件要派发的事件就是 update:show。
如果存在多个独立内容要分发,可以使用具名插槽,把指定的内容放到指定的位置。
<message :show.sync="show">
// 通过template的方式
<template v-slot:title>
<h2>恭喜</h2>
</template>
<template>新增课程成功!</template>
</message>
Vue.component(‘message‘, {
props: [‘show‘],
template: `
<div class="message-box" v-if="show">
<strong><slot name="title">默认标题</slot></strong>
<slot></slot>
</div>
`
})
作用域插槽: 数据值来自于子组件内部还是父组件。
<message :show.sync="show">
// 通过template的方式
<template v-slot:title="slotProps">
<h2>{{slotProps.title}}</h2>
</template>
<template>新增课程成功!</template>
</message>
Vue.component(‘message‘, {
props: [‘show‘],
template: `
<div class="message-box" v-if="show">
<slot name="title" title="来自message的标题">默认标题</slot>
<slot></slot>
</div>
`
})
Vue 应用就是由一个个组件构成的。Vue 的组件化涉及到的内容非常多。
组件化是 Vue 的最精髓的特性,也是体现使用 Vue 的熟练程度,精通程度的一个非常重要的参考。于是在面试的过程中经常被问到:谈一下你对 Vue 组件化的理解,这时候有可能无从下手,组件化这个问题很大,怎么能够把它具体的落地去回答到这个问题中,可以从以下几点进行阐述,由浅入深来说:
首先遵循的原则是先要介绍下组件化含义是什么:组件是可复用的 Vue 实例,准确讲它们是 VueComponent 的实例,继承自 Vue。
组件化其实是软件工程中的一些原则性的践行,软件工程中一个最重要的原则就是高内聚,低耦合,从而增强程序的复用性,可维护性,可测试性,这些优点在 Vue 程序中就是用组件化去实现的。
组件化的使用场景:什么时候使用组件?组件用到什么地方去?以下分类可作为参考:
如何使用组件
组件的本质
组件到底是什么,组件的本质是什么,组件最终的输出目标是什么。
Vue 中的组件经历如下过程:
组件配置 => VueComponent 实例 => render() => Virtual DOM => DOM
所以组件最终的输出目标是虚拟 DOM。
组件大家平常写的只是配置对象,但是这个过程中 Vue 内部到底做了什么事情呢,是中间这三步,大家看不到的,因为你写了配置最后希望看到 DOM,但实际上 Vue 内部为你做了三件事。
所以隐藏在 Vue 内部的这个过程你应当知道。
最后我们需要知道组件的本质就是产生虚拟 DOM。
后续动态的向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。
使用方法: Vue.set(target, propertyName/index, value)
动态的删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。
使用 delete obj[‘property‘] 这种方式删除的时候,我们定义的拦截器是无法拦截到的,所以导致这样删除,最终界面中是不会响应的。
使用方法: Vue.delete(target, propertyName/index)
监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。
相当于在模版中@事件的方式:@test="callback"
vm.$on(‘test‘, function (msg) {
console.log(msg)
})
事件监听的时候,虽然我们 @test 是写在父组件里头,但是这个事件的监听者其实是事件的派发者。事件的监听者和派发者是同一个实例。而不是在子组件中派发事件,在父组件中监听,要区分开来,只是用 @ 那种语法写到父组件。将来转换成真正的代码其实还是 $on 这样的。
触发当前实例上的事件。附加参数都会传给监听器回调。
$on 和 $emit 其实有一个比较常见的应用方式,叫事件总线。
通过在 Vue 的构造函数的原型上添加另外一个独立的 Vue 的实例,把它作为事件总线,这是一个设计模式,总线模式,因为我是放到了原型上,那将来我在任意的组件中都可以通过 this.$bus 的方式来访问到这个 new Vue() 的实例,所以这个 Vue 的实例就可以作为所有组件的中间人,在任何的组件中只要想给其他的组件传递消息你都可以用 $bus 进行 $emit,在其他组件里用 $on 去监听,这样大家的通信就非常方便了,实现组件间相互通信,不受组件间关系的影响。比如之前我们只能在父子间去通信,而现在哪怕跨很多层,没有任何关系都可以随意的去通信,是一种非常好的组件通信的手段。
Vue.prototype.$bus = new Vue();
监听一个自定义事件,但是只触发一次。一旦触发之后,监听器立刻就会解除绑定。
vm.$once(‘test‘, function (msg) {
console.log(msg)
})
移除自定义事件监听器。
随着参数不同,它的处理方式也不同。
vm.$off() // 移除所有的事件监听器
vm.$off(‘test‘) // 移除该事件所有的监听器
vm.$off(‘test‘, callback) // 只移除这个回调的监听器
ref 被用来给元素或子组件来设置引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件的实例。
范例:改造 message 组件显示隐藏方式,可以在子组件内部暴露出控制组件显示隐藏的方法,然后在父组件里通过 ref 引用到子组件的方法。
注意:
原文:https://www.cnblogs.com/zhaoyang007/p/13278684.html