- Vue常见的指令有哪些,有什么用
1. v-model
作用: 多用于表单元素实现双向数据绑定,也可实现父子组件传值,本质上是一个语法糖,底层原理是由@input事件+value实现的
2. v-bind 动态绑定
为元素的属性动态绑定值,一般简写为 :
作用: 及时对页面的数据进行更改
3、v-for 动态渲染
v-for 指令基于一个数组来渲染一个列表。建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化
4、v-show 显示内容
v-show的原理是动态为元素添加或移除 `display` 样式,来实现元素的显示和隐藏
5、v-if 显示与隐藏
v-if 的原理是动态的在DOM树上创建或移除元素,实现元素的显示和隐藏
6. v-on 给标签绑定函数
可以缩写为@,例如绑定一个点击函数 函数必须写在 methods 里面
7、v-text和v-html 解析文本
- 双向数据绑定的原理
核心是采用数据劫持结合发布者订阅者模式,通过Object.defineProperty()对每个属性的get和set进行拦截。在数据发生变化的时候发布消息给订阅者,触发相应的监听回调。
仅仅使用Object,defineProperty()就能完成一个简单的双向绑定,但是效率比较低。观察者模式让双向绑定更有效率,它是一对多的模式,一指的是修改的一处数据,多是凡是用了这个数据的地方都更新。数据劫持就是利用Object.defineProperty()读操作数据的set,get。
- 路由的跳转和传参有哪几种方式
1.声明式路由跳转
通过router-link标签进行跳转,使用name或者path都可以,在dom结构中会被渲染成a标签
注意:router-link中链接如果是’/‘开始就是从根路由开始,如果开始不带’/’,则从当前路由开始。
params传参数 (类似post)
路由配置 path: "/home/:id"
不配置path,路由跳转可请求,刷新页面传递的参数会丢失,
配置path,刷新也买你id会被保留
获取路由跳转传递的参数:
html 通过 $route.params.id, script 通过this.$route.params.id
2.编程式路由跳转
1.字符串形式
router.push('home')
2.对象形式
router.push({ path: 'home' })
router.push({ name: 'user'})
3.函数内调用
(不带参数)
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
(query传参)
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
html 取参 $route.query.id
script 取参 this.$route.query.id
(params传参)
只可以使用name
this.$router.push({name:'home',params: {id:'1'}})
html 取参$route.params.id, script 取参this.$route.params.id
- 组件间的通讯方式有哪些
一、父传子
在Vue.component(){}中,加入与template、data同级的props用来接受传值
//接受传值 利用props
props:{
// 自定义接受
//定义数据类型
type : String,
size:Number,
pageSize:{
//定义多种数据类型,用数组形式
type:[Number,String],
//设置默认值,若为基本数据类型不存在深拷贝的问题则可以写成对象的形式
//(也可以写成函数形式),复杂数据类型写为函数形式,目的也是为了数据隔离,
default(){
return 5
}
}
},
然后再vue实例(即父级)控制区内加入组件标签,给标签的属性加冒号(:)即可获取到父级的data中的数据
二、子传父
(一)子组件调用父组件的方法
1.在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的)
<div id='app'>
<div class="container">
<!-- 1.在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的) -->
<!-- 可以传入$event(在这里只能写$event来获取子组件的参数)也可以拿到子组件传递的数据 -->
<son @comment="comment($event,'hello')"></son>
<ul class="list-group">
<li v-for="(item,index) in list " :key="index" class="list-group-item">{{item.content}}
<span class="badge">{{item.name}}</span>
</li>
</ul>
</div>
</div>
--------------------js中
methods: {
// 1.在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的)
// 父组件这个方法没有自定参数,在父组件的方法直接加这个参数就可以拿到子组件给父组件传递的数据
comment(data){
console.log(data);
this.list.push(data)
sessionStorage.setItem('list',JSON.stringify(this.list))
}
},
2.子组件可以触发这个事件$emit('事件名字')
(1)$emit方法第二个参数可以定义子组件给父组件传递的内容
Vue.component('son',{
template:"#son",
data(){
return {
name:'',
content:''
}
} ,
methods:{
issue(){
if(this.name != '' && this.content != '') {
// 2.子组件可以触发这个事件$emit('事件名字')
// 1.$emit方法第二个参数可以定义子组件给父组件传递的内容
this.$emit('comment',{
name:this.name,content:this.content
})
this.name = ''
this.content = ''
} else {
window.alert('请输入评论人及评论内容')
}
}
}
})
(二)在父组件中怎么拿到这内容
1.父组件这个方法没有自定参数,在父组件的方法直接加这个参数就可以拿到
const vm = new Vue({
el: '#app',
data: {
list:[]
},
methods: {
// 1.在父组件中给引用的子组件注册一个事件(这个事件的名字是自定义的)
// 父组件这个方法没有自定参数,在父组件的方法直接加这个参数就可以拿到子组件给父组件传递的数据
comment(data){
//data就是子组件传来的数据
console.log(data);
this.list.push(data)
sessionStorage.setItem('list',JSON.stringify(this.list))
}
},
})
2.父组件有自定义参数,可以传入$event也可以拿到子组件传递的数据。通过$event只能传递第一个参数
<div id='app'>
<div class="container">
<!-- 可以传入$event(在这里只能写$event来获取子组件的参数)也可以拿到子组件传递的数据 -->
<son @comment="comment($event,'hello')"></son>
</div>
</div>
data即为标签中的$event,也就是子组件传来的值,data为标签中调用方法时传的参
const vm = new Vue({
el: '#app',
data: {
list:[]
},
methods: {
//父组件有自定义参数,可以传入$event也可以拿到子组件传递的数据。通过$event只能传递第一个参数。
comment(data,data2){
//data即为标签中的$event,也就是子组件传来的值,data为标签中调用方法时传的参
console.log(data);
console.log(data2);
this.list.push(data)
sessionStorage.setItem('list',JSON.stringify(this.list))
}
},
})
三、vuex
1.定义:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储,管理应用中所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
2.vuex解决的问题
(1)行统一的状态管理,解决不同组件共享数据的问题。
(2)不同视图需要变更同一状态的问题。
(3)使用vuex之后,状态变化更加清晰。
3.uvex的五大核心模块
(1)state
state是什么? 是一个单一状态树,是vuex中为一个的数据源,我们的数据都是放在state中的。
组件中去取state的值,通过this.$store.state,
(2)getters
对state中的数据进行加工(派生)
取getters中的值,通过this.$store.getters,
(3)mutation
修改state中的值,我们state每次变化,都应该由mutation去修改,方便追踪数据的流转。
①this.$store.commit('mutation类型(函数名)',"参数,参数一般是对象形式")
②this.$store.commit({type:'mutation类型(函数名)'},...其他参数)
(4)action
action类似于mutation,不同的是
action可以包含异步操作
action不能直接修改state,如果想修改state的话,需要触发mutation
(5)module
由于使用单一的状态树,项目中的状态会集中在一起,导致难以维护,这个时候可以通过module对store进行拆分。
使用module之后,每个模块都有自己的state、mutation等内容,方便维护
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// number:5,
visible: false,
userInfo:{},
},
getters: {
},
mutations: {
changeVisible(state,payload){
state.visible = payload
},
changeUserInfo(state,payload){
state.userInfo = payload
}
},
actions: {
},
modules: {
}
})
四、ref
(一)获取dom节点
1.给dom节点记上ref属性,可以理解为给dom节点起了个名字。
<!-- 给dom节点记上ref属性,可以理解为给dom节点起了个名字。 -->
<h1 ref="h11" id="h11">h1标签</h1>
2.加上ref之后,在$refs属性中多了这个元素的引用。
3.通过vue实例的$refs属性拿到这个dom元素。
const vm = new Vue({
el: '#app',
data: {
},
methods: {
changeSon(){
this.$refs.myson.sonMsg = '被父组件的按钮改变'
}
},
mounted(){
console.log(document.getElementById('h11'));
console.log(this.$refs);
// 加上ref之后,在$refs属性中多了这个元素的引用。
// 通过vue实例的$refs属性拿到这个dom元素,可以对dom元素进行操作
this.$refs.h11.style.color = 'red'
},
})
(二)获取组件
1.给组件记上ref属性,可以理解为给组件起了个名字。
<!-- 给组件记上ref属性,可以理解为给组件起了个名字。 -->
<son id="myson" ref="myson"></son>
2.加上ref之后,在$refs属性中多了这个组件的引用。
3.通过vue实例的$refs属性拿到这个组件的引用,之后可以通过这个引用调用子组件的方法,或者获取子组件的数据。
const vm = new Vue({
el: '#app',
data: {
},
methods: {
changeSon(){
this.$refs.myson.sonMsg = '被父组件的按钮改变'
}
},
mounted(){
// 加上ref之后,在$refs属性中多了这个组件的引用
// 通过vue实例的$refs属性拿到这个组件的引用,之后可以通过这个引用调用子组件的方法,或者获取子组件的数据
// 获取子组件的数据
console.log(this.$refs.myson.sonMsg);
//可以通过这个引用调用子组件的方法
console.log(this.$refs.myson.log1());
},
})
五、本地缓存
1.localStorage
(1)保存数据:localStorage.setItem(key,value)
(2)读取数据:localStorage.getItem(key)
(3)删除单个数据:localStorage.removeItem(key)
(4)删除所有数据:localStorage.clear()
(5)得到某个索引的key:localStorage.key(index)
2.sessionStorage
(1)保存数据:sessionStorage.setItem(key,value)
(2)读取数据:sessionStorage.getItem(key)
(3)删除单个数据:sessionStorage.removeItem(key)
(4)删除所有数据:sessionStorage.clear()
(5)得到某个索引的key:sessionStorage.key(index)
六、事件总线
6.1定义事件总线对象
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
//给Vue绑定属性
Vue.prototype.xyz=100;
// Vue.prototype.$EventBus=vm
new Vue({
beforeCreate(){
//安装事件总线
Vue.prototype.abc=900;
Vue.prototype.$EventBus=this
},
render: h => h(App),
}).$mount('#app')
6.2 向总线发送事件
语法:this.$EventBus.$emit(发送的事件名,传递的参数)
<!-- -->
<template>
<div id="demo01">
<h1>Demo01组件</h1>
<h2>从Demo02接受的收据:{{msg}}</h2>
<button @click="fasong">发送数据给Demo02</button>
</div>
</template>
<script>
export default {
name: "Demo01",
data () {
return {
msg: ''
}
},
methods: {
test01 (data) {
console.log(data);
this.msg = data
},
fasong () {
this.$EventBus.$emit("send", "我是Demo01页面");
}
},
mounted () {
// console.log(this);
// 2.接受全局的haha事件
this.$EventBus.$on('haha', this.test01)
}
}
</script>
<style scoped>
#demo01 {
background-color: red;
padding: 20px;
margin-bottom: 20px;
}
</style>
6.3接收总线事件
语法:this.$EventBus.$on(监听的事件名, 回调函数)
<!-- -->
<template>
<div id="demo02">
<h1>Demo02组件</h1>
<button @click="sendData">发送事件给Demo01</button>
<h2>从Demo01接受的收据:{{msg}}</h2>
</div>
</template>
<script>
export default {
name: "Demo02",
data () {
return {
msg: ''
}
},
methods: {
sendData () {
// 触发全局的haha事件
this.$EventBus.$emit("haha", '老王')
},
display (data) {
console.log(data);
this.msg = data
}
},
mounted () {
// console.log(this.abc);
console.log(this.$EventBus);
this.$EventBus.$on("send", this.display)
}
}
</script>
<style scoped>
#demo02 {
background-color: blue;
padding: 20px;
}
</style>
6.3总线事件解绑
语法:this.$EventBus.$off(要移除事件名)
在组件离开,也就是被销毁前,要将该监听事件给移除,以免下次再重复创建监听。
beforeDestory () {
//移除事件监听send
this.$EventBus.off("send")
}
- 谈一谈对路由守卫的理解(谈理解,一般就是总分总,它是什么,用在哪,有什么用,怎么用,有什么注意事项,再结合项目说明更好)
路由原理
路由就是用来解析URL以及调用对应的控制器,并返回从视图对象中提取好的网页代码给web服务器,最终返回给客户端。
hash模式:在浏览器中符号的“#”,以及#后面的字符称之为hash,用window.location.hash读取;
特点:
hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,
hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),
replaceState()可以对浏览器历史.,记录栈进行修改,以及popState事件的监听到状态变更。
特点:
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 地址后加上/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。
路由传参:
三种:
分别是query,params,动态路由传参
接收:
通过query方式传递过来的参数一般是通过this.$route.query接收
通过params方式传递过来的参数一般是通过this.$route.params接收
通过动态路由传参方式传递过来的参数一般是通过this.$route.params接收
query使用path和name传参跳转都可以,而params只能使用name传参跳转。
传参跳转页面时,query不需要再路由上配参数就能在新的页面获取到参数,params也可以不用配,但是params不在路由配参数的话,当用户刷新当前页面的时候,参数就会消失。
也就是说使用params不在路由配参数跳转,只有第一次进入页面参数有效,刷新页面参数就会消失。
路由守卫:
2.路由守卫使用的方式有几种? 全局的 单个路由独享的 组件级的
3.vue-router全局有三个守卫
router.beforeEach 全局前置守卫 进入路由之前
router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用 router.afterEach 全局后置钩子 进入路由之后
组件内的守卫:
beforeRouteEnter
beforeRouteUpdata(2.2新增)
beforeRouteLeave
3. 路由守卫钩子函数里面的三个参数分别是什么?
to,from,next 这三个参数:
to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。
next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。
next() 进入该路由。
next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。 next 跳转新路由,当前的导航被中断,重新开始一个新的导航。
- 谈一谈对Vuex的理解
在大型复杂的项目中(多级组件嵌套),需要实现一个组件更改某个数据,多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用vuex比较合适。假如只是多个组件间传递数据,使用vuex未免有点大材小用,其实只用使用组件间常用的通信方法即可 。
uex是专门为Vue服务,用于管理页面的数据状态、提供统一数据操作的生态系统,相当于数据库mongoDB,MySQL等,任何组件都可以存取仓库中的数据。
理解性(可读)
Vuex采用MVC模式中的Model层,规定所有的数据必须通过action—>mutaion—>state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰,同时在调试时也可以通过DEVtools去查看状态。
在当前前端的spa模块化项目中不可避免的是某些变量需要在全局范围内引用,此时父子组件的传值,子父组件间的传值,兄弟组件间的传值成了我们需要解决的问题。虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。vuex的诞生也是为了解决这些问题,从而大大提高我们vue项目的开发效率
- 谈一谈对混入的理解
将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗余度,也可以让后期维护起来更加容易。
这里需要注意的是:提取的是逻辑或配置,而不是HTML代码和CSS代码。其实大家也可以换一种想法,mixin就是组件中的组件,Vue组件化让我们的代码复用性更高,那么组件与组件之间还有重复部分,我们使用Mixin在抽离一遍。
- 谈一谈对插槽的理解
slot是组件内的一个占位符,该占位符可以在后期使用自己的标记语言填充。
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信方式,适用于父组件===>子组件
通过插槽可以让用户拓展组件,去更好地复用组件和对其做定制化处理;
如果父组件在使用到一个复用组件的时候,这个组件在不同的地方有少量的更改,如果去重写组件是很浪费,这时,通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用。
比如:布局组件、表格列、下拉选项、弹框显示内容等。
子组件用<slot>来标记渲染的位置,在父组件的自定义标签中写slot中的结构。如果在子组件的<slot>中写了结构,那么会作为默认显示的内容。
具名插槽为每个slot标记名字,也是处理具有多个插槽时的对应关系
- 什么是跨域,如何解决
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
所谓同源是指,域名,协议,端口均相同,只要有一个不同,就是跨域。
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
解决跨域的方法:
1、jsonp跨域
JSONP(JSON with Padding:填充式JSON),应用JSON的一种新方法,
JSON、JSONP的区别:
1、JSON返回的是一串数据、JSONP返回的是脚本代码(包含一个函数调用)
2、JSONP 只支持get请求、不支持post请求
(类似往页面添加一个script标签,通过src属性去触发对指定地址的请求,故只能是Get请求)
2、nginx反向代理:
www.baidu.com/index.html需要调用www.sina.com/server.php,可以写一个接口www.baidu.com/server.php,由这个接口在后端去调用www.sina.com/server.php并拿到返回值,然后再返回给index.html
3、PHP端修改header
header(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
header(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式
- 谈一谈封装请求的心得
为什么进行封装
简化使用成本。不同于库,封装是针对项目的,我们可以给定请求的主域名、请求头等默认值,减少使用请求时的需要传的参数和其他配置等
可以统一处理一些逻辑,例如对请求异常的提醒处理等
可以对请求进行一些改造以满足使用习惯
怎么封装
回调 VS PROMISE
很明显,回调容易陷入回调地狱,所以无论请求还是其他场景我们目前的编程方式基本都是推荐使用promise的,尤其是新的async/await的引入更是让promise的编程方式更加优雅