关于前端

289 阅读21分钟

1. 框架类:

vue

  • MVVM:MVVM是Model-View-ViewModel的简写,MVC改进版,vm是模式的核心,它是连接view和model的桥梁,通过数据绑定将 模型 转为 视图,通过 DOM 事件监听将 视图 转为 模型,即双向绑定。不同于 MVC 的单向通信,只能通过 C(Controller)来双向控制。
  • 原理:
    • 数据响应:Object.defineProperty利用setter、getter劫持数据,监听数据变化后,通知需要更新的地方做更新;
    • 依赖收集:发布订阅,Observe监听器,Watcher订阅者,Dep消息订阅器;
    • 指令解析器Compile:每个节点元素进行扫描和解析,用正则对带有 '{{变量}}' 这种形式的指令进行处理,初始化成一个订阅者Watcher;
    • 过程:首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
    • 参考vue的双向绑定原理及实现
    • 关于vue源码:Vue源码系列-Vue中文社区 未标题-1.png
  • v-key的作用(diff 算法):"就地复用"策略,高效的更新虚拟DOM;
  • 父子组件生命周期:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 监听子组件生命周期:@hook:mounted="onListenMounted"
  • 组件通信
    • prop
    • $emit
    • 事件总线,发布订阅
    • vuex
    • provide/inject
  • Vue.use():调用插件内部的install方法,并将Vue实例作为参数传入;
  • Vue添加全局功能:
    • Vue.use
    • Vue.prototype
    • 全局混入mixin
    • this.$root
  • directive 自定义组件
    • 钩子函数:bind、inserted、update、componentUpdated、unbind
    • 函数参数:el、binding、vnode、oldVnode
  • 组件按需加载:
