We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2021.7 货拉拉、虾皮、有赞。。。高级前端岗面试整理
function Person() {} Person.prototype.name = 'Zaxlct'; Person.prototype.sayName = function() { alert(this.name); } var person1 = new Person(); //JS 在创建对象的时候,都有一个__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。 // 对象 person1 有一个 __proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype console.log(person1.__proto__ == Person.prototype) //true //所有函数对象的__proto__都指向Function.prototype String.__proto__ === Function.prototype // true String.constructor == Function //true
详解
原型继承
function Parent () { this.name = 'Parent' this.sex = 'boy' } function Child () { this.name = 'child' } // 将子类的原型对象指向父类的实例 Child.prototype = new Parent() //优:继承了父类的模板,又继承了父类的原型对象 //缺:1.无法实现多继承(因为已经指定了原型对象了) // 2.创建子类时,无法向父类构造函数传参数
构造函数继承
在子类构造函数内部使用call或apply来调用父类构造函数,复制父类的实例属性给子类。
call或apply
function Parent (name) { this.name = name } function Child () { //用.call 来改变 Parent 构造函数内的指向 Parent.call(this, 'child') } //优:解决了原型链继承中子类实例共享父类引用对象的问题,实现多继承,创建子类实例时,可以向父类传递参数 //缺:构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法
组合继承
组合继承就是将原型链继承与构造函数继承组合在一起。
寄生组合继承
class继承
在class 中继承主要是依靠两个东西:
class
extends
super
class Parent { constructor (name) { this.name = name } getName () { console.log(this.name) } } class Child extends Parent { constructor (name) { super(name) this.sex = 'boy' } }
同步与异步、宏任务和微任务分别是函数两个不同维度的描述。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务; 异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行。
task queue
当某个宏任务执行完后,会查看是否有微任务队列。 如果有,先执行微任务队列中的所有任务; 如果没有,在执行环境栈中会读取宏任务队列中排在最前的任务; 执行宏任务的过程中,遇到微任务,依次加入微任务队列。 栈空后,再次读取微任务队列里的任务,依次类推。
同步(Promise)>异步(微任务(process.nextTick ,Promises.then, Promise.catch ,resove,reject,MutationObserver)>宏任务(setTimeout,setInterval,setImmediate))
await阻塞 后面的代码执行,因此跳出async函数执行下一个微任务
async/await是基于Promise实现的,看起来更像同步代码,
const makeRequest = () => { try { getJSON().then(result => { // JSON.parse可能会出错 const data = JSON.parse(result) console.log(data) }) // 取消注释,处理异步代码的错误 // .catch((err) => { // console.log(err) // }) } catch (err) { console.log(err) } }
使用aync/await的话,catch能处理JSON.parse错误:
aync/await
JSON.parse
const makeRequest = async () => { try { // this parse may fail const data = JSON.parse(await getJSON()) console.log(data) } catch (err) { console.log(err) } }
实现链式调用:使用.then()或者.catch()方法之后会返回一个promise对象,可以继续用.then()方法调用,再次调用所获取的参数是上个then方法return的内容
.then()
.catch()
promise
then
return
promise的三种状态是 fulfilled(已成功)/pengding(进行中)/rejected(已拒绝)
fulfilled
pengding
rejected
状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改;
Promise 中使用 resolve 和 reject 两个函数来更改状态;
resolve
reject
then 方法内部做的事情就是状态判断:
柯里化(Currying) 是把接收多个参数的原函数变换成接受一个单一参数(原来函数的第一个参数的函数)并返回一个新的函数,新的函数能够接受余下的参数,并返回和原函数相同的结果。
柯里化(Currying)
// 普通的add函数 function add(x, y) { return x + y } // Currying后 function curryingAdd(x) { return function (y) { return x + y } } add(1, 2) // 3 curryingAdd(1)(2) // 3
递归遍历对象,解决循环引用问题
解决循环引用问题,我们需要一个存储容器存放当前对象和拷贝对象的对应关系(适合用key-value的数据结构进行存储,也就是map),当进行拷贝当前对象的时候,我们先查找存储容器是否已经拷贝过当前对象,如果已经拷贝过,那么直接把返回,没有的话则是继续拷贝。
function deepClone(target) { const map = new Map() function clone (target) { if (isObject(target)) { let cloneTarget = isArray(target) ? [] : {}; if (map.get(target)) { return map.get(target) } map.set(target,cloneTarget) for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } } return clone(target) };
nodeJS里面的模块是基于commonJS规范实现的,原理是文件的读写,导出文件要使用exports、module.exports,引入文件用require。 每个文件就是一个模块;每个文件里面的代码会用默认写在一个闭包函数里面 AMD规范则是非同步加载模块,允许指定回调函数,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
nodeJS
commonJS
exports
module.exports
require
AMD
RequireJS
AMD推崇依赖前置, CMD推崇依赖就近。对于依赖的模块AMD是提前执行,CMD是延迟执行。
CMD
在ES6中,我们可以使用 import 关键字引入模块,通过 exprot 关键字导出模块,但是由于ES6目前无法在浏览器中执行,所以,我们只能通过babel将不被支持的import编译为当前受到广泛支持的 require。
ES6
import
exprot
babel
CommonJs 和 ES6 模块化的区别:
前端模块化:CommonJS,AMD,CMD,ES6
import 的ES6 标准模块; require 是 AMD规范引入方式;
import是编译时调用,所以必须放在文件开头;是解构过程 require是运行时调用,所以require理论上可以运用在代码的任何地方;是赋值过程。其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量
(function(){ var scriptEle = document.createElement("script"); scriptEle.type = "text/javasctipt"; scriptEle.async = true; scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js"; var x = document.getElementsByTagName("head")[0]; x.insertBefore(scriptEle, x.firstChild); })();
// async属性规定一旦加载脚本可用,则会异步执行 <script type="text/javascript" src="xxx.js" async="async"></script>
// defer属性规定是否对脚本执行进行延迟,直到页面加载为止 <script type="text/javascript" src="xxx.js" defer="defer"></script>
Set对象可以存储任何类型的数据。 值是唯一的,没有重复的值。
Map对象保存键值对,任意值都可以成为它的键或值。
WeakSet 结构与 Set 类似,也是不重复的值的集合 . WeakMap 对象是一组键值对的集合
不同: WeakSet 的成员只能是对象,而不能是其他类型的值。 WeakSet 不可遍历。
WeakSet
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
WeakMap
null
WeakMap的键名所指向的对象,不计入垃圾回收机制。
call( this,a,b,c ) 在第一个参数之后的,后续所有参数就是传入该函数的值。 apply( this,[a,b,c] ) 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。
call( this,a,b,c )
apply( this,[a,b,c] )
共同之处:都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
addEventListener的第三个参数干嘛的,为true时捕获,false时冒泡
Object.prototype.toString.call() 判断对象类型
Object.prototype.toString.call()
// new Set是实现数组去重,// Array.from()把去重之后转换成数组let arr2 = Array.from(new Set(arr));
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
ES5只有全局作用域没和函数作用域,ES6增加块级作用域
暂时性死区:在代码块内,使用 let 和 const 命令声明变量之前,该变量都是不可用的,语法上被称为暂时性死区。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
函数的作用域在函数定义的时候就决定了。
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
function _new(constructor, ...arg) { // 创建一个空对象 var obj = {}; // 空对象的`__proto__`指向构造函数的`prototype`, 为这个新对象添加属性 obj.__proto__ = constructor.prototype; // 构造函数的作用域赋给新对象 var res = constructor.apply(obj, arg); // 返回新对象.如果没有显式return语句,则返回this return Object.prototype.toString.call(res) === '[object Object]' ? res : obj; }
this/arguments
this
Array.isArray() 判断
instanceof 判断: 检验构造函数的prototype属性是否出现在对象的原型链中,返回一个布尔值。 let a = []; a instanceof Array; //true
let a = []; a instanceof Array; //true
constructor判断: 实例的构造函数属性constructor指向构造函数 let a = [1,3,4]; a.constructor === Array;//true
let a = [1,3,4]; a.constructor === Array;//true
Object.prototype.toString.call() 判断 let a = [1,2,3]; Object.prototype.toString.call(a) === '[object Array]';//true
let a = [1,2,3]; Object.prototype.toString.call(a) === '[object Array]';//true
静态输入:静态类型化是一种功能,可以在开发人员编写脚本时检测错误。
大型的开发项目:使用TypeScript工具来进行重构更变的容易、快捷。
更好的协作:类型安全是在编码期间检测错误,而不是在编译项目时检测错误。
更强的生产力:干净的 ECMAScript 6 代码,自动完成和动态输入等因素有助于提高开发人员的工作效率。
interface 只能定义对象类型。type声明可以声明任何类型。
interface 能够声明合并,两个相同接口会合并。Type声明合并会报错
type可以类型推导
代理,可以理解为在对象之前设置一个“拦截”,当该对象被访问的时候,都必须经过这层拦截。意味着你可以在这层拦截中进行各种操作。比如你可以在这层拦截中对原对象进行处理,返回你想返回的数据结构。
ES6 原生提供 Proxy 构造函数,MDN上的解释为:Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
const p = new Proxy(target, handler); //target: 所要拦截的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理) //handler:一个对象,定义要拦截的行为 const p = new Proxy({}, { get(target, propKey) { return '哈哈,你被我拦截了'; } }); console.log(p.name);
新增的属性,并不需要重新添加响应式处理,因为 Proxy 是对对象的操作,只要你访问对象,就会走到 Proxy 的逻辑中。
Vue3.x 推出了Composition API。 setup 是组件内使用 Composition API的入口。setup 执行时机是在 beforeCreate 之前执行.
Vue3.x
Composition API
setup
beforeCreate
Vue3.x 可以使用reactive和ref来进行数据定义。
// props 传入组件对属性 // context 一个上下文对象,包含了一些有用的属性:attrs,parent,refs setup(props, context) { // ref 定义数据 const year = ref(0); // reactive 处理对象的双向绑定 const user = reactive({ nickname: "xiaofan", age: 26, gender: "女" }); setInterval(() => { year.value++; user.age++; }, 1000); return { year, // 使用toRefs,结构解构 ...toRefs(user), }; }, // 提供isRef,用于检查一个对象是否是ref对象
用于定义可更改的计算属性
const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } });
setup现在支持返回一个渲染函数,这个函数返回一个JSX,JSX可以直接使用声明在setup作用域的响应式状态:
JSX
export default { setup() { const count = ref(0); return () => (<div>{count.value}</div>); }, };
相同点:
不同点: Vue 是MVVM框架,双向数据绑定,当ViewModel对Model进行更新时,通过数据绑定更新到View。
MVVM
ViewModel
Model
View
React是一个单向数据流的库,状态驱动视图。State --> View --> New State --> New View ui = render (data)
State --> View --> New State --> New View
ui = render (data)
模板渲染方式不同。React是通过JSX来渲染模板,而Vue是通过扩展的HTML来进行模板的渲染。
组件形式不同,Vue文件里将HTML,JS,CSS组合在一起。react提供class组件和function组
Vue封装好了一些v-if,v-for,React什么都是自己实现,自由度更高
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,dep.addSub来收集订阅的依赖,watcher监听数据的变化,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty()
setter
getter
dep.addSub
watcher
监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。 订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而调用对应update更新视图。
Observer
Watcher
v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定。
v-model
computed: 支持缓存,只有依赖数据结果发生改变,才会重新进行计算,不支持异步操作,如果一个属性依赖其他属性,多对一,一般用computed
watch: 数据变,直接触发相应操作,支持异步,监听数据必须data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数
data
props中
端路由简介以及vue-router实现原理 原理核心就是 更新视图但不重新请求页面。路径之间的切换,也就是组件的切换。 vue-router实现单页面路由跳转模式:hash模式、history模式。根据设置mode参数
hash模式:通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。 history模式:利用 window.history.pushState API 来完成 URL 跳转而无须重新加载页面。
hash模式
#
history模式
window.history.pushState
Vue.use(vuex)会调用vuex的install方法
Vue.use(vuex)
在beforeCreate钩子前混入vuexInit方法,vuexInit方法实现了store注入vue组件实例,并注册了 vuex store的引用属性$store。
vuexInit
store
vue组件实例
vuex
$store
Vuex的state状态是响应式,是借助vue的data是响应式,将state存入vue实例组件的data中;
Vuex
state
vue
Vuex的getters则是借助vue的计算属性computed实现数据实时监听。
getters
computed
nextTick的源码分析
vue进行DOM更新内部也是调用nextTick来做异步队列控制。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
DOM至少会在当前事件循环里面的所有数据变化完成之后,再统一更新视图。而当我们自己调用nextTick的时候,它就在更新DOM的microtask(微任务队列)后追加了我们自己的回调函数,
从而确保我们的代码在DOM更新后执行,同时也避免了setTimeout可能存在的多次执行问题。 确保队列中的微任务在一次事件循环前被执行完毕。
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为。
// 高阶组件(HOC)接收到的 props 应该透传给被包装组件即直接将原组件prop传给包装组件// 高阶组件完全可以添加、删除、修改 propsexport default function Console(BaseComponent) { return { props: BaseComponent.props, mounted() { console.log("高阶组件"); }, render(h) { console.log(this); // 将 this.$slots 格式化为数组,因为 h 函数第三个参数是子节点,是一个数组 const slots = Object.keys(this.$slots) .reduce((arr, key) => arr.concat(this.$slots[key]), []) .map((vnode) => { vnode.context = this._self; // 绑定到高阶组件上,vm:解决具名插槽被作为默认插槽进行渲染 return vnode; }); // 透传props、透传事件、透传slots return h( BaseComponent, { on: this.$listeners, attrs: this.$attrs, // attrs 指的是那些没有被声明为 props 的属性 props: this.$props, }, slots ); }, };}
Vue.component()方法注册全局组件。
import Vue from 'vue';// 引入loading组件 import Loading from './loading.vue';// 将loading注册为全局组件,在别的组件中通过<loading>标签使用Loading组件Vue.component('loading', Loading);
Vue.use注册插件,这接收一个参数。这个参数必须具有install方法。Vue.use函数内部会调用参数的install方法。
import Vue from 'vue';// 这个插件必须具有install方法const plugin = { install (Vue, options) { // 添加全局方法或者属性 Vue.myGlobMethod = function () {}; // 添加全局指令 Vue.directive(); // 添加混入 Vue.mixin(); // 添加实例方法 Vue.prototype.$xxx = function () {}; // 注册全局组件 Vue.component() }}// Vue.use内部会调用plugin的install方法Vue.use(plugin);
将Hello方法挂载到Vue的prototype上.
import Vue from 'vue';import Hello from './hello.js';Vue.prototype.$hello = Hello;
vue组件中就可以this.$hello('hello world')
如果子组件想修改prop中数据:
加载渲染过程
父beforeCreate -> 父created -> 父beforeMount-> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新过程
父beforeUpdate -> 父updated
销毁过程
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
自定义指令提供了几个钩子函数: bind:指令第一次绑定到元素时调用 inserted:被绑定元素插入父节点时调用 update:所在组件的 VNode 更新时调用
bind
inserted
update
使用slot后可以在子组件内显示插入的新标签
compiler(整个生命周期 [kəmˈpaɪlər]) 钩子 https://webpack.docschina.org/api/compiler-hooks/ compilation(编译 [ˌkɑːmpɪˈleɪʃn]) 钩子
compiler 对象包含了Webpack 环境所有的的配置信息。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
compiler
compilation对象包含了当前的模块资源、编译生成资源、变化的文件等。当运行webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。 compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
compilation
compiler代表了整个webpack从启动到关闭的生命周期,而compilation 只是代表了一次新的编译过程
webpack
生命周期
编译过程
Webpack 的编译流程是一个串行的过程,从启动到结束会依次执行以下流程:
Compiler
run
entry
Loader
Chunk
1.构建打点:构建过程中,每一个 Loader 和 Plugin 的执行时长,在编译 JS、CSS 的 Loader 以及对这两类代码执行压缩操作的 Plugin上消耗时长 。一款工具:speed-measure-webpack-plugin
Plugin
2.缓存:大部分 Loader 都提供了 cache 配置项。cache-loader ,将 loader 的编译结果写入硬盘缓存
cache
cache-loader
3.多核编译,happypack项目接入多核编译,理解为happypack 将编译工作灌满所有线程
happypack
4.抽离,webpack-dll-plugin 将这些静态依赖从每一次的构建逻辑中抽离出去,静态依赖单独打包,Externals将不需要打包的静态资源从构建逻辑中剔除出去,使用 CDN 引用
webpack-dll-plugin
Externals
CDN 引用
5.tree-shaking,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块剔除,来达到删除无用代码的目的。
tree-shaking
路由懒加载:改为用import引用,以函数的形式动态引入,可以把各自的路由文件分别打包,只有在解析给定的路由时,才会下载路由组件;
element-ui按需加载:引用实际上用到的组件 ;
element-ui
组件重复打包:CommonsChunkPlugin配置来拆包,把使用2次及以上的包抽离出来,放进公共依赖文件,首页也有复用的组件,也会下载这个公共依赖文件;
CommonsChunkPlugin
gzip: 拆完包之后,再用gzip做一下压缩,关闭sourcemap。
gzip
UglifyJsPlugin: 生产环境,压缩混淆代码,移除console代码
CDN部署静态资源:静态请求打在nginx时,将获取静态资源的地址进行重定向CDN内容分发网络
移动端首屏加载可以使用骨架屏,自定义loading,首页单独做服务端渲染。
如何进行前端性能优化(21种优化+7种定位方式)
热更新流程总结:
启动本地server,让浏览器可以请求本地的静态资源
server
页面首次打开后,服务端与客户端通过 websocket建立通信渠道,把下一次的 hash 返回前端
客户端获取到hash,这个hash将作为下一次请求服务端 hot-update.js 和 hot-update.json的hash
修改页面代码后,Webpack 监听到文件修改后,开始编译,编译完成后,发送 build 消息给客户端
客户端获取到hash,成功后客户端构造hot-update.js script链接,然后插入主文档
hot-update.js 插入成功后,执行hotAPI 的 createRecord 和 reload方法,获取到 Vue 组件的 render方法,重新 render 组件, 继而实现 UI 无刷新更新。
loader 它就是一个转换器,将A文件进行编译形成B文件,
plugin ,它就是一个扩展器,来操作的是文件,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,会监听webpack打包过程中的某些节点(run, build-module, program)
Babel 能把ES6/ES7的代码转化成指定浏览器能支持的代码。
css-loader 的作用是把 css文件进行转码 style-loader: 使用<style>将css-loader内部样式注入到我们的HTML页面
css-loader
style-loader
先使用 css-loader 转码,然后再使用 style-loader插入到文件
https://segmentfault.com/a/1190000037513682
webpack 插件的组成:
new XXXPlugin()
Webpack 会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。
先打包
而 Vite 是直接启动开发服务器,请求哪个模块再对该模块进行实时编译。
直接启动
实时编译
Vite 将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像 Webpack 那样进行打包合并。
打包合并
由于 Vite 在启动的时候不需要打包,也就意味着不需要分析模块的依赖、不需要编译。因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。
不需要打包
不需要分析模块的依赖
不需要编译
使用 download-git-repo 下载仓库代码demo commander:完整的 node.js 命令行解决方案。声明program,使用.option() 方法来定义选项 Inquirer.js:命令行用户界面的集合。
download-git-repo
commander
node.js
program
.option()
Inquirer.js
前端监控通常包括行为监控(PV/UV,埋点接口统计)、异常监控、性能监控。
一个监控系统,大致可以分为四个阶段:日志采集、日志存储、统计与分析、报告和警告。
Vue专门的错误警告的方法 Vue.config.errorHandler,(Vue提供只能捕获其页面生命周期内的函数,比如created,mounted)
Vue.config.errorHandler
Vue.config.errorHandler = function (err) { console.error(‘Vue.error’,err.stack) // 逻辑处理 };
框架:betterjs,fundebug(收费) 捕获错误的脚本要放置在最前面,确保可以收集到错误信息 方法:
window.onerror()
window.addEventListener('error'), function(e) {}, true
前端JS错误捕获--sourceMap
Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃;
Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;
卡顿:加载中,渲染遇到阻塞
性能指标:
FP
FCP
LCP
FPS
TTI
HTTP
DNS
TCP
性能数据采集需要使用 window.performance API , JS库 web-vitals:import {getLCP} from 'web-vitals';
window.performance API
web-vitals
import {getLCP} from 'web-vitals'
// 重定向耗时 redirect: timing.redirectEnd - timing.redirectStart, // DOM 渲染耗时 dom: timing.domComplete - timing.domLoading, // 页面加载耗时 load: timing.loadEventEnd - timing.navigationStart, // 页面卸载耗时 unload: timing.unloadEventEnd - timing.unloadEventStart, // 请求耗时 request: timing.responseEnd - timing.requestStart, // 获取性能信息时当前时间 time: new Date().getTime(), // DNS查询耗时 domainLookupEnd - domainLookupStart // TCP链接耗时 connectEnd - connectStart // request请求耗时 responseEnd - responseStart // 解析dom树耗时 domComplete - domInteractive // 白屏时间 domloadng - fetchStart // onload时间 loadEventEnd - fetchStart
性能优化常用手段:缓存技术、 预加载技术、 渲染方案。
缓存 :主要有 cdn、浏览器缓存、本地缓存以及应用离线包
预加载 :资源预拉取(prefetch)则是另一种性能优化的技术。通过预拉取可以告诉浏览器用户在未来可能用到哪些资源。
prefetch支持预拉取图片、脚本或者任何可以被浏览器缓存的资源。
在head里 添加 <linkrel="prefetch"href="image.png">
<linkrel="prefetch"href="image.png">
prerender是一个重量级的选项,它可以让浏览器提前加载指定页面的所有资源。
subresource可以用来指定资源是最高优先级的。当前页面需要,或者马上就会用到时。
https://www.cnblogs.com/whu-2017/p/9471670.html
观察者模式模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态变化时,会通知所有的观察者对象。
发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来
工厂模式 主要是为创建对象提供了接口。场景:在编码时不能预见需要创建哪种类的实例。
代理模式 命令模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。 (window)
应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源) 。(应用场景:HTP,HTTP,邮件)
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快 ,可能丢包(应用场景:即时通讯)
是否连接 面向连接 面向非连接传输可靠性 可靠 不可靠应用场合 少量数据 传输大量数据
客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。服务器公钥放在数字证书中。
DNS 协议提供通过域名查找 IP地址,或逆向从 IP地址反查域名的服务。 DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。
域名查找 IP地址
IP地址反查域名
TCP 三次握手,四次挥手:握手挥手都是客户端发起,客户端结束。 三次握手与四次挥手详解
负载均衡:请求在进入到真正的应用服务器前,可能还会先经过负责负载均衡的机器,它的作用是将请求合理地分配到多个服务器上,转发HTTP请求;同时具备具备防攻击等功能。可分为DNS负载均衡,HTTP负载均衡,IP负载均衡,链路层负载均衡等。
合理地分配到多个服务器上
Web Server: 请求经过前面的负载均衡后,将进入到对应服务器上的 Web Server,比如 Apache、Tomcat
Apache
Tomcat
反向代理是工作在 HTTP 上的,一般都是 Nginx。全国各地访问baidu.com就肯定要通过代理访问,不可能都访问百度的那台服务器。 (VPN正向代理,代理客户端)
Nginx
浏览器解析渲染过程: 返回的html传递到浏览器后,如果有gzip会先解压,找出文件编码格式,外链资源的加载 html从上往下解析,遇到js,css停止解析渲染,直到js执行完成。 解析HTML,构建DOM树 解析CSS,生成CSS规则树 合并DOM树和CSS规则,生成render树去渲染
不会引起DOM树变化,页面布局变化,改变元素样式的行为叫重绘
引起DOM树结构变化,页面布局变化的行为叫回流
GUI渲染线程负责渲染浏览器界面HTML元素,当界面需要 重绘(Repaint) 或由于某种操作引发 回流(reflow) 时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了. 直到JS程序执行完成,才会接着执行。因此如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面,渲染前后元素数据可能不一致
GUI渲染线程
重绘(Repaint)
回流(reflow)
GPU绘制 多进程的浏览器:主控进程,插件进程,GPU,tab页(浏览器内核) 多线程的浏览器内核:每一个tab页面可以看作是浏览器内核进程,然后这个进程是多线程的。
它有几大类子线程:
多路复用:相同域名多个请求,共享同一个TCP连接,降低了延迟
请求优先级:给每个request设置优先级
二进制传输;之前是用纯文本传输
数据流:数据包不是按顺序发送,对数据包做标记。每个请求或回应的所有数据包成为一个数据流,
服务端推送:可以主动向客户端发送消息。
头部压缩:减少包的大小跟数量
HTTP/1.1 中的管道( pipeline)传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了 HTTP/2 多请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP! http1 keep alive 串行传输
请求阻塞
响应头中设置 keep-alive 可以在一个 TCP 连接上发送多个 http 请求
keep-alive
强缓存:cache-control;no-cache max-age=<10000000>;expires;其中Cache-Conctrol的优先级比Expires高;
控制强制缓存的字段分别是Expires和Cache-Control,如果客户端的时间小于Expires的值时,直接使用缓存结果。
协商缓存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / 首次请求,服务器会在返回的响应头中加上Last-Modified字段,表示资源最后修改的时间。
浏览器再次请求时,请求头中会带上If-Modified-Since字段,比较两个字段,一样则证明资源未修改,返回304,否则重新返回资源,状态码为200;
垃圾回收机制:
标记清除:进入执行环境的变量都被标记,然后执行完,清除这些标记跟变量。 查看变量是否被引用。
标记清除
引用计数:会记录每个值被引用的次数,当引用次数变成0后,就会被释放掉。
引用计数
同源策略:如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。 因为浏览器有cookies。
cookies
XSS:跨站脚本攻击(Cross Site Scripting) input, textarea等所有可能输入文本信息的区域,输入<script src="http://恶意网站"></script>等,提交后信息会存在服务器中 。
<script src="http://恶意网站"></script>
CSRF:跨站请求伪造 。引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求。
A站点img的src=B站点的请求接口,可以访问;解决:referer携带请求来源
A
img
src=B
referer
访问该页面后,表单自动提交, 模拟完成了一次POST操作,发送post请求
解决:后端注入一个随机串到Cookie,前端请求取出随机串添加传给后端。
随机串
Cookie
http 劫持:电信运营商劫持
SQL注入
点击劫持:诱使用户点击看似无害的按钮(实则点击了透明 iframe 中的按钮) ,解决后端请求头加一个字段 X-Frame-Options
iframe
X-Frame-Options
文件上传漏洞 :服务器未校验上传的文件
BFC(Block formatting context),即块级格式化上下文,它作为HTML页面上的一个独立渲染区域,只有区域内元素参与渲染,且不会影响其外部元素。 简单来说,可以将 BFC 看做是一个“围城”,外面的元素进不来,里面的元素出不去(互不干扰)。
BFC(Block formatting context)
块级格式化上下文
独立渲染区域
一个决定如何渲染元素的容器 ,渲染规则 :
1、内部的块级元素会在垂直方向,一个接一个地放置。
2、块级元素垂直方向的距离由margin决定。属于同一个BFC的两个相邻块级元素的margin会发生重叠。
3、对于从左往右的格式化,每个元素(块级元素与行内元素)的左边缘,与包含块的左边缘相接触,(对于从右往左的格式化则相反)。即使包含块中的元素存在浮动也是如此,除非其中元素再生成一个BFC。
4、BFC的区域不会与浮动元素重叠。
5、BFC是一个隔离的独立容器,容器里面的子元素和外面的元素互不影响。
6、计算BFC容器的高度时,浮动元素也参与计算。
形成BFC的条件:
1、浮动元素,float 除 none 以外的值;
2、定位元素,position(absolute,fixed);
3、display 为以下其中之一的值 inline-block,table-cell,table-caption;
4、overflow 除了 visible 以外的值(hidden,auto,scroll);
BFC 一般用来解决以下几个问题
flex: 0 1 auto;
元素会根据自身宽高设置尺寸。它会缩短自身以适应 flex 容器,但不会伸长并吸收 flex 容器中的额外自由空间来适应 flex 容器 。 水平的主轴(main axis)和垂直的交叉轴(cross axis)几个属性决定按哪个轴的排列方向
flex
main axis
cross axis
flex-grow
0
<flex-grow>的值。
flex-shrink
1
<flex-basis>的值。
flex-basis
auto
none
initial
放大比例、缩小比例、分配多余空间之前占据的主轴空间。
const styles = { bar: { backgroundColor: '#000' }}const example = (props)=>{ <div style={styles.bar} />}
// 选择器上>要记得写,免得污染所有ul下面的liul{ >li{ color:red; }}
阮一峰 CSS Modules
CSS Modules是一种构建步骤中的一个进程。通过构建工具来使指定class达到scope的过程。
指定class
scope
CSS Modules 允许使用: :global(.className)的语法,声明一个全局规则。凡是这样声明的class,都不会被编译成哈希字符串。 :local(className): 做 localIdentName 规则处理,编译唯一哈希类名。
CSS Modules
:global(.className)
哈希字符串
:local(className)
CSS Modules使用特点:
box-sizing
width: 160px; padding: 20px; border: 8px solid orange; 标准 box-sizing: content-box; 元素的总宽度 = 160 + 202 + 82; IE的 border-box: 总宽度160
width: 160px; padding: 20px; border: 8px solid orange;
content-box;
border-box
160
margin/padding取百分比的值时 ,基于父元素的宽度和高度的。
margin/padding
// border 处理.class { width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 100px solid red;}// 宽高+borderdiv { width: 50px; height: 50px; border: 2px solid orange;}
div{ clip-path: polygon(0 100%, 50% 0, 100% 100%);}
div { width: 200px; height: 200px; background:linear-gradient(to bottom right, #fff 0%, #fff 49.9%, rgba(148,88,255,1) 50%,rgba(185,88,255,1) 100%);}
???
两列布局分为两种,一种是左侧定宽、右侧自适应,另一种是两列都自适应(即左侧宽度由子元素决定,右侧补齐剩余空间)。
// 两个元素都设置dislpay:inline-block .left { display: inline-block; width: 100px; height: 200px; background-color: red; vertical-align: top; } .right { display: inline-block; width: calc(100% - 100px); height: 400px; background-color: blue; vertical-align: top; }// 两个元素设置浮动,右侧自适应元素宽度使用calc函数计算 .left{ float: left; width: 100px; height: 200px; background-color: red; } .right{ float: left; width: calc(100% - 100px); height: 400px; background-color: blue; }// 父元素设置display:flex,自适应元素设置flex:1 .box{ height: 600px; width: 100%; display: flex; } .left{ width: 100px; height: 200px; background-color: red; } .right{ flex: 1; height: 400px; background-color: blue; } // 父元素相对定位,左侧元素绝对定位,右侧自适应元素设置margin-left的值大于定宽元素的宽度 .left{ position: absolute; width: 100px; height: 200px; background-color: red; } .right{ margin-left: 100px; height: 400px; background-color: blue; }
// flex布局 同上// 父元素设置display:grid; grid-template-columns:auto 1fr;(这个属性定义列宽,auto关键字表示由浏览器自己决定长度。fr是一个相对尺寸单位,表示剩余空间做等分)grid-gap:20px(行间距) .parent{ display:grid; grid-template-columns:auto 1fr; grid-gap:20px } .left{ background-color: red; height: 200px; } .right{ height:300px; background-color: blue; } //浮动+BFC 父元素设置overflow:hidden,左侧定宽元素浮动,右侧自适应元素设置overflow:auto创建BFC .box{ height: 600px; width: 100%; overflow: hidden; } .left{ float: left; width: 100px; height: 200px; background-color: red; } .right{ overflow: auto; height: 400px; background-color: blue; }
float布局: 左边左浮动,右边右浮动,中间margin:0 100px;
Position布局: 左边left:0; 右边right:0; 中间left: 100px; right: 100px;
table布局: 父元素 display: table; 左右 width: 100px; 三个元素display: table-cell;
弹性(flex)布局:父元素 display: flex; 左右 width: 100px;
网格(gird)布局:
// gird提供了 gird-template-columns、grid-template-rows属性让我们设置行和列的高、宽 .div{ width: 100%; display: grid; grid-template-rows: 100px; grid-template-columns: 300px auto 300px; }
// 兼容IOS和安卓callMobile(parameters,messageHandlerName) { //handlerInterface由iOS addScriptMessageHandler与andorid addJavascriptInterface 代码注入而来。 if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { // alert('ios') window.webkit.messageHandlers[messageHandlerName].postMessage(JSON.stringify(parameters)) } else { // alert('安卓') //安卓传输不了js json对象,只能传输string window.webkit[messageHandlerName](JSON.stringify(parameters)) }}
由app将原生方法注入到window上供js调用
messageHandlerName 约定的通信方法 parameters 需要传入的参数
messageHandlerName
parameters
rem是相对于HTML的根元素 em相对于父级元素的字体大小。 VW,VH 屏幕宽度高度的高分比
rem
em
VW,VH
//按照宽度375图算, 1rem = 100px;(function (win, doc) { function changeSize() { doc.documentElement.style.fontSize = doc.documentElement.clientWidth / 3.75 + 'px'; console.log(100 * doc.documentElement.clientWidht / 3.75) } changeSize(); win.addEventListener('resize', changeSize, false);})(window, document);
/* Pubsub */ function Pubsub(){ //存放事件和对应的处理方法 this.handles = {}; } Pubsub.prototype = { //传入事件类型type和事件处理handle on: function (type, handle) { if(!this.handles[type]){ this.handles[type] = []; } this.handles[type].push(handle); }, emit: function () { //通过传入参数获取事件类型 //将arguments转为真数组 var type = Array.prototype.shift.call(arguments); if(!this.handles[type]){ return false; } for (var i = 0; i < this.handles[type].length; i++) { var handle = this.handles[type][i]; //执行事件 handle.apply(this, arguments); } }, off: function (type, handle) { handles = this.handles[type]; if(handles){ if(!handle){ handles.length = 0;//清空数组 }else{ for (var i = 0; i < handles.length; i++) { var _handle = handles[i]; if(_handle === handle){ //从数组中删除 handles.splice(i,1); } } } } }
// MyPromise.js// 先定义三个常量表示状态const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';// 新建 MyPromise 类class MyPromise { constructor(executor){ // executor 是一个执行器,进入会立即执行 // 并传入resolve和reject方法 executor(this.resolve, this.reject) } // 储存状态的变量,初始值是 pending status = PENDING; // resolve和reject为什么要用箭头函数? // 如果直接调用的话,普通函数this指向的是window或者undefined // 用箭头函数就可以让this指向当前实例对象 // 成功之后的值 value = null; // 失败之后的原因 reason = null; // 更改成功后的状态 resolve = (value) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态修改为成功 this.status = FULFILLED; // 保存成功之后的值 this.value = value; } } // 更改失败后的状态 reject = (reason) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态成功为失败 this.status = REJECTED; // 保存失败后的原因 this.reason = reason; } } then(onFulfilled, onRejected) { // 判断状态 if (this.status === FULFILLED) { // 调用成功回调,并且把值返回 onFulfilled(this.value); } else if (this.status === REJECTED) { // 调用失败回调,并且把原因返回 onRejected(this.reason); } }}
// Promise.all function all(promises) { let len = promises.length, res = [] if (len) { return new Promise(function (resolve, reject) { for(let i=0; i<len; i++) { let promise = promises[i]; promise.then(response => { res[i] = response // 当返回结果为最后一个时 if (res.length === len) { resolve(res) } }, error => { reject(error) }) } }) }
> 将entries 按照 level 转换成 result 数据结构 const entries = [ { "province": "浙江", "city": "杭州", "name": "西湖" }, { "province": "四川", "city": "成都", "name": "锦里" }, { "province": "四川", "city": "成都", "name": "方所" }, { "province": "四川", "city": "阿坝", "name": "九寨沟" } ]; const level = ["province", "city", "name"]; const result = [ { value:'浙江', children:[ { value:'杭州', children:[ { value:'西湖' } ] } ] }, { value:'四川', children:[ { value:'成都', children:[ { value:'锦里' }, { value:'方所' } ] }, { value:'阿坝', children:[ { value:'九寨沟' } ] } ] }, ]
思路: 涉及到树形数组,采用递归遍历的方式
function transfrom(list, level) { const res = []; list.forEach(item => { pushItem(res, item, 0); }); function pushItem(arr, obj, i) { const o = { value: obj[level[i]], children: [], }; // 判断传入数组里是否有value等于要传入的项 const hasItem = arr.find(el => el.value === obj[level[i]]); let nowArr; if(hasItem) { // 存在,则下一次遍历传入存在项的children nowArr = hasItem.children; }else{ // 不存在 压入arr,下一次遍历传入此项的children arr.push(o); nowArr = o.children; } if(i === level.length - 1) delete o.children; i++; if(i < level.length) { // 递归进行层级的遍历 pushItem(nowArr, obj, i); } } } transfrom(entries, level);
简单用法
function Fn () {} const fn = new Fn() fn instanceof Fn // true
实现如下:
// left instanceof right function _instanceof(left, right) { // 构造函数原型 const prototype = right.prototype // 实列对象属性,指向其构造函数原型 left = left.__proto__ // 查实原型链 while (true) { // 如果为null,说明原型链已经查找到最顶层了,真接返回false if (left === null) { return false } // 查找到原型 if (prototype === left){ return true } // 继续向上查找 left = left.__proto__ } } const str = "abc" _instanceof(str, String) // true
将有同样元素的数组进行合并
// 例如: const arr = [ ['a', 'b', 'c'], ['a', 'd'], ['d', 'e'], ['f', 'g'], ['h', 'g'], ['i'] ] // 运行后的返回结果是: [ ['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h'], ['i'] ] // 思路一: const arr = [['a', 'b', 'c'], ['a', 'd'], ['d', 'e'], ['f', 'g'], ['h', 'g'], ['i']] function transform(arr){ let res = [] arr = arr.map(el => el.sort()).sort() const item = arr.reduce((pre, cur) => { if (cur.some(el => pre && pre.includes(el))) { pre = pre.concat(cur) } else { res.push(pre) pre = cur } return [...new Set(pre)] }) res.push(item) return res; } transform(arr) // console.log(transform(arr)); // 思路二: function r (arr) { const map = new Map() arr.forEach((array, index) => { const findAlp = array.find((v) => map.get(v)) if (findAlp) { const set = map.get(findAlp) array.forEach((alp) => { set.add(alp) const findAlp2 = map.get(alp) if (findAlp2 && findAlp2 !== set) { for(const v of findAlp2.values()){ set.add(v) map.set(v, set) } } map.set(alp, set) }) } else { const set = new Set(arr[index]) array.forEach((alp) => map.set(alp, set)) } }) const set = new Set() const ret = [] for (const [key, value] of map.entries()) { if (set.has(value)) continue set.add(value) ret.push([...value]) } return ret }
The text was updated successfully, but these errors were encountered:
您好 请问这就是您在掘进上发的那篇的原稿吗?
Sorry, something went wrong.
No branches or pull requests
2021.7 货拉拉、虾皮、有赞。。。高级前端岗面试整理
JS相关
JS原型及原型链
JS继承的几种方式
详解
原型继承
构造函数继承
在子类构造函数内部使用
call或apply
来调用父类构造函数,复制父类的实例属性给子类。组合继承
组合继承就是将原型链继承与构造函数继承组合在一起。
寄生组合继承
class继承
在
class
中继承主要是依靠两个东西:extends
super
Event Loop 事件循环
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入任务队列(
task queue
)的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行。当某个宏任务执行完后,会查看是否有微任务队列。
如果有,先执行微任务队列中的所有任务;
如果没有,在执行环境栈中会读取宏任务队列中排在最前的任务;
执行宏任务的过程中,遇到微任务,依次加入微任务队列。
栈空后,再次读取微任务队列里的任务,依次类推。
await阻塞 后面的代码执行,因此跳出async函数执行下一个微任务
Promise 与 Async/Await 区别
async/await是基于Promise实现的,看起来更像同步代码,
使用
aync/await
的话,catch能处理JSON.parse
错误:promise怎么实现链式调用跟返回不同的状态
实现链式调用:使用
.then()
或者.catch()
方法之后会返回一个promise
对象,可以继续用.then()
方法调用,再次调用所获取的参数是上个then
方法return
的内容promise的三种状态是
fulfilled
(已成功)/pengding
(进行中)/rejected
(已拒绝)状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改;
Promise 中使用
resolve
和reject
两个函数来更改状态;then 方法内部做的事情就是状态判断:
函数柯里化
柯里化(Currying)
是把接收多个参数的原函数变换成接受一个单一参数(原来函数的第一个参数的函数)并返回一个新的函数,新的函数能够接受余下的参数,并返回和原函数相同的结果。只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
柯里化的函数可以延迟接收参数,就是比如一个函数需要接收的参数是两个,执行的时候必须接收两个参数,否则没法执行。但是柯里化后的函数,可以先接收一个参数
JS对象深克隆
解决循环引用问题,我们需要一个存储容器存放当前对象和拷贝对象的对应关系(适合用key-value的数据结构进行存储,也就是map),当进行拷贝当前对象的时候,我们先查找存储容器是否已经拷贝过当前对象,如果已经拷贝过,那么直接把返回,没有的话则是继续拷贝。
JS模块化
nodeJS
里面的模块是基于commonJS
规范实现的,原理是文件的读写,导出文件要使用exports
、module.exports
,引入文件用require
。每个文件就是一个模块;每个文件里面的代码会用默认写在一个闭包函数里面
AMD
规范则是非同步加载模块,允许指定回调函数,AMD
是RequireJS
在推广过程中对模块定义的规范化产出。AMD
推崇依赖前置,CMD
推崇依赖就近。对于依赖的模块AMD是提前执行,CMD是延迟执行。在
ES6
中,我们可以使用import
关键字引入模块,通过exprot
关键字导出模块,但是由于ES6目前无法在浏览器中执行,所以,我们只能通过babel
将不被支持的import
编译为当前受到广泛支持的require
。CommonJs 和 ES6 模块化的区别:
前端模块化:CommonJS,AMD,CMD,ES6
import 和 require 导入的区别
import 的ES6 标准模块; require 是 AMD规范引入方式;
import是编译时调用,所以必须放在文件开头;是解构过程
require是运行时调用,所以require理论上可以运用在代码的任何地方;是赋值过程。其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量
异步加载JS方式
Set、Map、WeakSet、WeakMap
Set对象可以存储任何类型的数据。 值是唯一的,没有重复的值。
Map对象保存键值对,任意值都可以成为它的键或值。
WeakSet 结构与 Set 类似,也是不重复的值的集合 . WeakMap 对象是一组键值对的集合
不同:
WeakSet
的成员只能是对象,而不能是其他类型的值。 WeakSet 不可遍历。WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。WeakMap
的键名所指向的对象,不计入垃圾回收机制。call、apply
call( this,a,b,c )
在第一个参数之后的,后续所有参数就是传入该函数的值。apply( this,[a,b,c] )
只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。共同之处:都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。
addEventListener的第三个参数干嘛的,为true时捕获,false时冒泡
Object.prototype.toString.call()
判断对象类型// new Set是实现数组去重,// Array.from()把去重之后转换成数组let arr2 = Array.from(new Set(arr));
词法作用域与作用域链
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
暂时性死区:在代码块内,使用 let 和 const 命令声明变量之前,该变量都是不可用的,语法上被称为暂时性死区。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
函数的作用域在函数定义的时候就决定了。
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
new关键字做了4件事:
不应该使用箭头函数一些情况:
this/arguments
时,由于箭头函数本身不具有this/arguments
,因此它们取决于外部上下文this
即对象本身。判断数组的四种方法
Array.isArray() 判断
instanceof 判断: 检验构造函数的prototype属性是否出现在对象的原型链中,返回一个布尔值。
let a = []; a instanceof Array; //true
constructor判断: 实例的构造函数属性constructor指向构造函数
let a = [1,3,4]; a.constructor === Array;//true
Object.prototype.toString.call() 判断
let a = [1,2,3]; Object.prototype.toString.call(a) === '[object Array]';//true
TS有什么优势
静态输入:静态类型化是一种功能,可以在开发人员编写脚本时检测错误。
大型的开发项目:使用TypeScript工具来进行重构更变的容易、快捷。
更好的协作:类型安全是在编码期间检测错误,而不是在编译项目时检测错误。
更强的生产力:干净的 ECMAScript 6 代码,自动完成和动态输入等因素有助于提高开发人员的工作效率。
interface 和 type的区别
interface 只能定义对象类型。type声明可以声明任何类型。
interface 能够声明合并,两个相同接口会合并。Type声明合并会报错
type可以类型推导
框架 Vue | React
Vue3.0 新特性
双向数据绑定 Proxy
代理,可以理解为在对象之前设置一个“拦截”,当该对象被访问的时候,都必须经过这层拦截。意味着你可以在这层拦截中进行各种操作。比如你可以在这层拦截中对原对象进行处理,返回你想返回的数据结构。
ES6 原生提供 Proxy 构造函数,MDN上的解释为:Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
新增的属性,并不需要重新添加响应式处理,因为 Proxy 是对对象的操作,只要你访问对象,就会走到 Proxy 的逻辑中。
Vue3 Composition API
Vue3.x
推出了Composition API
。setup
是组件内使用 Composition API的入口。setup
执行时机是在beforeCreate
之前执行.reactive、ref 与 toRefs、isRef
Vue3.x 可以使用reactive和ref来进行数据定义。
watchEffect 监听函数
computed可传入get和set
用于定义可更改的计算属性
使用TypeScript和JSX
setup
现在支持返回一个渲染函数,这个函数返回一个JSX
,JSX
可以直接使用声明在setup
作用域的响应式状态:Vue 跟React 对比?
相同点:
不同点:
Vue 是
MVVM
框架,双向数据绑定,当ViewModel
对Model
进行更新时,通过数据绑定更新到View
。React是一个单向数据流的库,状态驱动视图。
State --> View --> New State --> New View
ui = render (data)
模板渲染方式不同。React是通过JSX来渲染模板,而Vue是通过扩展的HTML来进行模板的渲染。
组件形式不同,Vue文件里将HTML,JS,CSS组合在一起。react提供class组件和function组
Vue封装好了一些v-if,v-for,React什么都是自己实现,自由度更高
Vue 初始化过程,双向数据绑定原理
监听器
Observer
,用来劫持并监听所有属性,如果有变动的,就通知订阅者。订阅者
Watcher
,可以收到属性的变化通知并执行相应的函数,从而调用对应update更新视图。v-model
指令,它能轻松实现表单输入和应用状态之间的双向绑定。computed: 支持缓存,只有依赖数据结果发生改变,才会重新进行计算,不支持异步操作,如果一个属性依赖其他属性,多对一,一般用computed
watch: 数据变,直接触发相应操作,支持异步,监听数据必须
data
中声明过或者父组件传递过来的props中
的数据,当数据变化时,触发其他操作,函数有两个参数vue-router实现原理
端路由简介以及vue-router实现原理
原理核心就是 更新视图但不重新请求页面。路径之间的切换,也就是组件的切换。
vue-router实现单页面路由跳转模式:hash模式、history模式。根据设置mode参数
hash模式
:通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。每一次改变#
后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。history模式
:利用window.history.pushState
API 来完成 URL 跳转而无须重新加载页面。vuex实现原理:
Vue.use(vuex)
会调用vuex的install方法在
beforeCreate
钩子前混入vuexInit
方法,vuexInit
方法实现了store
注入vue组件实例
,并注册了vuex
store
的引用属性$store
。Vuex
的state
状态是响应式,是借助vue
的data
是响应式,将state
存入vue实例组件的data中;Vuex
的getters
则是借助vue的计算属性computed
实现数据实时监听。nextTick 的原理以及运行机制?
nextTick的源码分析
vue进行DOM更新内部也是调用nextTick来做异步队列控制。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
DOM至少会在当前事件循环里面的所有数据变化完成之后,再统一更新视图。而当我们自己调用nextTick的时候,它就在更新DOM的microtask(微任务队列)后追加了我们自己的回调函数,
从而确保我们的代码在DOM更新后执行,同时也避免了setTimeout可能存在的多次执行问题。
确保队列中的微任务在一次事件循环前被执行完毕。
Vue 实现一个高阶组件
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为。
// 高阶组件(HOC)接收到的 props 应该透传给被包装组件即直接将原组件prop传给包装组件// 高阶组件完全可以添加、删除、修改 propsexport default function Console(BaseComponent) { return { props: BaseComponent.props, mounted() { console.log("高阶组件"); }, render(h) { console.log(this); // 将 this.$slots 格式化为数组,因为 h 函数第三个参数是子节点,是一个数组 const slots = Object.keys(this.$slots) .reduce((arr, key) => arr.concat(this.$slots[key]), []) .map((vnode) => { vnode.context = this._self; // 绑定到高阶组件上,vm:解决具名插槽被作为默认插槽进行渲染 return vnode; }); // 透传props、透传事件、透传slots return h( BaseComponent, { on: this.$listeners, attrs: this.$attrs, // attrs 指的是那些没有被声明为 props 的属性 props: this.$props, }, slots ); }, };}
Vue.component()、Vue.use()、this.$xxx()
Vue.component()方法注册全局组件。
Vue.use注册插件,这接收一个参数。这个参数必须具有install方法。Vue.use函数内部会调用参数的install方法。
将Hello方法挂载到Vue的prototype上.
vue组件中就可以this.$hello('hello world')
Vue父组件传递props数据,子组件修改参数
单向数据流,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。
如果子组件想修改prop中数据:
Vue父子组件生命周期执行顺序
加载渲染过程
子组件更新过程
父组件更新过程
销毁过程
Vue 自定义指令
自定义指令提供了几个钩子函数:
bind
:指令第一次绑定到元素时调用inserted
:被绑定元素插入父节点时调用update
:所在组件的 VNode 更新时调用使用slot后可以在子组件内显示插入的新标签
webpack 及工程化
webpack的生命周期,及钩子
compiler(整个生命周期 [kəmˈpaɪlər]) 钩子 https://webpack.docschina.org/api/compiler-hooks/
compilation(编译 [ˌkɑːmpɪˈleɪʃn]) 钩子
compiler
对象包含了Webpack 环境所有的的配置信息。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。compilation
对象包含了当前的模块资源、编译生成资源、变化的文件等。当运行webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
compiler
代表了整个webpack
从启动到关闭的生命周期
,而compilation
只是代表了一次新的编译过程
webpack 编译过程
Webpack 的编译流程是一个串行的过程,从启动到结束会依次执行以下流程:
Compiler
对象,加载所有配置的插件,执行对象的run
方法开始执行编译;entry
找出所有的入口文件;Loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;Loader
翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;Chunk
,再把每个Chunk
转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;优化项目的webpack打包编译过程
1.构建打点:构建过程中,每一个
Loader
和Plugin
的执行时长,在编译 JS、CSS 的 Loader 以及对这两类代码执行压缩操作的 Plugin上消耗时长 。一款工具:speed-measure-webpack-plugin2.缓存:大部分 Loader 都提供了
cache
配置项。cache-loader
,将 loader 的编译结果写入硬盘缓存3.多核编译,
happypack
项目接入多核编译,理解为happypack
将编译工作灌满所有线程4.抽离,
webpack-dll-plugin
将这些静态依赖从每一次的构建逻辑中抽离出去,静态依赖单独打包,Externals
将不需要打包的静态资源从构建逻辑中剔除出去,使用CDN 引用
5.
tree-shaking
,虽然依赖了某个模块,但其实只使用其中的某些功能。通过tree-shaking
,将没有使用的模块剔除,来达到删除无用代码的目的。首屏加载优化
路由懒加载:改为用
import
引用,以函数的形式动态引入,可以把各自的路由文件分别打包,只有在解析给定的路由时,才会下载路由组件;element-ui
按需加载:引用实际上用到的组件 ;组件重复打包:
CommonsChunkPlugin
配置来拆包,把使用2次及以上的包抽离出来,放进公共依赖文件,首页也有复用的组件,也会下载这个公共依赖文件;gzip: 拆完包之后,再用
gzip
做一下压缩,关闭sourcemap。UglifyJsPlugin: 生产环境,压缩混淆代码,移除console代码
CDN部署静态资源:静态请求打在nginx时,将获取静态资源的地址进行重定向CDN内容分发网络
移动端首屏加载可以使用骨架屏,自定义loading,首页单独做服务端渲染。
如何进行前端性能优化(21种优化+7种定位方式)
webpack 热更新机制
热更新流程总结:
启动本地
server
,让浏览器可以请求本地的静态资源页面首次打开后,服务端与客户端通过 websocket建立通信渠道,把下一次的 hash 返回前端
客户端获取到hash,这个hash将作为下一次请求服务端 hot-update.js 和 hot-update.json的hash
修改页面代码后,Webpack 监听到文件修改后,开始编译,编译完成后,发送 build 消息给客户端
客户端获取到hash,成功后客户端构造hot-update.js script链接,然后插入主文档
hot-update.js 插入成功后,执行hotAPI 的 createRecord 和 reload方法,获取到 Vue 组件的 render方法,重新 render 组件, 继而实现 UI 无刷新更新。
webpack的 loader和plugin介绍,css-loader,style-loader的区别
loader 它就是一个转换器,将A文件进行编译形成B文件,
plugin ,它就是一个扩展器,来操作的是文件,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,会监听webpack打包过程中的某些节点(run, build-module, program)
Babel 能把ES6/ES7的代码转化成指定浏览器能支持的代码。
css-loader
的作用是把 css文件进行转码style-loader
: 使用<style>将css-loader
内部样式注入到我们的HTML页面先使用
css-loader
转码,然后再使用style-loader
插入到文件如何编写一个webpack的plugin?
https://segmentfault.com/a/1190000037513682
webpack 插件的组成:
new XXXPlugin()
的方式)为什么 Vite 启动这么快
Webpack 会
先打包
,然后启动开发服务器,请求服务器时直接给予打包结果。而 Vite 是
直接启动
开发服务器,请求哪个模块再对该模块进行实时编译
。Vite 将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像 Webpack 那样进行
打包合并
。由于 Vite 在启动的时候
不需要打包
,也就意味着不需要分析模块的依赖
、不需要编译
。因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。你的脚手架是怎么做的
使用
download-git-repo
下载仓库代码democommander
:完整的node.js
命令行解决方案。声明program
,使用.option()
方法来定义选项Inquirer.js
:命令行用户界面的集合。前端监控
前端监控通常包括行为监控(PV/UV,埋点接口统计)、异常监控、性能监控。
一个监控系统,大致可以分为四个阶段:日志采集、日志存储、统计与分析、报告和警告。
错误监控
Vue专门的错误警告的方法
Vue.config.errorHandler
,(Vue提供只能捕获其页面生命周期内的函数,比如created,mounted)框架:betterjs,fundebug(收费)
捕获错误的脚本要放置在最前面,确保可以收集到错误信息
方法:
window.onerror()
当有js运行时错误触发时,onerror可以接受多个参数(message, source, lineno, colno, error)。window.addEventListener('error'), function(e) {}, true
会比window.onerror先触发,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误前端JS错误捕获--sourceMap
如何监控网页崩溃?**崩溃和卡顿有何差别?**监控错误
Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃;
Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;
卡顿:加载中,渲染遇到阻塞
性能监控 && 性能优化
性能指标:
FP
(首次绘制)FCP
(首次内容绘制 First contentful paint)LCP
(最大内容绘制时间 Largest contentful paint)FPS
(每秒传输帧数)TTI
(页面可交互时间 Time to Interactive)HTTP
请求响应时间DNS
解析时间TCP
连接时间性能数据采集需要使用
window.performance API
, JS库web-vitals
:import {getLCP} from 'web-vitals'
;性能优化常用手段:缓存技术、 预加载技术、 渲染方案。
缓存 :主要有 cdn、浏览器缓存、本地缓存以及应用离线包
预加载 :资源预拉取(prefetch)则是另一种性能优化的技术。通过预拉取可以告诉浏览器用户在未来可能用到哪些资源。
prefetch支持预拉取图片、脚本或者任何可以被浏览器缓存的资源。
在head里 添加
<linkrel="prefetch"href="image.png">
prerender是一个重量级的选项,它可以让浏览器提前加载指定页面的所有资源。
subresource可以用来指定资源是最高优先级的。当前页面需要,或者马上就会用到时。
常见的六种设计模式以及应用场景
https://www.cnblogs.com/whu-2017/p/9471670.html
观察者模式的概念
观察者模式模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态变化时,会通知所有的观察者对象。
发布订阅者模式的概念
发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来
工厂模式 主要是为创建对象提供了接口。场景:在编码时不能预见需要创建哪种类的实例。
代理模式 命令模式
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。 (window)
Http 及浏览器相关
七层网络模型
应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源) 。(应用场景:HTP,HTTP,邮件)
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快 ,可能丢包(应用场景:即时通讯)
https
客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。服务器公钥放在数字证书中。
url到加载渲染全过程
DNS 协议提供通过
域名查找 IP地址
,或逆向从IP地址反查域名
的服务。DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。
TCP 三次握手,四次挥手:握手挥手都是客户端发起,客户端结束。 三次握手与四次挥手详解
负载均衡:请求在进入到真正的应用服务器前,可能还会先经过负责负载均衡的机器,它的作用是将请求
合理地分配到多个服务器上
,转发HTTP请求;同时具备具备防攻击等功能。可分为DNS负载均衡,HTTP负载均衡,IP负载均衡,链路层负载均衡等。Web Server: 请求经过前面的负载均衡后,将进入到对应服务器上的 Web Server,比如
Apache
、Tomcat
反向代理是工作在 HTTP 上的,一般都是
Nginx
。全国各地访问baidu.com就肯定要通过代理访问,不可能都访问百度的那台服务器。 (VPN正向代理,代理客户端)浏览器解析渲染过程:
返回的html传递到浏览器后,如果有gzip会先解压,找出文件编码格式,外链资源的加载
html从上往下解析,遇到js,css停止解析渲染,直到js执行完成。
解析HTML,构建DOM树
解析CSS,生成CSS规则树
合并DOM树和CSS规则,生成render树去渲染
不会引起DOM树变化,页面布局变化,改变元素样式的行为叫重绘
引起DOM树结构变化,页面布局变化的行为叫回流
GUI渲染线程
负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)
或由于某种操作引发回流(reflow)
时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了. 直到JS程序执行完成,才会接着执行。因此如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面,渲染前后元素数据可能不一致GPU绘制
多进程的浏览器:主控进程,插件进程,GPU,tab页(浏览器内核)
多线程的浏览器内核:每一个tab页面可以看作是浏览器内核进程,然后这个进程是多线程的。
它有几大类子线程:
http1 跟HTTP2
http2
多路复用:相同域名多个请求,共享同一个TCP连接,降低了延迟
请求优先级:给每个request设置优先级
二进制传输;之前是用纯文本传输
数据流:数据包不是按顺序发送,对数据包做标记。每个请求或回应的所有数据包成为一个数据流,
服务端推送:可以主动向客户端发送消息。
头部压缩:减少包的大小跟数量
HTTP/1.1 中的管道( pipeline)传输中如果有一个
请求阻塞
了,那么队列后请求也统统被阻塞住了HTTP/2 多请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。
HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
http1 keep alive 串行传输
http 中的 keep-alive 有什么作用
响应头中设置
keep-alive
可以在一个 TCP 连接上发送多个 http 请求浏览器缓存策略
强缓存:cache-control;no-cache max-age=<10000000>;expires;其中Cache-Conctrol的优先级比Expires高;
控制强制缓存的字段分别是Expires和Cache-Control,如果客户端的时间小于Expires的值时,直接使用缓存结果。
协商缓存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified /
首次请求,服务器会在返回的响应头中加上Last-Modified字段,表示资源最后修改的时间。
浏览器再次请求时,请求头中会带上If-Modified-Since字段,比较两个字段,一样则证明资源未修改,返回304,否则重新返回资源,状态码为200;
垃圾回收机制:
标记清除
:进入执行环境的变量都被标记,然后执行完,清除这些标记跟变量。 查看变量是否被引用。引用计数
:会记录每个值被引用的次数,当引用次数变成0后,就会被释放掉。前端安全
同源策略:如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。 因为浏览器有
cookies
。XSS:跨站脚本攻击(Cross Site Scripting) input, textarea等所有可能输入文本信息的区域,输入
<script src="http://恶意网站"></script>
等,提交后信息会存在服务器中 。CSRF:跨站请求伪造 。引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求。
A
站点img
的src=B
站点的请求接口,可以访问;解决:referer
携带请求来源访问该页面后,表单自动提交, 模拟完成了一次POST操作,发送post请求
解决:后端注入一个
随机串
到Cookie
,前端请求取出随机串添加传给后端。http 劫持:电信运营商劫持
SQL注入
点击劫持:诱使用户点击看似无害的按钮(实则点击了透明
iframe
中的按钮) ,解决后端请求头加一个字段X-Frame-Options
文件上传漏洞 :服务器未校验上传的文件
CSS 及 HTML
什么是BFC(块级格式化上下文)、IFC(内联格式化上下文 )、FFC(弹性盒模型)
BFC(Block formatting context)
,即块级格式化上下文
,它作为HTML页面上的一个独立渲染区域
,只有区域内元素参与渲染,且不会影响其外部元素。简单来说,可以将 BFC 看做是一个“围城”,外面的元素进不来,里面的元素出不去(互不干扰)。
一个决定如何渲染元素的容器 ,渲染规则 :
1、内部的块级元素会在垂直方向,一个接一个地放置。
2、块级元素垂直方向的距离由margin决定。属于同一个BFC的两个相邻块级元素的margin会发生重叠。
3、对于从左往右的格式化,每个元素(块级元素与行内元素)的左边缘,与包含块的左边缘相接触,(对于从右往左的格式化则相反)。即使包含块中的元素存在浮动也是如此,除非其中元素再生成一个BFC。
4、BFC的区域不会与浮动元素重叠。
5、BFC是一个隔离的独立容器,容器里面的子元素和外面的元素互不影响。
6、计算BFC容器的高度时,浮动元素也参与计算。
形成BFC的条件:
1、浮动元素,float 除 none 以外的值;
2、定位元素,position(absolute,fixed);
3、display 为以下其中之一的值 inline-block,table-cell,table-caption;
4、overflow 除了 visible 以外的值(hidden,auto,scroll);
BFC 一般用来解决以下几个问题
flex: 0 1 auto;
是什么意思?元素会根据自身宽高设置尺寸。它会缩短自身以适应
flex
容器,但不会伸长并吸收flex
容器中的额外自由空间来适应flex
容器 。 水平的主轴(main axis
)和垂直的交叉轴(cross axis
)几个属性决定按哪个轴的排列方向flex-grow
:0
一个无单位数(): 它会被当作<flex-grow>的值。
flex-shrink
:1
一个有效的**宽度(width)**值: 它会被当作<flex-basis>的值。
flex-basis
:auto
关键字none
,auto
或initial
.放大比例、缩小比例、分配多余空间之前占据的主轴空间。
避免CSS全局污染
// 选择器上>要记得写,免得污染所有ul下面的liul{ >li{ color:red; }}
CSS Modules
阮一峰 CSS Modules
CSS Modules是一种构建步骤中的一个进程。通过构建工具来使
指定class
达到scope
的过程。CSS Modules
允许使用::global(.className)
的语法,声明一个全局规则。凡是这样声明的class
,都不会被编译成哈希字符串
。:local(className)
: 做 localIdentName 规则处理,编译唯一哈希类名。CSS Modules使用特点:
盒子模型和
box-sizing
属性width: 160px; padding: 20px; border: 8px solid orange;
标准 box-sizing:
content-box;
元素的总宽度 = 160 + 202 + 82;IE的
border-box
: 总宽度160
margin/padding
取百分比的值时 ,基于父元素的宽度和高度的。css绘制三角形
CSS实现了三角形后如何给三角形添加阴影
???
CSS两列布局的N种实现
CSS三列布局
float布局: 左边左浮动,右边右浮动,中间margin:0 100px;
Position布局: 左边left:0; 右边right:0; 中间left: 100px; right: 100px;
table布局: 父元素 display: table; 左右 width: 100px; 三个元素display: table-cell;
弹性(flex)布局:父元素 display: flex; 左右 width: 100px;
网格(gird)布局:
常见场景及问题
app与H5 如何通讯交互的?
// 兼容IOS和安卓callMobile(parameters,messageHandlerName) { //handlerInterface由iOS addScriptMessageHandler与andorid addJavascriptInterface 代码注入而来。 if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { // alert('ios') window.webkit.messageHandlers[messageHandlerName].postMessage(JSON.stringify(parameters)) } else { // alert('安卓') //安卓传输不了js json对象,只能传输string window.webkit[messageHandlerName](JSON.stringify(parameters)) }}
由app将原生方法注入到window上供js调用
messageHandlerName
约定的通信方法parameters
需要传入的参数移动端适配方案
rem
是相对于HTML的根元素em
相对于父级元素的字体大小。VW,VH
屏幕宽度高度的高分比//按照宽度375图算, 1rem = 100px;(function (win, doc) { function changeSize() { doc.documentElement.style.fontSize = doc.documentElement.clientWidth / 3.75 + 'px'; console.log(100 * doc.documentElement.clientWidht / 3.75) } changeSize(); win.addEventListener('resize', changeSize, false);})(window, document);
代码编程相关
实现发布订阅
promise怎么实现链式调用跟返回不同的状态
// MyPromise.js// 先定义三个常量表示状态const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';// 新建 MyPromise 类class MyPromise { constructor(executor){ // executor 是一个执行器,进入会立即执行 // 并传入resolve和reject方法 executor(this.resolve, this.reject) } // 储存状态的变量,初始值是 pending status = PENDING; // resolve和reject为什么要用箭头函数? // 如果直接调用的话,普通函数this指向的是window或者undefined // 用箭头函数就可以让this指向当前实例对象 // 成功之后的值 value = null; // 失败之后的原因 reason = null; // 更改成功后的状态 resolve = (value) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态修改为成功 this.status = FULFILLED; // 保存成功之后的值 this.value = value; } } // 更改失败后的状态 reject = (reason) => { // 只有状态是等待,才执行状态修改 if (this.status === PENDING) { // 状态成功为失败 this.status = REJECTED; // 保存失败后的原因 this.reason = reason; } } then(onFulfilled, onRejected) { // 判断状态 if (this.status === FULFILLED) { // 调用成功回调,并且把值返回 onFulfilled(this.value); } else if (this.status === REJECTED) { // 调用失败回调,并且把原因返回 onRejected(this.reason); } }}
实现Promise.all
对象数组转换成tree数组
思路: 涉及到树形数组,采用递归遍历的方式
JS instanceof方法原生实现
简单用法
实现如下:
编程题
The text was updated successfully, but these errors were encountered: