前言
一些自己的刷题笔记总结
目录
5.v- for和v-if同时使用 有一个优先级的问题 造成浪费
2.this.$route,通过this.$route获取参数
20.子组件可以直接修改父组件的数据吗?会发生什么?有哪几种解决方案
23.3 less特性 23.4 webpack使用less怎么配置
25.Vue自定义指令 钩子函数(指令名字,配置对象(属性,函数))
1.什么是SPA
SPA就是web页面初始化时就加载相应的HTML、css和js。
一旦页面加载完成后不会因为用户的操作而频繁刷新界面,取而代之的是使用路由机制进行跳转。
Vue单页面:只有一个页面index.html,在里面切换不同的组件。不仅与页面交互无刷新,而且与页面跳转都无刷新,为了实现单页面应用,从而产生了前端路由。
单页面应用(SPA,single-page application):仅在Web页面初始化时加载相应的HTML、js和CSS。一旦页面加载完成,SPA不会因用户的操作而进行页面的重新加载或跳转;而是利用路由机制实现HTML内容的变换,UI与用户的交互。
优点:
(1)良好的交互体验,内容的改变不需要重新加载整个页面。
(2)对服务器压力较小。
(3)前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。
缺点:
(1)不利于seo(Search Engine Optimization)。
(2)导航不可用,单页面不能用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理。
(3)首屏加载缓慢。
首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
首屏加载可以说是用户体验中最重要的环节
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新。
2.MVVM模型
- MVC
M:model 负责保存应用数据,与后端数据进行同步
V:view负责视图展示,将model中的数据可视化出来
C: controller负责业务逻辑,根据用户行为对model数据进行修改
单向通信
- MVVM
M: model 负责保存应用数据,与后端数据进行同步
V: view
VM :view-model
- VM通过实现一套数据响应式机制自动响应Model中的数据变化,
- VM会实现一套更新策略自动将数据变化转换为视图更新
- 通过事件监听响应V中用户交互修改M中的数据
- 这样VM中就减少了大量DOM操作代码
- MVVM在保持V和M松耦合的同时,还减少了维护她们关系的代码,使用户专注于业务逻辑,兼顾开发和可维护性
3.计算属性和监听属性的区别
- computed是依赖已有的变量来计算一个目标变量,大多数情况都是多个变量凑在一起计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下其会直接读取缓存进行复用;computed不能进行异步操作
- watch是监听某一个变量的变化,并执行相应的回调函数,通常是一个变量的变化决定多个变量的变化,watch可以进行异步操作
- 简单记就是:一般情况下computed是多对一,watch是一对多
computed能完成的功能,watch都可以完成
watch能完成的功能,computed不一定能完成。watch可以进行异步操作
4. v-bind、v-model、v-on、
- v-bind是绑定属性, 绑定DOM, : 是指令 “v-bind”的缩写,绑定style和class
- v-model 双向绑定数据 “.”是修饰符
- v-on是绑定事件,“@”是指令“v-on”的缩写,语法糖
5.v- for和v-if同时使用 有一个优先级的问题 造成浪费
答案解析
- v-for 和 v-if 不要在同一个标签中使用,因为解析时先解析 v-for 再解析 v-if(v-for 指令比 v-if 优先级高)。
- v-for在Vue2中优先级更高
思路分析:总分总模式
先给出结论
为什么是这样的
它们能放一起吗
如果不能,那应该怎样
总结
回答范例:
在
Vue 2
中,v-for
优先于v-if
被解析;但在Vue 3
中,则完全相反,v-if
的优先级高于v-for
。我曾经做过实验,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件
实践中也不应该把它们放一起,因为哪怕我们只渲染列表中一小部分元素,也得在每次渲染的时候遍历整个列表。
通常有两种情况下导致我们这样做:
为了过滤列表中的项目 (比如
v-for="user in users" v-if="user.isActive"
)。此时定义一个计算属性 (比如activeUsers
),让其返回过滤后的列表即可。
为了避免渲染本应该被隐藏的列表 (比如
v-for="user in users" v-if="shouldShowUsers"
)。此时把v-if
移动至容器元素上 (比如ul
、ol
)即可。
文档中明确指出永远不要把
v-if
和v-for
同时用在同一个元素上,显然这是一个重要的注意事项。看过源码里面关于代码生成的部分,
知其所以然:
在
Vue 2
中做个测试,test.html 两者同级时,渲染函数如下:ƒ anonymous( ) { with(this){return _c('div',{attrs:{"id":"app"}},_l((items),function(item){return (item.isActive)?_c('div',{key:item.id},[_v("\n "+_s(item.name)+"\n ")]):_e()}),0)} }在
Vue 3
中做个测试,test-v3.html 两者同级时,渲染函数如下:(function anonymous( ) { const _Vue = Vue return function render(_ctx, _cache) { with (_ctx) { const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createCommentVNode: _createCommentVNode } = _Vue return shouldShowUsers ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(items, (item) => { return (_openBlock(), _createElementBlock("div", { key: item.id }, _toDisplayString(item.name), 1 /* TEXT */)) }), 128 /* KEYED_FRAGMENT */)) : _createCommentVNode("v-if", true) } } })源码中找答案:
Vue 2
:compiler/codegen/index.jsVue 3
:compiler-core/src/codegen.ts
6.v-if和v-show的区别
- v-show:display:none,不管什么状态,子组件都会被渲染,只是通过display:none进行显示上的切换 适用于需要频繁切换的场景
- v-if: visibility:hidden,真正的创建或销毁对象,适用于运行时很少改变的场景
答案解析
- v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。表达式通过的情况下将元素渲染到 DOM 中。
- v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)。条件满足时基于 CSS display 属性来显示元素
- v-if 支持 v-else 和 v-else-if 指令,而 v-show 不支持 else 指令。
- v-if 有更高的切换开销,而 v-show 有更高的初始化渲染开销
- v-if 支持
<template>
选项卡,但 v-show 不支持
使用场景
- v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
- v-show 适用于需要非常频繁切换条件的场景
7.Vue中的key有什么作用
- 因为vue组件高度复用,增加key可以标识组件的唯一性,可以更好地区别各个组件。key的作用主要是为了高效的更新虚拟DOM
- 使用key,会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。也可以强制替换元素/组件而不是重复使用它。
- 当以数组的下标index作为key值时,其中一个元素通过增删改查发生了变化就有可能导致所有元素的key值发生改变。
答案解析
为了跟踪每个节点的特征,从而重用和重排存在的元素,你需要为每一个
v-for
迭代的项提供一个key
属性。一个理想的 key 值是条目的唯一 id<!--举个例子--> <div v-for="item in items" :key="item.id"> {{item.name}} </div>
因此,只要有可能,总是建议给 v-for 提供一个 key 值,除非迭代的 DOM 内容很简单。
注:不应该使用非基本类型的值,如对象和数组作为 v-for 的 key,使用 String 或 Number 类型替代。
8.关于Vue中的diff算法
9.Vue组件中data为什么必须是一个函数
Object是引用数据类型 如果不用function返回 每个组件的data都是内存的同一个地址
一个数据改变了其他也会改变 js中只有函数构成作用域
data是一个函数时 每个组件实例都有自己的作用域 每个实例相互独立 不会相互影响
10.Vue有哪几种通信方式
https://blog.csdn.net/qq_44810886/article/details/124191087?spm=1001.2014.3001.5501
11.Vue中组件通信方式
父子组件通信:父组件向子组件传递数据通过props,子组件向父组件传递事件通过$emit方法。祖先元素和后代元素通信:用父子组件通信只能一代一代传递,从而引出上下文的方式,把后代元素所需要的信息全部放在祖先元素上下文中。任意组件之间通信:用localStorage和Vuex。
- props / $emit适用父子组件通信。
- ref与$parent / $children适用父子组件通信。
- provide / inject适用于隔代组件通信:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。跨级组件间建立了一种主动提供与依赖注入的关系。
- Vuex 适用于父子、隔代、兄弟组件通信。
13.关于路由模式,路由切换的原理是什么呢
vue-router有两种模式
- history:如:http://localhost:8080/about,
原理:history通过H5提供的API history.pushState 和 history.replaceState实现跳转且不刷新页面,history模式需要后端进行配合
- hash模式: 如:http://localhost/#/about,
原理:window对象提供了onhashchange事件来监听hash值的改变,一旦url中的hash值发生改变,便会触发该事件
本质上是改变window.location的href属性, Hash模式基于锚点(#),以及onhashchange事件 —— 通过锚点的值作为路由地址
window.onhashchange = function(){
// hash 值改变
// do you want
}
14、路由守卫钩子函数
1.全局路由守卫的钩子函数有
- 全局前置守卫:router.beforeEach()
- 全局解析守卫:router.beforeResolve()
- 全局后置守卫:router:afterEach()
注意:名字中间没有“Route”,组件级路由守卫的钩子函数才有“Route”
2.路由独享守卫
beforeEnter:(to,from,next){
条件代码
}
3.组件内守卫
- 组件内的前置守卫:beforeRouteEnter((to,from,next)=>{})
- 组件内的后置守卫:beforeRouteLeave()
- 组件内的更新守卫:beforeRouteUpdate()
15、关于this.$router和this.$route
1.this.$router
- this.$router是全局的路由对象,options.routes包括所有的路由路径
- this.$router.push() ,导航到不同的URL
- this.$router.go()
2.this.$route,通过this.$route获取参数
- this.$route是本页面的路由对象, 可以获取name,path,query,params
- this.$route.params
- this.$route.query
this.$route是路由参数对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
this.$router是一个路由导航对象,包括路由的跳转方法,构造函数,可使用JS代码,实现路由的前进、后退(go)、跳转到新的URL地址。四种方式:
最简单的:this.$router.push("/home/goodsinfo/"+id)。
传递对象:this.$router.push({ path: "/home/goodsinfo/" + id })。
传递命名的路由:this.$router.push({ name: "goodsinfo" , params: { id }})。
带查询参数 /home/goodsinfo?plan=private:
this.$router.push({ path: "/home/goodsinfo/",query:{plan:'private'} })
16.路由懒加载
整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。
按需去加载路由对应的资源,提高首屏加载速度(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)。
实现原理:将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容。
- 结合Vue的异步组件和Webpack的代码分析
const Home = resolve => { require. ensure(['. . /components/Home.vue' ], ( ) => { resolve(require('. ./ components/Home.vue')) )};
- AMD写法
const About = resolve => require(['. ./ components/ About.vue'], resolve);
- 在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
const Home = () => import('. ./ components/Home. vue' )
路由传递参数有哪几种方式
路由怎么嵌套子路由
17. 关于<keep-alive>
< keep-alive >是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
keep-alive的作用:缓存组件,重新返回某个页面的时候不会重新发送http请求, 在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换时,它的 mounted 和 unmounted 生命周期钩子不会被调用,取而代之的是 activated 和 deactivated。(这会运用在 <keep-alive> 的直接子节点及其所有子孙节点。)
对于被 <keep-alive> 包裹的组件:
第一次进入:created ==> mounted ==> activated
离开:deactivated
再一次进入:activated
18. VUE的生命周期
Vue的生命周期(11个钩子函数)
- beforeCreate(创建前):在此生命周期函数执行的时候,data和methods中的数据都还没有初始化。
- created(创建后):在此生命周期函数中,data和methods都已经被初始化好了,如果要调用 methods中的方法,或者操作data中的数据,最早只能在created中操作。
- beforeMount(载入前):在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。
- mounted(载入后):此时页面和内存中都是最新的数据,这个钩子函数是最早可以操作dom节点的方法。
- beforeUpdate(更新前):此时页面中显示的数据还是旧的,但是data中的数据是最新的,且页面并未和最新的数据同步。
- Updated(更新后):此时页面显示数据和最新的data数据同步。
- beforeDestroy(销毁前):当执行该生命周期函数的时候,实例身上所有的data,所有的methods以及过滤器…等都处于可用状态,并没有真正执行销毁。
- destroyed(销毁后):此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了。
- //下面两个钩子函数一般配合使用
- activated(组件激活时):和上面的beforeDestroy和destroyed用法差不多,但是如果我们需要一个实例,在销毁后再次出现的话,用beforeDestroy和destroyed的话,就太浪费性能了。实例被激活时使用,用于重复激活一个实例的时候
- deactivated(组件未激活时):实例没有被激活时。
- errorCaptured(错误调用):当捕获一个来自后代组件的错误时被调用\
19. 父组件和子组件的生命周期执行顺序
1、加载渲染过程
父beforeCreate ->父created ->父beforeMount ->子beforeCreate ->子created ->子 beforeMount ->子mounted ->父mounted
2、子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
3、父组件更新过程
父 beforeUpdate -> 父 updated
4、销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
20.子组件可以直接修改父组件的数据吗?会发生什么?有哪几种解决方案
21.Vue双向数据绑定
Vue 对数组原型链上的几个方法进行劫持,对于会导致元素新增的3个方法 push 、 pop 、 unshift 会在内部获取新增的元素,执行响应式的处理。如果需要为数字元素重新赋值,可以使用 vm.$set(arr, indexOfItem, newValue) 方法,这里展示一下 $set 的具体实现:
22.Vue3.x响应式数据原理
Vue3.x改用Proxy替代Object.defineProperty。
因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger
①Vue3中Template支持多个根标签,Vue2不支持【跟标签】
②Vue3中的生命周期的挂载钩子是onMounted,需要引入才能使用【钩子的名字变了】
③Vue3引入了tree-shaking,以模块的方式引入api,减小打包体积【更小】
④引入setup
ou what is setup??
是什么:组合式 API(name)
23.Webpack
webpack是js中的模块打包工具。其工作原理:
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。在3.0出现后,Webpack还肩负起了优化项目的责任。
23.1 webpack打包原理
把一切都视为模块:不管是 css、JS、Image 还是 html 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。处理模块化之间的依赖关系。
按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
23.2 打包流程
六种常见的前端构建工具: npm script , grunt, gulp, fis3, webpack, rollup(专注于es6)!
grunt的核心是Task,
- entry:配置入口文件。
- output:指定输出文件。
- module:决定如何处理模块,使用各种loader处理CSS、JS、image等资源,并将它们编译与打包成浏览器可以解析的内容等。
- plugins:扩展插件,在webpack构建流程中特定时机注入扩展逻辑来改变构建结果。
5.devServer:启动一个HTTP代理服务器用于网页请求,同时启动webpack并接收webpack发出的文件变更信号。
23.3 less特性
23.4 webpack使用less怎么配置
24. babel是啥
将ES6的语法转换为ES5,也是一个loader
常用的loader
- css-loader读取合并css文件
- style-loader把css内容注入到Javascript里
- sass-loader解析sass文件(安装sass-loader,node-sass)
- postcss-loader自动添加浏览器兼容前缀(postcss.config配置)
- url-loader将文件转换为base64 URL
- vue-loader处理vue文件
25.Vue自定义指令 钩子函数(指令名字,配置对象(属性,函数))
①属性
•el: 指令绑定的元素。 •vm: 拥有该指令的上下文 ViewModel。 •expression: 指令的表达式,不包括参数和过滤器。 •arg: 指令的参数。 •name: 指令的名字,不包含前缀。 •modifiers: 一个对象,包含指令的修饰符。 •descriptor: 一个对象,包含指令的解析结果
②函数
•bind:只调用一次,在指令第一次绑定到元素上时调用。 •update: 在 bind 之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值。 •unbind:只调用一次,在指令从元素上解绑时调用
26、vuex
vuex可保存组件间共享的数据。
data存放组件内部私有的数据。props存放父组件传递给子组件的数据。什么样数据需要放在vuex中 ? 首先这个数据被多个组件频繁用到。
场景:单页面应用中组件之间的状态,音乐播放、登录状态、加入购物车等。
如何操作vuex中数据:
const store = new Vuex.Store({
state: { },//基本数据(数据源存放地).
mutations: { },//同步更改state数据方法集合, 不能写ajax等一部请求
getters: { },//从基本数据派生出来的数据
actions: { }, //负责获取、处理数据(如果有异步操作必须在actions处理,再提交到mutation同步更新)
modules: //模块化Vuex,将状态树分隔成不同的模块,每个模块拥有自己的state,getters,mutations,actions;而且允许每个module里面嵌套子module
})
- state中的数据,不能直接修改,若需要修改,必须通过mutations提供的方法,需通过this.$store.commit(‘方法的名称’, [参数数组])。
- 若组件想要从state上获取数据,需要this.$store.state.变量名
- 如果store中state数据,在对外提供的时候,需要一层包装,则使用getters,this.$store.getters.方法名。
- getters中方法和组件中过滤器类似,都没有修改数据,只是给数据一层包装后提供给调用者。getters也和computed比较像,只要state中数据改变了,若getters正好引用这个数据,那么就会触发getters中方法重新求值。
页面刷新了之后vuex中的数据消失怎么解决?
vuex数据位于内存中,页面刷新重置会导致数据消失,本地持久化可以解决这个问题,sesstionStorage或者localStorage。
实施方案: state的持久化,也就是分别在state数据初始化、更新的时候进行读取和设置本地存储。
27、Vue的异步渲染
Vue实现响应式并不是数据发生变化之后DOM立即变化,而是在同步任务执行完成之后才执行渲染任务,简单说Vue的渲染是异步的