1. 说说你对Vue的理解?
Vue是一个单页面应用的Web框架,也是一个渐进式的MVVM框架。什么是渐进式:强制主张最少,相比于传统的非常多的html文件,Vue只有一个单页面,只是不同页面需要路由去控制。其中的组件化系统、全局状态管理工具、客户端路由等都是传统开发比不了的。
2. 什么是双向绑定?/ 双向绑定的原理是什么?
什么是双向绑定:单向绑定相当于一个使用js代码更新model层,view层相对应的就自动更新,双向绑定就是加上改变view层的数据,同时model层的数据也同时改变,这就是双向绑定。
双向绑定的原理:相当于MVVM,M-model:应用的数据及逻辑,v-view: 页面上显示的数据,vm-viewModel: 核心,关联数据和视图,两部分组成:
(1)监听器(observer)(2)解析器(compiler)
viewModel主要职责:
(1) 数据变化后更新视图
(2) 视图变化后更新数据
3. v-if 和 v-show 的区别
(1) v-if是添加一个节点,v-show是给元素添加一个display:none。
(2) v-if会触发生命周期,v-show不会触发。
(3) v-if有更高的性能消耗,v-show有更高的初始性能消耗。
(4) 频繁的使用建议使用v-show,较少使用建议使用v-if。
4. Vue实例挂载过程中发生了什么?
(1) new Vue()的时候会调用_init() 方法。
(1)$set、$get、$delete、$watch等方法。
(2)$on、$emit等事件。
(3)$_update、$forceUpdate、$destroy等生命周期
(2) 调用$mount进行页面的挂载。
(3) 执行render生成虚拟DOM。
(4) _update将虚拟DOM生成真实DOM结构,渲染到页面上。
5.说说你对Vue生命周期的理解?
一:什么是Vue的生命周期:
Vue 实例从开始创建、初始化数据、编译模板、挂载Dom和渲染、更新和渲染、卸载等一系列过程,这是 Vue 的生命周期。
二:生命周期的过程:
(1) beforeCreate -> created: 初始化vue实例,初始化默认的生命周期钩子和事件,比如data,methods都还未初始化,不能调用。
(2) created: data,methods,watch等都初始化完成,都可以使用,但是vm.$el还未创建。
(3) beforeMount: 已获取到vm.$el,并且vm.el已初始化完成,但并未挂载到el上。
(4) beforeMount -> Mounted: vm.el已挂载完,生成的DOM结构替换el。
(5) Mounted: 已完成vm.el的挂载和渲染,可对DOM进行操作。
(6) beforeUpdate: 数据的view层并未更新,而model层的数据已经更新。
(7) Updated: 完成视图层view的更新。
(8) beforeDestroy: 实例被销毁前调用,实例中所有属性和方法都可以调用。
(9) Destroyed:所有属性和方法都不可用,组件已被销毁。
三:数据请求放在在created还是mouted?
(1)因为如果放在mounted请求有可能导致页面闪动,因为DOM解构已经生成并且渲染,所有放在created里面,因为当前页面DOM结构并未生成,不会发生该情况。
6.为什么vue中的v-if与v-for不能一起使用?
(1)因为v-for的优先级大于v-if,在循环里面每次循环都会去判断一次,会带来性能方面的浪费。
(2)解决办法:在外嵌套一层template模板使用v-if。内层使用v-for进行循环。
7.SPA(单页应用)首屏加载速度慢怎么解决?
一:加载慢的原因
(1)网络延时问题
(2)加载资源太大
(3)资源重复请求
(4)加载脚本,渲染堵塞
二:解决方案
(1)减小入口文件 --> 路由懒加载
(2)静态资源本地缓存 --> 合理利用localStorage、采用http缓存,设置Cache-Control等请求头、采用Service Worker离线缓存
(3)UI框架按需加载 --> element-UI
(4)组件重复打包 --> 在webpach的config文件修改CommonsChunkPlugin的配置。
--> minChunks: 3
(5)图片资源的压缩 --> 使用工具压缩大小、使用雪碧图等方式减轻http请求压力
8.为什么vue中的data属性是一个函数而不是一个对象?
1.当一个组件中data为一个对象时,Vue通过Vue.extend()构成组件实例,相当于构造函数,多次调用,调用的都是相同地址,所有的调用者都会改变,而函数有单独的作用域,地址是不同的,所有data是对象的话会造成变量全局污染。
2.根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
9.Vue给对象新增属性,页面不刷新?
(1).data(){
return {
obj : {
name : "skam",
age: 20
}
}
}
this.obj.gender = '男';
这时页面不会刷新,因为这种添加属性方式并不是通过Object.defineProperty设置成响应式。
(2).解决方式
(2.1)Vue.set(目标对象,'key', 'value');
(2.2) 新建一个响应式空对象接收。this.newArr = Object.assign(this.arr, {"gender", "男"});
10.Vue组件间通信方式有哪些?
(1) props传递(父传子) --> <Father :sendname="name" /> <Child />中的props属性 --> props: ['sendname'];
(2) $emit传递(子传父) --> <Child /> 中的this.$emit("事件名", 参数) <Father 事件名="receivedHandle(data)"/>
(3) ref (父使用子的属性和方法) --> <Child ref='foo' /> <Father /> this.$ref.foo 获取子组件的属性和方法
(4) EventBus (兄弟组件通信)
新建一个EventBus.js文件,引入-> import vue from 'vue' ,然后暴露实例 export default new Vue;
在兄弟组件都引入EventBus.js文件,然后<Child1 />中使用bus.$emit('fn', 传递值) <Child2 />中使用bus.$on('fn', 接受值)
(5) provid和inject (父传子,父传子孙...) <Father /> provide(){ return { foo: 'foo' } },<Child /> 中的inject:['foo'];
(6) vuex --> 适用场景: 复杂关系的组件数据传递
11.Vue中的$nextTick有什么作用?
{{ num }}
for(let i = 0; i < 1000; i++) num++;
当num发生变化时,vue并不会去立即更新,而是放入一个异步队列中,等待所有循环都执行完毕,DOM才会更新。如果没有$nextTick机制,每次循环页面都会刷新,有机制就只会刷新一次。
12.说说Vue中key的原理
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点。
13.keep-alive的理解
1.vue的内置组件,能在组件切换中将状态保存在内存中,防止重复渲染。
2.包裹组件时,会缓存不活动的组件实例,不会销毁它。
3.设置props属性
--> 3.1 include -><keep-alive include="name"><keep-alive/> 名称匹配会被缓存。
--> 3.2 exclude -><keep-alive exclude="name"><keep-alive/> 名称匹配不会被缓存。
--> 3.1 max -> 最多缓存几个组件实例
14.Vue的常用修饰符
1.lazy <input type="text" v-model.lazy="value"> -> 失焦才会触发更新
2.trim <input type="text" v-model.trim="value"> -> 去掉首空格
3.number <input type="text" v-model.number="value"> -> 移动端只会弹出数字键盘,web端会把不是数字和后面的所有去掉
4.stop <div @click="btnFn"><button @click.stop="btnFn">点击</button></div> -> 阻止事件冒泡
5.prevent <form v-on:submit.prevent="onSubmit"></form> -> 阻止事件默认行为
6.self <div @click.self="btnFn"></div> -> 只有点击e.target目标元素才会触发函数
7.once <div @click.once="btnFn"></div> -> 事件只触发一次,第二次不会触发
8.keyCode <div @keyup.keyCode="btnFn"></div> -> 按键为keyCode就会触发事件
9.left.right.middle <div @click.left/right/middle="btnFn"></div> -> 鼠标点击左、右、中键就会触发事件
15.什么是虚拟DOM?为什么需要虚拟DOM?
1.是对真实DOM的抽象,以js对象(虚拟节点)作为基本的树,用对象的属性来描述节点,通过一系列操作使这棵树映射到真实环境
2.在js对象中,虚拟DOM表现为一个Object对象,并且最少包含标签(tag)、属性(attr)、子对象元素(children),不同框架不同区别。
3.目的:更好的将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一对应。Vue可以对抽象树进行新增、修改、删除的操作,经过diff算法得出最小单位,最后渲染到页面,减少了DOM操作,提高了性能。
为什么需要虚拟DOM?
-> 原因:操作DOM是非常慢的,大部分的性能问题,基本上都是操作ODM引起的,频繁操作可能会影响页面卡顿,非常影响用户的体验。
例子:原生api与vue同时更新十次数据。
-> 原生会直接渲染十次DOM节点。
-> Vue使用虚拟DOM,不会立即操作DOM,将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算。
虚拟DOM的优势:(1)diff算法,减少js操作真实DOM带来的性能消耗。(2)抽象了原本的渲染过程,实现了跨平台能力,不仅仅浏览器的DOM,安卓和ios的原生组件、小程序、GUI等。
16.说说Vue中的diff算法
1.diff算法是一种比较同层树节点的高效算法
2.特点
(1)只会比较同层级,不会跨层级比较
(2)循环从两端向中间比较。 -> 旧节点的末端与新节点的始端
3.总结:深度优先,同层比较。
17.Vue项目怎么划分结构和划分组件比较合理呢?
1.为什么要划分?
(1)项目结构清晰会提高开发效率,熟悉项目的各种配置开发效率会更高。
(2)划分项目结构的基本原则。
-> 文件夹和文件夹内部文件的语义一致性。如pages,只包含路由模块,不该有其他模块,好处:一眼就能看到项目的路由有哪些。
-> 单一入口/出口。如:把所有请求接口的函数都放在一个js文件内,使用时只需要导入同一个文件。好处:无论你的模块文件夹内部有多乱,外部引用的时候,都是从一个入口文件引入,这样就很好的实现了隔离,如果后续有重构需求,你就会发现这种方式的优点
-> 就近原则,紧耦合的文件应该放到一起,且应以相对路径引用。 好处:提高移植性。
-> 公共的文件应该以绝对路径的方式从根目录引用。如:更改公共组件目录结构,只需要全局搜索替换。
-> /src 外的文件不应该被引入。好处: 方便划分项目代码文件和配置文件
18.跨域问题
1.JSONP
(1)核心: 动态添加<script>标签来调用服务器提供的js脚本。
(2)原理: 利用script的src属性跨域引用文件,实现跨域请求,前端提前定义一个callback函数,服务端把所有json数据放在 callback里面并执行函数,浏览器在解析script标签把数据当作参数,传入callback函数里。
(3)缺点: 只支持get请求。
2.cors
(1)使用:后端实现cors就可以实现完整的跨域。以下node为例子:
app.use(function (req, res, next) {
res.set({
"Access-Control-Allow-Origin": "www.a.com",
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Headers": "Content-Type, Content-Length, Authorization, 'Origin', Accept, X- Requested-With",
"Access-Control-Allow-Methods": "*",
"Access-Control-Max-Age": 86400
})
next();
});
3.proxy
(1)网络代理
(2)三种方式
(2.1)使用vue-cli的项目中在vue.config.js文件中添加。
module.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
open: true,
proxy: {
'/api': {
target: "http://xxx.xxx.xx.xx:8080",
changeOrigin: true,
pathRewrite: {
'^/api': ""
}
}
}
}
}
(2.2)后端配置代理,以express框架为例
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false
}));
module.exports = app
(2.3)配置nginx代理
server {
listen 80;
# server_name www.josephxia.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http:
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
19.Vue3与Vue2改变了哪些?Vue3的新增特性?
1.Vue3介绍 -->
(1)速度更快 --> 编译模板的变化、更高效的组件初始化、重写了虚拟DOM、ssr速度提升、update性能提升。
(2)体积更小 --> webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
(3)更易维护 --> 可与option API一起使用、灵活的逻辑组合和复用、更好的Typescript支持:vue3是基于TS写的
(4)更接近原生 --> 可以自定义渲染API
(5)更易使用 --> 响应式API暴露出来、轻松识别组件重新渲染原因
2.Vue3新增特性 -->
(1)framents 组件支持多个根节点
(2)teleport 通过Teleport,我们可以在组件的逻辑位置写模板代码,然后在 Vue 应用范围之外渲染它
(3)createRenderer 能够构建自定义渲染器,我们能够将 vue 的开发模型扩展到其他平台
(4)composition API 组合式API,更加容易维护我们的代码,将相同功能的变量进行一个集中式的管理
--> 超链接图片 --> https:
20.什么是SSR?Vue中对SSR的解释?
1.由服务器完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。
2.Vue SSR的解释:
(1)是一个在SPA上进行改良的服务端渲染、
(2)需要在客户端激活才能实现交互
(3)Vue SSR将包含两部分:服务端渲染的首屏,包含交互的SPA
3.解决了什么?
(1)SEO:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于SEO
(2)首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些 交给客户端)
21.Vuex是什么?Vuex有几种属性,它们存在的意义分别是什么?
1.Vuex是一种状态管理模式,存在的目的是共享可复用的组件状态。
2.有五种,分别是 State、 Getter、Mutation 、Action、 Module
(1)State 单一状态树,相当于data。
(2)Getter 相当于computed,对State中的数据进行过滤计算。
(3)mutation 相当于methods,对State中的数据进行状态改变。
(4)Action 类似于mutation,不是直接改变状态,而是提交mutation,用来解决异步操作。
(5)Module 将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter,将Vuex模块化的对象,目 的是更好的维护
22.Vuex中的辅助函数怎么使用?
1.vuex 中的 mapState、mapGetters、mapMutations、mapActions 等辅助函数是我们经常使用到的。
2.如何使用?
(1)mapState:把state属性映射到computed身上。
computed:{
...Vuex.mapState({
input:state=>state.inputVal,
n:state=>state.n
})
}
(2)mapAcions:把actions里面的方法映射到methods中
methods:{
...Vuex.mapActions({
add:"handleTodoAdd",
change:"handleInput"
})
}
等价于
methods: {
handleInput(e){
let val = e.target.value;
this.$store.dispatch("handleInput",val )
},
handleAdd(){
this.$store.dispatch("handleTodoAdd")
}
}
(3)mapMutations:把mutations里面的方法映射到methods中
methods:{
...Vuex.mapMutations({
handleAdd:"handlMutationseAdd"
})
}
(4)mapGetters:把getters属性映射到computed身上
computed:{
...Vuex.mapGetters({
NumN:"NumN"
})
}
23.自定义指令是什么?有哪些应用场景?
1.vue中指令比如v-on、v-for等等,自定义指令就是自己封装的指令。
2.注册方式
(1)全局注册
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
(2)局部注册
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
(3)使用:<input v-focus />
(4)封装自定义指令 --> 时间
(4.1)在src下建立一个direactives文件下,然后定义一个index.js出口文件。
index.js ->
import registerFormatTime from './formatTime'
export default function registerDireactives (app) {
registerFormatTime(app)
}
(4.2)在direactives文件下建立formatTime.js文件为自定义时间指令。
formatTime.js ->
import dayjs from 'dayjs'
export default function (app) {
let formatString = "YYYY-MM-DD HH:mm:ss"
app.directive("format-time", {
created(el, bindings){
if(bindings.value) formatString = bindings.value
},
mounted(el) {
const textContent = el.textContent
let timestamp = parseInt(textContent)
if(textContent.length === 10) timestamp = timestamp * 1000
el.textContent = dayjs(timestamp).format(formatString)
},
})
}
(4.3)在main的入口文件中注册
main.js ->
import registerDireactives from './direactives/index'
registerDireactives(app)
(4.4)在组件中使用 <div v-format-time="'YYYY/MM/DD HH:mm:ss'">{{ format }}</div>
3.应用场景 -> 防抖、图片懒加载等。
24. vue-loader做了哪些事情?
1.配置各种loader主要是把.vue文件中的 template、script、style切割,根据不同loader分别解析,最终为浏览器识别的html文件。
2.css scoped -> css 样式只作用于当前组件中的元素,他会给标签生成唯一标识,例如-> data-v-9ea40744
25.Computed和watch的区别
1.computed
(1)computed的属性都有一个set和get方法,当数据变化时,都会调用set方法。
(2)computed有缓存效果,只有计算的属性发生变化时computed才会重新计算。
(3)当computed是一个函数时,默认会走get方法,必须要有一个返回值,返回值就是定义函数属性的值。
computed:{
fillName () {
return this.firstName + this.lastName;
}
}
(4)当computed是对象时,需要同时给set和get方法。
computed: {
fillName : {
get () {
return this.firstName + this.lastName;
},
set (val) {
const names = val ? val.split('') : [];
this.firstName = names[0];
this.lastName = names[1];
}
}
}
2.watch
(1)监听data、props、computed的数据变化。
(2)支持异步,不支持缓存,监听数据发生变化会触发相应操作。
(3)有两个参数,第一个是新值,第二个是旧值。
watch : {
fillName (newValue, oldValue) {
console.log(newValue);
console.log(oldValue);
}
}
26.react和vue的区别
1.react是通过jsx语法编写,vue是通过模板引擎。
2.react是函数式的思想,单向流思想, vue是响应式的思想。
3.react的性能优化需要手动修改,vue的性能优化是自动的,但是响应式机制也有问题,当state特别多的时候,watcher会很多,导致卡顿。
27.说说vue的页面渲染流程?
1. _init -> $mount -> vm._render(VNOde) -> vm._update -> patch -> createElm
2. 总结:初始化调用$mount挂在组件,render树通过createElmenet方法构建虚拟节点,构建完成后传给_update函数,patch阶段根据VNOde创建真实节点树并挂载到页面上。
28.vue中history模式和hash模式的区别?
1.hash
(1)hash模式是把前端路由的路径用'#'拼接在真实路径后面。
http://localhost:8080/
(2)优缺点:
(2.1) -> 浏览器兼容性好,IE8都支持。
(2.2) -> 比较丑
2.history
(1)直接拼接在真实的路径后面 http://localhost:8080/index.html
(2)优缺点
(2.1) 路径比较正规,没有井号
(2.2) 兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了
29:Vue中的this.$route和this.$router的区别
1.this.$router
this.$router获取的是router实例,通过 this.$router 访问路由器,相当于获取了整个路由文件,在$router.option.routes中,或 查看到当前项目的整个路由结构 具有实例方法
router.beforeEach((to, from, next) => {
})
router.beforeResolve((to, from, next) => {
})
router.afterEach((to, from) => {})
router.push
router.replace
router.go
router.back
router.forward
2.this.$route
当前激活的路由信息对象。这个属性是只读的,里面的属性是 immutable (不可变) 的,不过可以 watch (监测变化) 它。
通过 this.$route 访问的是当前路由,获取和当前路由有关的信息
fullPath: ""
hash: ""
matched: []
meta: {}
name: ""
params: {}
path: ""
query: {}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?