一.html5新特性总结
一、语义标签
二、增强型表单
三、视频和音频
四、Canvas绘图
五、SVG绘图
六、地理定位
七、拖放API
八、WebWorker
九、WebStorage
十、WebSocket
十一.
二.html
1.行内元素有哪些?块级元素有哪些? 空(void)元素有那些?行内元素和块级元素有什么区别?
行内元素
1、设置宽高无效
2、对margin仅设置左右方向有效,上下无效;padding上下左右都有效,会撑大空间
3、不会自动进行换行
a、b、span、img、input、strong、select、label、em、button、textarea
块级元素
1、能够识别设置宽高
2、margin和padding的上下左右均对其有效
3、独占一行
div、ul、li、dl、dt、dd、p、h1-h6、blockquote
2.请描述一下cookies,sessionStorage和localStorage的区别
1.localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,当浏览器被关闭时数据不会被删除,直到手动去除。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
// 存储
localStorage.setItem("lastname", "Gates");
// 取回
document.getElementById("result").innerHTML = localStorage.getItem("lastname");
//删除
localStorage.removeItem("lastname");
2.sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
3.cookie只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭 ,数据不能超过4K,
3.viewport
width: 设置viewport宽度,为一个正整数,或字符串 device-width
device-width: 设备宽度
height: 设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置
initial-scale: 默认缩放比例(初始缩放比例),为一个数字,可以带小数
minimum-scale: 允许用户最小缩放比例,为一个数字,可以带小数
maximum-scale: 允许用户最大缩放比例,为一个数字,可以带小数
user-scalable: 是否允许手动缩放
三.css
1.display none visibility hidden区别?
2.为什么要初始化css样式?
因为浏览器的兼容问题,不同浏览器对有些标签的默认值不同,如果不初始化css,会导致不同浏览器页面间的显示差异。
3.CSS实现垂直水平居中
HTML结构:
<div class="wrapper">
<div class="content"></div>
</div>
CSS:
.wrapper{position:relative;}
.content{
background-color:#6699FF;
width:200px;
height:200px;
position: absolute; //父元素需要相对定位
top: 50%;
left: 50%;
margin-top:-100px ; //二分之一的height,width
margin-left: -100px;
}
4.什么是CSS Hack?
一般来说是针对不同的浏览器写不同的CSS,就是 CSS Hack。
IE浏览器Hack一般又分为三种,条件Hack、属性级Hack、选择符Hack(详细参考CSS文档:css文档)。例如:
// 1、条件Hack
<!--[if IE]>
<style>
.test{color:red;}
</style>
<![endif]-->
// 2、属性Hack
.test{
color:#0909; /* For IE8+ */
*color:#f00; /* For IE7 and earlier */
_color:#ff0; /* For IE6 and earlier */
}
// 3、选择符Hack
* html .test{color:#090;} /* For IE6 and earlier */
* + html .test{color:#ff0;} /* For IE7 */
5.简述同步和异步的区别
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
6.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景
display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击,
visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击
opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击
7.link与@import的区别
link是 HTML 方式, @import是 CSS 方式
link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC
link可以通过rel="alternate stylesheet"指定候选样式
浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
@import必须在样式规则之前,可以在 css 文件中引用其他文件
总体来说:link 优于@import
8.css水平、垂直居中的写法,请至少写出4种?
水平居中
行内元素: text-align: center
块级元素: margin: 0 auto
position:absolute +left:50%+ transform:translateX(-50%)
display:flex + justify-content: center
垂直居中
设置line-height 等于height
position:absolute +top:50%+ transform:translateY(-50%)绝对定位
display:flex + align-items: center
display:table+display:table-cell + vertical-align: middle;
垂直水平居中
/*flex 布局*/
display: flex;
/*实现垂直居中*/
align-items: center;
/*实现水平居中*/
justify-content: center;
9.1rem、1em、1vh、1px各自代表的含义?
rem
rem是全部的长度都相对于根元素<html>元素。通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。
em
子元素字体大小的em是相对于父元素字体大小
元素的width/height/padding/margin用em的话是相对于该元素的font-size
vw/vh
全称是 Viewport Width 和 Viewport Height,视窗的宽度和高度,相当于 屏幕宽度和高度的 1%,不过,处理宽度的时候%单位更合适,处理高度的 话 vh 单位更好。
px
px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
一般电脑的分辨率有{1920*1024}等不同的分辨率
1920*1024 前者是屏幕宽度总共有1920个像素,后者则是高度为1024个像素
10.画一条0.5px的直线
height: 1px;
transform: scale(0.5);
11.说一下盒模型
盒模型的组成,由里向外content,padding,border,margin.
Margin(外边距) - 清除边框外的区域,外边距是透明的。
Border(边框) - 围绕在内边距和内容外的边框。
Padding(内边距) - 清除内容周围的区域,内边距是透明的。
Content(内容) - 盒子的内容,显示文本和图像。
12.position有哪几种呢?
static:默认
relative:相对于自身
absolute:相对于最近的一个非static的祖先级元素进行定位
fixed:相对于屏幕窗口进行定位
sticky:用来实现吸顶效果
四.js和es6
1.es6的新特性
const let
模板字符串
箭头函数
函数的参数默认值
对象和数组解构
for...of 和 for...in
展开运算符
字符串扩展
Map和Reduce
(1)var const let
1.var定义的变量有时候会成为全局变量
2.let声明变量只在所在的代码块内有效;
let 只能声明一次 var 可以声明多次;
let 不存在变量提升,var 会变量提升
console.log(a); //ReferenceError: a is not defined
let a = "apple";
console.log(b); //undefined
var b = "banana";
3.const声明的变量是常量,一旦声明必须初始化,不能被修改
(2)模板字符串
包含嵌入式表达式的字符串字面量.
let message = `${student.name} please see ${teacher.name} in ${teacher.room}
to pick up your report card.`;
(3)展开运算符
展开运算符(用三个连续的点 (…) 表示)是 ES6 中的新概念
如果你需要结合多个数组,在有展开运算符之前,必须使用 Array的 concat() 方法。
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = fruits.concat(vegetables);
console.log(produce);
使用展开符来结合数组
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = [...fruits,...vegetables];
console.log(produce);
(4).剩余参数(可变参数)
用途1: 将变量赋数组值时:
const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"];
const [total, subtotal, tax, ...items] = order;
console.log(total, subtotal, tax, items);
用途2: 可变参数函数
对于参数不固定的函数,ES6之前是使用参数对象(arguments)处理:
function sum() {
let total = 0;
for(const argument of arguments) {
total += argument;
}
return total;
}
(5)字符串扩展
includes(“xxx”):返回布尔值,表示是否包含xxx
startsWith(“xxx”):返回布尔值,表示是否以xxx开头
endsWith(“xxx”):返回布尔值,表示是否以xxx结尾
这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。
(6)Map和Reduce
map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新的数组返回。
2.undefined 和 null 区别
1、null
什么都没有,表示一个空对象引用(主动释放一个变量引用的兑现那个,表示一个变量不再指向任何引用地址)
2、undefined
没有设置值的变量,会自动赋值undefined
3、区别
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
4.JS哪些操作会造成内存泄露
内存泄漏是指一块被分配的内存既不能使用,也不能回收,直到浏览器进程结束。
1、意外的全局变量
2、闭包
3、没有清理的dom元素
dom元素赋值给变量,又通过removeChild移除dom元素。但是dom元素的引用还在内存中
4、被遗忘的定时器或者回调
5.什么是闭包,如何使用它,为什么要使用它?
(1)闭包就是能够读取其它函数内部变量的函数
(2)使用方法:在一个函数内部创建另一个函数
优点:
避免全局变量的污染
希望一个变量长期存储在内存中(缓存变量)
缺点:
内存泄露(消耗)
常驻内存,增加内存使用量
6.Promise和Async处理失败的时候有什么区别
promise是异步编程的一种解决方案,用来解决回调地狱的问题(回调函数里面嵌套回调函数)
(1)Promise错误可以在构造体里面被捕获,而async/await返回的是promise,可以通过catch直接捕获错误。
(2)await 后接的Promise.reject都必须被捕获,否则会中断执行
async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
作用:避免有更多的请求操作,出现多重嵌套,也就是俗称的“回调地狱”
this.$http.jsonp('/login', (res) => {
this.$http.jsonp('/getInfo', (info) => {
// do something
})
})
因此提出了ES6的Promise,将回调函数的嵌套,改为了链式调用
var promise = new Promise((resolve, reject) => {
this.login(resolve);
})
.then(() => {
this.getInfo()
})
.catch(() => {
console.log('Error')
})
7.let、var、const的区别
var 在全局范围内都有效, 没有块级作用域,支持变量提升, var 可以声明多次
let 声明的变量只在它所在的代码块有效, 有块级作用域,不支持变量提升。 不允许重复声明,暂存性死区。
const 有块级作用域,不支持变量提升,不允许重复声明,暂存性死区。 在声明时必须赋值
8.简述深浅拷贝
浅拷贝应该是不考虑对象的引用类型的属性,只对当前对象的所有成员进行拷贝。Object.assign()
深拷贝的就是在拷贝的时候,需要将当前要拷贝的对象内的所有引用类型的属性进行完整的拷贝。JSON.parse()
和JSON.stringify()
浅拷贝:浅拷贝通过ES6新特性Object.assign()或者通过扩展运算法...来达到浅拷贝的目的,浅拷贝修改
副本,不会影响原数据,但缺点是浅拷贝只能拷贝第一层的数据,且都是值类型数据,如果有引用型数据,修改
副本会影响原数据。
深拷贝:通过利用JSON.parse(JSON.stringify())来实现深拷贝的目的,但利用JSON拷贝也是有缺点的,
当要拷贝的数据中含有undefined/function/symbol类型是无法进行拷贝的,当然我们想项目开发中需要
深拷贝的数据一般不会含有以上三种类型,如有需要可以自己在封装一个函数来实现
9.JS有几种数据类型,其中基本数据类型有哪些?
七种数据类型
三种基本类型(数字,字符串,布尔),
两种引用数据类型(对象,数组),
两种特殊数据类型(undefined,null)
10.陈述输入URL回车后的过程
1.读取缓存:
搜索自身的 DNS 缓存。(如果 DNS 缓存中找到IP 地址就跳过了接下来查找 IP 地址步骤,直接访问该 IP 地址。)
2.DNS 解析:将域名解析成 IP 地址
3.TCP 连接:TCP 三次握手,简易描述三次握手
客户端:服务端你在么?
服务端:客户端我在,你要连接我么?
客户端:是的服务端,我要链接。
连接打通,可以开始请求来
4.发送 HTTP 请求
5.服务器处理请求并返回 HTTP 报文
6.浏览器解析渲染页面
7.断开连接:TCP 四次挥手
11.说一说什么是跨域,怎么解决
因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。
为来防止CSRF攻击
1.JSONP
JSONP 的原理很简单,就是利用 <script> 标签没有跨域限制的漏洞。
通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。
<script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
JSONP 使用简单且兼容性不错,但是只限于 get 请求。
2.CORS
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
3.document.domain
该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。
只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域
4.webpack配置proxyTable设置开发环境跨域
5.nginx代理跨域
6.iframe跨域
7.postMessage
这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息
12.数组中方法
一.数组去重的方法
(1)1.ES6 的 Set
let arr = [1,1,2,3,4,5,5,6]
let arr2 = [...new Set(arr)]
(2)reduce()
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.reduce(function(ar,cur) {
if(!ar.includes(cur)) {
ar.push(cur)
}
return ar
},[])
(3)filter()
// 这种方法会有一个问题:[1,'1']会被当做相同元素,最终输入[1]
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
// indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
return arr.indexOf(item) === index
})
二.查找数组中符合条件的元素,
find():查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
findIndex():查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
三.fill()
将一定范围索引的数组元素内容填充为单个指定的值
let arr = Array.of(1, 2, 3, 4);
// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]
13.前端性能优化的七大手段
减少请求数量
减小资源大小
优化网络连接
优化资源加载
减少重绘回流
性能更好的API
webpack优化
14.forEach、for in、for of、for区别
for:最原始的数组遍历写法,写法比较麻烦。
forEach:不可以遍历对象,用来遍历数组,中途无法跳出循环,break和return不凑效。
for...in:主要用来遍历对象的
for...of:语法简单,可以与break、continue和return配合使用,主要用来遍历数组键值。不会遍历原型链
15.es6中对象新增的方法
①object.assign() 用于合并对象和浅拷贝
②object.keys() 返回值是一个数组,数组包含了对象可遍历属性的所有key值
③object.values() 返回值是一个数组,数组包含了目标对象可遍历的所有键值
④object.entries() 返回的是所有可遍历属性键和键值的数组
五.vue
1.怎么注册插件
main.js中
import com from './com.js'
Vue.use(com)
所有vue文件中都可以调用组件<com></com>
2.router的meta有什么用
在meta对象中可以设置一些状态,通常设置标题或是否需要缓存。$route.meta.keepAlive/$route.meta.title
{
path:"/test",
name:"test",
component:()=>import("@/components/test"),
meta:{
title:"测试页面", //配置title
keepAlive: true //是否缓存
}
}
3.双向绑定原理
Vue是采用数据劫持配合发布者-订阅者模式,通过Object.defineProperty来()来劫持各个属性的getter和setter
在数据发生变化的时候,发布消息给依赖收集器,去通知观察者,做出对应的回调函数去更新视图。
4.Vue路由守卫有哪些,怎么设置,使用场景等
常用的两个路由守卫:router.beforeEach 和 router.afterEach
每个守卫方法接收三个参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。
在项目中,一般在beforeEach这个钩子函数中进行路由跳转的一些信息判断。
判断是否登录,是否拿到对应的路由权限等等。
总体来讲vue里面提供了三大类钩子,两种函数
1、全局钩子
2、某个路由的钩子
3、组件内钩子
> https://www.cnblogs.com/WQLong/p/8135553.html
5.vuex中,有默认的五种基本的对象
vuex的优点
1.解决了非父子组件的消息传递(将数据存放在state中)
2.减少了AJAX请求次数,有些情景可以直接从内存中的state获取
vuex的缺点
1.刷新浏览器,vuex中的state会重新变为初始状态
```bash
场景:多个组件共享数据或者是跨组件传递数据时
[添加链接描述](https://www.cnblogs.com/mica/p/10757965.html)
(1)state
存储状态(变量)
a.访问
通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
b.mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性
(2)Getter
对数据编译,可以理解为state的计算属性。我们在组件中使用 $sotre.getters.fun()调用
a.访问
通过属性访问。Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
store.getters.doneTodos
b.mapGetters 辅助函数
(3)mutations
a.存放处理数据逻辑的方法,修改状态,并且是同步的。
b.在组件中使用$store.commit(‘’,params)调用。这个和我们组件中的自定义事件类似。
在 mutation 传参(载荷)可以传递一个参数
(4)actions
异步操作。在组件中使用是$store.dispath(‘’)方法触发
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation
(5)modules
模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
6.vue父组件向子组件传递数据?
答:通过props
子组件像父组件传递事件?
答:$emit方法
7.如何让CSS只在当前组件中起作用?
在组件中的style前面加上scoped
8.<keep-alive></keep-alive>
的作用是什么?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
9.如何获取dom?
ref="domName" 用法:this.$refs.domName
10.为什么使用key?
1.vue在渲染的时候,会 先把 新DOM 与 旧DOM 进行对比, 如果dom结构一致,则vue会复用旧的dom。 (此时可能造成数据渲染异常)
2.使用key可以给dom添加一个 唯一标识符,让vue强制更新dom
11.computed和watch的使用场景
1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
4、使用场景:computed----当一个属性受多个属性影响的时候,使用computed-------购物车商品结算。watch----当一条数据影响多条数据的时候,使用watch-------搜索框。
5.computed不支持异步,有异步操作时无法监听数据变化;watch支持异步操作
12.$nextTick的使用
当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功
this.$set:
在Vue实例创建时,obj.b并未声明,因此就没有被Vue转换为响应式的属性,
自然就不会触发视图的更新,这时就需要使用Vue的全局api $set()
13.v-if和v-for的优先级
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级
v-if 和 v-show 的区别
14.assets和static的区别
assets中存放的静态资源文件在项目打包时,也就是运行npm run build时会将assets中放置的静态资源文件
进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在static文件中跟着index.html一同上传至服务器。
static中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。
但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交
较大点。在服务器中就会占据更大的空间。
15.引进组件的步骤
在template中引入组件;
在script的第一行用import引入路径;
用component中写上组件名称。
16.请说下封装 vue 组件的过程?
使用Vue.extend方法创建一个组件,
然后使用Vue.component方法注册组件
在另一组件import 导入,并在components中注册,
子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
17.$route和$router的区别
$ router只写要跳转的路由, $ route 只读(参数的获取)
怎么定义 vue-router 的动态路由? 怎么获取传过来的值
在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。
18.vue-router 是什么?它有哪些组件
vue用来写路由一个插件。router-link、router-view
19.怎么定义 vue-router 的动态路由? 怎么获取传过来的值?
在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id。
20.怎样理解 Vue 的单项数据流
数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解。
21.Vue路由
(1)vue-router 的路由跳转的方法
第一种 : 编程式的导航
<router-link to="/" tag="p">耳机频道</router-link>
//to是一个prop.指定需要跳转的路径,也可以使用v-bind动态设置
//tag可以指定渲染成标签,默认是a标签
<router-link to="/" replace>跳转回去</router-link>
//这样的写法是不会留下历史痕迹,回退键无效
<router-link :to="{ name: 'product', params: { id : 1 }}">User</router-link>
// /product/1
第二种 : 函数式的导航
//这里假设 我要跳转product页面并且附带参数id
//这里定义好了list.id 就是 动态的值
this.$router.push('./product/' + list.id) // 字符串的方式进行描述
this.$router.push({name : 'product',params: { id : list.id }}) // 命名的路由的方式进行描述
this.$router.push({ path: `/product/${list.id}` }) // 直接定义path类似第一种
//比较常用的跳转路由的方法
//假如是带查询参数
router.push({ path: 'product', query: { id: list.id }}) // /product?id=1
(2)全局导航守卫(beforeEach、afterEach)
vue-router 提供了导航钩子:全局前置导航钩子 beforeEach 和全局后置导航钩子 afterEach,他们会在路由即将改变前和改变后进行触发。
导航钩子有3个参数:
1、to:即将要进入的目标路由对象;
2、from:当前导航即将要离开的路由对象;
3、next :调用该方法后,才能进入下一个钩子函数(afterEach)。
和 beforeEach 不同的是 afterEach 不接收第三个参数 next 函数
22.父组件传递给子组件的数据,能不能进行修改
父子组件传值时,父组件传递的参数,数组和对象,子组件接受之后可以直接进行修改,并且会传递给父组件相应的值也会修改。
如果传递的值是字符串,直接修改会报错。
不推荐子组件直接修改父组件中的参数,避免这个参数多个子组件引用,无法找到造成数据不正常的原因
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
23.路由后台获取
vue-admin-template后台管理的权限:
https://www.cnblogs.com/liuyanan/p/13560643.html
一.菜单
(1)在路由实例中保留基础路由
router/index.js中只需要保留基础路由,其他的都删
(2)获取用户菜单,并保存到Vuex中
stroe/modules/user.js中,有个getInfo方法查询用户基本信息,返回了用户的菜单列表
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
console.log(data)
const menus =
[{
path: '/books',
component: 'Layout',
children: [{
path: 'index',
name: 'AddressBook',
component: 'workbench/addressbook',
meta: { title: '通讯录', icon: 'company' }
}]
},
{
path: '/systool',
component: 'Layout',
redirect: '/systool/coder',
name: 'SysTool',
meta: { title: '实验室', icon: 'example' },
children: [
{
path: 'calendar',
name: 'Calendar',
component: 'workbench/calendar',
meta: { title: '日程', icon: 'table' }
}
]
}]
const { name, avatar, companyName, employeeid } = data
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_CMPNAME', companyName)
commit('SET_USERID', employeeid)
commit('SET_MENUS', menus)
resolve(data)
}).catch(error => {
reject(error)
})
})
}
getter.js
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
cmpname: state => state.user.cmpname,
userid: state => state.user.userid,
menus: state => state.user.menus
}
export default getters
(3)在路由钩子中,过滤路由,动态添加路由
核心在src目录下的permission.js中,router.beforeEach路由钩子
import router from './router'
import store from './store'
import {
Message
} from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import {
getToken
} from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import Layout from '@/layout'
const _import = require('./router/_import_' + process.env.NODE_ENV) // 获取组件的方法
NProgress.configure({
showSpinner: false
}) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({
path: '/'
})
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// get user info
await store.dispatch('user/getInfo')
if (store.getters.menus.length < 1) {
global.antRouter = []
next()
}
const menus = filterAsyncRouter(store.getters.menus) // 1.过滤路由
router.addRoutes(menus) // 2.动态添加路由
global.antRouter = menus // 3.将路由数据传递给全局变量,做侧边栏菜单渲染工作
next({
...to,
replace: true
}) // hack方法 确保addRoutes已完成 ,set the replace
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === 'Layout') {
route.component = Layout
} else {
route.component = _import(route.component) // 导入组件
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
})
return accessedRouters
}
24.怎么定义vue-router的动态路由以及如何获取传过来的动态参数?
在router目录下的index.js文件中,对path属性加上/:id。
使用router对象的params.id。
25.如何让CSS只在当前组件中起作用
在组件中的style前面加上scoped
26.如何获取dom?
ref=“domName” 用法:this.$refs.domName
27.vue-loader是什么?
vue文件的一个加载器,将template/js/style转换成js模块。 用途:js可以写es6、style样式可以scss或less、template可以加jade等
28.为什么使用key?
需要使用key来给每个节点做一个唯一标识,作用主要是为了高效的更新虚拟DOM。
29.分别简述computed和watch的使用场景
computed: 当一个属性受多个属性影响的时候就需要用到computed
最典型的栗子: 购物车商品结算的时候
watch:当一条数据影响多条数据的时候就需要用watch
栗子:搜索数据
30.Vue通信
1.props和$emit
2.中央事件总线 EventBus(基本不用)
3.vuex(官方推荐状态管理器)
4.$parent和$children
31.webpack配置入口出口
https://segmentfault.com/a/1190000011333071
32.webpack打包优化的几种方案
(1)组件按需加载
现在大多的ui库都是以组件的形式进行处理,所以只需导入需要模块的即可
(2)去掉生成map文件
config/index.js找到productionSourceMap把true改为false
(3).cdn引入
通过外部引入的方式引入这些UI组件库,从而减少打包文件过大的问题
33.webpack3和webpack4区别
(1).mode
webpack增加了一个mode配置,只有两种值development | production。对不同的环境他会启用不同的配置。
34.vue生命周期
将要创建 ===>调用beforeCreate函数
创建完毕 ===>调用created函数
将要挂载 ===>调用beforeMount函数
挂载完毕 ===>调用mounted函数
将要更新 ===>调用beforeUpdate函数
更新完毕 ===>调用updated函数
将要销毁 ===>调用beforeDestory函数
销毁完毕 ===>调用destroyed函数
35.自定义组件
在 components 目录新建组件文件
在需要用到的页面import中导入
使用component注册
在 template 视图中使用组件标签
36.vue-router中导航守卫有哪些?
全局前置守卫、路由独享守卫、组件内守卫
全局前置/钩子:beforeEach、beforeResolve、afterEach
路由独享的守卫:beforeEnter
组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
35.vue-router 中常用的路由模式
1.hash 模式
location.hash 的值实际就是 URL 中#后面的东西,改变 hash 不会重新加载页面,可以来实现前端路由“更新视图但不重新请求页面”的功能了
2.history 模式
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。提供了对历史记录进行修改的功能
虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。
总结:hash模式是开发中默认的模式,如果想要切换到history模式,就要进行以下配置
(后端也要进行配置):
const router = new VueRouter({
mode: 'history',
routes: [...]
})
六.vue2和vue3的区别
1.vue2使用Object.defineProperty方法实现响应式数据,vue3使用proxy实现响应式数据
Object.defineProperty的缺点:
无法检测到对象属性的动态添加和删除
无法检测到数组的下标和length属性的变更
解决方法:
vue2提供Vue.
s
e
t
动态给对象添加属性
V
u
e
.
set动态给对象添加属性 Vue.
set动态给对象添加属性 Vue.delete动态删除对象属性
重写数组的方法,检测数组变更
proxy的缺点:
es6的proxy不支持低版本浏览器(IE11)
会针对IE11出一个特殊版本进行支持
proxy的有点:
可以坚持到代理对象属性的动态新增和删除
可以监测到数组的下标和length属性的变化
2、vue3新特性:
①:数据响应重新实现(ES6的proxy代替ES5的Object.defineProperty)
②:源码使用ts重写,更好的类型推导
③:虚拟DOM新算法(更快、更小)
④:提供了composition api,为实现基于函数的逻辑复用机制而产生的
(5)打包体积变小
(6)首次渲染更快
原因在于 Object.defineProperty 本身存在的一 些问题 :
Object.defineProperty 只能劫持对象属性的 getter 和 setter 方法。
Object.definedProperty 不支持数组(可以监听数组,不过数组方法无法监听自己重写),更准确的说是不支持数组的各种 API(所以 Vue 重写了数组方法。
而相比 Object.defineProperty,Proxy 的优点在于:
Proxy 是直接代理劫持整个对象。
Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
目前,Object.definedProperty 唯一比 Proxy 好的一点就是兼容性,不过 Proxy 新标准
3.使用 ref & reactive 使得数据经过变成Proxy响应式数据
(1)用ref定义的变量,如果需要取到其变量值,需要使用.value属性
<template>
<div ref="divBox">ref的调用用法</div>
</template>
<script>
import { onMounted, reactive, ref } from "vue";
export default {
name: "HelloWorld",
props: {
msg: String,
},
setup(props) {
let divBox = ref(null);
onMounted(()=>{
console.log(divBox.value) //<div ref="divBox">ref的调用用法</div>
})
return {
divBox
};
},
};
</script>
(2)reactive函数
reactive的用法与ref的用法相似,也是将数据变成响应式数据,当数据发生变化时UI也会自动更新。不同的是ref用于基本数据类型,而reactive是用于复杂数据类型
4.父子传值
父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数
(1)props
<template>
<div>
<div>{{msg}}</div>
<div>{{str}}</div>
</div>
</template>
<script>
import {computed} from 'vue'
export default {
props:{
msg:String
},
setup(props){//提供的函数props直接用就完事了嗷~~~
const str = computed(()=>{
return '父组件值为:'+ props.msg
})
return{
str
}
}
}
</script>
5.创建项目对比
vite 优势
(1)真正的按需编译,不再等待整个应用编译完成,传统 webpack 编译:每次执行编译时,都会通过入口 entry 先去找到各个路由,再去加载每个路由各自的模块,然后会进行打包成为 bundle.js 文件,最后才通知服务器热更新。所以换句话说就是等所有文件加载就绪之后才去渲染更新页面的==》较慢
(2)按需加载,总体体积较小且更新更快
<template>
<child :msg="msg"></child>
</template>
<script>
import Child from './Child.vue'
import {ref} from 'vue'
export default {
components:{
Child
},
setup(){
const msg = ref('I am father value!')
return{
msg
}
}
}
</script>
(2).emit
<template>
<div>
子组件: <button @click="childEmit">传值给父组件</button>
</div>
</template>
<script>
export default {
setup(props,{emit}){ //分解context对象取出emit
function childEmit(){
emit('my-emit','我是子组件值')
}
return{
childEmit
}
}
};
</script>
<template>
<div>
父组件 <child @my-emit="parentEmit"></child>
</div>
</template>
<script>
import Child from "./Child.vue";
import { ref } from "vue";
export default {
components: {
Child,
},
setup() {
function parentEmit(val){
alert(val)
}
return{
parentEmit
}
},
};
</script>
七.uniapp
八.微信小程序
1.微信登录
(1)使用wx.login获取code
(2)调用后台接口传递code获取token
2. 解决wx.navigateTo跳转10级限制
(1)使用 wx.navigateBack代替wx.navigateTo
let pages = getCurrentPages()//当前页面栈
let prevPage = pages[pages.length - 2]//上一页面
prevPage.setData({ //直接给上移页面赋值
needFresh: true,
pay: 0
});
wx.navigateBack({
delta: 1
})
(2)页面跳转后自动刷新,使用onShow方法接收页面参数,刷新列表
let pages = getCurrentPages()
let currPage = pages[pages.length - 1]
// 告诉列表页也需要刷新
app.globalData.orderListRefresh = currPage.data.needFresh
if (currPage.data.needFresh) {
this.getData()
}
3.跳转上一页,并且刷新上一页面数据
(1)跳转上一页面,并传值
let pages = getCurrentPages()//当前页面栈
let prevPage = pages[pages.length - 2]//上一页面
prevPage.setData({ //直接给上移页面赋值
needFresh: true,
pay: 0
});
wx.navigateBack({
delta: 1
})
(2)在onShow方法里判断刷新列表
onShow() {
let pages = getCurrentPages()
let currPage = pages[pages.length - 1]
// 告诉列表页也需要刷新
app.globalData.orderListRefresh = currPage.data.needFresh
if (currPage.data.needFresh) {
this.getData()
}
},
4.刷新本页数据
var pages = getCurrentPages();
var curPage = pages[pages.length - 1];
curPage.onLoad(curPage.options);
5.微信小程序行内点击事件冲突解决
说明:比如这种情况下,比如tr上有一个点击事件,第一个cell下也有点击事件,但是点击cell时会触发到tr的事件
<view class="tr" bindtap='parentClick'>
<view class="cell" bindtap='childClick'>1</view>
<view class="cell">2</view>
<view class="cell">3</view>
</view>
解决:在cell设置hover-stop-propagation=‘true’,再把cell的bindtap替换为catchtap即可
<view class="tr" bindtap='parentClick'>
<view class="cell" hover-stop-propagation='true' catchtap='childClick'>1</view>
<view class="cell">2</view>
<view class="cell">3</view>
</view>
6.微信小程序服务订阅
wx.requestSubscribeMessage传入参数tmplIds(需要订阅的消息模板的 id 的集合)
7.缓存Storage
小程序的缓存是没有失效期的,最大上限不能超过10MB
8.Vue插槽
Vue插槽是Vue中常见的一种组件间的相互通信方式,通过标签来起到占位的作用
(1)默认插槽
首先在父组件中引入子组件,
并在引入的子组件标签内插入需要的html元素,
在子组件中把需要用插槽的地方用标签替代
(2)具名插槽
有时候我们会遇到一个组件多用,但是结构稍微有所差别的情况,这时,就会用得到具名插值。
a.首先在要插入html的部分需要用一个标签包裹住各个部分:
<template slot="one">
<ul>
<li v-for="game , index in games" :key="index">{{game}}</li>
</ul>
</template>
b.当给标签绑定slot=“name” 属性时,需要在子组件中对应每个 给出一个 并且添加上name属性
<div class="box">
<h4>{{title}}</h4>
<slot name="one"></slot>
<slot name="tow"></slot>
</div>
(3)作用域插槽
数据在子组件中,需要通过数据绑定传给使用者
九.electron
使用纯JavaScript来创建桌面应用程序
十。vue3
1.优势
(1)性能的提升:使用vite按需编译代替传统的 webpack 编译,
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
(2)使用 ES6 新语法Proxy代替defineProperty实现响应式
vue2新增属性、删除属性, 直接通过下标修改数组,监听不到变化
(3)Vue3可以更好的支持TypeScript
(4)Composition API(组合API)
传统OptionsAPI中,实现一个需求,就需要分别在data,methods,computed里修改,当代码量巨大逻辑复杂时,就会造成代码难以维护和查找
Composition API可以更加优雅的组织的代码,函数。让相关功能的代码更加有序的组织在一起,更加有逻辑性可读性
setup:值为一个函数。所有的组合 api 都要在它里面使用。
使用变量 或者事件 需要把名字 return 出去即可在模板中使用。
(5)生命周期
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有2个钩子发生了改变 - beforeDestroy改名为beforeUnmount(卸载前) - destroyed改名为unmounted(卸载) 与vue2不同的是,vue3中是有了el模板之后才会去初始化,而vue2中是先created之后再去找模板。
2.ref 函数
定义一个响应式的数据(主要针对基础类型数据) 方法:引入 ref 函数,const xxx = ref(initValue) ,模板中读取数据: 不需要.value,直接:
ref 通过Object.defineProperty()的get与set来实现响应式(数据劫持)
3.reactive 函数
定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
reactive 通过使用 Proxy 来实现响应式(数据劫持)
4.reactive对比ref
从定义数据角度对比:
- ref用来定义:基本类型数据。
reactive用来定义:对象(或数组)类型数据。
备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
- 从原理角度对比:
ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- 从使用角度对比:
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
reactive定义的数据:操作数据与读取数据:均不需要.value。
5.vue3响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
6.Vue组件通讯有哪些方式
1、props 和 $emit。父组件向子组件传递数据是通过props传递的,子组件传递给父组件是通过$emit触发事件来做到的。
2、$refs 获取组件实例。
3、envetBus 兄弟组件数据传递,这种情况下可以使用事件总线的方式。
4、vuex 状态管理。
7。谈一下对 vuex 的个人理解
vuex 是全局状态管理系统,用于多个组件中数据共享、数据缓存```
主要包括以下几个模块:
State:定义了应用状态的数据结构,可以在这里设置默认的初始化状态。
Getter:允许组件从Store中获取数据。
Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步请求。
Module:允许将单一的 Store 拆分更多个 store
8.keep-alive 使用场景和原理
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
9.nextTick 使用场景和原理
在下次 DOM 更新循环结束之后执行的延迟回调
10.能说下 vue-router 中常用的路由模式和实现原理吗
- hash 模式
实际就是 URL 中 # 后面的东西,改变 hash 不会重新加载页面。
- history 模式
11.webpack打包优化
a.按需加载
b.优化Loader
对于Loader来说,首先优化的当然是babel了,babel会将代码转成字符串并生成AST,然后继续转化成新的代码,转换的代码越多,效率就越低。
优化Loader的搜索范围
module.exports = {
module: {
rules: [
test: /\.js$/, // 对js文件使用babel
loader: 'babel-loader',
include: [resolve('src')],// 只在src文件夹下查找
// 不去查找的文件夹路径,node_modules下的代码是编译过得,没必要再去处理一遍
exclude: /node_modules/
]
}
}
c.cache-loader缓存loader处理结果
d.对图片进行压缩和优化
image-webpack-loader这个loder可以帮助我们对打包后的图片进行压缩和优化,例如降低图片分辨率,压缩图片体积等。
在一些性能开销较大的 loader 之前添加 cache-loader,以将处理结果缓存到磁盘里,这样下次打包可以直接使用缓存结果而不需要重新打包。
e.以CDN方式加载资源
f.按需加载&动态加载
https://blog.csdn.net/LSS2288/article/details/127002870
https://blog.csdn.net/guxin_duyin/article/details/125575187
## 12.常用的js类库
1. Vue Router - 通过提供路由器功能,可以构建单页面应用程序(SPA)的客户端路由。
2. Vuex - 用于数据管理的状态管理库。它将组件分离,并将组件的状态统一维护。
3. Axios - 用于AJAX请求的JavaScript库。 它提供了易于使用的API来发起HTTP请求。
4. js-cookie cookie处理
5. Moment.js - 用于日期和时间处理的库。它支持格式化日期,计算时间间隔等。
6. jQuery - 用于DOM操作和事件处理的库。
7. D3.js - 用于数据可视化的JavaScript库。它提供各种图表和图形来呈现数据。
8. Chart.js - 另一个用于数据可视化的JavaScript库。它提供各种图表和图形来呈现数据。
9. crypto-js MD5加密
10.clipboard 将内容放入剪切板
11.lodash是一个 JavaScript 库,它为常见的编程任务提供实用功能
## 13.计算给定数组 arr 中所有元素的总和
第一种:function sum(arr) {
var s = 0;
for (var i=arr.length-1; i>=0; i--) {
s += arr[i];
}
return s;
}
第二种:
let arr=[1,1,1,1,1,1];
let res=arr.reduce(function(tmp,item,index){
// tmp 上一次结果,item 当前项 ,index 下标(从1开始)
return tmp+item;
});
console.log(res);
## 14.filter
// 筛选出能被3整出的
let arr=[3,23,12,434,6,15,9]
let res=arr.filter(item=>{
if(item%3==0){
return true;
}else{
return false;
}
})
alert(res); 得到输出:3,12,6,15,9
15.es6中将数组转为对象的方法
a.利用扩展运算符
const arr = [
{ key: "id", name: "编号" },
{ key: "name", name: "名称" },
];
const obj = {...arr} ;
console.log(obj);
b. Object.fromEntries 可以将数组转化为对象,Object.entries将对象转化为数组
c.onst arr = [{date: "2018-11-18", name: "demo1"}, {date: "2018-11-19", name: "demo2"}];
const target = {};
arr.forEach(a => {
const source = JSON.parse(`{"${a.date}":"${a.name}"}`);//利用JSON.parse将对象
//格式直接造出来
Object.assign(target,source);
})
## 16.Array.from ()方法详解
a.将类数组对象转换为真正数组
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane','john','Mary'],
'length': 4
}
let arr = Array.from(arrayLike)
console.log(arr)
b.将Set结构的数据转换为真正的数组
let arr = [12,45,97,9797,564,134,45642]
let set = new Set(arr)
console.log(Array.from(set)) // [ 12, 45, 97, 9797, 564, 134, 45642 ]
## 17.用于遍历数组的ES6新增方法:entries()、keys()、values()
keys():对键名的遍历
values():对键值的遍历
entries():对键值对的遍历
18.修改element ui的样式,避免全局污染
样式穿透的写法有三种:>>>、/deep/、::v-deep
https://blog.csdn.net/weixin_45272449/article/details/123083687
https://blog.csdn.net/ZMJ_QQ/article/details/120703859
https://codeleading.com/article/62126377972/
19.scoped的原理
当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染
给组件中的每个标签对应的dom元素添加一个标签属性,data-v-xxxx
scoped后选择器最后默认会加上当前组件的一个标识,比如[data-v-49729759]
20.父子组件渲染,生命周期
加载页面执行步骤
1、父组件:beforeCreate -> created -> beforeMount
2、子组件:beforeCreate -> created -> beforeMount -> mounted
3、父组件:mounted
销毁组件执行步骤
1、父组件:beforeDestroy
2、子组件:beforeDestroy
3、子组件:destroyed
2、父组件:destroyed
当父组件进行到beforeMount阶段,即开始模板解析时,就发现需要去构建子组件,所以这个时候会触发子组件的生命周期,当子组件执行到mounted阶段时,子组件已经挂载到了父组件上,父组件随之进入到自身的mounted阶段,将自身挂载。
21.什么是MVVM
在MVVM框架下 视图和模型是不能直接通信 的,只能通过ViewModel进行交互,它能够监
听到数据的变化,然后通知视图进行自动更新,而当用户操作视图时,VM也能监听到视图
的变化,然后通知数据做相应改动,这实际上就实现了数据的 双向绑定
优点:
低耦合 :View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化
的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性 : 可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发 : 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面的设计。
22.MVC与MVVM的区别
MVC中Controller演变成MVVM中的ViewModel
MVVM通过数据来显示视图层而不是节点操作
MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验
23.v-slot插槽与作用域插槽
1.插槽作用:父组件 传递 html结构 给 子组件
2.具名插槽作用:父组件 传递 多个html结构 给 子组件
3.作用域插槽作用:父组件 给 子组件 传递插槽 时,可以使用子组件内部的数据
24.自定义指令(v-check,v-docus)的方法有哪些?它有哪些钩子函数?还有那些勾子函数参数?
全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令:directives
钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)
钩子函数参数:el、binding
25.路由之间是怎么跳转的?有哪些方式?
1、<router-link to="需要跳转到页面的路径">
2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面
3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面
4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数
26.说一下你在vue中踩过的坑
1操作data中的数据,发现没有响应式
原因: 数组中有很多方法,有的会改变数组(例如pop push),有的不会改变数组(例如slice, filter)
解决方案:通过Vue.set(对象,属性,值)这种方式就可以达到,对象新添加的属性是响应式的
2.在created操作dom的时候,是报错的,获取不到dom,这个时候实例vue实例没有挂载
解决方案:Vue.nextTick(回调函数进行获取
27.类数组用不了数组的方法,所以必须要转成真正的数组
第一中方式
let arr = Array.from(arguments)
第二种方式 利用拓展运算符 ...实现
let arr1 = [...arguments]