// vue异步组件
component: require('@/pages/login')
// 结合 webpack 做一些加工处理
component: r => require.ensure([], () => r(require('@/components/home')), 'chunk')
// es6
component: () => import('@/pageslogin')
  • 动态渲染图片:把图片路径用require()调用,或提前 import 图片实例;
  • 数组变更方法:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,pop、push、unshift、shift、splice、reserve、sort;
  • $set:在实例创建之后添加新的属性到实例上,它不会触发 响应式 更新,需要使用 Vue.set(object, key, value) 来重新添加响应式;
  • Vue.extend():vue的一个构造器,继承自vue。可用于即用即显的弹框;
  • Methods、computed、watch 区别:methods 在调用时才会计算。computed 当依赖的数据变化时才会计算, 当数据没有变化时, 它会读取缓存数据;异步无法监听数据的变化。watch 会在属性变化后重新计算,支持异步,对象内部值的变化可指定 deep: true来监听。
  • 同时 watch 多个值:先用computed返回需监听的值,然后对这个计算属性进行监听
    computed: { 
      watchInfo(){ 
        return {
          age: this.age,
          name: this.userInfo.name
        }
      } 
    },
    watch: {
      watchInfo(val){
        // doing somthing..
      }
    },
  • 插槽简写方式
    • 具名插槽
    // 父组件 
    <child-component>
        <template v-slot:header><h1>这是头部内容</h1></template>
        <!-- 简写 -->
        <template #footer><p>这是底部内容</p></template>
    </child-component>
    
    // 子组件 
    <template>
        <slot name="header"></slot>
    </template>
    
    • 作用域插槽
    // 父组件 
    <child-component>
        <template v-slot:content="slotProps">
            <p>{{ slotProps.text }}</p>
        </template>
        <!-- 简写 -->
        <template #content="slotProps"><p>{{ slotProps.content.text }}</p></template>
    </child-component>
    
    // 子组件 
    <template>
        <slot :content="info"></slot>
    </template>
    
  • 怎样给插槽传递事件
    // 子组件 child-component 
    <div>
        <slot :onClick="onClick"></slot>
    </div>
    methods: {
        onClick(){
            // do something
        }
    }
    
    // 父组件 
    <div>
        <child-component>
            <template #default="{onClick}">
                <button @click="onClick">点击</button>
            </template>
        </child-component>
    </div>
    
  • data为什么是函数:为了达到数据隔离,由于组件可以复用,当组件被实例化后,属性也会被创建在原型链上,因此需要用data 返回一个可独立维护的数据拷贝;
  • nextTick 实现方式,Promise.resolve(),渐弱:setTimeout();修改模型数据后,对更新后的Dom处理,需要放在 nextTick 中;
  • axios 原理:axios可用于浏览器和 node.js 中,它会根据当前环境选择实现方式。如果是浏览器环境中,会基于XMLHttpRequests实现axios。如果是node.js环境,就会基于node内置 http模块 实现axios;
  • 路由实现方式及原理:hash模式,根据 window.location.hash 读取地址变化,监听 hashchange 事件来响应;history模式,通过H5新增的两个api window.history.pushStatewindow.history.replaceState 来实现路由切换。参考:vue-router 原理
  • 路由 history 模式刷新404:由于失去了“#”,前端的路由在浏览器刷新页面时,会请求服务端同样的路由,就会出现404,需要在服务端增加一个覆盖所有情况的候选资源,例如index.html;
  • 路由守卫
    • 全局:router.beforeEach((to, from, next) => {})、router.afterEach((to, from) => {})
    • 路由独享守卫:beforeEnter: (to, from, next) => {})
    • 组件内的守卫:beforeRouteEnter(to, from, next)、beforeRouteUpdate(to, from, next)、beforeRouteLeave(to, from, next) 页面离开前询问
  • Vuex:state、mutation、action
  • Vuex、v-model 绑定:
    • v-model直接绑定 state 的值
    • 借用Computed的set、get访问器进行操作
    computed: { 
      getNickName: { 
        get() { return this.$store.state.nickName }, 
        set(val) { this.$store.commit('setName', val) } 
      } 
    }
    
  • Vue2、Vue3
    • Object.defineProperty 改为 Proxy
    • Teleport 允许在 DOM 中选择节点渲染 HTML
    • 动态样式
  • 优化:
    • v-for 遍历必须加 key
    • computed 和 watch 区分使用场景
    • v-if 和 v-show 区分使用场景
    • 长列表 虚拟滚动
    • 对象层级不要过深
    • 路由懒加载
  • 错误信息收集:
    Vue.config.errorHandler = error => {
      errors.push({time: Date.now(), content: error.stack})
    }
    

