1. Vue常见的指令有哪些,有什么用
常用指令 | 作用 |
---|---|
v-model | 多用于表单元素双向数据绑定 |
v-bind | 动态数据绑定,简写为冒号 |
v-on | 事件绑定,简写为@ |
v-for | 动态渲染 |
v-once | 能执行一次性地插值,当数据再次改变时,插值处的内容不会更新 |
v-show | 动态渲染元素的显示与隐藏 |
v-if | 动态渲染和删除元素 |
v-else | 和 v-if 搭配使用 |
v-else-if | 同上 |
v-html | 动态渲染节点的html内容,原内容会被覆盖,可以解析富文本 |
v-text | 动态渲染节点的文本内容,原内容会被覆盖,不可以解析富文本 |
2. Vue 双向数据绑定的原理
Vue2版本:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布 消息给订阅者,触发相应的监听回调来渲染视图。
Vue3版本:使用 ES6 的 Proxy 作为其观察者机制,取代之前使用的Object.defineProperty。
因为Object.defineProperty方法存在一定的局限性,比如它无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。
3. 路由的跳转和传参有哪几种方式
① router-link 不带参数
<div id='app'>
<router-link to="/index" tag="div">去首页</router-link>
<!-- 5.预留显示位置 -->
<router-view></router-view>
</div>
② router-link 带参数
<router-link to="/index/login/personal?id=1&name=张三" tag="button">去个人中心页</router-link>
<router-link :to=`/index/login/personal/${id}/${name}` tag="button">去个人中心页</router-link>
<router-link :to="{name:'geren',params:{id,name}}" tag="button">去个人中心页</router-link>
③ 函数式跳转带参数和不带参数
toIndex() {
this.$router.push({ path: '/index' })
},
toMine() {
this.$router.push({
name: "my",
params: { userid: "芜湖" }
})
},
4. 组件间的通讯方式有哪些
组件间通信的分类可以分成以下
- 父子组件之间的通信
- 兄弟组件之间的通信
- 祖孙与后代组件之间的通信
- 非关系组件间之间的通信
Vue 中8种常规的通信方案:
- 通过 props 传递
- 通过 $emit 触发自定义事件
- 使用 ref
- EventBus
- 通过 $parent 或 $root
- attrs 与 listeners
- Provide 与 Inject (vue3)
- Vuex
5. 谈一谈你对路由守卫的理解?
路由守卫是路由在跳转前、后过程中的一些钩子函数,这些函数可以让你操作一些其他的事,在后台管理中设置权限时经常看到,在实现路由跳转前校验是否有权限,有权限就可以通过,反之就会被执行其他操作,如返回首页。
- 全局守卫:
- 还有一些其他的钩子函数如:beforeResolve(to,from, next)、afterEach(to,from)
const router = createRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
// 返回 false 以取消导航
return false
})
- 路由独享守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
- 组件内的守卫:
<template>
...
</template>
<script>
export default{
data(){
//...
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
</script>
<style>
...
</style>
6. 谈一谈你对Vuex的理解?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。
Vuex有五大核心概念:
- State :提供唯一的公共数据源
- Mutations :用于变更state中的数据,必须是同步函数
- Actions:用于变更state中的数据,但不能直接修改,需要通过context.commit()调用mutation,且可以包含任意的异步操作
- Modules :用于模块化导入其他store
- Getter : Getter用于对 store 中的数据进行加工处理后形成新的数据,类似 Vue 中的计算属性。Getter不会修改store中的数据,而是起到一个包装的作用。Store 中的数据发生变化,Getter的数据也会跟着变化。
Vuex解决了多组件之间状态共享的问题,组件间不用再把数据互相传来传去了,维护了单向数据流的简洁性。
7. 谈一谈你对Mixin混入的理解?
mixin混入就像共用的业务逻辑,可以混入到组件中去,但是组件之间不受影响;
比如有两个非常相似的组件,他们的基本功能是一样的,但他们之间又存在着足够的差异性(如页面结构不同),那么代码中的相同业务就可以把它剥离出来,封装后单独写个mixin,到需要用的组件时再混入进去。代码维护方便,复用率高,书写简洁。
mixin的写法举例:
const mixin= {
data() {
return {
name: '初始名字:张三',
mixinMsg: 'mixinMsg'
};
},
methods: {
// 获取mixin中的msg
getMixinMsg() {
alert(
'我是mixin.js中的getmsg方法,mixinmsg的数据是' + this.mixinMsg
);
},
// 获取home中的homeMsg
getHomeMsg() {
alert(
'我是mixin.js中的getHomeMsg方法,HomeMsg的数据是' + this.homeMsg
);
}
},
created() {
alert('在mixin中vue的data、生命周期、方法等都可以使用');
}
};
export default mixin;
注意:当组件中有与mixin同名的方法或数据时,优先调用本组件的方法或数据,混入的部分失效;
更加详细的解释可以去简书原文查看
8. 谈一谈你对插槽的理解?
slot是组件内的一个占位符,主要的作用就是拓展组件的复用性,让我们在重复使用一个组件的时候可以通过少量的修改就达到复用的效果。
插槽分为具名插槽和默认插槽,默认插槽直接在子组件中书写slot后,再在子组件标签中传递需要的html结构即可,具名插槽有两种写法:
具名插槽写法一(标签法)这种写法2.6.0版本后已废弃 :
//父组件
<Category title="美食">
<ul slot="foods">
<li v-for="(item,key) in foods" :key="key">{{item}}</li>
</ul>
</Category>
//子组件
<h3>{{title}}分类</h3>
<slot name="foods">默认显示内容</slot> /*给插槽取名*/
具名插槽写法二(template法,v-slot只能写在template标签中):
<template v-slot:foods>
<ul>
<li v-for="(item,key) in foods" :key="key">{{item}}</li>
</ul>
</template>
还有一种 scope 作用域插槽,这里不做注解,详情在此原文
9. 什么是跨域,如何解决?
跨域即受 同源策略 影响,两个页面资源之间的 “协议+域名+端口” 三者之中有任何一项不同即为跨域。(同源策略会阻止一个域的JavaScript脚本和另一个域的内容进行交互,是用于隔离潜在恶意文件的关键安全机制。)
如何解决跨域问题:
-
后台直接不做限制,放开所有请求。优点:方便;缺点:显而易见的不安全;
-
JSONP
基本原理:利用script标签的 src 不受同源策略限制,需要后端配合,已经不常用了 -
配置代理:Proxy
首先在项目根目录下创建一个vue.config.js文件,然后在其中添加以下代码:
// vue.config.js for less-loader@6.0.0
module.exports = {
devServer: {
proxy: {
//👇 代理的名称,一般以这种格式命名,当然你也可以随便起
'/xxx-api': {
target: 'http://1.111.xx.123:3000', // 后台接口的协议、域名、端口号
changeOrigin: true, //是否跨域
pathRewrite: {
'^/xxx-api': ''//路径重写
//如果不重写,则请求时的路径中会带有这个/xxx-api
}
},
}
}
};
10. 谈一谈你对封装请求的心得
封装请求可以提高程序的复用性、简洁性和可维护性,能够提高工作效率;
假设在一个项目中有20个页面,每个页面都需要向服务器发送请求来完成数据的交互,突然有一个新增的需求,要给请求中的data里加一个参数,如果没有请求进行封装的话,这20个页面中每个请求都需要修改一次,非常的不方便,而封装之后只需要修改封装的js文件里的内容就好了;
而且在 VueCLI 创建的 Vue 项目中,如果不封装 axios 请求,一般需要把 axios 实例挂载在 Vue 实例的原型上;
而封装之后则可以哪里需要哪里引入,不必再挂载在原型上,降低了代码的耦合度,提高了安全性;
如何封装 axios 见此:封装axios