web面试必问的题

此文转载自:https://blog.csdn.net/GYF_123/article/details/112268866#commentBox

1. vue双向数据绑定原理

vue是采用数据劫持结合发布-订阅的方式,通过object.defineProperty()来劫持各个属性的setter,getter,在数据发生变动时发布消息给订阅者,触发相应的监听回调来渲染视图
第一步:
需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上setter,getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据变化
简单来说就是实现一个数据监听器Observer,它能够对数据对象的所以属性进行监听,如果有变动就可以拿到最新的值并通知订阅者

第二步:
compile解析模块指令,讲模块中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
简单来说就是实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,替换数据,绑定更新函数
第三步:
Watcher订阅者时ObserverCompile之间的通讯桥梁,主要做的事情是:
1.在自身实例化时往属性订阅器(dep)里面添加自己
2.自身必须有一个update()方法
3.待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中的绑定回调
简单来说就是实现一个Watcher,作为Observer和Compile的桥梁,能够收到每个属性变动的通知,绑定相应的回调函数,从而更新视图
第四步:
MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭建起Observer和Compile之间的通信桥梁,达到数据变化->视图更新; 视图交互变化->数据model变更的双向绑定效果

MVVM就是实现Vue双向数据绑定的根本,MVVM本质上是MVC的改进版,即模型-视图-视图模型,【模型】指的是后端传递的数据。【视图】指的是所看到的页面。【视图模型】MVVM的核心,他是连接view和model的桥梁。它有两个方向: 一个是将【模型】转换成【视图】,即后端传递的数据转换成所看到的页面:实现方式是:数据绑定。第二个是【视图】转化成【模型】,即将所看到的页面转换成后端的数据:实现方式是:DOM事件监听。这两个方向都实现的话,我们称之为数据的双向绑定

2.vue虚拟dom,diff算法

虚拟DOM就是为了解决游览器性能问题而被设计出来的。

就是用对象来模拟dom元素和嵌套关系,指的就是用js对象的形式,来模拟页面上的DOM嵌套关系

目的:实现页面元素的高效更新,避免大量无谓的计算(若一次操作中有10次更新DOM的动作,虚拟DOM不会立即去操作DOM,而是将10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一个姓attch到DOM树上,再进行后续操作)

作用:用来比较两次DOM结构

web界面由DOM树(树的意思就是数据结构)来搭建,当其中一部分发生变化时,其实就时对应某个DOM节点发生了变化

diff算法是作为DOM的加速器,改进优化了页面渲染的基础和性能保障

用js对象表示dom树结构,用这个树建立一个真正的dom树,插入到文档中,状态更新,重新构造一棵新树,两树比较差异,把差异应用到真的dom树上,更新视图

3. 组件 通讯

1.父传子
父组件是通过props属性给子组件通信的
父组件代码:

<template>
	<div>
		<h2>{{msg}}</h2>
		<Son :faMsg="msg"></Son>
	</div>
</template>
	
 <script>
	import Son from "./Son";
	export default {
		name:"HelloWorld",
		data(){
			return{
				msg:"父组件"
			}
		},
		components:{Son}
	}
 </script>

子组件代码:

<template>
	<div>
		<h2>{{sonMsg}}</h2>
		<p>子组件接收到的内容是:{{faMsg}}</p>
	</div>
</template>
	
 <script>
	export default {
		name:"Son",
		data(){
			return{
				sonMsg:"子组件"
			}
		},
		props:['faMsg'], //接收faMsg值
		//还可以这样,上面的不是很严谨
		props:{
			faMsg:{
				type:String,
				default:"123"
			}
		}
	}
 </script>

在父组件中给子组件标签上绑定一个属性, 属性上挂载需要传递的值
在子组件通过props:[“自定义属性名”]来接收数据

子传父:
(1) 在父组件中给子组件标签绑定一个自定义事件,给这个事件挂载需要调用的方法
(2) 在子组件的方法通过this.$emit(‘自定义事件名’)来调用这个方法

