Привет~ Давно не виделись.
последний отсортированный100 вопросов для фронтенд-интервьюПосле того, как Наггетс стал популярным, многие мелкие партнеры сообщили, что было неудобно читать ответы.На самом деле, главная причина заключалась в том, что ответы и содержание анализа, которые я организовал, были очень полными, в результате чего каждый вопрос был очень длинным, а опыт чтения был не очень хорошим, поэтому я дал его всем.Выложите ответ на github.
Недавно разблокировал новую функцию Наггетс-складывания контента, в этой статье я поставлю как можно больше ответов и анализов.
1. Как получить фактическое значение стиля элемента html?
Компания: Jingdong
Категория: JavaScript
Посмотреть анализ
Код
Фактическое значение стиля можно понимать как рассчитанный браузером стиль.
Объект стиля содержит информацию о стиле, установленную для этого атрибута элементами, которые поддерживают атрибут стиля, но не содержит информацию о стиле, унаследованную от других каскадов таблиц стилей, которые также влияют на элемент.
DOM2 Style добавляет getComputedStyle() в document.defaultView метод. Этот метод принимает два параметра: элемент, из которого нужно получить вычисляемый стиль, и строку псевдоэлемента (например, ":after"). Если вам не нужно запрашивать псевдоэлементы, второму параметру можно передать значение null. Метод getComputedStyle() возвращает CSSStyleDeclaration. Объект (того же типа, что и свойство стиля), содержащий вычисленный стиль для элемента.
<!DOCTYPE html>
<html>
<head>
<title>Computed Styles Example</title>
<style type="text/css">
#myDiv {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
</head>
<body>
<div
id="myDiv"
style="background-color: red; border: 1px solid black"
></div>
</body>
<script>
let myDiv = document.getElementById("myDiv");
let computedStyle = document.defaultView.getComputedStyle(myDiv, null);
console.log(computedStyle.backgroundColor); // "red"
console.log(computedStyle.width); // "100px"
console.log(computedStyle.height); // "200px"
console.log(computedStyle.border); // "1px solid black"(在某些浏览器中)
/* 兼容写法 */
function getStyleByAttr(obj, name) {
return window.getComputedStyle
? window.getComputedStyle(obj, null)[name]
: obj.currentStyle[name];
}
let node = document.getElementById("myDiv");
console.log(getStyleByAttr(node, "backgroundColor"));
console.log(getStyleByAttr(node, "width"));
console.log(getStyleByAttr(node, "height"));
console.log(getStyleByAttr(node, "border
</script>
</html>
2. Расскажите, как вы понимаете React Hook, принцип его реализации и чем он отличается от жизненного цикла?
Компания: Гаоде, Тутиао
Категория: Реагировать
Посмотреть анализ
1. Реагировать на крючок
1.1 Что такое React-хук
Hook— новая функция в React 16.8. Он позволяет использовать состояние и другие функции React без написания классов.
Из этого предложения на официальном сайте мы можем ясно понять, чтоHookДобавлены функциональные компонентыstateИспользование , в прошлом, функциональные компоненты не могли иметь своего состояния, только черезpropsтак же какcontextсделать свой собственныйUI, а в бизнес-логике некоторые сценарии должны использоватьstate, то мы можем определить функциональные компоненты только какclassкомпоненты. а теперь черезHook, мы можем легко поддерживать наше состояние в функциональных компонентах, не меняя их наclassкомпоненты.
В React 16.8 добавлены хуки, чтобы сделать функциональные компоненты React более гибкими.
У React было много проблем до хуков
- Повторное использование логики состояния между компонентами сложно
- Сложные компоненты становятся трудными для понимания, а компоненты более высокого порядка и функциональные компоненты глубоко вложены друг в друга.
- this компонента класса указывает на проблему
- Незабываемый жизненный цикл
Хуки очень хорошо решают вышеуказанные проблемы, хуки предоставляют множество методов
- useState возвращает значение с сохранением состояния и функцию для обновления этого значения состояния.
- useEffect принимает функции, которые содержат императивный, возможно, побочный код.
- useContext принимает объект контекста (значение, возвращаемое React.createContext ) и возвращает текущее значение контекста,
- Альтернатива useReducer useState . Принимает редьюсер типа (состояние, действие) => newState и возвращает текущее состояние в паре с методом отправки.
- useCallback возвращает запомненную версию отзыва, которая изменяется только при изменении одного из входных данных. Входной и выходной детерминизм чистых функций
- useMemo — это функция чистой памяти.
- useRef возвращает изменяемый объект ref, чье свойство .current инициализируется переданным параметром.
- Пользовательский useImperativeMethods для использования экземпляра родительской сборки, раскрытого в значении ref.
- useMutationEffect срабатывает синхронно на том же этапе, когда React выполняет свои изменения DOM перед обновлением родственных компонентов.
- useLayoutEffect Срабатывает синхронно после изменения DOM. Используйте его для чтения макета из DOM и синхронного повторного рендеринга.
1.2 Какую проблему решает React Hook
React HooksПроблема, которую необходимо решить, — это совместное использование состояния.Под общим состоянием здесь понимается только повторное использование логики состояния, а не совместное использование данных. мы знаем, что вReact HooksРаньше для решения проблемы повторного использования логики состояния мы обычно использовалиhigher-order componentsа такжеrender-props.
Теперь, когда есть оба решения, почемуReactРазработчики также должны представитьReact Hook? дляhigher-order componentsа такжеrender-props,React HookГде преимущества?
PS: Самым большим преимуществом Hook на самом деле является удобство повторного использования логики состояния, простота кода и улучшение функциональных компонентов.
Давайте сначала посмотримReactофициально даноReact Hookизdemo
import { useState } from "React";
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Посмотрим еще разReact HookЕсли да, то как добиться
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Видно, что вReact Hookсередина,class ExampleКомпонент становится функциональным компонентом, но функциональный компонент имеет свое собственное состояние и также может обновлять свое состояние. Все это благодаряuseStateэтоHook,useStateвернет пару значений: текущее состояние и функцию, позволяющую обновить его, которую можно вызвать в обработчике событий или в другом месте. это что-то вродеclassкомпонентthis.setState, но новую не поставитstateи старыйstateобъединить
1.3 Принцип реализации
Основные типы крючков:
type Hooks = {
memoizedState: any, // 指向当前渲染节点 Fiber
baseState: any, // 初始化 initialState, 已经每次 dispatch 之后 newState
baseUpdate: Update<any> | null, // 当前需要更新的 Update ,每次更新完之后,会赋值上一个 update,方便 react 在渲染错误的边缘,数据回溯
queue: UpdateQueue<any> | null, // UpdateQueue 通过
next: Hook | null, // link 到下一个 hooks,通过 next 串联每一 hooks
};
type Effect = {
tag: HookEffectTag, // effectTag 标记当前 hook 作用在 life-cycles 的哪一个阶段
create: () => mixed, // 初始化 callback
destroy: (() => mixed) | null, // 卸载 callback
deps: Array<mixed> | null,
next: Effect, // 同上
};
React Hooks поддерживает глобальную переменную workInProgressHook.Каждый раз, когда вызывается Hooks API, функция createWorkInProgressHooks будет вызываться первой.
function createWorkInProgressHook() {
if (workInProgressHook === null) {
// This is the first hook in the list
if (firstWorkInProgressHook === null) {
currentHook = firstCurrentHook;
if (currentHook === null) {
// This is a newly mounted hook
workInProgressHook = createHook();
} else {
// Clone the current hook.
workInProgressHook = cloneHook(currentHook);
}
firstWorkInProgressHook = workInProgressHook;
} else {
// There's already a work-in-progress. Reuse it.
currentHook = firstCurrentHook;
workInProgressHook = firstWorkInProgressHook;
}
} else {
if (workInProgressHook.next === null) {
let hook;
if (currentHook === null) {
// This is a newly mounted hook
hook = createHook();
} else {
currentHook = currentHook.next;
if (currentHook === null) {
// This is a newly mounted hook
hook = createHook();
} else {
// Clone the current hook.
hook = cloneHook(currentHook);
}
}
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
} else {
// There's already a work-in-progress. Reuse it.
workInProgressHook = workInProgressHook.next;
currentHook = currentHook !== null ? currentHook.next : null;
}
}
return workInProgressHook;
}
Предположим, нам нужно выполнить следующий код хуков:
function FunctionComponet() {
const [ state0, setState0 ] = useState(0);
const [ state1, setState1 ] = useState(1);
useEffect(() => {
document.addEventListener('mousemove', handlerMouseMove, false);
...
...
...
return () => {
...
...
...
document.removeEventListener('mousemove', handlerMouseMove, false);
}
})
const [ satte3, setState3 ] = useState(3);
return [state0, state1, state3];
}
Когда мы понимаем простой принцип React Hooks, конкатенация Hooks — это не массив, а связанная структура данных, которая объединяется от корневого узла workInProgressHook до следующего. Вот почему хуки нельзя вкладывать друг в друга, использовать в условных суждениях и нельзя использовать в циклах. В противном случае цепная структура будет нарушена.
Во-вторых, разница с жизненным циклом
Суть функционального компонента — это функция, без понятия состояния, поэтому нет жизненного цикла, это просто функция рендеринга.
Но после введения хуков все становится иначе.Они позволяют компонентам иметь состояние без использования класса, поэтому существует концепция жизненного цикла.Так называемый жизненный цикл на самом деле представляет собой useState, useEffect() и useLayoutEffect().
То есть: Компоненты хуков (функциональные компоненты, которые используют хуки) имеют жизненный цикл, в то время как функциональные компоненты (функциональные компоненты, которые не используют хуки) не имеют жизненного цикла.
Ниже приведены соответствующие отношения между конкретным классом и жизненным циклом хуков:
3. Конкретная реализация и сравнение схемы адаптации мобильного терминала
Компания: Тутиао
Категория: CSS
Посмотреть анализ
Общие решения по адаптации мобильных терминалов
- media queries
- гибкий макет
- rem + viewport
- vh vw
- процент
1. Запросы Мейда
Можно сказать, что метод запросов meida — это метод компоновки, который я использовал в первые дни.Он в основном выполняет различные коды CSS, запрашивая ширину устройства, и, наконец, достигает конфигурации интерфейса.
Основной синтаксис:
@media only screen and (max-width: 374px) {
/* iphone5 或者更小的尺寸,以 iphone5 的宽度(320px)比例设置样式*/
}
@media only screen and (min-width: 375px) and (max-width: 413px) {
/* iphone6/7/8 和 iphone x */
}
@media only screen and (min-width: 414px) {
/* iphone6p 或者更大的尺寸,以 iphone6p 的宽度(414px)比例设置样式 */
}
преимущество:
- Медиа-запрос может оценить соотношение пикселей устройства, метод прост и недорог, особенно при использовании одного и того же набора кода для мобильного терминала и ПК-терминала. В настоящее время такие фреймворки, как Bootstrap, используют этот макет.
- Изображения легко изменить, просто измените файл css.
- Отзывчивое отображение без обновления страницы при настройке ширины экрана
недостаток:
- Объем кода относительно велик, а обслуживание неудобно.
- Чтобы принять во внимание большой экран или устройство высокой четкости, это приведет к пустой трате ресурсов других устройств, особенно ресурсов загрузки изображений.
- Чтобы принять во внимание соответствующие эффекты отзывчивого отображения мобильного терминала и ПК-терминала, неизбежно теряются их уникальные методы взаимодействия.
2. Гибкая гибкая компоновка
Объясните способ реализации Tmall:
Его область просмотра фиксирована:<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
Высота фиксированная, ширина адаптивная, а все элементы в пикселях.
По мере изменения ширины экрана страница также будет меняться, и эффект аналогичен гибкому макету страницы ПК.Когда необходимо отрегулировать ширину, просто используйте адаптивную настройку макета (например, NetEase News), чтобы что "адаптация" достигнута.
3. rem+увеличение видового экрана
Принцип реализации:
Масштабируйте страницу на dpr раз в соответствии с rem, затем установите область просмотра на 1/dpr.
- Например, если dpr iphone6 plus равно 3, общая страница будет увеличена в 3 раза, а 1px (единица css) по умолчанию равна 3px (физический пиксель) при плюсе.
- Затем область просмотра устанавливается на 1/3, так что вся страница сжимается до исходного размера для достижения высокой четкости.
Таким образом, когда вся веб-страница отображается на устройстве, ширина страницы будет равна логическому размеру устройства в пикселях, то есть ширине устройства. Формула для расчета этой ширины устройства:
设备的物理分辨率/(devicePixelRatio * scale), когда масштаб равен 1,device-width = 设备的物理分辨率/devicePixelRatio.
В-четвертых, реализация rem
rem- относительная единица длины,remСтили в схеме разработаны относительно корневого элементаfont-sizeВычисляет кратное значение. Установите в соответствии с шириной экранаhtmlпомеченfont-size, который используется при выкладкеremКомпоновка блока для достижения цели самоадаптации.
исправлено окно просмотра:<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">.
Используйте следующий код для управления эталонным значением rem (фактический размер эскиза измеряется при ширине 720 пикселей)
!(function (d) {
var c = d.document;
var a = c.documentElement;
var b = d.devicePixelRatio;
var f;
function e() {
var h = a.getBoundingClientRect().width,
g;
if (b === 1) {
h = 720;
}
if (h > 720) h = 720; //设置基准值的极限值
g = h / 7.2;
a.style.fontSize = g + "px";
}
if (b > 2) {
b = 3;
} else {
if (b > 1) {
b = 2;
} else {
b = 1;
}
}
a.setAttribute("data-dpr", b);
d.addEventListener(
"resize",
function () {
clearTimeout(f);
f = setTimeout(e, 200);
},
false
);
e();
})(window);
CSS предварительно скомпилирован sass, а значение измерения в px установлено для преобразования переменной rem$px: (1/100)+rem;
преимущество:
- Хорошая совместимость, страница не будет деформироваться из-за масштабирования, а адаптивный эффект лучше.
недостаток:
- Не чисто css решение для мобильной адаптации, нужно вставить абзац в шапку
jsСкрипт прослушивает изменения разрешения, чтобы динамически изменять размер шрифта корневого элемента,cssстиль иjsКод несколько связан и должен быть измененfont-sizeКод помещается вcssперед стилем. - Проблема с десятичными пикселями, наименьшая единица рендеринга браузера — это пиксель, элемент адаптируется в соответствии с шириной экрана, через
remПосле вычисления могут быть десятичные пиксели, и браузер будет округлять эту часть десятичной дроби, отображая ее как целое число, что может быть не столь точным.
Пять, чистая схема vw
Область просмотра — это область браузера, используемая для отображения веб-страниц.
- vw : 1vwравныйШирина области просмотраиз1%
- vh : 1vhравныйВысота области просмотра1% **
- vmin: Выбратьvwа такжеvhсерединаминимумтот самый
- vmax: Выбратьvwа такжеvhсерединамаксимумтот самый
Хотя vw можно приспособить более изящно, все же есть небольшая проблема, то есть нельзя ограничивать ширину и высоту.
$base_vw = 375;
@function vw ($px) {
return ($px/$base_vw) * 100vw
};
преимущество:
- чистый
cssРешение для адаптации мобильного терминала, нет проблемы зависимости скрипта. - относительно
remРазмер элемента определяется кратно размеру шрифта корневого элемента с ясной и простой логикой.
недостаток:
- Есть некоторые проблемы с совместимостью, некоторые браузеры не поддерживают
Шестерка, схема vw+rem
// scss 语法
// 设置html根元素的大小 750px->75 640px->64
// 将屏幕分成10份,每份作为根元素的大小。
$vw_fontsize: 75
@function rem($px) {
// 例如:一个div的宽度为100px,那么它对应的rem单位就是(100/根元素的大小)* 1rem
@return ($px / $vw_fontsize) * 1rem;
}
$base_design: 750
html {
// rem与vw相关联
font-size: ($vw_fontsize / ($base_design / 2)) * 100vw;
// 同时,通过Media Queries 限制根元素最大最小值
@media screen and (max-width: 320px) {
font-size: 64px;
}
@media screen and (min-width: 540px) {
font-size: 108px;
}
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
max-width: 540px;
min-width: 320px;
}
7. Процент
Используйте процент %, чтобы определить ширину и высоту с помощьюpxФиксированный, настроенный в соответствии с размером видимой области в реальном времени, максимально адаптирующийся к различным разрешениям, обычно с использованиемmax-width/min-widthДиапазон размера элемента управления слишком велик или слишком мал.
преимущество:
- Принцип прост и проблем с совместимостью нет
недостаток:
- Если диапазон размеров экрана слишком велик, экран, который слишком велик или слишком мал по отношению к проекту дизайна, не может нормально отображаться, а элементы страницы могут растягиваться и деформироваться в сценарии мобильных телефонов с большим экраном или переключаться между горизонтальными и вертикальные экраны, и размер шрифта не может быть изменен с размером экрана.
- При задании различных свойств блочной модели ссылочные элементы для настройки в процентах не уникальны, что легко усложняет задачу компоновки.
4.var arr =[[‘A’,’B’],[‘a’,’b’],[1,2]]Найти все перестановки и комбинации двумерных массивов Результат: Aa1, Aa2, Ab1, Ab2, Ba1, Ba2, Bb1, Bb2
Компания: Мейтуан
Категория: Алгоритмы
Посмотреть анализ
Реализация эталонного кода
- Первый способ реализации
function foo(arr) {
// 用于记录初始数组长度, 用于将数组前两组已经获取到全排列的数组进行截取标识
var len = arr.length;
// 当递归操作后, 数组长度为1时, 直接返回arr[0], 只有大于1继续处理
if (len >= 2) {
// 每次只做传入数组的前面两个数组进行全排列组合, 即arr[0]和arr[1]的全排列组合
var len1 = arr[0].length;
var len2 = arr[1].length;
var items = new Array(len1 * len2); // 创建全排列组合有可能次数的数组
var index = 0; // 记录每次全排列组合后的数组下标
for (var i = 0; i < len1; i++) {
for (var j = 0; j < len2; j++) {
if (Array.isArray(arr[0])) {
// 当第二次进来后, 数组第一个元素必定是数组包着数组
items[index] = arr[0][i].concat(arr[1][j]); // 对于已经是第二次递归进来的全排列直接追加即可
} else {
items[index] = [arr[0][i]].concat(arr[1][j]); // 这里因为只需要去arr[0]和arr[1]的全排列, 所以这里就直接使用concat即可
}
index++; // 更新全排列组合的下标
}
}
// 如果数组大于2, 这里新的newArr做一个递归操作
var newArr = new Array(len - 1); // 递归的数组比传进来的数组长度少一, 因为上面已经将传进来的数组的arr[0]和arr[1]进行全排列组合, 所以这里的newArr[0]就是上面已经全排列好的数组item
for (var i = 2; i < arr.length; i++) {
// 这里的for循环是为了截取下标1项后的数组进行赋值给newArr
newArr[i - 1] = arr[i];
}
newArr[0] = items; // 因为上面已经将传进来的数组的arr[0]和arr[1]进行全排列组合, 所以这里的newArr[0]就是上面已经全排列好的数组item
// 重新组合后的数组进行递归操作
return foo(newArr);
} else {
// 当递归操作后, 数组长度为1时, 直接返回arr[0],
return arr[0];
}
}
var arr = [
["A", "B"],
["a", "b"],
[1, 2],
];
console.log(foo(arr));
- Способ реализации второй
const getResult = (arr1, arr2) => {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return;
}
if (!arr1.length) {
return arr2;
}
if (!arr2.length) {
return arr1;
}
let result = [];
for (let i = 0; i < arr1.length; i++) {
for (let j = 0; j < arr2.length; j++) {
result.push(String(arr1[i]) + String(arr2[j]));
}
}
return result;
};
const findAll = (arr) =>
arr.reduce((total, current) => {
return getResult(total, current);
}, []);
var arr = [
["A", "B"],
["a", "b"],
[1, 2],
];
console.log(findAll(arr));
- Способ реализации третий
var arr = [
["A", "B"],
["a", "b"],
[1, 2],
];
let res = [],
lengthArr = arr.map((d) => d.length);
let indexArr = new Array(arr.length).fill(0);
function addIndexArr() {
indexArr[0] = indexArr[0] + 1;
let i = 0;
let overflow = false;
while (i <= indexArr.length - 1) {
if (indexArr[i] >= lengthArr[i]) {
if (i < indexArr.length - 1) {
indexArr[i] = 0;
indexArr[i + 1] = indexArr[i + 1] + 1;
} else {
overflow = true;
}
}
i++;
}
return overflow;
}
function getAll(arr, indexArr) {
let str = "";
arr.forEach((item, index) => {
str += item[indexArr[index]];
});
res.push(str);
let overflow = addIndexArr();
if (overflow) {
return;
} else {
return getAll(arr, indexArr);
}
}
getAll(arr, indexArr);
console.log(res);
5. Расскажите о рабочем процессе (процесс разработки, спецификация кода, процесс упаковки и т. д.)
Компания: Тенсент Микровижн
Категория: Инжиниринг
Посмотреть анализ
1. Получите диаграмму прототипа, сначала проанализируйте требования, нарисуйте карту ума, блок-схему
- Прежде чем получить PSD, предоставленный пользовательским интерфейсом, мы можем сначала уточнить наши потребности.
- Зависимые внешние ресурсы
- Интерфейс, предоставляемый серверной частью
- Приблизительный макет рисунка пользовательского интерфейса
- Места, которые часто меняются позже
- Зависимые внешние ресурсы
- эффект, который должен быть достигнут
- Потяните вниз, чтобы обновить
- эффект анимации
- эффект потолка
- Ленивая загрузка, предварительная загрузка, защита от сотрясений, дросселирование
2. Продукт созывает соответствующий персонал проекта, проводит собрание для обсуждения спроса и объясняет прототип продукта.
- Понять потребности продукта и задавать вопросы: что такое функция, как это сделать, почему это сделано
- Оцените сложность реализации и стоимость реализации, а также наличие потенциальных технических проблем/рисков.
- Сравните составленную вами карту спроса и задайте вопросы, если они не совпадают с вашим мнением.
- Понять цель запроса PM на это время, понять, какой контент важен, а какой второстепенный, и может сделать правильный выбор
- Если продукт требует времени, простые проекты могут быть оценены, но сложные проекты не могут быть выделены сразу.Требуется тщательная оценка.Время оценки включает в себя разработку, самотестирование, тестирование тестировщиком, исправление ошибок и подготовку к запуску.
3. Дальнейшее уточнение потребностей после встречи
- Уточняйте детали, разбирайтесь с вопросами, спрашивайте у других о продуктах, дизайне и т. д.
- Оценка времени завершения проекта с учетом факторов: потребность в рабочей силе, требования к посредникам, разработка, самотестирование, тестирование тестировщиком, исправление ошибок, онлайн-подготовка, другие риски (такие как ошибки выбора технологии и т. д.)
- Предварительная разработка графика
4. Вторичное подтверждение требований (если при разработке встречаются неопределенности, все равно необходимо найти соответствующий персонал для подтверждения требований, чтобы предотвратить бесполезные усилия)
- Подтверждение связи с инструментом обмена мгновенными сообщениями
- Подтверждение электронной почты
- Обсуждение небольших потребностей/проектов
- Определить окончательный график
5. Развитие
- Технический отбор
- Настройка среды разработки: набор инструментов
- Построить структуру проекта
- Подразделение бизнес-модуля
- расстановка приоритетов
- Новое вмешательство в проект требует приоритета Pk текущего проекта и соответствующего лица, ответственного за проект вмешательства, а затем корректирует график проекта.
- В процессе разработки обнаруживается серьезное несоответствие между рабочей нагрузкой и ожиданиями, и необходимо как можно скорее дать обратную связь другим сотрудникам проекта, чтобы облегчить их график внесения изменений.
- Индивидуальные спецификации разработки
- спецификация разработки
- формат отправки фиксации:
[改动文件类型]:[改动说明] - Одноотраслевая разработка или многоотраслевая разработка
- Для небольших проектов и нескольких параллельных разработок используйте только основную ветку.
- Средние и крупные проекты, со сложными требованиями и множеством параллельных функций, нужно разделить на master, developer и developer ветки; разработчикам нужно создать ветку для разработки, слить ее в developer, подтвердить, что проблем нет, опубликовать на Мастер, и, наконец, выйти в Интернет
- формат отправки фиксации:
- спецификация кода
- jsconfig.json
.postcssrc.js.babelrc-
.prettierrc(плагин vscode prettier-code fomatter) — обратите внимание, что он должен соответствовать eslint. .editorconfig-
.eslintrc.js(режим принудительной проверки включен)
- управление исходным кодом
- Управление версиями
- управление безопасностью
- спецификация разработки
6. Самопроверка
- ручное тестирование
- модульный тест
- Интеграционное тестирование
Семь, тест - тестер тест
- Разработчики исправляют ошибки
- Потребности, отнимающие много времени, не могут быть удовлетворены в течение периода
- Существуют требования с неопределенными приоритетами, и каждый заказчик должен определить приоритеты друг друга, а затем определить, что делать или не делать, чтобы точка завершения проекта не могла быть отложена.
- Время тестирования и исправления ошибок может быть больше, чем время разработки, поэтому разработчики не могут с оптимизмом смотреть на время разработки.
8. Онлайн
-
Готов к работе А. Заявка на доменное имя б. Заявление о подаче в. Серверное приложение г. Развертывание
-
тестовая онлайн-среда
- Вернуться к исправлению ошибки
-
мониторинг журнала
- стек вызовов
- sourcemap
- локальный журнал
- Пользовательская среда, IP
- недорогой доступ
- Функция статистики
- Функция будильника
9. Техническое обслуживание
- Технологические инновации (оптимизация существующих технических областей и методов реализации конкретных проектов)
- Повышение эффективности: например, развертывание сборки jenkins
- Снизить стоимость
- Улучшить стабильность
- безопасность
6. Могут ли родительские компоненты в Vue отслеживать жизненный цикл дочерних компонентов?
Компания: Водопад
Категория: Вью
Посмотреть анализ
Метод реализации
1. Используйте и излучайте
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
2. Используйте функцию крюка
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},
// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
7. Как определить глобальные методы для Vue
Категория: Вью
Посмотреть анализ
Метод реализации
1. Смонтируйте метод на Vue.prototype
Недостаток: при вызове этого метода нет подсказки.
// global.js
const RandomString = (encode = 36, number = -8) => {
return Math.random() // 生成随机数字, eg: 0.123456
.toString(encode) // 转化成36进制 : "0.4fzyo82mvyr"
.slice(number);
},
export default {
RandomString,
...
}
// 在项目入口的main.js里配置
import Vue from "vue";
import global from "@/global";
Object.keys(global).forEach((key) => {
Vue.prototype["$global" + key] = global[key];
});
// 挂载之后,在需要引用全局变量的模块处(App.vue),不需再导入全局变量模块,而是直接用this就可以引用了,如下:
export default {
mounted() {
this.$globalRandomString();
},
};
Во-вторых, используйте глобальный миксин
Преимущество: потому что методы миксина будут объединены с каждым созданным однофайловым компонентом. Преимущество этого в том, что при вызове этого метода появляется подсказка.
// mixin.js
import moment from 'moment'
const mixin = {
methods: {
minRandomString(encode = 36, number = -8) {
return Math.random() // 生成随机数字, eg: 0.123456
.toString(encode) // 转化成36进制 : "0.4fzyo82mvyr"
.slice(number);
},
...
}
}
export default mixin
// 在项目入口的main.js里配置
import Vue from 'vue'
import mixin from '@/mixin'
Vue.mixin(mixin)
export default {
mounted() {
this.minRandomString()
}
}
3. Используйте метод плагина
Реализация Vue.use не имеет функции монтирования, а только запускает метод установки плагина, который, по сути, по-прежнему использует Vue.prototype.
// plugin.js
function randomString(encode = 36, number = -8) {
return Math.random() // 生成随机数字, eg: 0.123456
.toString(encode) // 转化成36进制 : "0.4fzyo82mvyr"
.slice(number);
}
const plugin = {
// install 是默认的方法。
// 当外界在 use 这个组件或函数的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数。
install: function(Vue){
Vue.prototype.$pluginRandomString = randomString
...
},
}
export default plugin
// 在项目入口的main.js里配置
import Vue from 'vue'
import plugin from '@/plugin'
Vue.use(plugin)
export default {
mounted() {
this.$pluginRandomString()
}
}
В-четвертых, пишите глобальные функции в любой файл vue.
// 创建全局方法
this.$root.$on("test", function () {
console.log("test");
});
// 销毁全局方法
this.$root.$off("test");
// 调用全局方法
this.$root.$emit("test");
8. Расскажите о принципе vm.$set
Компания: Аврора Пуш
Категория: Вью
Посмотреть анализ
vm.$set()какая проблема была решена
В Vue.js только свойства, которые уже существуют в данных, будут рассматриваться как реагирующие данные.Если вы добавите новые свойства, они не станут реагирующими данными, поэтому Vue предоставляет API (vm.$set) для решения этой проблемы. .
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Vue Demo</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
{{user.name}} {{user.age}}
<button @click="addUserAgeField">增加一个年纪字段</button>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
user: {
name: "test",
},
},
mounted() {},
methods: {
addUserAgeField() {
// this.user.age = 20 这样是不起作用, 不会被Observer
this.$set(this.user, "age", 20); // 应该使用
},
},
});
</script>
</body>
</html>
принцип
vm.$set() внедряется в прототип Vue, когда new Vue().
Расположение исходного кода:vue/src/core/instance/index.js
import { initMixin } from "./init";
import { stateMixin } from "./state";
import { renderMixin } from "./render";
import { eventsMixin } from "./events";
import { lifecycleMixin } from "./lifecycle";
import { warn } from "../util/index";
function Vue(options) {
if (process.env.NODE_ENV !== "production" && !(this instanceof Vue)) {
warn("Vue is a constructor and should be called with the `new` keyword");
}
this._init(options);
}
initMixin(Vue);
// 给原型绑定代理属性$props, $data
// 给Vue原型绑定三个实例方法: vm.$watch,vm.$set,vm.$delete
stateMixin(Vue);
// 给Vue原型绑定事件相关的实例方法: vm.$on, vm.$once ,vm.$off , vm.$emit
eventsMixin(Vue);
// 给Vue原型绑定生命周期相关的实例方法: vm.$forceUpdate, vm.destroy, 以及私有方法_update
lifecycleMixin(Vue);
// 给Vue原型绑定生命周期相关的实例方法: vm.$nextTick, 以及私有方法_render, 以及一堆工具方法
renderMixin(Vue);
export default Vue;
- stateMixin()
Vue.prototype.$set = set;
Vue.prototype.$delete = del;
- set()
Расположение исходного кода:vue/src/core/observer/index.js
export function set(target: Array<any> | Object, key: any, val: any): any {
// 1.类型判断
// 如果 set 函数的第一个参数是 undefined 或 null 或者是原始类型值,那么在非生产环境下会打印警告信息
// 这个api本来就是给对象与数组使用的
if (
process.env.NODE_ENV !== "production" &&
(isUndef(target) || isPrimitive(target))
) {
warn(
`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
);
}
// 2.数组处理
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 类似$vm.set(vm.$data.arr, 0, 3)
// 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
//如果不设置length,splice时,超过原本数量的index则不会添加空白项
target.length = Math.max(target.length, key);
// 利用数组的splice变异方法触发响应式, 这个前面讲过
target.splice(key, 1, val);
return val;
}
//3.对象,且key不是原型上的属性处理
// target为对象, key在target或者target.prototype上。
// 同时必须不能在 Object.prototype 上
// 直接修改即可, 有兴趣可以看issue: https://github.com/vuejs/vue/issues/6845
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
// 以上都不成立, 即开始给target创建一个全新的属性
// 获取Observer实例
const ob = (target: any).__ob__;
// Vue 实例对象拥有 _isVue 属性, 即不允许给Vue 实例对象添加属性
// 也不允许Vue.set/$set 函数为根数据对象(vm.$data)添加属性
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== "production" &&
warn(
"Avoid adding reactive properties to a Vue instance or its root $data " +
"at runtime - declare it upfront in the data option."
);
return val;
}
//5.target是非响应式数据时
// target本身就不是响应式数据, 直接赋值
if (!ob) {
target[key] = val;
return val;
}
//6.target对象是响应式数据时
//定义响应式对象
defineReactive(ob.value, key, val);
//watcher执行
ob.dep.notify();
return val;
}
- Вспомогательная функция
// 判断给定变量是否是未定义,当变量值为 null时,也会认为其是未定义
export function isUndef(v: any): boolean %checks {
return v === undefined || v === null;
}
// 判断给定变量是否是原始类型值
export function isPrimitive(value: any): boolean %checks {
return (
typeof value === "string" ||
typeof value === "number" ||
// $flow-disable-line
typeof value === "symbol" ||
typeof value === "boolean"
);
}
// 判断给定变量的值是否是有效的数组索引
export function isValidArrayIndex(val: any): boolean {
const n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val);
}
- О(ob && ob.vmCount)
export function observe(value: any, asRootData: ?boolean): Observer | void {
// 省略...
if (asRootData && ob) {
// vue已经被Observer了,并且是根数据对象, vmCount才会++
ob.vmCount++;
}
return ob;
}
- В процессе инициализации Vue есть
export function initState(vm: Component) {
vm._watchers = [];
const opts = vm.$options;
if (opts.props) initProps(vm, opts.props);
if (opts.methods) initMethods(vm, opts.methods);
if (opts.data) {
//opts.data为对象属性
initData(vm);
} else {
observe((vm._data = {}), true /* asRootData */);
}
if (opts.computed) initComputed(vm, opts.computed);
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
- initData(vm)
function initData(vm: Component) {
let data = vm.$options.data;
data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
// 省略...
// observe data
observe(data, true /* asRootData */);
}
Из исходного кода видно, что основная логика set такова:
- Типовое суждение
- target — это массив: вызовите метод splice
- цель - это объект, а ключ не является обработкой свойства прототипа: напрямую изменить
- цель не может быть экземпляром Vue или корневым объектом данных экземпляра Vue, иначе будет сообщено об ошибке
- Когда целью являются неотзывчивые данные, мы обрабатываем их, добавляя свойства к обычным объектам.
- target - это данные ответа, аключ - это новый атрибут, мы устанавливаем ключ как реактивный и вручную запускаем обновление значения его свойства
Суммировать
vm.$set(target、key、value)
- Когда цель является массивом, непосредственно вызовите реализацию метода сплайсинга массива;
- Если целью является объект, он сначала определит, существует ли атрибут и является ли объект реактивным.
- Наконец, если свойство должно быть обработано реактивно, это делается реактивно, вызывая метод defineReactive.
- Метод defineReactive — это метод, вызываемый Vue для динамического добавления геттеров и сеттеров к свойствам объекта с помощью Object.defineProperty при инициализации объекта.
9. Как глубокое копирование разрешает циклические ссылки?
Компания: Аврора Пуш
Категория: JavaScript
Посмотреть анализ
проблема с круговыми ссылками
посмотреть пример
function deepCopy(obj){
const res = Array.isArray(obj) ? [] : {};
for(let key in obj){
if(typeof obj[key] === 'object'){
res[key] = deepCopy(obj[key]);
}else{
res[key] = obj[key];
}
}
return res
}
var obj = {
a:1,
b:2,
c:[1,2,3],
d:{aa:1,bb:2},
};
obj.e = obj;
console.log('obj',obj); // 不会报错
const objCopy = deepCopy(obj);
console.log(objCopy); //Uncaught RangeError: Maximum call stack size exceeded
Как видно из примера, при циклической ссылке deepCopy сообщит об ошибке и стек переполнится.
- Когда объект obj имеет циклическую ссылку, он не будет переполнять стек при печати.
- При глубоком копировании obj это вызовет переполнение стека
Решение проблем с циклическим приложением
- То есть: обработка ошибок, когда целевой объект существует в круговом приложении.
Все мы знаем, что ключ объекта не может быть объектом.
{{a:1}:2}
// Uncaught SyntaxError: Unexpected token ':'
Эталонное решение 1. Используйте карту недели:
Чтобы решить проблему циклической ссылки, мы можем открыть дополнительное пространство для хранения соответствия между текущим объектом и скопированным объектом.
Это место для хранения должно иметь возможность хранитьkey-valueданные в видеkeyможет быть ссылочным типом,
мы можем выбратьWeakMapЭта структура данных:
- экзамен
WeakMapЕсть ли клонированные объекты? - Да, вернуться напрямую
- Нет, использовать текущий объект как
key, клонировать объект какvalueхранить - продолжать клонировать
function isObject(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function cloneDeep(source, hash = new WeakMap()) {
if (!isObject(source)) return source;
if (hash.has(source)) return hash.get(source); // 新增代码,查哈希表
var target = Array.isArray(source) ? [] : {};
hash.set(source, target); // 新增代码,哈希表设值
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep(source[key], hash); // 新增代码,传入哈希表
} else {
target[key] = source[key];
}
}
}
return target;
}
Эталонное решение два:
Вы можете использовать Set, чтобы найти тот же объект и назначить его напрямую, или вы можете использовать Map
const o = { a: 1, b: 2 };
o.c = o;
function isPrimitive(val) {
return Object(val) !== val;
}
const set = new Set();
function clone(obj) {
const copied = {};
for (const [key, value] of Object.entries(obj)) {
if (isPrimitive(value)) {
copied[key] = value;
} else {
if (set.has(value)) {
copied[key] = { ...value };
} else {
set.add(value);
copied[key] = clone(value);
}
}
}
return copied;
}
10. Как тестируются модульные тесты? Как покрытие кода?
Компания: Программирующий Кот
Категория: Инжиниринг
Посмотреть анализ
1. Почему модульное тестирование?
Наличие поддержки модульного тестирования может гарантировать качество поставляемого кода и повысить уверенность в себе и других. Когда мы выбираем сторонние библиотеки, разве мы не отдаем приоритет гарантиям тестирования? Это также экономит время регрессионного тестирования при внесении изменений в код в будущем.
Во-вторых, как измерить?
При выполнении модульного тестирования постарайтесь сосредоточиться на интеграционном тестировании и выполняйте модульное тестирование небольшого объема кода, который трудно охватить интеграционным тестированием или который необходимо распределить, а также может иметь небольшой объем сквозного тестирования. помощь в тестировании.
Старайтесь не тестировать реализацию кода, тестирование реализации кода может очень быстро сделать тестовый пример недействительным. Например, переменная утверждения не пройдет проверку при изменении имени переменной, но функция может не измениться.
2.1 Какие тесты писать?
Проверяйте функциональность программы с точки зрения пользователя, а не с точки зрения бога.
Для компонента, передающего различные параметры для рендеринга dom, пользователь может увидеть определенный текст или выполнить некоторые операции. На этом этапе можно утверждать, появляются ли определенные слова в dom и есть ли правильный ответ на действия (такие как щелчок, ввод, отправка формы и т. д.).
2.2 Какие тесты не пишут?
Не проверяйте детали реализации. например, рассматривать с точки зрения Богаredux storeданные о ,stateданные и т. д., которых не существует в глазах конечного пользователя, а то, что пользователь может воспринимать, является лишь отображаемой функцией.
3. Тестовая среда и поддержка периферийных устройств
Jestэто среда тестирования, созданная facebook. Из коробки он поставляется с библиотекой утверждений, макетом, отчетом о покрытии и другими функциями.
Поскольку передняя часть должна тестировать пользовательский интерфейс и визуализировать DOM в моделируемой среде браузера, такая библиотека необходима. Таких библиотек много, обычно используютсяEnzyme,@testing-library/react.
4. Отчеты о тестовом покрытии/эффективности
JestПоставляется с отчетом о тестировании, но многие проекты разбросаны по гитлабу, что доставляет проблемы с просмотром отчета. Подумайте о централизованном месте для просмотра отчетов о тестировании. объединены здесьsonar а также reportportalСобирайте отчеты о тестировании, вы можете просматривать отчеты о тестировании всех проектов в одном централизованном месте.
который сочетает в себеsonarФункция сканирования кода может просматривать информацию отчета, такую как тестовое покрытие.reportportalВы можете просмотреть скорость выполнения тестов.Кроме того, официальное объявление поставляется с аналитическим отчетом AI, который может отображать многомерную статистическую информацию.
11. Расскажите о повторном использовании логики состояния React
Компания: Программирующий Кот
Категория: Реагировать
Посмотреть анализ
Повторное использование логики состояния реакции
1. Миксины
Хотя сам React имеет несколько функциональный оттенок, чтобы удовлетворить привычки пользователей, в первые дни для определения компонентов был предоставлен только API React.createClass(): естественно, наследование (класса) стало интуитивной попыткой. В режиме расширения JavaScript, основанном на прототипах, схема примесей, подобная наследованию, становится первым выбором:
// 定义Mixin
var Mixin1 = {
getMessage: function () {
return "hello world";
},
};
var Mixin2 = {
componentDidMount: function () {
console.log("Mixin2.componentDidMount()");
},
};
// 用Mixin来增强现有组件
var MyComponent = React.createClass({
mixins: [Mixin1, Mixin2],
render: function () {
return <div>{this.getMessage()}</div>;
},
});
Но есть много недостатков
Существует неявная зависимость между компонентом и примесью (примесь часто зависит от определенного метода компонента, но не знает об этой зависимости, когда компонент определен) несколько примесей могут конфликтовать (например, определение одного и того же поля состояния) Тенденции примесей Чем больше состояний в вашем приложении, тем сложнее об этом рассуждать Чем больше состояний в вашем приложении, тем сложнее об этом рассуждать.
Неявные зависимости приводят к непрозрачным зависимостям, а затраты на обслуживание и понимание быстро растут: трудно быстро понять поведение компонентов, и необходимо полностью понять все поведения расширений, которые зависят от Mixin и их взаимодействия. Собственные методы компонента и поля состояния удалить непросто, потому что сложно определить, есть ли миксин, зависящий от него. трудно понять ввод и вывод Mixin.
В том, что эти проблемы фатальные, сомнений нет, поэтому React v0.13.0 отказался от Mixin (наследование) и двинулся в сторону HOC (состав).
2. Компоненты высшего порядка
// 定义高阶组件
var Enhance = (ComposedComponent) =>
class extends Component {
constructor() {
this.state = { data: null };
}
componentDidMount() {
this.setState({ data: "Hello" });
}
render() {
return <ComposedComponent {...this.props} data={this.state.data} />;
}
};
class MyComponent {
render() {
if (!this.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
// 用高阶组件来增强普通组件,进而实现逻辑复用
export default Enhance(MyComponent);
Теоретически любая функция, которая принимает параметр типа компонента и возвращает компонент, является компонентом более высокого порядка ((Component, ...args) => Component), но для удобства совмещения рекомендуетсяComponent => ComponentФорма HOC, передающая другие параметры через приложение частичной функции, например: React Reduxconnect const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
преимущество:
- Древовидная структура компонентов снижает связанность и сложность;
- Повторное использование кода, логическая абстракция
- Перехват рендеринга, проксирование свойств, перехват реквизитов и состояния компонентов
- Декоратор, который можно использовать как декоратор;
- каррирование функций
Недостатки: Хотя у HOC не так много фатальных проблем, но есть и небольшие недостатки:
- Ограничения масштабируемости
- Не используйте его в рендеринге.Каждый рендеринг будет воссоздавать компонент более высокого порядка, что приводит к потере состояния компонентов и подкомпонентов и влияет на производительность;
- Статические методы будут потеряны, у новых компонентов нет статических методов и их нужно обрабатывать вручную;
- refs не будут переданы, вам нужно использовать forwardRef
- Множественное вложение, возрастающая сложность и стоимость понимания;
- Если пространства имен не используются, могут возникнуть конфликты имен, перезаписывающие старые свойства;
- Невидимость, неизвестно что снаружи, черный ящик;
3. Рендер реквизит
«поддержка рендеринга» относится к простой технике совместного использования кода между компонентами React с использованием реквизита, значением которого является функция;
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY,
});
}
render() {
return (
<div style={{ height: "100%" }} onMouseMove={this.handleMouseMove}>
{/* Instead of providing a static representation of what <Mouse> renders, use the `render` prop to dynamically determine what to render. */}
{this.props.render(this.state)}
</div>
);
}
}
Преимущества: совместное использование данных, повторное использование кода Использование, в состоянии сборки передаются в передачу Использование Реквизит, логика рендеринга для настройки Использование по
Недостатки: доступ к данным вне оператора return невозможен, а вложенная запись недостаточно элегантна;
4. Крючки
function MyResponsiveComponent() {
const width = useWindowWidth();
// Our custom Hook
return <p>Window width is {width}</p>;
}
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
});
return width;
}
По сравнению с другими решениями, упомянутыми выше, Hooks делает повторное использование логики внутри компонентов более не привязанным к повторному использованию компонентов и действительно пытается решить проблему повторного использования мелкозернистой логики (между компонентами) с нижнего уровня.
Кроме того, эта схема повторного использования декларативной логики еще больше расширяет идею явного потока данных и композиции между компонентами в компоненты, что соответствует концепции React.
Преимущества заключаются в следующем:
- Решить проблему вложенности, лаконично, меньше кода: React Hooks решают проблему вложенности HOC и Render Props, более лаконично
- Развязка: React Hooks может более легко отделить пользовательский интерфейс и состояние для более тщательной развязки.
- Комбинация: хуки могут ссылаться на другие хуки, чтобы сформировать новые хуки, и комбинация может варьироваться.
- Решите 3 проблемы компонентов класса: React Hooks рождаются для функциональных компонентов, тем самым решая несколько основных проблем компонентов класса:
- это указывает на подверженность ошибкам
- Бизнес-логика разделена на разные циклы объявлений, что затрудняет понимание и поддержку кода.
- Высокая стоимость повторного использования кода (компоненты более высокого порядка могут легко увеличить объем кода)
Крючки не идеальны, но пока их недостатки следующие:
- Также есть две функции жизненного цикла компонентов класса, которые нельзя заменить хуками: getSnapshotBeforeUpdate и componentDidCatch.
- Дополнительные затраты на обучение (путаница между функциональным компонентом и компонентом класса)
- Существуют ограничения на написание (нельзя использовать в условиях, циклах и вложенных функциях) и можно использовать только на верхнем уровне функций, что увеличивает стоимость рефакторинга старого кода, потому что для обновления react нужно использовать вызывающую последовательность функции состояния и вызова, поместите его в циклы или условные переходы, вызывающая последовательность может быть непоследовательной, что приводит к странным ошибкам;
- Уничтожен эффект оптимизации производительности поверхностного сравнения PureComponent и React.memo (чтобы получить последние реквизиты и состояние, каждый render() должен воссоздавать функцию события)
- В сценарии закрытия старые значения состояния и реквизита могут ссылаться на внутреннюю реализацию, не интуитивно понятную (полагаясь на изменяемое глобальное состояние, уже не такое «чистое»)
- React.memo не может полностью заменить shouldComponentUpdate (потому что он не может получить изменение состояния, только для изменения реквизита)
- Дизайн useState API не идеален
- При использовании useState объект массива напрямую обновляется с помощью push, pop и splice, что недопустимо; например
let [nums, setNums] = useState([0,1,2]); nums.push(1) 无效,必须使用 nums=[...nums, 1], а затем setNums(nums); вставить напрямую в компонент класса не проблема - Не могу использовать декораторы
- Ссылка функционального компонента требует forwardRef
12. Каковы принципы проектирования библиотеки компонентов?
Компания: Программирующий Кот
Категория: JavaScript
Посмотреть анализ
Принципы проектирования библиотеки компонентов
1.1 Стандартизация
Любой компонент должен соответствовать набору стандартов, чтобы разработчики в разных регионах могли разработать набор стандартных и унифицированных компонентов по этому стандарту
1.2 Независимость
- Описывает мелкозернистые компоненты компонентов, следует принципу единой ответственности и поддерживает чистоту компонентов.
- API-интерфейсы, такие как конфигурация атрибутов, открыты для внешнего мира, а внутреннее состояние компонентов закрыто для внешнего мира, чтобы как можно меньше быть связанным с бизнесом.
1.3 Повторное использование и простота использования
- Различия пользовательского интерфейса, переработанные внутри компонента (обратите внимание, что это не набор if/else)
- Ввод и вывод удобный, простой в использовании
1.4 Применение правила SPOT
Единственная истина заключается в том, чтобы стараться не повторять код из книги «Искусство программирования в Unix».
1.5 Избегайте раскрытия внутренней реализации компонента
1.6 Избегайте прямых манипуляций с DOM, избегайте использования ref
Используйте состояние родительского компонента для управления состоянием дочернего компонента вместо прямого управления дочерним компонентом через ref
1.7 Проверяем правильность параметров на входе, а корректность возврата проверяем на выходе
1.8 Принцип ациклической зависимости (ADP)
1.9 Принцип стабильной абстракции (SAP)
- Степень абстракции компонента пропорциональна степени его стабильности,
- Стабильный компонент должен быть абстрактным (логически не связанным)
- Нестабильный компонент должен быть конкретным (логически связанным)
- Чтобы уменьшить связь между компонентами, нам нужно программировать для абстрактных компонентов, а не для бизнес-реализации.
1.10 Избегайте избыточных состояний
- Если данные могут быть преобразованы другим состоянием, то данные не являются состоянием, вам нужно только написать функцию обработки преобразования, вы можете использовать вычисляемые свойства в Vue
- Если данные являются фиксированными, постоянными, которые не изменяются, то эти данные подобны фиксированному заголовку сайта в HTML, жестко запрограммированы или используются в качестве атрибута глобальной конфигурации и т. д. и не относятся к состоянию.
- Если одноуровневые компоненты имеют одинаковое состояние, это состояние должно быть помещено на более высокий уровень и передано обоим компонентам с помощью свойств.
1.11 Разумные зависимости
Родительский компонент не зависит от дочернего компонента, и удаление дочернего компонента не приведет к неправильной работе.
1.12 Параметры сглаживания
В дополнение к данным избегайте сложных объектов и старайтесь принимать значения только примитивных типов.
1.13 Хороший дизайн интерфейса
- Максимизируйте работу, которую можно выполнить внутри компонента.Хотя внесение изменений рекомендуется, чем больше интерфейсов, тем лучше.
- Если константы становятся реквизитами для работы с большим количеством сценариев, их можно использовать в качестве реквизитов, а исходные константы можно использовать в качестве значений по умолчанию.
- Если вам нужно написать много кода для конкретных нужд вызывающей стороны, вы можете рассмотреть возможность создания нового компонента путем его расширения.
- Убедитесь, что свойств и событий компонента достаточно для использования большинства компонентов.
1.14 API должен максимально согласовываться с известными концепциями.
13. Запишите выходное значение кода и объясните, почему
function F() {
this.a = 1;
}
var obj = new F();
console.log(obj.prototype);
Компания: Футу
Категория: JavaScript
Посмотреть анализ
Отвечать
undefined
Эталонный анализ
Экземпляры конструктора обычно не имеют свойства прототипа. Кроме конструктора функций
Атрибут прототипа есть только у функций, и значением этого атрибута является объект объекта.Такого атрибута нет, когда объект экземпляра
Объекты-экземпляры передают внутреннее свойство __proto__ ([[prototype]]) составить цепочку прототипов, через которую можно найти свойства,
Метод Когда объект-функция инициализируется с помощью оператора new, создается объект-экземпляр.
Объект, на который указывает свойство прототипа объекта функции, является объектом-прототипом объекта-экземпляра, то есть объектом, на который указывает __proto__
Классические и прототипные цепные диаграммы
14. Что будет, если вставить в середину html код из 100 000 циклов? Как решить застрявший феномен? Когда скрипт будет выполнен после добавления атрибута defer? Что происходит после принятия отсрочки, когда пользователь нажимает на страницу? Есть ли другой способ отключить WebWoker?
Компания: Футу
Категория: JavaScript
Посмотреть анализ
1. В тело вставляется 100 000 раз зацикленный код, и страница застревает
100 000 раз код зацикливания вставляется в тело, страница зависает, и узел DOM после кода не может быть загружен
2. Решить
Установите атрибут defer тега script, другие потоки браузера будут загружать скрипт, и скрипт не будет выполняться, пока не будет завершен парсинг документа.
3. После принятия отсрочки пользователь нажимает на проблему
-
Если событие щелчка в кнопке определено до сценария отсрочки, на событие щелчка будет отреагировать после загрузки сценария отсрочки.
-
Если событие щелчка на кнопке определено после скрипта отсрочки, не будет ответа, когда пользователь нажмет кнопку.После загрузки скрипта будет ответ, когда пользователь снова нажмет.
-
пример кода
<!-- test.html -->
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div class="test1">test1</div>
<div id="hello"></div>
<script>
// 待defer脚本下载完成后响应
function alertMsg() {
alert("123");
}
</script>
<input type="button" id="button1" onclick="alertMsg()" />
<script src="./test.js" defer></script>
<div class="test2">test2</div>
</body>
<style>
.test1 {
color: red;
font-size: 50px;
}
.test2 {
color: yellow;
font-size: 50px;
}
</style>
</html>
// test.js
for (let i = 0; i < 100000; i++) {
console.log(i);
}
document.getElementById("hello").innerHTML = "hello world";
4. Есть ли другой способ, если WebWoker отключен?
4.1 Использование Concurrent.Thread.js
- Concurrent.Thread.js используется для имитации многопоточности для многопоточной разработки.
Concurrent.Thread.create(function () {
$("#test").click(function () {
alert(1);
});
for (var i = 0; i < 100000; i++) {
console.log(i);
}
});
4.2 Использование виртуальных списков
Если необходимо отобразить 100 000 фрагментов данных, можно использовать виртуальный список. Виртуальный список рендерит только данные в видимой области, так что в случае огромного количества данных рендеринг DOM уменьшается, а список можно прокручивать плавно и бесконечно.
План реализации:
На основе виртуального списка есть функция рендеринга видимой области, нам нужно сделать следующие три вещи
- Необходимо рассчитать высоту пустой области вверху и внизу и поддерживать высоту всего списка, чтобы сделать его такой же высоты, как если бы данные не усекались Эти две высоты называются topHeight и bottomHeight соответственно.
- Рассчитайте начальную позицию усечения и конечную позицию, тогда данные в видимой области будут list.slice(start,end)
- В процессе прокрутки topHeight, bottomHeight, start и end должны постоянно обновляться, чтобы обновлять вид видимой области. Конечно, нам нужно сравнить старое начало и конец, чтобы определить, нужно ли его обновлять.
Вычисление topHeight относительно простое, то есть на какую высоту прокручивается, topHeight=scrollTop.
Вычисление start зависит от topHeight и высоты itemHeight каждого элемента Предположим, мы переместились вверх на два элемента списка, тогда start равно 2, поэтому у нас естьstart = Math.floor(topHeight / itemHeight).
Вычисление конца зависит от того, сколько элементов списка может быть отображено по высоте экрана, мы называем это visibleCount, тогда естьvisibleCount = Math.ceil(clientHeight / itemHeight), округление делается для того, чтобы вычисление не было слишком маленьким и на экране не отображалось достаточное количество контента, тогдаend = start + visibleCount.
bottomHeight требует, чтобы мы знали высоту всего списка, прежде чем он будет усечен, вычесть высоту его вершины и вычислить высоту вершины. С end это очень просто. Предполагая, что количество элементов всего нашего списка равно totalItem , тогдаbottomHeight = (totalItem - end - 1) \* itemHeight.
Проблемы, которые возникнут:
Но при такой реализации возникают две проблемы:
- При прокрутке вверху или внизу видимой области будет пустое пространство.
- Каждый раз, когда вы прокручиваете до точки, где вам нужно заменить пустое пространство фактическим элементом списка, страница будет трястись. Причина в том, что высота каждого элемента списка несовместима. Когда вы хотите заменить, замененный элемент списка больше или меньше, чем itemHeight, и находится в поле Если заменить в видимой области, браузер будет трястись.Это решение можно сделать, опережая время замены, то есть вверху нашего невидимого верха.
Чтобы проанализировать, для первого вопроса будет пустая ситуация, тогда мы можем зарезервировать определенную позицию вверху или внизу, а для второго вопроса мы также можем зарезервировать определенное место вверху и внизу, поэтому есть только одно решение для решения этой проблемы, то есть зарезервировать определенную позицию для верха и низа.
Предположим, что ReserveTop — это количество позиций, зарезервированных для топа, а ReserveBottom — это количество позиций, зарезервированных для низа, тогда расчет приведенных выше данных будет переопределен, подробнее см. рисунок ниже.
ReserveTop и ReserveBottom должны быть как можно больше (конечно, не слишком большими), или, если вы знаете максимальную высоту элемента списка, просто используйте эту максимальную высоту. Когда вы обнаружите, что при прокрутке вверху есть пустое пространство, увеличьте значение ReserveTop.Когда вы обнаружите, что при прокрутке внизу есть пространство, увеличьте значение ReserveBottom.
15.Есть 100 бутылок с водой,одна из которых ядовитая.Мыши умрут через 3 дня отведав немного ядовитой воды.По крайней мере,сколько мышей нужно,чтобы за 3 дня определить какая бутылка воды ядовитая?
Компания: Футу
Категория: Другое
Посмотреть анализ
Отвечать
7 只
анализировать
Каждая мышь имеет только 2 состояния, мертвая или живая, поэтому каждую мышь можно рассматривать как бит, а 0 или 1N мышей можно рассматривать как N битов, которые могут выражать 2 ^ N состояний (где n-е состояние представляет n-е состояние) , бутылка ядовита), поэтому количество состояний, которые могут представлять все крысы, может быть больше или равно 100.
Код
let n = 1;
while (Math.pow(2, n) < 100) {
n++;
}
console.log(n);
Простое понимание:
Промаркируйте 100 бутылок следующим образом (длиной 7 цифр): 0000001 (1-я бутылка) 0000010 (2-й флакон) 0000011 (3-я бутылка) ...... 1100100 (100 бутылка)
Возьмите по капле из всех флаконов, у которых последняя цифра 1, и смешайте их вместе (например, возьмите капли из первого флакона, третьего флакона, ... и смешайте их вместе) и пометьте это как 1. И так далее, возьмите по 1 капле из всех бутылочек, у которых первая цифра 1, перемешайте их и обозначьте как 7. Теперь возьмите смесь из 7 чисел, мышей ряд и ряд, и обозначьте их цифрами 7, 6 соответственно. . . №1, и заполнить их соответствующим количеством смесей соответственно. Прошло три дня, приходите и делайте вскрытие:
Слева направо мертвые мыши помечаются 1, мертвые мыши помечаются 0, и, наконец, они получают порядковый номер, который заменяется десятичным числом, которое является номером ядовитой бутылки с водой.
Проверьте это: если первая бутылка ядовита, согласно 0000001 (первая бутылка), это означает, что смесь № 1 ядовита, поэтому символ жизни и смерти мыши - 0000001 (мышь с номером 1 умерла) , а двоичная метка 0000001 преобразуется в десятичную = бутылка №1 ядовита, если третья бутылка ядовита, 0000011 (бутылка 3), то смесь №1 и №2 ядовита, поэтому жизнь мыши и символ смерти 0000011 (братья мыши с номерами 1 и 2 мертвы) , двоичная метка 0000011 преобразована в десятичную = бутылка номер 3 ядовита.
Таким образом, результат 2^7 = 128 >= 100, что требует как минимум 7 мышей.
16. Promise.allSettled понял? Рукописное обещание.allSettled
Компания: Куайшоу
Категория: JavaScript
Посмотреть анализ
Promise.allSettled(iterable)концепция
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
-
Promise.allSettled()метод принимает наборPromiseЭкземпляр в качестве параметра возвращает новый экземпляр Promise. - Просто подождите, пока все эти экземпляры параметров вернут результаты, либо
fulfilledещеrejected, экземпляр оболочки завершится. - вернуть новый
Promiseнапример, после завершения состояние всегдаfulfilled, не станетrejected. - новый
PromiseПримеры передачи массива для мониторинга функцииresults. Каждый член массива является объектом, соответствующим входящемуPromise.allSettledЭкземпляр Promise. Каждый объект имеет свойство состояния, соответствующееfulfilledа такжеrejected.fulfilledКогда объект имеетvalueАтрибуты,rejectedиногдаreasonсвойство, соответствующее возвращаемому значению двух состояний. - Иногда нас не волнуют результаты асинхронных операций, а только если эти операции закончились.Этот метод полезен.
рукописная реализация
const formatSettledResult = (success, value) =>
success
? { status: "fulfilled", value }
: { status: "rejected", reason: value };
Promise.all_settled = function (iterators) {
const promises = Array.from(iterators);
const num = promises.length;
const resultList = new Array(num);
let resultNum = 0;
return new Promise((resolve) => {
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
resultList[index] = formatSettledResult(true, value);
if (++resultNum === num) {
resolve(resultList);
}
})
.catch((error) => {
resultList[index] = formatSettledResult(false, error);
if (++resultNum === num) {
resolve(resultList);
}
});
});
});
};
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
Promise.all_settled([resolved, rejected]).then((results) => {
console.log(results);
});
17. Почему браузеры блокируют междоменные запросы? Как решить междомен? Должен ли каждый запрос из разных источников достигать сервера?
Компания: Куайшоу
Категория: JavaScript
Посмотреть анализ
1. Что такое междоменный
Кросс-происхождение — это термин, обозначающий «политику одного и того же происхождения» браузера. Причина использования модели «политики того же источника» основана на соображениях сетевой безопасности. Так называемая политика того же происхождения сосредоточена на трех моментах:
- Соглашение (
http:www.baidu.com & https.www.baidu.com httpРазные протоколы, междоменные) - доменное имя (
https://www.aliyun.com & https://developer.aliyun.comразные доменные имена, междоменные) - порт (
http://localhost:8080 & http://localhost:8000Разные номера портов, междоменные)
2. Какие сетевые ресурсы включают междоменные
«Политика того же происхождения» очень понятна для настройки междоменных сетевых ресурсов.
Эти сценарии включают запрещенные операции между доменами:
- Невозможно получить файлы cookie, localstorage и indexedDB для веб-страниц с разными источниками.
- Невозможно получить доступ к DOM (iframe) веб-страниц из разных источников.
- Не удается отправить запросы AJAX или запросы на выборку на разные адреса (могут быть отправлены, но браузер отказывается принимать ответ).
Зачем блокировать междоменные? Выше мы упоминали, что это основано на политиках безопасности: например, страница вредоносного веб-сайта встраивает страницу входа в банк через iframe (два из разных источников), время для получения имени пользователя и пароля.
3. Как решить кроссдоменность
Как мы решаем проблему скрещивания, основные решения заключаются в следующем:
1. Кроссдоменный через jsonp 2. междоменный домен document.domain + iframe 3. location.hash + iframe 4. междоменное имя окна + iframe 5. междоменное сообщение postMessage 6. Совместное использование ресурсов между источниками (CORS) 7, междоменный прокси nginx 8. Междоменный прокси промежуточного ПО Nodejs 9. Междоменный протокол WebSocket
4. Вопросы, которые необходимо прояснить в отношении междоменного доступа
Междоменный не означает, что браузер ограничивает инициирование межсайтовых запросов, но что межсайтовые запросы могут инициироваться нормально, но возвращаемый результат перехватывается браузером.
Каждый раз, когда отправляется запрос, сторона сервера также будет отвечать, но сторона браузера будет перехватывать на основе политики того же источника при получении ответа.
Примечание. Некоторые браузеры не разрешают междоменный доступ к HTTP из доменов HTTPS, например Chrome и Firefox, и эти браузеры блокируют запросы до их отправки, что является особым случаем.
18. Вы понимаете кеш браузера? Где обычно хранится сильный кеш? Вычисление всего файла для получения etag будет потреблять производительность, как это решить? Что делать, если я не хочу использовать кеш и каждый раз запрашивать последнюю версию? В чем разница между no-store и no-cache?
Компания: Куайшоу
Категория: Сеть и безопасность
Посмотреть анализ
1. Кэш браузера
Кэширование браузера в основном делится на четыре этапа:
- Фаза принудительного кэширования: Сначала найдите ресурс локально, и, если ресурс найден и срок действия ресурса не истек, используйте этот ресурс, вообще не отправляя HTTP-запрос на сервер.
- Этап согласования кэша: Если соответствующий ресурс найден в локальном кеше, но вы не знаете, срок действия ресурса истек или истек, отправьте http-запрос на сервер, и сервер оценит запрос.Если запрошенный ресурс не был изменен на сервере он вернет 304, пусть браузер использует ресурс, который он находит локально.
- Этап эвристического кэширования: когда поле времени истечения срока действия кэша отсутствует, браузер не будет напрямую переходить к этапу согласования в следующий раз, а сначала перейдет к этапу эвристического кэширования.В качестве периода кэширования берется 10% разницы во времени. То есть, когда есть поле Last-Modified, даже если сеть отключена и сильный кеш недействителен, файл кеша будет считываться напрямую в течение определенного периода времени. etag не имеет этого этапа.
- фаза сбоя кэша: Когда сервер обнаруживает, что запрошенный ресурс был изменен, или это новый запрос (ресурс не был найден изначально), сервер возвращает данные ресурса и возвращает 200. Конечно, это относится к случаю, когда ресурс найден, если на сервере такого ресурса нет, возвращается 404.
2. Где обычно размещается сильный кэш?
Сильный кеш обычно хранится в кеше памяти или кеше диска.
3. Вычисление всего файла для получения etag будет потреблять производительность, как это решить
etag может быть вычислен из Last-Modified и content-length файла.
Официальный метод расчета ETag по умолчанию для Nginx: «время последней модификации файла в шестнадцатеричном формате — длина файла в шестнадцатеричном формате». Пример: ETag: "59e72c84-2404"
Уведомление:
Независимо от того, какой алгоритм, расчет должен выполняться на стороне сервера, и расчет будет иметь накладные расходы и потерю производительности. Поэтому, чтобы немного снизить производительность, многие веб-сайты полностью отключают Etags (например, Yahoo!), что на самом деле не соответствует положениям HTTP/1.1, поскольку HTTP/1.1 всегда поощряет серверы включать Etags как можно чаще. насколько это возможно.
В-четвертых, не используйте метод кэширования, чтобы каждый запрос был последним
Обычный способ не использовать кеш — это объединить случайные URL-адреса или установить Cache-Control, чтобы установить отсутствие кеша.
Пять, без строев и без кеша
- no-store отключает кеширование браузера и промежуточного сервера. Получайте его с сервера каждый раз.
- Обратите внимание, что no-store — это реальное полное отключение локального кэширования.
- no-cache Каждый запрос будет проверять, что кеш устарел. Он может кэшироваться локально или кэшироваться на прокси-сервере, но этот кеш можно использовать только после аутентификации на сервере.
- Обратите внимание, что отсутствие кэширования не означает отсутствие кэширования.
19. Расскажите, как обычно оптимизируется проект? Как он измеряется после оптимизации? Как рассчитывается экранное время?
Компания: Куайшоу
Категория: Другое
Посмотреть анализ
Оптимизирован для каждого процесса
Веб-страницы проходят ряд процессов от загрузки до рендеринга, оптимизируя каждый процесс.
- интернет-соединение
- запросить оптимизацию
- Отзывчивая оптимизация
- рендеринг в браузере
пройти черезperformance.timingAPI, вы можете получить время выполнения каждого этапа:
{
navigationStart: 1578537857229; //上一个文档卸载(unload)结束时的时间戳
unloadEventStart: 1578537857497; //表征了unload事件抛出时的时间戳
unloadEventEnd: 1578537857497; //表征了unload事件处理完成时的时间戳
redirectStart: 0; // 重定向开始时的时间戳
redirectEnd: 0; //重定向完成时的时间戳
fetchStart: 1578537857232; //准备好HTTP请求来获取(fetch)文档的时间戳
domainLookupStart: 1578537857232; //域名查询开始的时间戳
domainLookupEnd: 1578537857232; //域名查询结束的时间戳
connectStart: 1578537857232; //HTTP请求开始向服务器发送时的时间戳
connectEnd: 1578537857232; //浏览器与服务器之间的连接建立时的时间戳
secureConnectionStart: 0; //安全链接的握手时的U时间戳
requestStart: 1578537857253; //HTTP请求(或从本地缓存读取)时的时间戳
responseStart: 1578537857491; //服务器收到(或从本地缓存读取)第一个字节时的时间戳。
responseEnd: 1578537857493; //响应结束
domLoading: 1578537857504; //DOM结构开始解析时的时间戳
domInteractive: 1578537858118; //DOM结构结束解析、开始加载内嵌资源时的时间戳
domContentLoadedEventStart: 1578537858118; //DOMContentLoaded 事件开始时间戳
domContentLoadedEventEnd: 1578537858118; //当所有需要立即执行的脚本已经被执行(不论执行顺序)时的时间戳
domComplete: 1578537858492; //当前文档解析完成的时间戳
loadEventStart: 1578537858492; //load事件被发送时的时间戳
loadEventEnd: 1578537858494; //当load事件结束时的时间戳
}
1.1 Оптимизация сетевого подключения
В основном оптимизирован для перенаправления, DNS, TCP-соединений
- Избегайте перенаправлений
- Оптимизация поиска DNS: страницы используют предварительное разрешение
dns-prefetch, и объединяйте ресурсы одного типа, чтобы уменьшитьdomainЧисло также может уменьшить количество запросов DNS. - Используйте CDN (сеть доставки контента)
- Версия HTTP/1.1, клиент может пройти
Keep-AliveОпция устанавливает постоянное соединение с сервером, позволяя передавать несколько ресурсов по одному TCP-соединению.
1.2 Оптимизация запроса
уменьшатьбраузер в браузерколичество запросовтак же какРазмер запрашиваемого ресурсаоптимизирован под запросыГлавная мысль
- Разумное использование сжатия и слияния файлов
- Разумно используйте функцию браузера для параллельной загрузки ресурсов и найдите разумный баланс между количеством загружаемых ресурсов и их размером.
- На мобильной странице запрашиваемые ресурсы на первом экране контролируются в пределах 5, а размер каждого ресурса после Gzip контролируется в пределах 28,5 КБ, что может значительно улучшить время в верхней части страницы.
- Сжимайте изображения, используйте изображения спрайтов, используйте встроенный Base64 для небольших изображений.
- Ленивая загрузка компонента
- Печенье для похудения
- Статические ресурсы размещаются в домене, отличном от текущего домена, с использованием CDN и других методов, чтобы избежать переноса файлов cookie при запросе статических ресурсов.
- Эффективно используйте CDN, чтобы улучшить возможности загрузки ресурсов браузера.
- Ресурсы распределяются по нескольким различным CDN, что максимально увеличивает возможности параллельной загрузки браузера.
- Разумное использование стратегий кэширования для кэширования статических ресурсов, ответов Ajax и т. д.
- Используйте манифест + локальное хранилище для постоянного кэширования
- Другие ресурсы, не требующие высокого уровня доступа в реальном времени, такие как изображения и рекламные сценарии, хранятся в IndexDB или WebSQL.После IndexDB объем хранилища WebSQL намного больше, чем у LocalStorage, который можно использовать для хранения этих ресурсов. .
- Постоянный кеш с использованием операции localForage
- Поместите файлы библиотеки в CDN или включите сильное кэширование
1.3 Оптимизация ответа
- Оптимизация потока обработки на стороне сервера, если использоватьКэширование, оптимизация запросов к базе данных, сокращение времени выполнения запросовЖдать
- Оптимизация размера ресурса ответа, если правильноРесурсы ответа включают сжатие GzipЖдать.
1.3.1 Основные показатели загрузки страницы
- TTFB
- первый байт
- FP
- Первый рисунок, только div и node, соответствующие созданному жизненному циклу vue
- FCP
- Впервые есть отрисовка контента, основной фрейм страницы, но нет контента данных, соответствующего смонтированному жизненному циклу vue.
- FMP
- Первый осмысленный рисунок, включающий все элементы и содержимое данных, соответствующий обновленному жизненному циклу vue.
- TTI
- время первого взаимодействия
- Long Task
>=50ms 的任务
- SSR&CSR
- Рендеринг на стороне сервера и рендеринг на стороне клиента
- Isomorphic javascript
- изоморфизм
1.4 Оптимизация рендеринга первого экрана браузера
1.4.1 Время выше сгиба:
Относится ко времени с момента, когда пользователь открывает веб-сайт, до завершения отображения содержимого на первом экране браузера. Для взаимодействия с пользователем время в верхней части страницы является важным фактором для пользователей, чтобы они испытали веб-сайт. Обычно веб-сайт, если первое экранное время составляет менее 5 секунд, является относительно хорошим, менее 10 секунд приемлемым, а более 10 секунд недопустимым. Свыше 10 секунд вверху страницы пользователи решат обновить страницу или немедленно уйти.
1.4.2 Расчет времени выше сгиба:
- Надпись на этикетке модуля сгиба
Обычно это подходит для ситуации, когда контенту первого экрана не нужно извлекать данные, чтобы выжить, и страница не учитывает загрузку ресурсов, таких как изображения.Мы будем использовать встроенный код JavaScript для записи текущей метки времени в HTML-документ, соответствующий концу метки содержимого первого экрана. Следующим образом:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>首屏</title>
<script type="text/javascript">
window.pageStartTime = Date.now();
</script>
<link rel="stylesheet" href="common.css" />
<link rel="stylesheet" href="page.css" />
</head>
<body>
<!-- 首屏可见模块1 -->
<div class="module-1"></div>
<!-- 首屏可见模块2 -->
<div class="module-2"></div>
<script type="text/javascript">
window.firstScreen = Date.now();
</script>
<!-- 首屏不可见模块3 -->
<div class="module-3"></div>
<!-- 首屏不可见模块4 -->
<div class="module-4"></div>
</body>
</html>
В это время первое экранное время равноfirstScreen - performance.timing.navigationStart
На самом деле метод маркировки первого модуля экрана относительно редко используется в бизнесе, и большинству страниц необходимо извлекать данные через интерфейс, чтобы отобразить их полностью.
- Статистика самого медленного времени загрузки картинки над сгибом:
Обычно самой медленной загрузкой контента на первом экране является ресурс изображения, поэтому мы будем считать время загрузки самого медленного изображения на первом экране временем первого экрана.
После того, как дерево DOM построено, оно будет проходить по всем тегам изображений на первом экране и прослушивать событие загрузки всех тегов изображений и, наконец, проходить максимальное время загрузки тегов изображений и вычитать максимальное значение из этого максимума. стоимость.navigationStartВы можете получить приблизительное время экрана.
В это время первое экранное время равно加载最慢的图片的时间点 - performance.timing.navigationStart
- Пользовательский метод расчета содержимого верхней части страницы
Потому что сложнее подсчитать время, необходимое для загрузки изображений на первом экране. Поэтому в нашем бизнесе мы обычно упрощаем подсчет времени выше сгиба, настраивая содержимое модуля. Сделайте следующее:
- Игнорировать загрузку ресурсов, таких как изображения, и рассматривать только основной DOM страницы.
- Учитывайте только основные модули выше сгиба, а не строго все над линией сгиба.
1.4.3 Схема оптимизации первого экрана:
- Страница прямо: скелетный экран или SSR
- Оптимизация рендеринга первого кадра
- Динамическая загрузка ресурсов
- кеш браузера
- Оптимизация времени выполнения скрипта JavaScript
- Уменьшить перекомпоновку и перерисовку
- Оптимизированные решения для рендеринга страниц, такие как аппаратное ускорение для повышения производительности анимации.
1.5 Оптимизация рендеринга в браузере
- Оптимизация времени выполнения скрипта JavaScript
- Уменьшить перекомпоновку и перерисовку
- Аппаратное ускорение улучшает производительность анимации и т. д.
20. Как посчитать, сколько раз компонент появляется во вьюпорте? Как используется IntersectionObserver? Откуда вы знаете, что узел DOM появляется внутри области просмотра?
Компания: Куайшоу
Категория: JavaScript
Посмотреть анализ
1. Прокрутка монитора
Чтобы узнать, вошел ли элемент в «окно просмотра» (viewport), то есть может ли его увидеть пользователь, традиционный метод реализации заключается в прослушиванииscrollПосле события вызовите целевой элементgetBoundingClientRect()метод, получить координаты, соответствующие верхнему левому углу области просмотра, а затем определить, находится ли он в пределах области просмотра. Затем объявите глобальную переменную, которая увеличивается на единицу каждый раз, когда она появляется, и по ней можно подсчитать, сколько раз она появляется в окне просмотра. Недостатком этого метода является то, что из-заscrollСобытия происходят интенсивно и требуют большого объема вычислений, что может легко вызвать проблемы с производительностью.
Так что естьIntersectionObserver API
2. Обозреватель пересечения
2.1 API
var io = new IntersectionObserver(callback, option);
В приведенном выше кодеIntersectionObserverЭто конструктор, изначально предоставленный браузером и принимающий два параметра:callbackфункция обратного вызова при изменении видимости,optionявляется объектом конфигурации (этот параметр является необязательным).
Возвращаемое значение конструктора является экземпляром наблюдателя. примерobserveметод, чтобы указать, какой узел DOM отслеживать.
// 开始观察
io.observe(document.getElementById("example"));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
В приведенном выше кодеobserveАргумент является объектом узла DOM. Если вы хотите наблюдать за несколькими узлами, вам нужно вызвать этот метод несколько раз.
io.observe(elementA);
io.observe(elementB);
2.2 Параметры обратного вызова
При изменении видимости целевого элемента вызывается функция обратного вызова наблюдателяcallback.
callbackОбычно срабатывает дважды. Один раз, когда целевой элемент только что вошел в область просмотра (начиная с видимого), и один раз, когда он полностью покинул область просмотра (начало с невидимого).
var io = new IntersectionObserver((entries) => {
console.log(entries);
});
callbackаргументы функции (entries) представляет собой массив, каждый элемент которого являетсяIntersectionObserverEntryобъект. Если видимость двух наблюдаемых объектов изменяется одновременно,entriesМассив будет состоять из двух элементов.
IntersectionObserverEntryОбъект предоставляет информацию о целевом элементе и имеет в общей сложности шесть свойств.
{
time: 3893.92,
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
boundingClientRect: ClientRect {
// ...
},
intersectionRect: ClientRect {
// ...
},
intersectionRatio: 0.54,
target: element
}
Значение каждого атрибута следующее.
-
time: время изменения видимости, высокоточная метка времени в миллисекундах. -
target: наблюдаемый целевой элемент является объектом узла DOM. -
rootBounds: Информация о прямоугольной области корневого элемента,getBoundingClientRect()Возвращаемое значение метода или при отсутствии корневого элемента (т.е. прокрутка непосредственно относительно области просмотра)null -
boundingClientRect: Информация о прямоугольной области целевого элемента -
intersectionRect: Информация об области пересечения целевого элемента и области просмотра (или корневого элемента) -
intersectionRatio: видимый масштаб целевого элемента, т.е.intersectionRectЗаниматьboundingClientRect, когда полностью виден1, меньше или равно, когда полностью невидим0
2.3 Дополнительные объекты
IntersectionObserverВторой параметр для конструктора является объектом конфигурации. Он может установить следующие свойства.
2.3.1 пороговый атрибут:
thresholdСвойство определяет, когда запускается функция обратного вызова. Это массив, каждый элемент которого является пороговым значением, значение по умолчанию равно[0], то есть перекрестное отношение (intersectionRatio)достигать0Когда срабатывает функция обратного вызова.
new IntersectionObserver(
(entries) => {
/* ... */
},
{
threshold: [0, 0.25, 0.5, 0.75, 1],
}
);
Пользователь может настроить этот массив. Например,[0, 0.25, 0.5, 0.75, 1]Это означает, что когда целевой элемент видим 0%, 25%, 50%, 75%, 100%, будет запущена функция обратного вызова.
2.3.2 корневой атрибут, атрибут rootMargin:
Много раз целевой элемент прокручивается не только вместе с окном, но и внутри контейнера (например, вiframeпрокрутка в окне). Прокрутка внутри контейнера также влияет на видимость целевого элемента.
IntersectionObserver API поддерживает прокрутку внутри контейнера.rootАтрибут указывает узел-контейнер (т. е. корневой элемент), в котором находится целевой элемент. Обратите внимание, что элемент-контейнер должен быть предком целевого элемента.
var opts = {
root: document.querySelector(".container"),
rootMargin: "500px 0px",
};
var observer = new IntersectionObserver(callback, opts);
В приведенном выше коде, кромеrootсвойства и свойство rootMargin. Последний определяет корневой элементmargin, чтобы расширить или уменьшитьrootBoundsразмер этого прямоугольника, который влияетintersectionRectРазмер площади пересечения. Он использует методы определения CSS, такие как10px 20px 30px 40px, представляющие значения в верхнем, правом, нижнем и левом направлениях.
После этой настройки, будь то прокрутка окна или прокрутка внутри контейнера, пока изменяется видимость целевого элемента, наблюдатель будет срабатывать.
---------------------------------- Разделительная линия о боевых искусствах ------------------------------
Из-за ограничения количества символов в текстах Nuggets остальные ответы можно просмотреть, отсканировав QR-код.
21.versions – это список номеров версий проекта. Из-за многократного обслуживания и нарушений реализуйте функцию обработки номеров версий вручную.
var versions = ["1.45.0", "1.5", "6", "3.3.3.3.3.3.3"];
// 要求从小到大排序,注意'1.45'比'1.5'大
function sortVersion(versions) {
// TODO
}
// => ['1.5','1.45.0','3.3.3.3.3.3','6']
Компания: Тутиао
Категория: JavaScript
22. Что такое микросервисы, в чем разница между микросервисами и монолитными приложениями и каковы преимущества использования микросервисов?
Категория: узел
23. Реализуйте метод повтора вручную
function repeat(func, times, wait) {
// TODO
}
const repeatFunc = repeat(alert, 4, 3000);
// 调用这个 repeatFunc ("hellworld"),会alert4次 helloworld, 每次间隔3秒
Компания: Тутиао
Категория: JavaScript
24. Пожалуйста, перечислите текущие основные технологии модуляции JavaScript? подскажите разницу между ними?
Компания: Xuanwu Technology
Категория: JavaScript
25. Пожалуйста, опишите концепции Scope, Closure и Prototype в JavaScript, а также объясните принципы инкапсуляции и наследования JavaScript.
Компания: Xuanwu Technology
Категория: JavaScript