vue3

  • 生命周期

    • beforeCreate ===> setup()
    • created =======> setup()
    • beforeMount ===> onBeforeMount
    • mounted =======> onMounted
    • beforeUpdate ===> onBeforeUpdate
    • updated =======> onUpdated
    • beforeUnmount ==> onBeforeUnmount
    • unmounted =====> onUnmounted
  • 为什么要用 Proxy API 替代 defineProperty API:

    • Object.defineProperty 只能遍历对象属性进行劫持,如果嵌套对象,需要深层监听,造成性能问题
    • Object.defineProperty 检测不到对象属性的添加和删除
    • Object.defineProperty 需要额外重写数组 API
    • Proxy 直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的
    • Proxy 可以直接监听数组的变化(pushshiftsplice
  • 实现原理:

    • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
    • 通过Reflect(反射): 对源对象的属性进行操作。
    • MDN文档中描述的Proxy与Reflect:
    • 伪代码:
    new Proxy(data, {
        // 拦截读取属性值
        get (target, prop) {
            return Reflect.get(target, prop)
        },
        // 拦截设置属性值或添加新属性
        set (target, prop, value) {
            return Reflect.set(target, prop, value)
        },
        // 拦截删除属性
        deleteProperty (target, prop) {
            return Reflect.deleteProperty(target, prop)
        }
    })
    proxy.name = 'tom'   
    
    

参考 vue3面试题:2024 最新前端 Vue 3

react

  • 事件绑定 this 丢失:绑定事件时,传递的不是字符串,而是一个方法,onClick 即为中间变量,render 方法执行过程会丢失原来的 this 引用;
  • hook
  • 路由封装:react 路由封装及页面授权

小程序

  • 压缩、组件传参(设计模式)、胶囊按钮的位置
  • setData 同步还是异步(数据同步,界面渲染异步)
  • 双线程模型:
    • 渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本,经由微信客户端(Native)做中转
    • 技术选型:为什么双线程
  • 外部样式引用:externalClasses
  • behavior:类似 mixin
  • wxs 文件的功能
  • 数据驱动:WXML先转成JS对象,然后再渲染出真正的Dom树。通过setData修改数据,产生的JS对象对应的节点就会发生变化,此时可以对比前后两个JS对象得到变化的部分,然后把这个差异应用到原来的Dom树上,从而达到更新UI的目的,这就是“数据驱动”的原理。
  • 与vue双向邦定的区别

Webpack

  • Plugin

    • webpack整体是一个插件架构,所有的功能都以插件的方式集成在构建流程中,通过发布订阅事件来触发各个插件执行。webpack核心使用Tapable来实现插件(plugins)的binding(绑定)和applying(应用)。
    • Compiler 模块是 webpack 的支柱引擎,它通过 CLI 或 Node API 传递的所有选项,创建出一个 compilation 实例。它扩展(extend)自 Tapable 类,以便注册和调用插件。大多数面向用户的插件首先会在 Compiler 上注册。
  • 配置项: entry、output、module( loader )、plugins、devServer、resolve、devtool、optimization。

  • 多入口配置

    • entry 选项配置多入口的目录,同时配置 HtmlWebpackPlugin 生成对应 html 模板。chunks 表示 html 文件的入口chunks;
    • vendor 是指提取涉及 node_modules 中的公共模块,manifest 是对 vendor 模块做的缓存
  • 热更新

  • 性能优化

  • 多个loader对同一资源进行处理,他们之间如何通信

    • webpack4之后,可以用 this.data 共享数据

2. CSS:

  • 布局
    • 左侧固定,右侧自适应宽度;
    • 垂直水平居中;
    • 圣杯布局
  • 样式实现
    • 三角形、正方形;
  • 异常的处理
    • 移动端兼容行高 line-height (Android、IOS)
    • margin 溢出
  • 移动端布局兼容
    • rem
  • 盒模型
    • 普通盒模型 content 包含 padding,IE盒模型 content 不包含 padding
  • 伪类、伪元素
    • 伪类:向选择器增加特殊状态效果:active、focus 等
    • 伪元素:用于创建文档树以外的元素并为其添加样式
  • BFC:块级格式化上下文
    • 触发条件:
      • body 根元素
      • 浮动元素:float 除 none 以外的值
      • 绝对定位元素:position (absolute、fixed)
      • display 为 inline-block、table-cells、flex
      • overflow 除了 visible 以外的值 (hidden、auto、scroll)
    • 解决问题:
      • float带来的高度塌陷
      • margin重叠

3. JavaScript:

  • 原始类型 => 装箱拆箱

  • 对象构造

  • 原型链、闭包 => 垃圾回收(标记清除)

  • 关于正则:

  • 面向对象的基本特征:

    • 封装、继承、多态
    • 继承(原型继承、构造继承、组合继承等)
  • 数据类型的判断typeofinstanceofconstructorObject.prototype.toString.call()

  • js线程、事件循环 Event Loop

    • 同步任务
    • 异步任务
      • 宏任务:setTimeout, setInterval, I/O 操作, UI 渲染
      • 微任务:Promise.then/catch/finally, process.nextTick(Node.js), MutationObserver
    • 栈中代码 => 微任务 => 宏任务 => 微任务 => 宏任务 => ...
    • try/catch 操作只能捕获当次事件循环内的异常,对异步执行时抛出的异常将无能为力
  • 常见排序:冒泡(比较相邻两数,换位),选择排序(每次选最小的数交换到前面),快排(选择一个基准数分前后区,递归该操作),桶排序(数据分配到有限数量的桶里,把非空桶拼接起来)

  • 常用 Array、String操作

  • this 绑定(new操作符、bind等、上下文调用绑定、默认绑定window)

  • cookie、sessionStorage、localStorage:

  • app交互

  • 常用JS手写题

    • new 关键字实现(return若为自定义的object、array,会代替构造函数中创建的对象)
    • call、apply、bind 模拟实现
    • 防抖节流原理
  • 模块化:主要是用来抽离公共代码,隔离作用域

    • IIFE:自执行函数,在一个单独的函数作用域中执行代码
    • AMD:使用requireJS 来编写模块化
    • CommonJS:nodejs 中自带的模块化require('fs');
    • webpack(require.ensure):webpack 2.x 版本中的代码分割
    • ES Modules:ES6 引入的模块化,支持 importexport
    • require是“运行时加载”,只有在代码真正运行的时候才会去加载对应需要的东西,所以不能做到编译时就把想加载的模块加载进来,即不能做到编译时静态化
    • import是编译时调用,所以必须放在文件开头
  • JS编译解析的流程

    • 语法分析,生成抽象语法树(AST)和执行上下文:
      • 生成 AST 需要经过两个阶段:
      1. 第一阶段是分词(tokenize),又称为词法分析
      2. 第二阶段是解析(parse),又称为语法分析
    • 预编译,解释器 lgnition 生成字节码
    • 解释执行,第一次执行,解释器 lgnition 通常会一行行执行代码,解释过程中,若发现一段代码被重复执行多次,即为热点代码,则 编译器 TurboFan 会把该段热点的字节码编译为机器码,再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了,这样就大大提升了代码的执行效率。这就是 即时编译(JIT)
    • ES6 转 ES5 原理
      1. Parser 解析:将 ES6 语法解析为 AST 抽象语法树,主要是通过 babylon 插件来完成。
      2. Transformer 转换:将零散的 AST 语法通过配置好的 plugins 和 presets 转换成新的 AST 语法,由 babel-transform 插件完成。plugins 和 presets 通常在 .babelrc 文件中配置。
      3. Generator 生成:将新的 AST 语法树对象再生成浏览器都可以识别的 ES5 语法,这一步主要是由 babel-generator 插件完成。
  • 设计模式:

    • 发布订阅 (Publish Subscribe Pattern) -- 多个订阅者对象同时监听某一个主题对象,这个主题对象状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态
    • 代理模式 (Proxy Pattern) -- 当对一个对象的访问不能直接引用时,可通过一个称之为 “代理” 的中间层来实现间接引用。通过引入代理对象来间接访问一个对象
  • ES6 +

    • 箭头函数与普通函数的区别
      • 没有this,会捕获其所在的上下文的this值
      • 使用call、bind等绑定无效
      • 不能作为构造函数,不能使用 new
      • 不绑定arguments,用rest参数...解决
      • 没有原型属性
      • 不能当做Generator函数,不能使用yield关键字
    • Set、Map 操作方法
    • Map、WeekMap 区别:
      • Map 对key强引用,不会被垃圾回收掉
      • WeekMapWeekMap 对key弱引用,能被垃圾回收及时清除掉,导致weakmap中的这对key-value也会消失
      var map = new Map();
      {
          let a = {}
          map.set(a, 'something');
      }
      console.log(map);
      
      var wmap = new WeakMap();
      {
          let b = {}
          wmap.set(b, 'something week');
      }
      console.log(wmap); 
      
    • 运算符(扩展运算符... 可选链?. 空值合并?? )
    • Proxy
      • 作为构造函数,Proxy接受两个参数,第一个参数是所要代理的目标对象,第二个参数是一个配置对象,可以设置目标对象的setget行为
      let proxy = new Proxy({}, {
        get: function(target, propKey) {
          return 35;
        }
      });
      proxy.time     // 35
      proxy.name     // 35
      proxy.title    // 35
      
    • Class 和 构造函数的区别:
      • 类和模块的内部,默认就是严格模式
      • Class 不存在变量提升
      • Class 的方法默认不可枚举
      • Class 必须使用 new 调用
      • ES5 中的继承先创造子类的实例对象this,然后再将父类的方法添加到this上面;Class 先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
  • 继承的实现方式

    • 原型链继承
    • 构造函数继承
    • 组合继承:结合构造函数和原型链
    • 原型式继承:临时创建一个构造函数,以现有对象作为原型,实例化对象并返回
    • 寄生式继承:在原型式继承的基础上,在内部增强对象并返回
    • class extend
  • 统计帧率FPS

    • 1s内 requestAnimationFrame 调用的次数

4. TypeScript:

  • Declare关键字:第三方库没有.d.ts声明文件的时候,我们可以通过declare来写申明文件,可以声明该模块,甚至声明一个值为any的同名的变量,然后我们就可以直接使用。
  • 泛型:泛指某一类型,更像是一个类型变量,由尖括号包裹<T>
  • interface、type:interface描述数据结构,用type描述类型

5. Node.js:

  • handleCount
  • koa、中间件

6. HTTP:

  • 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求 - 响应协议,归属于应用层协议,主要解决如何包装数据。
  • HTTP请求的结构
    • 请求行:请求方法、URI、HTTP协议版本 GET /index.html HTTP/1.1
    • 请求头:描述请求正文 Host: www.enjoytoday.cn Connection: keep-alive User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
    • 空行
    • 请求数据:存放POST请求类型的请求正文 username=ZhangSan&sex=male
  • 三次握手、四次挥手
  • TCP、UDP
    • TCP面向连接,高可靠,适用于文件传输等:网页浏览等 http 请求、文件传输;
    • UDP无连接,低开销,速度快,适用于实时通信等:音频/视频会议、实时播放、广播。
  • 安全:xss、csrf
  • 跨域: JSONP、CORS(预检请求)、代理
  • 网图地址没有后缀也能正常显示:浏览器根据数据包的 Content-Type 来决定如何解析数据
  • GET 和 POST:
    • HTTP 协议 未规定 GET 和POST的长度限制
    • GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
  • cookie, session和token:参考 Cookie、Session和Token的区别
    • cookie存储在浏览器本地,会被js篡改,不安全,单个4K,最多20条;
    • session存储在服务端,根据客户端cookie等信息进行甄别,占用服务器的性能;
    • token是来自服务端的令牌,保存在客户端,发送请求时在服务端比对;

7. 浏览器:

浏览器的渲染原理

  • 根据 html 文档,构建一棵 DOM 树,由元素及其附加属性组成。
  • 对 CSS 解析,生成 CSSOM 规则树,样式规则。
  • 根据 DOM 树和 CSSOM 规则树构建渲染树 Render Tree。由一些包含颜色和大小等属性的矩形组成。
  • 根据 Render Tree 来布局,计算各节点的位置、大小,此过程也就是“重排”。
  • 绘制。遍历render树,调用 Paint 方法使用 UI 基础组件绘制出来。这个过程并非是等 html 完全解析完再执行,而是解析完一部分就执行显示一部分。如下图,

JavaScript 的加载,解析,执行 都会阻塞文档的解析;

CSS 是并行下载,不会阻塞后面 JS 的下载,但会阻塞后面 JS 的执行;

CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。

因此,script 标签被建议放在body 标签底部,CSS 被建议放在头部。

重绘重排

  • 重排,重新排列,意味着重新计算文档中元素的位置和几何信息(元素的位置、大小),会影响部分或整个页面的布局。元素的重排将导致DOM中所有子元素和其他兄弟元素重新排列。
  • 重绘是更改元素的可见性,但是不影响元素的布局。例如可见性、背景色、轮廓等。
  • 如何减少重排和重绘:
    • 尽量避免style的使用,对于需要操作DOM元素节点,重新命名className,更改className名称。
    • 如果增加元素或者clone元素,可以先把元素通过 documentFragment 放入内存中,等操作完毕后,再appendChildDOM元素中。
    • 不要经常获取同一个元素,可以第一次获取元素后,用变量保存下来,减少遍历时间。
    • 尽量少使用dispaly:none,可以使用visibility:hidden代替,dispaly:none会造成重排visibility:hidden会造成重绘
    • 不要使用Table布局,因为一个小小的操作,可能就会造成整个表格的重排重绘
    • 使用resize事件时,做防抖节流处理。
    • 对动画元素使用absolute / fixed属性。
    • 批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流。

缓存机制

  • 浏览器的缓存过程:
    1. 域名解析,DNS缓存:域名查找IP地址的过程就是dns解析,这个过程会对网络请求带来一定的损耗,浏览器第一次解析后会将其缓存起来,即 DNS缓存。

      • 下次识别到相同地址时,会先查找本地缓存,优先使用本地缓存;
      • DNS缓存不存在,读取系统的hosts文件查找对应映射关系,若存在,则域名解析到此完成;
      • 本地hosts文件不存在映射关系,查找本地DNS服务器,若存在,域名到此解析完成;
      • 本地DNS服务器未找到,向服务器发送请求解析IP。
    2. 内存缓存(memory缓存):缓存于本地内存中,页面关闭,内存释放;

      • HTTP缓存(强缓存和协商缓存):硬盘缓存,最主要的缓存方式,可控且优化空间大的一个缓存部分,也是面试中常问的一个缓存环节;
      • 服务端缓存(CDN缓存):CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。浏览器本地缓存的资源过期之后,会向服务器发起资源申请,优先转向 CDN 边缘节点请求资源,CDN 中的缓存可用,直接返回,不可用或者过期,CDN 边缘节点会向源服务器发出回源请求,从而来获取最新资源,并做缓存。
  • HTTP缓存的命中方式:
    1. 根据 http 中的 header 判断是否命中强缓存,若命中,则直接从本地缓存中获取资源,不发送服务器请求;
    2. 强缓存未命中时,浏览器发送服务端请求,通过请求头验证是否命中协商缓存,若命中,则返回请求,但不返回资源,而是通知浏览器从本地加载资源;
    3. 都未命中,直接从服务器加载资源。
  • 强缓存:
    1. 利用 Expires(HTTP/1.0)、Cache-Control(HTTP/1.1)这些请求头参数,控制资源过期时间。到了HTTP/1.1,Expires已经被Cache-Control替代,所以,这两个字段同时存在时,Cache-Control优先级高于Expires。
      • Cache-Control 主要取值:
        1. max-age:指定一个时间长度,在这个时间段内缓存是有效的,单位是s。
        2. s-maxage:同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。
        3. public:所有内容都将被缓存(客户端和代理服务器都可缓存)
        4. private:所有内容只有客户端可以缓存,Cache-Control的默认取值
        5. no-cache:在使用已缓存的数据前,发送带验证器的请求到服务器,不是字面意思上的不缓存。
        6. no-store:禁止缓存,所有内容都不会被缓存,既不使用强制缓存,也不使用协商缓存,每次请求都要向服务器重新获取数据。

    参考 浏览器缓存命中策略

    1. 使用时,在Web服务器返回的响应中添加 ExpiresCache-Control Header。
    2. 控制台显示为,状态码 Status: 200, size: (from cache)。
  • 协商缓存:
    1. 某个资源的请求没有命中强缓存,会向服务器发送请求验证协商缓存是否命中,若命中,浏览器会收到304的响应,就会从缓存中加载资源。
    2. 大部分web服务器都默认开启协商缓存,同时启用 Last-Modified,If-Modified-Since (资源在服务器上最后修改时间)和 ETag、If-None-Match (资源的唯一标识)控制。
    3. 控制台显示为,状态码 Status: 304 Not Modified 。

8. 性能优化:

9. 功能设计:

扫码登录

  1. PC端进入页面,请求服务端获取二维码ID。
  2. 服务端生成相应的二维码ID,设置二维码的过期时间、状态等。
  3. PC端获取二维码ID,展示二维码。
  4. 手机端扫描二维码,获取二维码ID。
  5. 手机端将手机端token和二维码ID发送给服务端,确认登录。
  6. 服务端校验手机端token,根据手机端token和二维码ID生成PC端token
  7. PC端通过轮询方式请求服务端,通过二维码ID获取二维码状态,如果已成功,返回PC token,登录成功。

登录方式

  1. Cookie + Session
    • 服务端创建 SessionId并保存,在HTTP请求响应头中通过 Set-Cookie 头信息,将 SessionId 写入 Cookie 中。后续页面访问会自动带上 Cookie,服务端进行比对;
    • 当对接客户端体量大时,也需存放大量 SessionId,增加服务器压力; Cookie在浏览器中无法避免 CSRF 攻击。
  2. Token 登录
    • 首次登录后,服务器会生成一个 Token 并返回给客户端,由客户端自由保存,客户端后续访问时,只需带上这个 Token 即可完成身份认证;
  3. SSO单点登录
    • 在同一平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。
    • 未登录情况下访问页面A,会重定向到认证中心,在认证中心登录完成后,再带上授权码ticket重定向到页面A,同时将认证中心的登录态写入 Cookie。并在A页面服务器中用ticket 向认证中心确认,通过后将登录信息写入 Cookie(同时存在认证中心、A页面的Cookie)。
  4. OAuth 三方登录
    • 以微信为例,运营者在微信平台注册账号后,得到 appid 等。用户进入业务页面A后,选择微信登录,会跳转到微信的 OAuth 授权登录页,并附上A页面地址,当用户授权登录后,会从当前微信授权页重定向回A页面,这时带上了临时票据 code
    • A页面拿到 code后,向调用微信API申请 token,验证成功后微信会下发一个 token,A页面服务器可据此拿到微信用户相关信息;
    • A页面登录成功后,会将登录态写入 Cookie,以作后续凭证。

10. 规范:

  • 命名: 小驼峰、下划线、短横杠; 图片命名,推荐用 page-type-name_index 方式命名,如 home-icon-check_1.jpg、friend-banner-top_3.jpg
  • css: 尽量避免内联样式; 0 后面不加单位,小数点前不加 0; 不用标签名做选择器; 若一个样式表内多处使用相同样式,应提取公用,如: display: flex; justify-content: center; align-items: center;
  • js 采用封装,减少重复代码; wx:key 注入id 等唯一标识; 不涉及到 dom 上的数据交互时,避免使用 this.setData,以 this.data 代替; 减少 this.setData 的单独调用,尽量合并,一次调用; 小程序中不需要用到的 钩子函数,应删掉,避免触发不必要的监听执行; 生命周期函数内,减少实际操作代码,可单独抽出写作 function; console.log() 在完成调试后,应及时删除,以免控制台输出太多;
  • git 提交时的 标题 应尽量涵盖此次所有的变动,能使项目其他人检索到; 一次提交过多时,可分次提交;
  • 其他 缩进: 用两个空格来代替制表符(tab)-- 保证在所有环境下获得一致展现的方法; 无用的代码及时删除; 图片使用前需压缩,不超200k; 推荐使用 mark 插件标记代码段;

相关面经:\ 时隔一年半,我,一个卑微的前端菜鸡,又来写面经了。\ 阿里社招两年前端面经

/**
*
*    ┏┓     ┏┓
*   ┏┛┻━━━━━┛┻┓
*   ┃      ┃
*   ┃  ━   ┃
*   ┃ ┳┛ ┗┳  ┃
*   ┃      ┃
*   ┃  ┻   ┃
*   ┃      ┃
*   ┗━┓   ┏━┛ Code is far away from bug with the animal protecting
*     ┃   ┃    神兽保佑,代码无bug
*     ┃   ┃
*     ┃   ┗━━━┓
*     ┃      ┣┓
*     ┃      ┏┛
*     ┗┓┓┏━┳┓┏┛
*      ┃┫┫ ┃┫┫
*      ┗┻┛ ┗┻┛
*
*/