父组件代码:

<template>
   <div>
     <h2>{{ msg }}</h2>
     <p>父组件接手到的内容:{{ username }}</p>
     <son psMsg="我是你爸爸" @transfer="getUser"></son> 
      <!-- 监听子组件触发的transfer事件,然后调用getUser方法 -->
   </div>
 </template>
 <script>
 import son from './Son'
 export default {
   name: 'HelloWorld',
   data () {
     return {
       msg: '父组件',
       username:'',
     }
   },
   components:{son},
   methods:{
     getUser(msg){
       this.username= msg
     }
   }
 }
 </script>

子组件代码:

	 <template>
   <div class="son">
     <p>{{ sonMsg }}</p>
     <p>子组件接收到内容:{{ psMsg }}</p>
     <!--<input type="text" v-model="user" @change="setUser">-->
     <button @click="setUser">传值</button>
   </div>
 </template>
 <script>
 export default {
   name: "son",
   data(){
     return {
       sonMsg:'子组件',
       user:'子传父的内容'
     }
   },
   props:['psMsg'],
   methods:{
     setUser:function(){
       this.$emit('transfer',this.user)//触发transfer方法,this.user 为向父组件传递的数据
     }
   }
 }
 </script>

兄弟通信: 比如说vuex 就可以说是一个兄弟通信

4. vuex

首先我们要知道Vuex是一个专为vue.js应用程序开发的状态管理模式,它由五部分组成:
分别是:state,actions,mutations,getters,modules
在这里插入图片描述
vuex的五部分分别是:

  • state:存储状态
  • actions:可以包含异步操作
  • getters:类似vue组件中的计算属性,会对state数据进行计算(会被缓存)
  • mutations:唯一可以对state数据修改的场所
  • modules:模块化管理仓库,每个模块都拥有自己的 state,actions,mutations,getters,modules

Vuex一般用来存储在vue.js中大量出现的数据,比如说token,我们就可以把token存放在Vuex中,当然购物车也是要用到Vuex的.

数据持久化
问题:存储在vuex中的状态,刷新页面会丢失。
为了解决刷新页面数据丢失,才有了数据持久化。
最简单的做法就是利用插件 vuex-persistedState
1.安装
cnpm i vuex-persistedState -S
备注:
-S 是 --save的简写,意思是:把插件安装到dependencies(生成环境依赖)中
-D 是 --save-dev的简写,意思是:把插件安装到devDependencies(开发环境依赖)中
2.使用

import createPersistedState from 'vuex-persistedstate'

const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  plugins: [createPersistedState({
    storage: sessionStorage,
    key: "token"
  })]//会自动保存创建的状态。刷新还在
})

参数:
storage:存储方式。(sessionStorage,localStarage) key:定义本地存储中的key

模块化管理数据(modules)
1.什么时候需要用到模块管理vuex数据

项目庞大,数据信息量特别大的时候,我们可以考虑分模块形式管理数据,比如user模块管理用户信息数据,cart模块管理购物车数据,shop模块管理商品信息数据

5. vue-router(路由原理,路由守卫,路由传参)

路由实现原理:

概念:
通过改变URL,在不重新请求页面的情况下,更新页面视图。
实现方式:
vue-router通过hashHistory interface两种方式实现前端路由,更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式:
1.hash ---- 利用URL中的hash(’#’);
2.利用History interface在HTML5中的新增方法

6. vue生命周期

简单粗暴:
答: 总共分为8个阶段。创建前/后 , 挂载前/后 , 更新前/后 , 销毁前/后
创建前/后: beforeCreated创建前,vue实例的挂载元素$el和数据对象data都是undefined,还未初始化。在created阶段/创建后,vue实例的数据对象data有了,$el还没有。
挂载前/后: beforeMount挂载前,vue实例的eldata都初始化了,但是还没有挂载html 到页面上。在Mounted阶段/挂载后,模板中的 HTML 渲染到 HTML 页面中,此时一般可以做一些 ajax 操作,mounted 只会执行一次。
更新前/后: 当data变化时,会触发beforeUpdateupdated方法
销毁前/后:destroy阶段/销毁前,对data的改变不会再触发周期函数,说明此时vue实列已经解除了事件监听和dom绑定,但是dom结构依然存在。destroyed阶段,组件销毁
在这里插入图片描述

7. 自定义指令,自定义过滤器

Vue 自定义指令如何使用

  • 自定义指令分为全局自定义指令和局部自定义指令:
  • 全局自定义指令:
    使用 Vue.directive()来全局注册指令
  • 局部自定义指令:
    也可以注册局部指令,组件或 Vue 构造函数中接受一个 directives 的选项
    钩子函数
    自定义指令定义函数提供了几个钩子函数:
  • bind
    只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作
  • inserted
    被绑定元素插入父节点时调用( 父节点存在即可调用, 不必存在于
    document 中)
  • update
    所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新
  • componentUpdated
    所在组件的 VNode 及其孩子的 VNode 全部更新时调用
  • unbind
    只调用一次,指令与元素解绑时调用

参数
钩子函数中有两个参数:

  1. el : 指令所绑定的元素,可以用来直接操作 DOM
  2. binding : 一个对象,包含以下属性:
    name: 指令名,不包括 v- 前缀
    value: 指令的绑定值
    oldValue: 指令绑定的前一个值(update 和 componentUpdated 钩子中可用。无论值是否改变都可用)
    expression: 绑定值的字符串形式
    arg: 传给指令的参数

使用场景: 当我们需要对普通的DOM元素进行底层操作时就可以用到自定义指令
全局自定义指令:

//main.js中
// 全局自定义指令
Vue.directive(
  'upper-text',{bind: (el, binding)=> {
    	console.log(el, binding)
    	el.innerHTML = binding.value.toUpperCase() //转换大写
  	}
  })

局部自定义指令

//组件中
<template>
  <div class="wrapper">
    <br />
    <span>原数据</span>
    <p>{{message}}</p>
    <br />
    <span>全局</span>
    <p v-upper-text="message" v-color="'blue'"></p>
    <br />
    <span>局部</span>
    <p v-lower-text="message"></p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      message: 'You can start the Internet!'
    }
  },
  computed: {},
  //局部自定义指令
  directives: {
    'lower-text'(el, binding) {
      //el:指令属性所在的标签对象
      //binding:包含指令相关信息数据的对象
      console.log(el, binding)
      el.textContent = binding.value.toLowerCase() //转换小写
    },
    color:{//改变颜色
        bind(el,binding){
            el.style.color = binding.value
        }
    }
  },
}
</script>

在这里插入图片描述
在这里插入图片描述

8. 自定义组件

简介
组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树
在这里插入图片描述
功能
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子

组件注册
组件名

组件名应该始终是多个单词的,根组件 App 除外
这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的
单文件组件的文件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)
混用文件命名方式有的时候会导致大小写不敏感的文件系统的问题,这也是横线连接命名同样完全可取的原因

  • 使用 kebab-case
    当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
Vue.component('my-component-name', { /* ... */ })
  • 使用 PascalCase
    当使用 PascalCase (驼峰式命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
    <my-component-name><MyComponentName>都是可接受的。注意,尽管如此,直接在 DOM
    (即非字符串的模板) 中使用时只有 kebab-case 是有效的
Vue.component('MyComponentName', { /* ... */ })
  • 全局注册
    以上方法都属于全局注册, 也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中, 比如
  • HTML
<div id="app">
  <component-a></component-a>
  <component-b></component-b>
  <component-c></component-c>
</div>
  • JS
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })

在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用

9. 常用的指令,修饰符

