vue的特点
Vue.js是一款轻量级的以数据驱动(数据的变化将引起视图的变化)的构建单页面应用
的mvvm
框架,它比较简单易学,具有双向数据绑定
,在数据操作方面更为简单减少大量的逻辑代码,组件化
,可以封装和重用通用组件,vue引入虚拟dom
,不再使用原生的dom操作节点,极大解放dom操作,使得运行速度更快
什么是单页面应用
单页面应用:
单页面跳转仅刷新局部资源 ,公共资源(js、css等)仅需加载一次,url 模式一般为a.com/#/pageone
多页面应用
多页面跳转刷新所有资源,每个公共资源(js、css等)需选择性重新加载,url 模式一般为a.com/pageone.html
什么是mvvm
MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。
Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;
View 代表UI 组件,它负责将数据模型转化成UI 展现出来,
ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的,因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
什么是虚拟dom
我们知道了Vue是数据驱动视图(数据的变化将引起视图的变化),当你你发现某个数据改变时,视图是局部刷新而不是整个重新渲染,如何精准的找到数据对应的视图并进行更新呢?那就需要拿到数据改变前后的dom结构,找到差异点并进行更新!
虚拟dom的比较,就是找出新节点(vnode)和旧节点(oldVnode)之间的差异新旧节点,如果不相似,直接根据新节点创建dom;如果相似,先是对data比较,包括class、style、event、props、attrs等,有不同就调用对应的update函数,然后是对子节点的比较,子节点的比较用到了diff算法
更详细请看这里
Vue组件化
首先组件的基本构成分别是:样式结构,行为逻辑,数据。web中的组件其实就是页面组成的一部分,每个组件都会提供一些对外接口,允许使用者设置和调整参数属性,可以将不同功能的组件结合在一起,快速的构成一个符合实际需求的应用
双向数据绑定原理
vue双向绑定就是指model层与view层的同步,两者之间任意一个发生变化都会同步更新到另一者。
vue.js 双向数据绑定则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty(obj, prop, descriptor)
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
更过关于Object.defineProperty()请查看
当我们在定义一个属性如下然后打印data这个对象:
data: {
name: "张三"
}
console.log(data)
这个时候除了data对象的值,原型等以外,还有两个方法分别是get和set,
var modelName= "李四";
var data = {};
Object.defineProperty(data,'name', {
get: function(){
return modelName;
},
set: function(newValue){
modelName= newValue;
console.log(`modelName的值已发生改变,目前的值是:${modelName}`);
}
});
data.name; // 当调用这个对象时候会触发get方法,这个时候会返回modelName的值
//,所以这里可以通过改变modelName来改变obj.key的值
data.name = "王五" //当修改这个对象时候会触发set方法,modelName的值已发生改
// 变,目前的值是王五
如此就实现了一个简单的双向绑定:改变modelName,data.name得到的值也会改变,重新设置data.name,modelName一样会随之改变。
更详细请看这里
vue+token验证实现登陆
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码
2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token
3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面
4、前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面
5、每次调后端接口,都要在请求头中加token
6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401
7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
vue的生命周期
vue生命周期就是从开始创建,初始化数据,编译模板,挂载DOM,渲染->更新->渲染,销毁等一系列过程
总共分为8个阶段:
阶段 | 作用 |
---|---|
beforeCreate----创建前 | 组件实例更被创建,组件属性计算之前,数据对象data都为undefined,未初始化。 |
created----创建后 | 组件实例创建完成,属性已经绑定,数据对象data已存在,但dom未生成,$el未存在这个时候html还没有渲染出来 |
beforeMount---挂载前 | vue实例的$el和data都已初始化,挂载之前为虚拟的dom节点,data.message未替换 |
mounted-----挂载后 | vue实例挂载完成,data.message成功渲染。 |
beforeUpdate----更新前 | 当data变化时,会触发beforeUpdate方法 |
updated----更新后 | 当data变化时,会触发updated方法 |
beforeDestory---销毁前 | 组件销毁之前调用 |
destoryed---销毁后 | 组件销毁之后调用,对data的改变不会再触发周期函数,vue实例已解除事件监听和dom绑定,但dom结构依然存在 |
vue生命周期的应用场景:
beforeCreate 可以在此时加一些loading效果,在created时进行移除
created 需要异步请求数据的方法可以在此时执行,完成数据的初始化
mounted 当需要操作dom的时候执行,可以配合$.nextTick 使用进行单一事件对数据的更新后更新dom
updated 当数据更新需要做统一业务处理的时候使用
只有ajax数据请求放在created里面就可以了,这样可以及早发请求获取数据;如果有依赖DOM的情况下,就放到mounted里面
组件之间的传值通信?
父组件向子组件传值:
父组件如下:
<template>
<div>
//这里data为传给子组件的字段,value为需要传的值
<child :data="value" @back= "getBack"></child>
</div>
</template>
<script>
export default {
data () {
return {
value:'父组件的值传给子组件'
}
},
methods: {
getBack(backValue){
//backValue 这个就是回传的值
}
}
</script>
父组件如下:
<template>
<div>
<p @click="backData">这是子组件</p>
</div>
</template>
<script>
export default {
data () {
return {
value1:"返回的值",
}
},
props: ['data'],
methods: {
backData() {
//这里back为事件名称用于父组件接收回传的值,后面为要传的值
this.$emit('back',this.value1);
}
}
}
</script>
兄弟组件传值
bus方式的组件间传值其实就是建立一个公共的js文件,专门用来传递消息
1.建立公共文件,并引入
//新建msgBus.js文件。只需两句代码。
1 import Vue from 'vue'
2 export default new Vue;
//然后在需要传递消息的两个组件引入
import MsgBus from '@/components/utils/msgBus.js';
2.发送消息
//触发组件的事件:
MsgBus.$emit('msg', _this.examineNum);
3.接受消息
//接受组件的事件:
//写在钩子函数内:例如:mounted created都可以
1 MsgBus.$on('msg', (e) => {
2 this.examineNum = e;
3 })
vue中watch、computed
computed
:当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性
computed: {
fullName: function () {
//这里不管是firstName ,还是lastName重要有一个改变就会触发
return this.firstName + ' ' + this.lastName
}
}
需要注意的是,就算在data中没有直接声明出要计算的变量,也可以直接在computed中写入。computed是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么computed会直接从缓存中获取值,多次访问都会返回之前的计算结果
watch
:如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择如:
watch(oldVal,newVal) {
setTimeOut(()=>{
},2000);
}
用watch可以实现2s后更改数据。而这种效果用computed不能实现,因为computed不适合做异步操作
watch监控现有的属性,computed通过现有的属性计算出一个新的属性
watch不会缓存数据,每次打开页面都会重新加载一次,但是computed如果之前进行过计算他会将计算的结果缓存,如果再次请求会从缓存中得到数据
v-show和v-if区别
v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果
一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好
Vue操作DOM
在vue中可以通过给标签加ref属性,就可以在js中利用ref去引用它,从而操作该dom元素,如下:
<template>
<div>
<div id="box" ref="mybox">
DEMO
</div>
</div>
</template>
<script>
export default {
data () {
return {
}
},
mounted () {
this.init();
},
methods:{
init() {
this.$refs.mybox.style.color = 'red';
}
}
}
</script>
vue中 key 值的作用
用于管理可复用的元素。因为Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做使 Vue 变得非常快,但是这样也不总是符合实际需求
例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
那么在上面的代码中切换loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素
这样也不总是符合实际需求,所以Vue为你提供了一种方式来表达这两个元素是完全独立的,不要复用它们。只需添加一个具有唯一值的 key 属性即可:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
现在,每次切换时,输入框都将被重新渲染。
vue事件中如何使用event对象
//html部分
<a href="javascript:void(0);" data-id="12" @click="showEvent($event)">event</a>
//js部分
showEvent(event){
//获取自定义data-id
console.log(event.target.dataset.id)
//阻止事件冒泡
event.stopPropagation();
//阻止默认
event.preventDefault()
}
vue中如何编写可复用的组件
在编写组件的时候,时刻考虑组件是否可复用是有好处的。一次性组件跟其他组件紧密耦合没关系,但是可复用组件一定要定义一个清晰的公开接口。
Vue.js组件 API 来自 三部分:prop、事件、slot:
prop 允许外部环境传递数据给组件,在vue-cli工程中也可以使用vuex等传递数据。
事件允许组件触发外部环境的 action
slot 允许外部环境将内容插入到组件的视图结构内。
代码示例:
<my-component
:foo="bar"
:bar="qux"
//子组件调用父组件方法
@event-a="doThis"
@event-b="doThat">
<!-- content -->
<img slot="icon" src="..." />
<p slot="main-text">Hello!</p>
</my-component>
如何解决非工程化项目,网速慢时初始化页面闪动问题
使用v-cloak指令,v-cloak不需要表达式,它会在Vue实例结束编译时从绑定的HTML元素上移除,经常和CSS的display:none配合使用。
<div id="app" v-cloak>
{{message}}
</div>
<script>
var app = new Vue({
el:"#app",
data:{
message:"这是一段文本"
}
})
</script>
这时虽然已经加了指令v-cloak,但其实并没有起到任何作用,当网速较慢、Vue.js 文件还没加载完时,在页面上会显示{{message}}的字样,直到Vue创建实例、编译模版时,DOM才会被替换,所以这个过程屏幕是有闪动的。只要加一句CSS就可以解决这个问题了:
[v-cloak]{
display:none;
}
在一般情况下,v-cloak是一个解决初始化慢导致页面闪动的最佳实践,对于简单的项目很实用。
如何在组件中使用全局常量
//第一步,在 src 下新建 const 文件夹下 新建 const.js
.
├── src
│ ├── const
│ │ ├── const.js
│ │
│ └── main.js
└── ...
//第二步,如何在 const.js 文件下,设置常量
export default {
install(Vue,options){
Vue.prototype.global = {
title:'全局',
isBack: true,
isAdd: false,
};
}
}
//第三步,在 main.js 下全局引入:
//引入全局常量
import constant from './const/const.js'
Vue.use(constant);
//第四步,即可在 .vue 组件中使用:
//通过js方式使用:
this.global.title
//或在 html 结构中使用
{{global.title}}
如何定义一个常量,允许项目打包后,修改 js 里面的值
第一步,在 static 下新建 config.js:
├── 项目路径
│ ├── static
│ │___├── config.js
第二步,在 config.js 里面设置全局变量:
window.g = {
PUBLIC_IP : "http://10.10.10.10:8080"
}
第三步,在 index.html 里面引入:
<script type="text/javascript" src="./static/config.js"></script>
第四步,在其他 .js 文件中即可使用:
window.g.PUBLIC_IP
第五步,打包后修改:
通过 npm run build
命令打包后,此 config.js 文件会被打包到 dist/static
文件夹下,
此时如果需要修改 PUBLIC_IP
,打开config.js
即可修改,无需重新打包!
vue如何禁止弹窗后面的滚动条滚动
methods : {
//禁止滚动
stop(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='hidden';
document.addEventListener("touchmove",mo,false);//禁止页面滑动
},
/***取消滑动限制***/
move(){
var mo=function(e){e.preventDefault();};
document.body.style.overflow='';//出现滚动条
document.removeEventListener("touchmove",mo,false);
}
}
vue-router实现原理
1. hash 模式
单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。 类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。但是这样存在一个问题,就是 url 每次变化的时候,都会造成页面的刷新。那解决问题的思路便是在改变 url 的情况下,保证页面的不刷新。在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于:
http://www.xxx.com/#/login
这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化
2. history 模式
因为HTML5标准发布。多了两个 API, pushState 和 replaceState ,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有 popstate 事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [...]
})
new Vue({
router
...
})
vue-router如何响应路由参数的变化
复用组件时,想对路由参数的变化作出响应的话, 可以watch (监测变化) $route 对象:
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
或者使用 2.2 中引入的 beforeRouteUpdate 守卫:
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
vue-router有哪几种导航钩子( 导航守卫 )
1、全局守卫: router.beforeEach
2、全局解析守卫: router.beforeResolve
3、全局后置钩子: router.afterEach
4、路由独享的守卫: beforeEnter
5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave
vue-router的几种实例方法以及参数传递
实例方法:
实例方法 | 说明 |
---|---|
this.$router.push(location, onComplete?, onAbort?) | 这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。并且点击 <router-link :to="...">等同于调用 router.push(...) |
this.$router.replace(location, onComplete?, onAbort?) | 这个方法不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录,所以,当用户点击浏览器后退按钮时,并不会回到之前的 URL |
this.$router.go(n) | 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n) |
参数传递方式:
vue-router提供了params、query、meta三种页面间传递参数的方式。
示例:
// 字符串,不带参数
this.$router.push('home')
// 对象,不带参数
this.$router.push({ path: 'home' })
// params(推荐):命名的路由,params 必须和 name 搭配使用
this.$router.push({ name:'user',params: { userId: 123 }})
// 这里的 params 不生效
this.$router.push({ path:'/user',params: { userId: 123 }})
// query:带查询参数,变成 /register?plan=private
this.$router.push({ path: 'register', query: { plan: 'private' }})
//meta方式:路由元信息
export default new Router({
routes: [
{
path: '/user',
name: 'user',
component: user,
meta:{
title:'个人中心'
}
}
]
})
在组件中使用:
//通过 $route 对象获取,注意是route,么有r
this.$route.params
this.$route.query
this.$route.meta
$route
和 $router
的区别
$route
是路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
$router
是“路由实例”对象包括了路由的跳转方法,钩子函数等。
vue-router实现动态加载路由组件( 懒加载 )
{
path: '/home',
name: 'home',
component: resolve => require(['@/components/home'],resolve)
}
什么是vuex
使用vuex的核心概念
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。
Vuex 和单纯的全局对象有以下两点不同
1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
2、你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
ajax请求代码应该写在组件的methods中还是vuex的actions中
一、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
二、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便。
axios有什么特点
1、Axios 是一个基于 promise 的 HTTP 库,支持promise所有的API
2、它可以拦截请求和响应
3、它可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据
4、安全性更高,客户端支持防御 XSRF
axios的封装
不定时更新