1 . 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
key是每一个vnode唯一的id,可以依靠key 更准确 更快的拿到oldVnode中对应的Vnode节点。
key的作用是在diff算法执行时更快的找到对应的节点 提高diff速度
第 2 题:[‘1’, ‘2’, ‘3’].map(parseInt)
数组map函数的第一个回调函数有三个参数,第一个是被处理的值 第二个是索引
parseInit用来处理字符串 使字符串成为指定基数的整数
而parseInt的两个参数 第一个是要处理的值 第二个是基数
parseInt('1',0) // 按照10为基数处理 返回1
parseInt("2",1) //基数为1(1进制)表示的数中,最大值小于2 所以无法解析 NaN
parseInt("3",2) //基数为2 (2进制)表示的数中 最大值小于3 所以无法解析 NaN
所以结果是 [1,NaN,NaN]
3 什么是防抖和节流?有什么区别?如何实现?
防抖
触发高频事件n秒只执行一次,如果n秒内再次触发,则重新计算时间
思路 每次调用前取消之前的延迟调用方法
function debounce(fn){
let timeout = null
return function(){
clearTimeout(timeout)
timeout = setTimeout(() => {
// apply() 方法接受数组形式的参数。
fn.apply(this,arguments)
}, 1000);
}
}
function func(){
console.log('func');
}
let inp = document.getElementById('input')
inp.addEventListener('input',debounce(func))
节流
高频率事件触发 n秒内只会执行一次,在n秒后才能触发下一次,所以节流会稀释函数执行的频率
思路
每次触发时先判断当前是否有等待执行的延迟函数
function throttle(fn){
let canRun = true
return function(){
if(!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this,arguments)
canRun = true
}, 1000);
}
}
function sayHi(){
console.log('hi');
}
window.addEventListener('resize',throttle(sayHi))
第 4 题:介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
set 和Map主要的应用场景在于数据重组和数据储存
*set *
set是一种叫做集合的数据结构
类似于数组 但成员是唯一且无序的,没有重复的值。
set 本身是一种构造函数 用来生成Set数据结构
Set对象允许你储存任何类型的唯一值 无论是原始值或是对象引用
向set加入值的时候,不会发生类型转换,所以5 和"5" 是两个不同的值
add(value):新增,相当于数组的push方法
delete(value):存在即删除集合中的value
has(value) : 判断集合中是否存在value
Array.from 方法可以将 Set 结构转为数组
*遍历方法*
keys(): 返回一个包含集合中所有键的迭代器
values(): 返魂一个包含集合中所有值的迭代器
entries():返回一个包含Set对象中所有元素的键值对迭代器
forEach(callbackFn,thisArg) :对集合中成员执行callbackFn操作,如果提供了thisArg参数 回调中的this会是这个参数 没有返回值
Set可默认遍历 默认迭代器生成函数是values()方法
Set.prototype[Symbol.iterator] === Set.prototype.values //true
Set 可以使用map filter方法
weakSet
WeakSet对象允许你将弱引用对象储存在一个集合中
WeakSet与Set的区别:
WeakSet只能存储对象引用,不能存储值,Set 都可以
WeakSet 对象中存储的对象值都是被弱引用的,即垃圾回收机制不考虑WeakSet对该对象的应用,如果没有其他的变量或属性引用这个对象值,则这个对象会被垃圾回收机制处理掉,不会考虑这个对象还在WeakSet中,所以WeakSet对象中有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致 遍历之后 可能有的成员取不到(被处理掉了)
所以WeakSet对象是无法被遍历的(es6规定WeakSet不可遍历),也无法拿到它包含的元素
属性:constructor:构造函数 任何一个具有Iterable接口的对象,都可以作参数
方法:
add(value):在WeakSet对象中添加一个元素
has(value):判断WeakSet对象中是否包含value
delete(value) : 删除元素value
clear():清空所有元素, 注意此方法已废弃
Map
字典(Map)
集合和字典的区别
共同点:集合、字典可以存储不重复的值
不同点:集合是以[value,value]的形式存储元素 字典是以[key,value]的形式存储
任何具有Iterator接口,且每个成员都是一个双元素的数组的数据结构 都可以当作Map构造函数的参数
如果读取一个未知的键 则返回undefined
Map的键实际上是跟内存地址绑定的 只要内存地址不一样 就视为两个键,这就解决了同名属性碰撞的问题 我们扩展别人的库的时候 如果使用对象作为键名 就不用了担心自己的属性和作者的属性同名
如果Map的键是一个简单类型的值(数字 字符串 布尔值),则只要两个值相同,Map就会将其视为一个键,比如0 与 -0 就是一个键 true与‘true’则是两个不同的键 undefined与null也是两个不同的键 虽然NaN不严格等于自身,但是Map将其视为同一个键
Map的属性和方法
属性:constructor:构造函数
size:返回字典中包含的元素个数
操作方法
set(key,value):向字典添加新元素
get(key) :通过键查找特定的值并返回
has(key):判断字典中是否存在键key
delete(key):通过key从字典中移除对应的数据
clear(): 将这个字典中的元素都删除
遍历方法:
Keys():将字典中的键名以迭代器的形式返回
Values():将字典中的值以迭代器形式返回
entries():返回所有成员的迭代器
forEach():遍历字典中的所有成员
Map结构转为数组结构 比较快速的方法是使用扩展运算符...
weakMap
WeakMap对象是一组键值对的集合 其中的键是弱引用对象 而值是任意
WeakMap弱引用的只是键名 而不是键值 键值依然是正常引用
WeakMap中每个键对自己所引用对象的引用都是弱引用
在没有其他引用和该键引用同一个对象 这个对象将会被垃圾回收(相应的key则变成无效的) 所以WeakMap的key是不可枚举的。
属性
constructor构造函数
方法:
has(key):判断集合中是有key关联的对象
get(key) :返回key所关联的对象
set(key) :设置一组key关联对象
delete(key):移除key的关联对象
let myElement = document.getElementById("logo")
let myWeakmap = new WeakMap()
myWeakmap.set(myElement,{timesClicked:0})
myElement.addEventListener('click',function(){
let logData = myWeakmap.get(myElement)
lodData.timesClicked ++
},false)
// // 指定事件是否在捕获或冒泡阶段执行。
// true 在捕获阶段执行
// false 在冒泡阶段执行
第 5 题:ES5/ES6 的继承除了写法以外还有什么区别?
1 class 声明会提升,但不会初始化赋值。Foo进入暂时性死亡,类似于let、const声明变量(即变量会绑定某个区域,不受外部影响)
2 class 声明内部会启用严格模式
3 class 的所有方法(包括静态方法和实例方法) 都是不可枚举的 (Object.keys(obj))
4.class的所有方法(包括静态方法和实例方法)都没有原型对象prototype,所以也没有constructor,不能使用new来调用
5 必须使用new 调用class
6 class 内部无法重写类名
继承 都是每个对象在创建之初 就存在的prototype属性进行关联、委托 从而建立联系,间接的实现继承
区别于Es5的继承 Es6的继承实现在于使用super关键字调用父类 反观Es5是通过call或者apply回调方法调用父类
第6题 js new一个function都发生了什么
proto是原型 prototype是函数默认的一个属性,它指向一个对象,这个对象的construtor属性指向函数本身
function Foo (){}
var o = new Object()
o.[[prototype]] = Foo.prototype;
Foo.call(o)
一个对象的内部属性[[prototype]]和proto指向一个值 所以说在new的时候 可以理解为
let fun = new Object()
fun._proto_ = TestFun.prototype
TestFun.call(fun)
1.创建一个空对象obj
2.然后将this指向新创建的对象obj
3 将这个对象的proto 指向构造函数的prototype
建立对象和原型的对应关系
4 最后执行构造函数中的代码
第7题CSS3的新特性
background-origin:当我们设置背景图片时 图片对其方式
是以border的左上角对齐
以padding的左上角
content的左上角对齐
border-box
padding-box
content-box
background-clip属性是用来设置背景(背景图片、背景颜色)延伸的范围,有4个值可选
border-box:背景延伸至边框外沿(但是在边框下层)
padding-box:背景延伸至内边距(padding)外沿,不会绘制到边框处
content-box:背景被裁剪至内容区(content box)外沿
text:背景被裁剪成文字的前景色
background-size 用以设置背景图片大小
contain 图片能够最大的包含
cover 等比例最小的覆盖背景区
border-radius 边框圆角
box-shadow 盒子阴影
属性选择器
div[class] {
/* 会选择包含class属性的div标签 */
}
div[class="active"] {
/* 会选择class属性值为active的div标签 */
}
div[class^="header"] {
/* 会选择class属性以header开头的div标签 */
}
结构伪类选择器
E:first-child
E:last-child
E:nth-child(n)
E:nth-last-child(n)
E:first-of-type
E:last-of-type
E:nth-of-type(n)
E:nth-last-of-type(n)
CSS3颜色渐变
线性渐变指的是颜色在一条线上平稳的变化
为了实现线性渐变,我们要规定线的方向,起点颜色和终止颜色
background-image: linear-gradient(direction, color-stop1, color-stop2, ...);
径向渐变
径向渐变是指以某点为圆心,向外进行颜色渐变。所以为了实现径向渐变,我们要规定圆心的位置和起点颜色和终点颜色
background-image: radial-gradient(shape size at position, start-color, ..., last-color);
CSS3 2D变换
CSS3 2D变换包括对元素进行移动、缩放、转动、拉长或拉伸。
translate():对元素进行进行移动
scale():对元素进行缩放
rotate():围绕中心旋转,正值顺时针,负值逆时针
skew():对元素进行倾斜
CSS3 3D
transition
transition-property:指定要添加效果的CSS属性
transition-duration:添加过渡的总时间
transition-timing-function:时间函数,设置过渡的变化速度
transition-delay:延时时间,默认值为0s
animation
animation-name:动画名称
animation-duration:动画持续时间
animation-timing-function:动画时间函数
animation-delay:动画延迟时间
animation-iteration-count:动画执行次数
animation-direction:动画执行方向
animation-paly-state:动画播放状态
animation-fill-mode:动画填充模式
CSS3 flex布局
当一个父元素被设置为display:flex时,它就是弹性布局,子元素的float、clear和vertical-align属性将失效
父元素称之为container(容器),把子元素称之为item(项目)
container上的属性
flex-direction是用来设置主轴的,它有以下四个值可选
row:默认值,主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。
设置flex-wrap,它有三个值可选
nowrap:默认值,不换行
wrap:换行,第一行在上方
wrap-reverse:换行,第一行在下方
justify-content是用来设置item在container在主轴上的对齐方式的
flex-start:左对齐
flex-end:右对齐
center:居中对齐
space-between:两端对齐,项目之间间隔相等
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
align-items定义了在侧轴上的对齐方式
first-start:往上对齐
first-end:往下对齐
center:居中对齐
baseline:item的第一行文字的基线对齐。
stretch:默认值,如果没有设置height或者height设置为auto,将占满整个容器的高度
align-content,当我们设置flex-wrap为wrap或者wrap-reverse时,item会占据多行,这个属性是用来设置占据多行item在侧轴上的对齐方式,如果没有多行,这个属性无效。
first-start:往上对齐
first-end:往下对齐
center:居中对齐
stretch:默认值,轴线占据整个侧轴
space-between:两端对齐,轴线之间平均分布
space-around
item上的属性
order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0,
.item:nth-child(2){
order: 1;
}
flex-grow可以用来设置item占据剩余空间的份数(这会导致item增大)
flex-shrink与flex-grow相反,当空间不足时,item缩小的以使得所有item被包含在container中。默认值是1,即每个item会等比例缩小
.item:last-child {
flex-shrink: 2;
}
slef-align属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。
.container {
... ...
align-items: center;
flex-wrap: wrap;
}
.item:first-child {
align-self: flex-start;
}
第8题H5新特性
1.拖放(Drog 和 drop)
2 地理定位
通过getCurrentPosition()获取一系列定位信息,getCurrentPosition()有两个回调函数参数,获取地理位置成功的回调和失败的回调。
navigator.geolocation.getCurrentPosition(successPos)
function successPos (pos){
console.log('用户定位数据获取成功')
//console.log(arguments);
console.log('定位时间:',pos.timestamp)
console.log('经度:',pos.coords.longitude)
console.log('纬度:',pos.coords.latitude)
console.log('海拔:',pos.coords.altitude)
console.log('速度:',pos.coords.speed)
}
离线存储
通过创建 cache manifest 文件,可以创建 web 应用的离线版本。如果要启用应用程序缓存,必须在文档的 <html> 标签中包含 manifest 属性:每个指定了 manifest 的页面在用户对其访问时都会被缓存。如果未指定 manifest 属性,则页面不会被缓存
manifest 文件的建议的文件扩展名是:".appcache".请注意,manifest 文件需要配置正确的 MIME-type,即 "text/cache-manifest"。必须在 web 服务器上进行配置
manifest 文件可分为三个部分:
CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存
FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面)
CACHE MANIFEST
2012-02-21 v1.0.0
/theme.css
/logo.gif
/main.js
NETWORK:
login.asp
FALLBACK:
/html5/ /404.html
web的资源文件存储起来,个人觉得移动端的应用效果会更好一些,毕竟流量贵呀,把一些文件存储起来,这样可以大大的节省流量和服务器的压力,
Web 存储
localStorage,没有时间限制的数据存储
sessionStorage,就是网页还没有关闭的情况下的存储,网页窗口关闭,则数据销毁。
WebSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
语义标签
<header><footer> <nav> <aside> <article>
增强型表单
color date month email number url tel
range (一个范围内数字值的输入域)
<audio>元素。
音频:<audio src=" "></audio>
视频:<video src=" "></video>
Canvas绘图
第9题vuex和localStorage的区别
vuex存储在内存 vuex用于组件之间的传值 当刷新页面时vuex存储的值会丢失 localstorage不会。
localstorage(本地存储)则以文件的方式存储在本地,永久保存 只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理 用于不同页面之间的传值。
对于不变的数据 用localstorage可以代替vuex
但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage 无法做到
第10题vue页面刷新,vuex中state的数据丢失问题
产生的原因是因为 vuex的store里的数据是保存在运行内存中的,当页面刷新时 页面会重新加载Vue实例 store里的数据会被重新赋值初始化
解决方法
1,将state中的数据保存在localstorage,sessionstorage或cookie中。
例如将token存储在cookie中 用户的信息是需要经常修改的 因此用户信息持久化本地中 需要每次拉取用户信息
2 使用vue的插件 例如vuex-persistedstate
实际上其根本的原理 就是将vuex中的state存在localstorage或者sessionStorage中,在页面刷新后vuex在从localStorage中把数据拿回来
第11题Vuex的对象
vuex 是我们需要共享的data,使用vuex进行统一集中式的管理
vuex中有默认的五种基本对象
state :存储状态(变量)
getters:对数据获取之前的再次编译,可以理解为state的计算属性 我们可以在组件中使用$store.getters.fun() getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
getters:{
getTodoById:(state)=>(id)=>{
return state.todos.find(todo=>todo.id === id)
}
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
store.getters.getTodoById(2) //{id:2,text:....}
mutations:修改状态 并且是同步的 在组件中使用$store.commit('',params)更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
// 以载荷形式
store.commit('increment',{
amount: 10 //这是额外的参数
})
// 或者使用对象风格的提交方式
store.commit({
type: 'increment',
amount: 10 //这是额外的参数
})
额外的参数会封装进一个对象,作为第二个参数传进mutation 定义的方法中
mutations:{
increment(state,payload){
state.count += payload.amount
}
}
action:异步操作 在组件中使用是 $store.dispatch('') Action 提交的是 mutation,而不是直接变更状态。
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
参数解构
actions:{
increment({commit}){
commit('increment')
}
}
发起action的方法形式和发起mutation一样,只是换个名字dispatch
以对象的形式分发action
store.dispatch({
type:'increment',
amount:10
})
Actions 支持同样的载荷方式和对象方式进行分发
**Action处理异步的正确使用方式**
1、store.dispatch返回相应action执行结果 而action的处理函数返回的就是promise,所以store.dispatch返回一个promise
actions:{
actionA({commit}){
return new Promise((resolve,reject)=>{
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000);
})
}
}
然后就可以写成
store.dispatch('actionA').then(()=>{
})
在另外一个action中也可以发起actionA
actions:{
actionB({diapatch,commit}){
return dispatch('actionA').then(()=>{
commit('someMutaton')
})
}
}
利用async/await进行组合action 代码更加简洁
假设getData和getOtherData() 返回的是Promise
actions:{
async actionA({commit}){
commit('getData',await getData())
}
async actionB({dispatch,commit}){
await dispatch('actionA') //等待A完成
commit('otherData',await otherData())
}
}
一个store.action 在不同的模块中可以触发多个action函数。在这种情况下 只有当所有触发函数完成后,返回的Promise才会执行
modules:store的子模块 为了开发大型项目,方便状态管理而使用。由于使用单一状态树 应用的所有状态会集中到一个比较大的对象 当应用变得非常复杂时 store对象就有可能变得相当臃肿这时 我们可以将store分割为模块 (module),每个模块 有自己的mutations actions state getters, 甚至是嵌套子模块 ----从上至下进行同样的分割
const moduleA = {
state:{...},
mutations:{..},
actions:{...},
getters:{....}
}
const moduleB = {
state:{...},
mutations:{..},
actions:{...},
getters:{....}
}
const store = new Vuex.store({
modules:{
a:moduleA,
b:moduleB
}
})
store.state.a //moduleA的状态
store.state.b // moduleB的状态
**嵌套子模块**
创建子模块的文件
//products.js
// initial state
const state = {
added:[],
checkoutStatus:null
}
//getters
const getters = {
checkoutStatus:state = > state.checkoutStatus
}
//actions
const actions = {
checkout({commit,state},products){
}
}
const mutations = {
mutation1(state,{id}){
}
}
export default{
state,
actions,
getters,
mutations
}
然后在总模块中引入
import Vuex from 'vuex'
import products from './mudules/products'
Vue.use(VueX)
export default new Vuex.Store({
modules:{
products //添加进模块中
}
})
各个模块 与Vue组件结合
将state和getter结合进组件需要使用计算属性
computed:{
count(){
return this.$store.state.count
// 或者 return this.$store.getter.count
}
}
将mutation与action结合进组件需要在methods中调用this.$store.commit()
methods:{
changeData(){
this.$store.commit('change')
}
}
为了简便起见 Vuex 提供了4个辅助函数方法用来方便将这些功能结合进组件
mapState
mapGetters
mapMutations
mapActions
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default{
computed:{
//使用对象展开运算符将此对象混入外部对象中
...mapState({
//为了能够使用`this` 获取局部状态,必须使用常规函数
count(state){
return state.count + this.localCount
}
}),
...mapGetters({
getterCount(state,getters){
return state.count + this.localCount
}
})
},
methods:{
...mapMutations({
//如果想将一个属性另外取一个名字 使用以下形式
add:'increment' //将this.add() 映射为this.$store.commit('increment')
}),
...mapActions({
add: 'increment' //将this.add() 映射为this.$store.dispatch('increment')
})
}
}
// 如果结合进组件之后不想修改名字 可以直接使用数组的方式
methods:{
...mapActions([
'increment', //将this.increment 映射为this.$store.dispatch('increment')
'incrementBy' //mapAction也支持载荷 this.increment(amount)映射为this.
])
}
为何使用展开运算符:mapState 等四个函数返回的都是一个对象,我们如果将它与局部计算属性混合使用呢?通常 我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给computed属性。但是有了对象展开运算符 我们就可以进行简化写法
项目结构
Vuex不限制代码结构,但是有一些规则
1 应用级别的状态应该集中到单个store对象中
2 mutation是改变状态的唯一方法 并且这个过程是同步的
3 异步逻辑操作都应该封装到action里面
如果store文件太大 只需要将action mutation getter 分割到单独的文件
对于大型应用 把vuex相关的代码 切割到模块中
第12题 computed和watch的区别和运用的场景
computed:是计算属性,依赖其他属性值 并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值才会重新计算computed的值
watch;更多的是观察的作用 类似于某些数据的监听回调,每当监听的数据发生改变时都会执行回调进行后续操作
运用场景:
当我们需要数值计算 并且依赖于其他数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值 都要重新计算
当我们需要在数据变化时执行异步或开销较大的操作时 应该使用watch, 使用watch选项允许我们执行异步操作(访问一个API),限制我们执行该操作的频率,
并在我们得到最终结果前 设置中间状态。这些都是计算属性无法做到的
第13题 Class与style如何动态绑定
Class可以通过对象语法和数组语法进行动态绑定
对象语法:
<div v-bind:class="{active:isActive,'text-danger':hasError}"></div>
data:{
isActive:true,
hasError:false
}
数组语法
<div v-bind:class="[isActive?activeClass:'',errorClass]"></div>
data:{
activeClass:'active',
errorClass:'text-danger'
}
Style也可以通过对象语法和数组语法进行动态绑定
对象语法
<div v-bind:style="{color:activeColor,fontSize:fontSize + 'px'}"></div>
data:{
activeColor: 'red',
fontSize:30
}
数组语法
<div v-bind:style="[styleColor,styleSize]"></div>
data:{
styleColor:{
color:'red'
},
styleSize:{
fontSize:'23px'
}
}
第14题 怎样理解Vue的单向数据流
所有prop都使得其父子prop之间形成了一个单向下行绑定 :父级prop的更新会向下流动到子组件中,但是反过来就不行。这样防止从子组件意外改变父组件的状态,从而导致你的数据流向难以理解
每次父组件发生更新,子组件的所有prop都会更新到最新状态,所以在子组件内部不能修改prop,不然浏览器控制台Vue会报错,如果想要修改值,通过$emit 派发一个自定义事件,父组件接受后,由父组件更改
有两种情况 子组件改变prop
1 prop用来传递一个初始值, 子组件希望这个prop值作为本地的prop数据值使用,作为子组件data属性的初始值
prop:['initialCounter']
data:function (){
return {
counter:this.initialCounter
}
}
2 这个prop以原始的值传入且需要转换 在这种情况下 使用prop的值;来定义一个计算属性
props:['size'],
computed: {
normalizedSize:function(){
return this.size.trim().toLowerCase()
}
}
第15题 Vue页面权限控制和登录验证
Vue 通过动态添加路由和菜单来做控制,不能访问的页面不添加到路由表里
另一种是所有的页面都在路由表里 只是访问的时候要判断一下角色权限,如果有,就访问,不然就是404页面
思路:
在每一个路由的meta属性中, 将可以访问该路由的角色添加到roles里,
每次用户登录后 将用户角色返回。然后再访问页面时,把路由的meta属性中的roles里的角色和用户角色做对比,如果用户的角色在roles中就可以访问 如果不在就拒绝访问
代码示例:
路由信息:
routes:{
{
path:'/login',
name:'login',
meta:{
roles:['admin','user']
},
component:()=> import ('../components/Login.vue')
},
{
path:'home',
name:'home',
meta:{
roles:['admin']
},
component:()=> import ('../component/Home.vue')
}
}
页面控制
//假设角色有两种:admin 和user
// 这里是后台获取的用户角色
const role = "user"
//在进入一个页面前就会触发router.beforeEach事件
Router.beforeEach((to,from,next)=>{
// 某个数组是否包含给定的值 返回一个布尔值
if(to.meta.roles.includes(role)){
next()
}else{
next({path:'/404'})
}
})
**登录验证**
网站一般只要登录一次后,接下来该网站的其他页面都是可以直接访问的,不用再次登录
我们可以通过token 或者cookie来实现
router.beforeEach((to,from,next)=>{
//如果有token说明该用户已登录
if(localStorage.getItem('token')){
//在已登录的情况下访问登录页会重定向首页
if(to.path === '/login'){
next({path:'/'})
}else{
next(path:to.path|| '/')
}
}else{
// 没有登录则访问任何页面都会重定向登录页
if(path.to === 'login'){
next({path:'/login'})
}else{
next(`login?redirect=${to.path}`)
}
}
})
第16题 Vue的父组件和子组件生命周期钩子函数执行顺序
加载渲染过程
父beforeCreate - 父 created -父beforeMount -子beforeCreate-子created-子beforeMount - 子mounted - 父 mounted
子组件 更新过程
父 beforeUpdate - 子beforeUpdate - 子updated - 父updated
父组件更新
父 beforeUpdate - 父 updated
销毁过程
父beforeDestroy - 子beforeDestroy - 子 destroyed - 父 destroyed
第17题 在哪个生命周期内适合调用异步请求
可以在钩子函数created,beforeMount,mounted中进行调用,因为在这三个钩子函数中 data已经创建 可以将服务器返回的数据进行赋值了
但是更推荐在created中调用,
1 能够更快的获取到服务端的数据 减少页面loadding的时间
2 ssr 不支持beforeMount mount钩子函数 所以放在created中有助于一致性
第18题 父组件可以监听到子组件的生命周期吗
比如有父组件Parent和子组件Child,如果父组件需要监听子组件挂载mounted就做一些逻辑处理 可以通过以下写法实现
//Parent.vue
<Child @mounted="doSomething"/>
//Child.vue
mounted(){
this.$emit('mounted')
}
以上需要手动通过$emit触发父组件的事件 更简单的方式 也可在父组件引用子组件通过@hook来监听即可
//Parent.vue
<Child @hook:mounted="doSomething"/>
doSomething(){
console.log('父组件监听mounted钩子函数');
},
//Child
mounted(){
console.log('子组件触发mounted钩子函数');
}
以上输出顺序为
子组件触发mounted钩子函数
父组件监听到mounted钩子函数
当然@hook不仅仅监听mounted钩子函数 也可以监听到其他的周期事件 created updated等
第19题 vue-cli配置跨域代理
”协议+域名+端口”三者相同即是同源
同源策略的限制 :
1 Cookie、LocalStorage,indexDB无法获取 (a页面的cookie不能被b页面获取)
2 DOM和Js对象无法获得(非同源的页面通过iframe 通信)
3 AJax请求不能发送
不受同源策略限制的是
1 script标签允许跨域嵌入脚本 jsonp就是利用该规则解决跨域
2 img标签 link标签 @font-face不受跨域影响
3 video 和 audio 嵌入的资源
4 iframe载入的任何资源
5 WebSocket不受同源策略的限制
解决方案:
1 WebSocket协议跨域
2 nginx代理跨域
3 跨域资源共享(CORS)
4 nodejs中间件代理跨域
5 通过jsonp跨域
6 document.domain + iframe跨域
7 location.hash + iframe
8 window.name + iframe跨域
config文件下 index.js中dev配置对象中proxyTable属性,这里是一个对象
proxyTable:{
'/api':{ //这里的api就是axios的baseUrl
target: 'http://127:0.0.1',//访问域名
changeOrigin:true, //开启跨域
pathRewrite:{ //路径重写
'^/api':'api/index.php' //替换target的请求地址
}
}
}
axios设置baseUrl可以设置为'/api',然后在proxyTable里面定义匹配这个路径的代理配置对象
设置target为访问的域名, 设置重写为访问域名的后缀,即域名后的地址,然后开启changeOrigin属性即可
由于这个配置文件不在src下 不会触发构建 每次修改重新npm run dev 来使用新的配置 可看到命令行输出代理服务器配置信息
第20题 为什么Vue中的data必须是一个函数
对象为引用类型,当复用组件时 由于数据对象指向同一个data对象,当在其中一个组件中修改data,其他重用组件的data也会被修改。而使用返回对象的函数,由于每次返回的都是一个新对象(Object实例),引用地址不同,则不会出现这个问题
第21题 谈谈你对 keep-alive 的了解?
keep-alive 是Vue内置的一个组件,可以使被包含的组件保留状态 避免重新渲染
1 一般结合路由和动态组件一起使用 用于缓存组件
2 提供exclude和include属性 两者都支持字符串或正则表达式 include 表示只有名称匹配的组件会被缓存 exclude表示匹配的组件不会被缓存, 其中exclude的优先级比include高
3 对应两个钩子函数 activated和deactivated,当组件被激活时,钩子函数activated,当组件移除时 触发钩子函数deactivated.
第22题 v-model的原理
在vue项目中主要使用v-model指令在表单input、textarea、select等元素上创建双向数据绑定。v-model本质上不过是语法糖 v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件;
text 和textarea元素使用value属性和input事件
checkbox和radio使用checked属性和change事件
select 字段将value作为prop 并将change作为事件
input表单元素为例:
<input v-model="someValue"/>
相当于
<input v-bind:value="someValue" v-on:input="someValue = $event.target.value">
如果在自定义组件中 v-model默认会利用名为value的prop和名为input的事件
{/* 父组件 */}
<modelChild v-model="message"></modelChild>
{/* 子组件 */}
<div>{{value}}</div>
props:{
value:String
},
methods:{
test1(){
this.$emit('input','小红')
}
}