在vue中提供了一些对于页面 + 数据的更为方便的输出,这些操作就叫做指令,指令中封装了一些DOM行为, 结合属性作为一个暗号, 暗号有对应的值,根据不同的值,框架会进行相关DOM操作的绑定
vue中的指令有很多,我们平时做项目常用的有:
v-if:是动态的9向DOM树种添加或者删除元素;
v-else:是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用
v-show : 是通过标签的CSS样式display的值是不是none,来控制显示隐藏

10. vue2.0和vue3.0的区别

11. keep-alive

概念:
是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现的父组件链中。
作用:
在组件切换的过程中,把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载事件以及性能消耗,提高用户体验性
原理:
created钩子函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果VNodename 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode实例进行渲染
VNode: 虚拟DOM,其实就是一个JS对象
参数:

  • include - 字符串或者正则表达式。只有匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。然后匹配的组件都不会缓存。
  • max - 数字。最多可以缓存多少组件实例

对生命周期函数变化:
被包含在keep-alive中的组件,会多出来两个生命周期的钩子函数,activateddeactiveated
1.activated
在keep-alive组件激活时调用
2.deactivated
在keep-alive组件离开时调用

//正常的生命周期:beforeRouterEnter --> created -->mounted --> updated --> destroyed


//使用keepAlive后生命周期:
//首次进入缓存页面 :beforeRouterEnter --> created -->mounted --> updated --> destroyed
//再次进入缓存页面 :beforeRouteEnter --> activated --> deactivated
//注:
//1、这里的activated非常有用,因为页面被缓存时,created,mounted等生命周期均失效,你若想进行一些操作,那么可以在activated内完成(下面会举个栗子:列表页回到上次浏览位置)
//2、activated   keep-alive组件激活时调用,该钩子在服务器端渲染期间不被调用。 
//3、deactivated   keep-alive组件停用时调用,该钩子在服务端渲染期间不被调用

可以结合Router中的meta,来缓存部分页面

12. 多环境变量

13. 对axios封装,(url统一管理,axios请求拦截,响应拦截,函数封装)

14. element-ui,vant-ui 按需引入

15. sass配置

16. rem , vw/vh 适配

rem适配:
使用vant里面的游览器适配,
Vant 中的样式默认使用 px 作为单位,如果需要使用 rem 单位,推荐使用以下两个工具:

postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem
lib-flexible用于设置 rem 基准值
安装 lib-flexiblepostcss-plugin-px2rem
cnpm i lib-flexible postcss-plugin-px2rem --save-dev
配置rem: mian.js 导入

import 'lib-flexible/flexible'; //rem 就可以生效了

配置px–> rem:创建vue.config.js

//可以在此配置的基础上根据项目需求进行修改。
module.exports = {
   
    css: {
        loaderOptions: {
            postcss: {
                plugins: [
                    require('postcss-plugin-px2rem')({
                        rootValue:75, //换算基数, 默认100  ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。
                        // unitPrecision: 5, //允许REM单位增长到的十进制数字。
                        //propWhiteList: [],  //默认值是一个空数组,这意味着禁用白名单并启用所有属性。
                        // propBlackList: [], //黑名单
                        // exclude: /(page_pc)/i,  //默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
                        exclude: /node_modules/i,
                        // selectorBlackList: ['van-'], //要忽略并保留为px的选择器,我们一般不转换vantui中的大小
                        // ignoreIdentifier: false,  //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
                        // replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
                        mediaQuery: false,  //(布尔值)允许在媒体查询中转换px。
                        minPixelValue: 3 //设置要替换的最小像素值(3px会被转rem)。 默认 0
                    }),
                ]
            }
        }
    },
}

17. webpack配置(配置跨域,路径别名,打包分析,cdn引入,去掉console.log,单独打包第三方模块,ie兼容,eslint规范,图片压缩)

   

更多内容详见微信公众号:Python测试和开发

Python测试和开发

原文地址:https://www.cnblogs.com/phyger/p/14251986.html