Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue是一套用于构建用户界面的渐进式框架。基于数据驱动,一些都是数据,面向数据。
Vue是一个MVVM的框架。
Mvvm定义MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。
【模型】指的是后端传递的数据。
【视图】指的是所看到的页面。
【视图模型】mvvm模式的核心,它是连接view和model的桥梁。
它有两个方向:
一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。
实现的方式是: DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。
总结:在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。
beforeCreate(创建前) 在数据观测和初始化事件还未开始
created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。
注意此时还没有挂载html到页面上。
mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。
完成模板中的html渲染到html页面中。此过程中进行ajax交互。
beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。
然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
简述每个周期具体适合哪些场景?
1、beforeCreate:可以在这加loading事件,在加载实例时触发。
2、created:初始化完成时的事件写在这里,如在这里结束loading,异步请求也适合在这里调用。
3、mounted:挂载元素,获取到dom节点。
4、updated:如果对数据统一处理,在这里写上相应的函数。
5、beforeDestroy:可以做一个确定停止事件的确认框。
{{数据名}} 模板 mustche 插值表达式 声明式渲染
v-text=“数据名” vue特有的属性(指令)
v-html=“strong” 非转义输出
v-for="(val,index) in 数据"
val值 index索引 变量数组、对象
默认 :key=“index” 指定key 是个bmw字符 vue是认得 修改VDom的key值
:key=“item.id” 指定key 是数据id(唯一性) 修改VDom的key值
key的优势: 避免数据错乱导致的视图问题,提供性能
属性绑定:
v-bind:html属性=“数据” 普通的html属性绑定数据
:html属性=“数据” 简写 title/src/url/…
事件:
v-on:事件名=“方法”
@事件名=“方法” 简写
@事件名=“方法(参数)”
@事件名=“方法($event,参数)” methods:{方法:function(ev,参数){ev/event}}
注意:vue提供的选项的值如果是函数时,不可用箭头函数
样式操作|属性绑定
v-bind:class=“数据|属性|变量|表达式”
:class/style = " 数据 " 数据类型:字符/对象 / 数组
:class="{类名:true,类名2:false}" 布尔值决定样式是否使用
:style="[{css属性名:值},{css属性名小驼峰:值}]"
指令: 扩展了html语法功能,区别了普通的html属性
vue自带的指令: v-text/v-html/v-bind/v-for/v-model/v-on
v-show="布尔" v-if="布尔"
区别: 操作css 操作dom
场景: 适合频繁切换 适合不频繁切换
性能: 初始渲染消耗 频繁切换回有消耗
其他指令: https://cn.vuejs.org/v2/api/#指令
指令(directive):
v-once 渲染一次
v-pre 原样输出,不编译
v-cloak 防闪烁
首先页面首次加载
点击按钮触发
这里可以得出
计算属性: 是一个函数,所依赖的元数据变化时,会再次执行,平时会缓存,是响应式的,需要在模板中渲染才可调用
computed:{
计算属性: function(){return 返回值} 使用: {{计算属性}}
}
与methods的区别: 方法会每次调用,计算属性不会
计算属性的性能高: 适合做筛选,基于它们的响应式依赖进行缓存的
方法:适合在列表渲染使用,强制渲染执行
属性检测|数据观测: 需要在数据变化时执行异步或开销较大的操作时
watch:{
数据名:'methods函数名' 数据名==data的数据
数据名:函数体(new,old)
数据名:{
handler:fn(new,old),
deep: true 深度检测 默认 false
immediate: true 首次运行 默认false
}
}
计算属性 vs 属性检测
计算属性computed: 首次运行 调用时需要在模板中渲染,修改计算所依赖元数据 默认深度依赖 适合做筛选,不可异步
属性检测watch: 首次不运行 调用时只需修改元数据 默认浅度观测 适合做执行异步或开销较大的操作
绑定行间事件:
原理:
vue事件是事件绑定
绑定行间事件:
修饰符:
事件|自定义事件修饰符: @click.stop.prevent
capture: 使用事件捕获模式
self: 点到时才触发,不是从内部元素触发的
once: 只会触发一次
passive: onScroll事件 结束时触发一次,不会频繁触发,移动端使用
native 作用与自定义组件
按键修饰符: @keyup.left/13
系统键 .ctrl
.alt
.shift
.meta
exact 严格默认 @键盘事件.修饰符1.修饰符2.exact 只有1+2才可触发
鼠标按钮修饰符:
.left
.right
.middle
表单修饰符:
v-model.lazy : 确认时才修改model数据
v-model.number : 提取数子
v-model.trim : 删除前后空格
组件
Vue根实例表示1个应用,一个应用有若干个组件拼装而成
使用组件
<组件名>组件名>
<组件名/> 需要模块化环境支持
脚手架环境下webpack协助解决了,调用时依然不能小写header
定义组件
定义:
a) let 组件变量名= Vue.extend({
template:'我是header组件'
});
b) let 组件变量名={}; √
注册(拼装)
a) Vue.component('组件名',组件变量名);
全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生
b) 选项
components:{
组件名:组件变量名 √
}
组件数据
data 要是个函数,且要有返回值 object
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,否则组件复用时,数据相互影响
单文件组件(.vue)
script + template + style
注意:
组件名不可和html同名
组件没有el选项,只有根实例存在el
组件的模板一定要有根元素
组件的data是个函数
推荐:
组件变量名: 大驼峰 XxxXxx
组件名: xx-xx | 大驼峰(模块化环境)
路由
SPA: single page application 单页面应用
特点: 速度快,数据ajax请求,通过路由,页面不会整体重载
实现: 路由 -> 根据url的不同,加载组件
使用流程:
-1. 安装 : npm i vue-router -S
0. import VueRouter from 'vue-router' -> Vue.use(VueRouter) 安装|注册到全局
1. 使用路由 (去哪)
首页
展示区
router-link 组件属性
to="/home"
tag='li' 指定编译后的标签
active-class='类名' 指定激活后的样式
2. 配置路由(建立组件和请求的对应关系) 数组
[{path:'/home',component:home},,{}]
path 路径
component: 指向的组件变量名
3. 创建路由(传递配置)
router = new VueRouter(配置)
配置: {routes:数组}
4. 顶层|根组件,注册路由 (路由控制页面组件的加载)
选项
router(选项):router (router对象)
子路由:children
routes=[
{},
{
path:xx
component:xx
children:[ 子路由
{}
..
]
},
{}
]
路由守卫
导航守卫: 路由授权|守卫
全局守卫/路由独享的守卫/组件内的守卫
beforeRouteEnter(to,from,next){} 前置守卫,进入
to 目标路由
from 当前路由
next 是个函数 next() == next(true) 运行跳转
next(false) 不让跳转
next('字符路径')/next({对象路径}) 重定向
beforeRouteLeave(to,from,next){} 后置守卫,离开
路由数据预载:
beforeRouteEnter(to,from,next){
1. 数据附加到目标路由上 to.query.数据名=值
2. next( _this => _this.属性="拿到的数据") √
}
路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
方式一:
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
方式二:
const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
export default new Router({
routes: [
{
path: '/importfuncdemo1',
name: 'ImportFuncDemo1',
component: ImportFuncDemo1
},
{
path: '/importfuncdemo2',
name: 'ImportFuncDemo2',
component: ImportFuncDemo2
}
]
})
动态组件,缓存组件
动态组件: 组件动态化(数据化),在不同组件之间进行动态切换,component自身不会渲染
缓存组件:
keep-alive 包裹了目标组件,对目标组件缓存,不会触发卸载挂载,但会触发activated/deactivated
keep-alive 不给属性时,默认内部出现过得组件所组成的视图,都会被缓存,初始缓存第0个组件
属性:
:include: ['组件名','组件名2'] 加入一部分
:exclude: ['组件名','组件名2'] 排除一部分
:max = 数字 最多可缓存的组件数
最多可以缓存多少组件实例。一旦这个数字达到了,
在新实例被创建之前,已缓存组件中最久没有被访问
的实例会被销毁掉(卸载挂载)。
组件钩子:
activated 活动组件 被缓存时起效果
deactivated 非活动组件
场景:
keep-alive 包裹 component | router-view
组件通讯
正向数据流,父 -> 子 通过props传递
这里父组件给两个组件都吃传递数据,child1正确接收,但是child是可以做类型校验的,当父组件传递数据的格式进行校验,类型不一致报错,以及当父组件没有传值是,也可以给默认值。
反向数据流,子 -> 父 使用 $emit
子->父 事件(自定义)
<子 @自定义事件="父方法">
子: this.$emit('自定义事件',子.数据名)
父: methods-> 父方法(接受数据){处理}
父子组件数据共享 使用$refs $parent
父子之间共享数据和方法
<子 ref="自定义子名称">
父访问子: this.$refs.自定义子名称.数据名/方法()
子访问父: this.$parent.数据名/方法()
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的,避免在模板或计算属性中访问 $refs
兄弟之间传值 公共总线 eventBus
特别注意:$emit和$on的事件必须在一个公共的实例上,我们可以使用一个空的 Vue 实例作为中央事件总线。
Vue.prototype.$player = new Vue()
this.$player.$emit('自定义的事件名',数据)
this.$player.$on('自定义的事件名',function(接){处理})
使用$root设置全局属性
let app = new Vue({
el: '#app',
// 全局数据,在其他页面或者组建可改变
data: function () {
return {
s: ''
}
},
router,
store,
template: ' '
})
组件内部
console.log(this.$root.s) // 设置了s属性
订阅发布模式(第三方库 pubsub )
- 首先安装pubsub-js
npm install --save pubsub-js
2.订阅方组件
PubSub.subscribe('deleteTodo',(msg,index)=>{
console.log(index)
});
- 发布方组件
PubSub.publish('deleteTodo', 111); //deleteTodo一定要与订阅方名称一样,index是通信的具体数据
状态管理 Vuex
场景: 打算开发中大型应用
特点: 集中式数据管理, 一处修改,多处使用
思维:
store
this.$store.commit('increment') -> mutations
this.$store.dispatch('jia') -> actions
mapActions() ->actions mapGetters('类型')->getters
学生 代课老师 校长 财务 班主任 学生
components - > actions -> mutations -> state <- getters <- components
发送请求 处理 修改状态
业务逻辑 修改state 读取state
异步
state<-$store.state <- 学生
简单使用步骤
安装: npm i vuex -s
引入
import Vue from 'vue'
import Vuex from 'vuex'
//挂载Vuex
Vue.use(Vuex)
//创建VueX对象
const store = new Vuex.Store({
state:{
//存放的键值对就是所要管理的状态
name:'helloVueX'
}
})
export default store
注册
import store from './store/index'
new Vue({
render: h => h(App),
router,
store,
data(){
return {
msg: 111
}
}
}).$mount('#app');
这里在组件里就可以获取到状态管理里面的数据了。
组件内部通过
核心内容:
在VueX对象中,其实不止有state,还有用来操作state中数据的方法集,以及当我们需要对state中的数据需要加工的方法集等等成员。
成员列表:
state 存放状态
mutations state成员操作
getters 加工state成员给外界
actions 异步操作
modules 模块化状态管理
首先,Vue组件如果调用某个VueX的方法过程中需要向后端请求时或者说出现异步操作时,需要dispatch VueX中actions的方法,以保证数据的同步。可以说,action的存在就是为了让mutations中的方法能在异步操作中起作用。
如果没有异步操作,那么我们就可以直接在组件内提交状态中的Mutations中自己编写的方法来达成对state成员的操作。
注意,不建议在组件中直接对state中的成员进行操作。
state
对于数据集中管理,可以理解为仓库
mutations
操作state数据的方法的集合,比如对该数据的修改、增加、删除等等。
mutations方法都有默认的形参:
([state] [,payload])
state是当前VueX对象中的state
payload是该方法在被调用时传递参数使用的
Getters
可以对state中的成员加工后传递给外界
Getters中的方法有两个默认参数
state 当前VueX对象中的状态对象
getters 当前getters对象,用于将getters下的其他getter拿来用
Actions
由于直接在mutation方法中进行异步操作,将会引起数据失效。所以提供了Actions来专门进行异步操作,最终提交mutation方法。
Actions中的方法有两个默认参数
context 上下文(相当于箭头函数中的this)对象
payload 挂载参数
可以看到在2s后数据发生改变
Models
当项目庞大,状态非常多时,可以采用模块化管理模式。Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
models:{
a:{
state:{},
getters:{},
....
}
}
mapActions/mapGetters
执行后, 返回来的是对象
对象: {incremen:fn,decrement:fn,xx,xx}
mapGetters 用来接管 computed的
computed:mapGetters(['类型1','类型2'])
mapActions 用来接管 methods的
methods:mapActions(['类型1','类型2'])
自定义事件,自定义指令
自定义事件
绑定自定义事件:
绑定:vm|组件.$on( '自定义事件名'|['自定义事件名1','自定义事件名2'], 回调(参数) )
<自定义组件 @|v-on:自定义事件
销毁:vm.$off( '自定义事件名'|['自定义事件名1','自定义事件名2'])
触发: vm.$emit(自定义事件名,参数)
自定义事件名: 使用 kebab-case 的事件名
场景: 在一个组件实例上手动侦听事件时
特点: 只有绑定方才可以触发
自定义的组件 触发自定义的事件需要native修饰符
自定义组件 可以使用v-on或@自定义事件
自定义指令
自定义指令: 指令是个函数|对象,用来操作dom的, 里面的this 返回window
a)全局: Vue.directive('指令名不带v-',函数(el,binding))
el == 使用指令的DOM元素
binding 是个对象 含有传入的 参数(binding.value)
b)局部: 定义在选项里面
directives:{
指令名不带v- : 函数(el,binding){}
}
指令是个函数(简写),可以是个对象
{
钩子函数
inserted:fn(el,binding) 绑定指令的元素插入到父节点时调用
bind:fn 指令第一次绑定到元素时调用
update:fn 指令所在的元素的model层的数据,view有更新请求时
componentUpdated:fn 更新完成时
}
简写方式: bind + update
Vue.directive("val",function(el,bind){
el.value=bind.value.max-bind.value.value;
})
第三方插件引入与使用(UI库)
elementUI:
官网:http://element.eleme.io
安装+全局引入
安装: npm i element-ui -S
全局引入:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
按需引入:
npm install babel-plugin-component -D
修改babel配置 baberc|babel.config.js
添加:
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
a) 全局使用: 所有应用内部组件直接使用
import { Button } from 'element-ui';
Vue.component(Button.name, Button); | Vue.use(Button)
b) 组件内部使用: 只有当前组件可使用
import { Select, Option } from 'element-ui';
components:{
'bulala':Select,
[Option.name]:Option,
},