1、组件间怎么传值,具体说说代码怎样实现
子传父:子向父是通过 events(
$emit
);通过父链 / 子链也可以通信($parent
/$children
);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
父传子:父向子传递数据是通过 props,
兄弟组件传值:bus,vuex
跨级父子通信:Bus;Vuex;provide / inject API、
$attrs/$listeners
2、Vue双向绑定原理
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
3、Vue的生命周期和钩子函数
[图片上传失败...(image-87e971-1566982812354)]
beforecreate
: 举个栗子:可以在这加个loading事件
created
:在这结束loading,还做一些初始化,实现函数自执行
mounted
: 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy
: 你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
4、应该在vue的生命周期的什么阶段发出ajax请求,为什么
看实际情况,一般在 created
里面就可以,如果涉及到需要页面加载完成之后的话就用 mounted
5、vuex是什么?怎么使用?哪种功能场景使用它?
只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事务; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,….. export 。
场景有:单页应用中,组件之间的状态同步、音乐播放、登录状态、加入购物车
[图片上传失败...(image-8e3ed4-1566982812354)]
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
})
6、Vue路由守卫
路由跳转前做一些验证,比如登录验证,是网站中的普遍需求。对此,vue-route 提供的 beforeRouteUpdate 可以方便地实现导航守卫。
全局守卫
你可以使用 router.beforeEach
注册一个全局前置守卫:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
每个守卫方法接收三个参数:
-
to: Route
: 即将要进入的目标 路由对象 -
from: Route
: 当前导航正要离开的路由 -
next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数,确保要调用next
方法,否则钩子就不会被 resolved
举一个例子:
- 列举需要判断登录状态的“路由集合”,当跳转至集合中的路由时,如果“未登录状态”,则跳转到登录页面LoginPage;
- 当直接进入登录页面LoginPage时,如果“已登录状态”,则跳转到首页HomePage;
7、常用的vue指令,Vue的自定义指令怎么做
常用的vue指令有v-if、v-show,v-for,v-model等等
- 创建局部指令
var app = new Vue({
el: '#app',
data: {
},
// 创建指令(可以多个)
directives: {
// 指令名称
drag: {
inserted(el) {
// 指令中第一个参数是当前使用指令的DOM
console.log(el);
console.log(arguments);
// 对DOM进行操作
el.style.width = '200px';
el.style.height = '200px';
el.style.background = '#000';
}
}
}
})
- 全局指令
Vue.directive('dir2', {
inserted(el) {
console.log(el);
}
})
- 指令的使用
<div id="app">
<div v-dir1></div>
<div v-dir2></div>
</div>
8、Vue登录流程?需要做什么验证
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
第一次登录的时候,前端调后端的登陆接口,发送用户名和密码
后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token
前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面
前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面
每次调后端接口,都要在请求头中加token,我们常用axios的请求库中,添加全局拦截器,将token设置在请求头中。
后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401
如果前端拿到状态码为401,就清除token信息并跳转到登录页面
// http request 拦截器
axios.interceptors.request.use(
config => {
if (store.state.token) { // 判断是否存在token,如果存在的话,则每个http header都加上token
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
});
// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 清除token信息并跳转到登录页面
store.commit(types.LOGOUT);
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data) // 返回接口返回的错误信息
});
9、讲一下MVVM中的vm工作流程
MVVM 是 Model-View-ViewModel 的缩写。
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
10、Vue和react的区别?
react和vue都是做组件化的,整体的功能都类似,但是他们的设计思路是有很多不同的。使用react和vue,主要是理解他们的设计思路的不同
react整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流,
而vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom
react的性能优化需要手动去做,而vue的性能优化是自动的,但是vue的响应式机制也有问题,就是当state特别多的时候,Watcher也会很多,会导致卡顿
react是类式的写法,api很少,而vue是声明式的写法,通过传入各种options,api和参数都很多
react可以通过高阶组件(Higher Order Components--HOC)来扩展,而vue需要通过mixins来扩展
总结:react整体的思路就是函数式,所以推崇纯组件,数据不可变,单向数据流,当然需要双向的地方也可以做到,比如结合redux-form,而vue是基于可变数据的,支持双向绑定。react组件的扩展一般是通过高阶组件,而vue组件会使用mixin。vue内置了很多功能,而react做的很少,很多都是由社区来完成的,vue追求的是开发的简单,而react更在乎方式是否正确。
11、Vue里面的插槽
<slot>
是组件的一块HTML模板,父组件决定这块模板显不显示以及怎么显示。
位置由子组件自身决定(slot现在组件template的什么位置,父组件传过来的模板将来就显示在什么位置)
匿名插槽:只能有一个,可以放在组件的任何位置
<v-xx><h1></h1></v-xx>
12、Vue-cli2.0和Vue-cli3.0的区别
用vue-cli3.0版本创建的项目与2.0版本相比较,我们会发现,文件目录少了很多 eg:build、config,那么如何像vue-cli 2.* 之前关于端口号的配置、打包之后的路径的配置、图片的配置等,到哪里配置呢??vue-cli 3.0 可以在项目的根目录下新建一个 vue.config.js 文件,之前繁琐的配置都可以在这里直接配置
3.0能直接运行单个组件
3.0有一个UI管理界面
安装了2.0版本,要先卸载
13、Vue的路由实现 || vue-router的原理
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 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
14、vue路由的钩子函数
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
15、对keep-alive 的了解
<keep-alive>
是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
<keep-alive>
与 <transition>
相似,只是一个抽象组件,它不会在DOM树中渲染(真实或者虚拟都不会),也不在父组件链中存在,比如:你永远在 this.$parent
中找不到 keep-alive
。
keep-alive生命周期钩子函数:activated、deactivated
使用<keep-alive>
会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated
阶段获取数据,承担原来created钩子中获取数据的任务。
被包含在 <keep-alive>
中创建的组件,会多出两个生命周期的钩子: activated
与 deactivated
activated:在组件被激活时调用,在组件第一次渲染时也会被调用,之后每次keep-alive激活时被调用。
deactivated:在组件被停用时调用。
注意:只有组件被 keep-alive
包裹时,这两个生命周期才会被调用,如果作为正常组件使用,是不会被调用,以及在 2.1.0
版本之后,使用 exclude
排除之后,就算被包裹在 keep-alive
中,这两个钩子依然不会被调用!另外在服务端渲染时此钩子也不会被调用的。
什么时候获取数据?
当引入keep-alive
的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。
16、DOM 和虚拟DOM的区别 以及你对于他们了解
DOM的本质:DOM是浏览器概念,浏览器从服务器端读取html页面,浏览器将html解析成一棵元素嵌套关系的dom树,用对象来表示页面上的元素,并提供操作dom对象的api。
虚拟DOM:框架概念,程序员用js对象来模拟页面上dom元素的嵌套关系( 本质 ),为了实现页面元素的高效更新( 目的 )
虚拟DOM是真实DOM结构的映射,即一个数据集合
// 对于这个Html 文件
<div class="">
<p>
<span>
Hello, the world!
</span>
<span>
Hello, the code!
</span>
</p>
</div>
// 它对应的虚拟DOM就是
let nodesData = {
tag: 'div',
attr: []
children: [
{
tag: 'p',
children: [
{
tag: 'span',
children: [
{
tag: '#text',
text: 'Hello, the world!'
}
]
}
]
},
{
tag: 'span',
children: [
{
tag: '#text',
text: 'Hello, the code!'
}
]
}
]
}
// 或这种写法
let vNodes = v('div', [
v('p', [
v('span', [ v('#text', 'Hello, the world!') ] )
]
),
v('span', [
v('#text', 'Hello, the code!')
])
]
)
虚拟DOM的核心就是一个diff算法:使用一个render()方法就可以将上面vNodes还原成真实的Html页面
17、vue指令的生命周期
自定义指令有五个生命周期(也叫钩子函数),分别是 bind,inserted,update,componentUpdated,unbind
- bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
- update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
18、vue封装公共组件(通用组件)需要考虑到什么
开发通用组件是很基础且重要的工作,通用组件必须具备高性能、低耦合的特性
一、数据从父组件传入
为了解耦,子组件本身就不能生成数据。即使生成了,也只能在组件内部运作,不能传递出去。
父对子传参,就需要用到 props,但是通用组件的的应用场景比较复杂,对 props 传递的参数应该添加一些验证规则
二、在父组件处理事件
在通用组件中,通常会需要有各种事件,
比如复选框的 change 事件,或者组件中某个按钮的 click 事件
这些事件的处理方法应当尽量放到父组件中,通用组件本身只作为一个中转
三、记得留一个 slot
一个通用组件,往往不能够完美的适应所有应用场景
所以在封装组件的时候,只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决
四、不要依赖 Vuex
父子组件之间是通过 props 和 自定义事件 来传参,非父子组件通常会采用 Vuex 传参
但是 Vuex 的设计初衷是用来管理组件状态,虽然可以用来传参,但并不推荐
因为 Vuex 类似于一个全局变量,会一直占用内存
在写入数据庞大的 state 的时候,就会产生内存泄露
五、合理运用 scoped 编写 CSS
在编写组件的时候,可以在 <style> 标签中添加 scoped,让标签中的样式只对当前组件生效
但是一味的使用 scoped,肯定会产生大量的重复代码
所以在开发的时候,应该避免在组件中写样式
当全局样式写好之后,再针对每个组件,通过 scoped 属性添加组件样式
19、父组件怎么调用子组件里的方法
在父组件中:首先要引入子组件 import Child from './child';
<child ref="mychild"></child>是在父组件中为子组件添加一个占位,ref="mychild"是子组件在父组件中的名字
父组件中 components: { 是声明子组件在父组件中的名字}
在父组件的方法中调用子组件的方法,很重要 this.$refs.mychild.parentHandleclick("嘿嘿嘿"); parentHandleclick是子组件中的方法
20、css只在当前组件起作用
答:在style标签中写入scoped即可 例如:<style scoped></style>
21、v-if 和 v-show 区别
答:v-if按照条件是否渲染,v-show是display的block或none;
22、router的区别
答:router是“路由实例”对象包括了路由的跳转方法,钩子函数等。
23、vue.js的两个核心是什么?
答:数据驱动、组件系统
24、vue常用的修饰符?
答:.prevent: 提交事件不再重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素本身而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用
25、v-on 可以绑定多个方法吗?
答:可以
26、vue中 key 值的作用?
答:当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用主要是为了高效的更新虚拟DOM。
27、什么是vue的计算属性?
答:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。好处:①使得数据处理结构清晰;②依赖于数据,数据更新,处理结果自动更新;③计算属性内部this指向vm实例;④在template调用时,直接写计算属性名即可;⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
计算属性就是当其依赖属性的值发生变化时,这个属性的值会自动更新。
<body>
<div id="app">
<input type="text" v-model="msg">
<p>原始字符:{{msg}}</p>
<p>计算属性翻转字符:{{reverseMsg}}</p>
</div>
<script>
new Vue({
el:'#app',
data:{
msg:'Hello'
},
//vue的计算属性
computed:{
reverseMsg(){
//返回翻转后的字符串,当msg变化后reverseMsg会跟着变化
return this.msg.split('').reverse().join('')
}
}
})
</script>
</body>
28、vue等单页面应用及其优缺点
答:优点:Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。
缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。
29、路由之间跳转
声明式(标签跳转) 编程式( js跳转)
通过router-link实现跳转
通过js的编程的方式
30、Vue中怎么实现跨域
使用http-proxy-middleware 代理解决(项目使用vue-cli脚手架搭建)
例如请求的url:“http://aa.com/demo.json“
1、打开config/index.js,在proxyTable中添写如下代码:
proxyTable: {
'/api': { //使用"/api"来代替"http://aa.com"
target: 'http://aa.com', //源地址
changeOrigin: true, //改变源
secure:false // 是否使用https
pathRewrite: {
'^/api': '/api' //路径重写
}
}
}
2、使用axios请求数据时直接使用“/api”
getData () {
axios.get('/api/demo.json', function (res) {
console.log(res)
})
以上配置只是在开发环境(dev)中解决跨域。要解决生产环境的跨域问题,则在config/dev.env.js
和config/prod.env.js
里也就是开发/生产环境下分别配置一下请求的地址API_HOST
,开发环境中我们用上面配置的代理地址api,生产环境下用正常的接口地址
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API_HOST:"/api/"
})
'use strict'
module.exports = {
NODE_ENV: '"production"',
API_HOST:"http://aa.com"
}
31、Vue首屏加载过慢的解决方法有哪些
vue首屏加载过慢的原因
- 网速慢肯定会导致首屏加载过慢,但是在这里我们不做讨论
- vue项目作为一个单页面应用,如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,这必将拖慢加载速度;
- 通过查看Network,发现整个网站加载试讲长达10几秒,加载时间最长的就是js、css文件和媒体文件及图片
解决方案
vue-router 路由懒加载
在项目开发中,我们会用到很多第三方库,如果可以按需引入,我们可以只引入自己需要的组件,来减少所占空间,但也会有一些不能按需引入,我们可以采用CDN外部加载,在index.html中从CDN引入组件,去掉其他页面的组件import,
关闭sourcemap,sourcemap是为了方便线上调试用的,因为线上代码都是压缩过的,导致调试极为不便,而有了sourcemap,就等于加了个索引字典,出了问题可以定位到源代码的位置。
但是,这个玩意是每个js都带一个sourcemap,有时sourcemap会很大,拖累了整个项目加载速度,为了节省加载时间,我们将其关闭掉开启gzip压缩,这个优化是两方面的,前端将文件打包成.gz文件,然后通过nginx的配置,让浏览器直接解析.gz文件。
加个loading效果:首页加个好看的loading阻塞一下,让用户别等的那么心焦。
如果首页真的有瓶颈,可以考虑用node单独做服务端渲染,而下面的子页面仍用spa单页的方式交互。
32、vue如何实现按需加载组件
https://www.cnblogs.com/-roc/p/9983177.html
33、 请说下封装 vue 插件封装过程
https://www.jb51.net/article/157120.htm
34、vue项目的多语言处理
Vue已经有了这个多语种的插件,vue-i18n
35、vue中的watch介绍和场景
监听并处理data属性的更新,对data属性的监听,说明属性是在data中声明过的
属性更新时调用监听函数,可选参数分别为新值和旧值,对属性重新设置值,只要跟原来的值相等就不会触发函数调用,这一点跟计算属性是相似的,
// 基础用法
watch: {
activeTab(newValue, oldValue) {
console.log(newValue, oldValue);
this.getList();
}
}
// 函数体调用Vue实例的方法可简写
watch: {
activeTab: 'getList'
}
属性初始化的值默认不会触发监听,解决办法添加说明immediate:true,表示监听初始值,此时使用handler写法
watch: {
activeTab: {
handler(newValue, oldValue) {
console.log(newValue, oldValue);
this.getList();
},
// 立即执行handler函数
immediate: true
}
}
当被监听的属性为对象时,默认不会监听对象内部属性的变化,而是只监听属性被赋值时的变化,解决办法添加说明deep:true(默认为false),此时监听器会深度遍历给对象的每一个属性都带上监听器,更新写法
// 监听对象的所有属性
watch: {
activeTab: {
handler(newValue, oldValue) {
console.log(newValue, oldValue);
this.getList();
},
// 深度监听
deep: true
}
}
// 监听对象的某些属性
watch: {
'activeTab.index': {
handler(newValue, oldValue) {
console.log(newValue, oldValue);
this.getList();
}
}
}
另外组件中的监听器会随组建的注销而注销,不会造成内存溢出,但如果使用命令式的( vm.$watch)全局的监听器需要手动注销才行