准备的面试

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','小红')
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值