1.Vue v-model 是如何实现的,语法糖实际是什么
公司:脉脉
分类:Vue
答案&解析👇👇👇
一、语法糖
指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。糖在不改变其所在位置的语法结构的前提下,实现了运行时的等价。可以简单理解为,加糖后的代码编译后跟加糖前一样,代码更简洁流畅,代码更语义自然.
二、实现原理
1.作用在普通表单元素上
动态绑定了 input
的 value
指向了 messgae
变量,并且在触发 input
事件的时候去动态把 message
设置为目标值
<input v-model="sth" />
// 等同于
<input
v-bind:value="message"
v-on:input="message=$event.target.value"
>
//$event 指代当前触发的事件对象;
//$event.target 指代当前触发的事件对象的dom;
//$event.target.value 就是当前dom的value值;
//在@input方法中,value => sth;
//在:value中,sth => value;
2.作用在组件上
在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件
本质是一个父子组件通信的语法糖,通过prop和$.emit实现
因此父组件v-model
语法糖本质上可以修改为 '<child :value="message" @input="function(e){message = e}"></child>'
在组件的实现中,我们是可以通过 v-model属性
来配置子组件接收的prop名称,以及派发的事件名称。
例:
// 父组件
<aa-input v-model="aa"></aa-input>
// 等价于
<aa-input v-bind:value="aa" v-on:input="aa=$event.target.value"></aa-input>
// 子组件:
<input v-bind:value="aa" v-on:input="onmessage"></aa-input>
props:{value:aa,}
methods:{
onmessage(e){
$emit('input',e.target.value)
}
}
默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event
但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
js 监听input 输入框输入数据改变,用oninput ,数据改变以后就会立刻触发这个事件。
通过input事件把数据$emit 出去,在父组件接受。
父组件设置v-model的值为input$emit过来的值。
2.React 中 setState 后发生了什么?setState 为什么默认是异步?setState 什么时候是同步?
公司:微医
分类:React
答案&解析👇👇👇
一、React中setState后发生了什么
在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。
在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。
在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
二、setState 为什么默认是异步
假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。
三、setState 什么时候是同步
在setTimeout或者原生事件中,setState是同步的。
3.多个 tab 只对应一个内容框,点击每个 tab 都会请求接口并渲染到内容框,怎么确保频繁点击 tab 但能够确保数据正常显示?
公司:爱范儿
分类:JavaScript
答案&解析👇👇👇
一、分析
因为每个请求处理时长不一致,可能会导致先发送的请求后响应,即请求响应顺序和请求发送顺序不一致,从而导致数据显示不正确。
即可以理解为连续触发多个请求,如何保证请求响应顺序和请求发送顺序一致。对于问题所在场景,用户只关心最后数据是否显示正确,即可以简化为:连续触发多个请求,如何保证最后响应的结果是最后发送的请求(不关注之前的请求是否发送或者响应成功)
类似场景:input输入框即时搜索,表格快速切换页码
二、解决方案
防抖(过滤掉一些非必要的请求) + 取消上次未完成的请求(保证最后一次请求的响应顺序)
取消请求方法:
XMLHttpRequest
使用abort api
取消请求axios
使用cancel token
取消请求
伪代码(以 setTimeout 模拟请求,clearTimeout 取消请求)
/**
* 函数防抖,一定时间内连续触发事件只执行一次
* @param {*} func 需要防抖的函数
* @param {*} delay 防抖延迟
* @param {*} immediate 是否立即执行,为true表示连续触发时立即执行,即执行第一次,为false表示连续触发后delay ms后执行一次
*/
let debounce = function(func, delay = 100, immediate = false) {
let timeoutId, last, context, args, result
function later() {
const interval = Date.now() - last
if (interval < delay && interval >= 0) {
timeoutId = setTimeout(later, delay - interval)
} else {
timeoutId = null
if (!immediate) {
result = func.apply(context, args)
context = args = null
}
}
}
return function() {
context = this
args = arguments
last = Date.now()
if (immediate && !timeoutId) {
result = func.apply(context, args)
context = args = null // 解除引用
}
if (!timeoutId) {
timeoutId = setTimeout(later, delay)
}
return result
}
}
let flag = false // 标志位,表示当前是否正在请求数据
let xhr = null
let request = (i) => {
if (flag) {
clearTimeout(xhr)
console.log(`取消第${i - 1}次请求`)
}
flag = true
console.log(`开始第${i}次请求`)
xhr = setTimeout(() => {
console.log(`请求${i}响应成功`)
flag = false
}, Math.random() * 200)
}
let fetchData = debounce(request, 50) // 防抖
// 模拟连续触发的请求
let count = 1
let getData = () => {
setTimeout(() => {
fetchData(count)
count++
if (count < 11) {
getData()
}
}, Math.random() * 200)
}
getData()
/* 某次测试输出:
开始第2次请求
请求2响应成功
开始第3次请求
取消第3次请求
开始第4次请求
请求4响应成功
开始第5次请求
请求5响应成功
开始第8次请求
取消第8次请求
开始第9次请求
请求9响应成功
开始第10次请求
请求10响应成功
*/
4.Webpack 为什么慢,如何进行优化
分类:工程化
答案&解析👇👇👇
一、webpack 为什么慢
webpack是所谓的模块捆绑器,内部有循环引用来分析模块间之间的依赖,把文件解析成AST,通过一系类不同loader的加工,最后全部打包到一个js文件里。
webpack4以前在打包速度上没有做过多的优化手段,编译慢的大部分时间是花费在不同loader编译过程,webpack4以后,吸收借鉴了很多优秀工具的思路,
如支持0配置,多线程等功能,速度也大幅提升,但依然有一些优化手段。如合理的代码拆分,公共代码的提取,css资源的抽离
二、优化 Webpack 的构建速度
- 使用高版本的 Webpack (使用webpack4)
- 多线程/多实例构建:HappyPack(不维护了)、thread-loader
- 缩小打包作用域:
exclude/include
(确定 loader 规则范围)resolve.modules
指明第三方模块的绝对路径 (减少不必要的查找)resolve.extensions
尽可能减少后缀尝试的可能性noParse
对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)IgnorePlugin
(完全排除模块)- 合理使用alias
- 充分利用缓存提升二次构建速度:
babel-loader
开启缓存terser-webpack-plugin
开启缓存- 使用 cache-loader 或者 hard-source-webpack-plugin 注意:thread-loader 和 cache-loader 兩個要一起使用的話,請先放 cache-loader 接著是 thread-loader 最後才是 heavy-loader
- DLL
- 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
三、使用Webpack4带来的优化
- V8带来的优化(for of替代forEach、Map和Set替代Object、includes替代indexOf)
- 默认使用更快的md4 hash算法
- webpack AST可以直接从loader传递给AST,减少解析时间
- 使用字符串方法替代正则表达式
来看下具体使用
1.noParse
- 不去解析某个库内部的依赖关系
- 比如jquery 这个库是独立的, 则不去解析这个库内部依赖的其他的东西
- 在独立库的时候可以使用
module.exports = {
module: {
noParse: /jquery/,
rules:[]
}
}
3.dillPlugin
- 不会多次打包, 优化打包时间
- 先把依赖的不变的库打包
- 生成 manifest.json文件
- 然后在webpack.config中引入
- webpack.DllPlugin、Webpack.DllReferencePlugin
4.happypack -> thread-loader
- 大项目的时候开启多线程打包
- 影响前端发布速度的有两个方面,一个是
构建
,一个就是压缩
,把这两个东西优化起来,可以减少很多发布的时间。
5.thread-loader
thread-loader
会将您的 loader 放置在一个 worker 池里面运行,以达到多线程构建。
把这个 loader 放置在其他 loader 之前,放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 你的高开销的loader放置在此 (e.g babel-loader)
]
}
]
}
}
每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。请在高开销的loader中使用,否则效果不佳。