From 6783d6ff33228ecbedc13d296ebc03b2a926a691 Mon Sep 17 00:00:00 2001 From: "pengpeng.chen" Date: Wed, 6 Mar 2024 22:06:41 +0800 Subject: [PATCH] feat: 0306 --- docs/interview/browser/webCache.md | 10 +- docs/interview/experiences/12interview0703.md | 123 ++++++++ docs/interview/experiences/21leetcodeA.md | 89 +++++- docs/interview/experiences/23leetcodeHotA.md | 2 + .../experiences/practise/202403/0305.js | 281 +++++++++++++++++- .../experiences/practise/202403/0306.js | 128 ++++++++ .../experiences/practise/202403/0307.js | 8 + .../experiences/practise/202403/0308.js | 3 + docs/interview/index.md | 21 +- docs/interview/js/15gc.md | 2 +- docs/interview/logic/treeMd.md | 2 +- docs/node/index.md | 38 ++- docs/node/vite/1.index.md | 2 +- 13 files changed, 688 insertions(+), 21 deletions(-) create mode 100644 docs/interview/experiences/practise/202403/0308.js diff --git a/docs/interview/browser/webCache.md b/docs/interview/browser/webCache.md index a20b647..18c23d1 100644 --- a/docs/interview/browser/webCache.md +++ b/docs/interview/browser/webCache.md @@ -45,6 +45,12 @@ web 缓存主要指的是两部分:浏览器缓存和 http 缓存。 占内存(有些缓存会被存到内存中) +### 基本原理 + +- 1. 浏览器在加载资源时,根据请求头的 expires 和 cache-control 判断是否命中强缓存,如果命中则直接从缓存系统读取资源,不会发请求到服务器 +- 2. 如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过 Last-modified 和 Etag 验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存系统中读取资源 +- 3. 如果前面两者都没有命中,直接从服务器加载资源 + ### http 之强缓存 http 缓存分为两种缓存,强制缓存和协商缓存, 我们来深度剖析一下强制缓存和协商缓存各自的优劣以及他们的使用场景以及使用原理 @@ -114,7 +120,7 @@ private 则表示资源只能在客户端被缓存,拒绝资源在代理服务 ### http 之协商缓存 -#### 基于 last-modified 的协商缓存 +#### 基于 last-modified/if-modified-since 的协商缓存 基于 last-modified 的协商缓存实现方式是: @@ -134,7 +140,7 @@ last-modified 和 if-Modified-Since 缺点 1.因为是根据文件修改时间 了解决上述的这两个问题。从 http1.1 开始新增了一个头信息,ETag -#### Etag/if-none-match +#### 基于 Etag/if-none-match 协商缓存 ETag 就是将原先协商缓存的比较时间戳的形式修改成了比较**文件指纹**。 diff --git a/docs/interview/experiences/12interview0703.md b/docs/interview/experiences/12interview0703.md index c3fbdb6..b98a65d 100644 --- a/docs/interview/experiences/12interview0703.md +++ b/docs/interview/experiences/12interview0703.md @@ -129,6 +129,129 @@ const arr = [ parentId: 4, }, ]; +[ + { + id: 0, + name: '', + children: [ + { + id: 2, + name: '部门B', + children: [ + { + id: 1, + name: '部门A', + children: [ + { + id: 3, + name: '部门C', + children: [], + }, + ], + }, + { + id: 5, + name: '部门D', + children: [], + }, + ], + }, + ], + }, +]; + +function arrToTree(arr) { + let ans = []; + var dfs = (arr, res, parentId) => { + for (let item of arr) { + if (parentId == item.parentId) { + const one = { + ...item, + children: [], + }; + res.push(one); + dfs(arr, one.children, item.id); + delete item.children; + } + } + }; + dfs(arr, ans, 0); + return ans; +} +``` + +树转数组 + +```js +var listTree = [ + { + id: 1, + name: '部门1', + pid: 0, + children: [ + { + id: 2, + name: '部门1-1', + pid: 1, + children: [ + { + id: 4, + name: '部门1-1-1', + pid: 2, + children: [], + }, + ], + }, + { + id: 3, + name: '部门1-2', + pid: 1, + children: [ + { + id: 5, + name: '部门1-2-1', + pid: 3, + children: [], + }, + ], + }, + ], + }, + { + id: 6, + name: '部门2', + pid: 0, + children: [ + { + id: 7, + name: '部门2-1', + pid: 6, + children: [], + }, + ], + }, + { + id: 8, + name: '部门3', + pid: 0, + children: [], + }, +]; +function treeToArr(tree) { + let ans = []; + var dfs = (arr, res) => { + for (let child of arr) { + res.push(child); + if (child.children.length) { + dfs(child.children, res); + } + delete child.children; + } + }; + dfs(tree, ans); + return ans; +} +treeToArr(listTree); ``` - Node 是怎么部署的? pm2 守护进程的原理? diff --git a/docs/interview/experiences/21leetcodeA.md b/docs/interview/experiences/21leetcodeA.md index 6188cb1..ff062eb 100644 --- a/docs/interview/experiences/21leetcodeA.md +++ b/docs/interview/experiences/21leetcodeA.md @@ -152,6 +152,36 @@ var reverseList = function (head) { ## 3.LRU 缓存机制 +```js +class LRU { + constructor(limit) { + this.limit = limit; + this.cache = new Map(); + } + get(key) { + if (this.cache.has(key)) { + const val = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, val); + } else { + return -1; + } + } + set(key, val) { + const size = this.cache.size; + if (this.cache.has(key)) { + this.cache.delete(key); + } else { + if (size >= this.limit) { + const oldKey = this.cache.keys().next().value; + this.cache.delete(oldKey); + } + } + this.cache.set(key, val); + } +} +``` + ## 4.数组中的第 K 个最大元素 ```js @@ -160,6 +190,8 @@ var reverseList = function (head) { ## 5.K 个一组链表反转 +> 不会! + ```js var reverseKGroup = function (head, k) { var dummy = { @@ -316,6 +348,8 @@ var mergeTwoLists = function (l1, l2) { }; ``` +## 9.两数之和 + ## [10.最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring/) > [参考解析](https://leetcode.cn/problems/longest-palindromic-substring/solutions/9001/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-bao-gu/) @@ -497,7 +531,20 @@ function allPermute(arr) { allPermute([1, 2, 3]); ``` -## 19.二叉树的最近公共祖先 +## [19.二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/) + +```js +// 最近公共祖先 +var lowestCommonAncestor = function (root, p, q) { + if (root == null || p == root || q == root) return root; + let left = lowestCommonAncestor(root.left, p, q); + let right = lowestCommonAncestor(root.right, p, q); + if (right != null && left != null) return root; + if (left != null) return left; + if (right != null) return right; + return null; +}; +``` ## 20.手撕快速排序 @@ -568,6 +615,42 @@ var zigzagLevelOrder = function (root) { }; ``` +## 23.螺旋矩阵 + +```js +function matrix(arr) { + if (!arr.length) return []; + let up = 0; + let down = arr.length - 1; + let left = 0; + let right = arr[0].length - 1; + let ans = []; + while (true) { + // 从左到右 + for (let i = left; i <= right; i++) { + ans.push(arr[up][i]); + } + if (++up > down) break; + // 从上到下 + for (let i = up; i <= down; i++) { + ans.push(arr[i][right]); + } + if (left > --right) break; + // 从右往左 + for (let i = right; i >= left; i--) { + ans.push(arr[down][i]); + } + if (up > --down) break; + // 从下往上 + for (let i = down; i >= up; i--) { + ans.push(arr[i][left]); + } + if (++left > right) break; + } + return ans; +} +``` + ## 24.相交链表 ```js @@ -576,7 +659,7 @@ function insectionLink(headA, headB) { var set = new Set(); while (cur) { set.add(cur); - cur = cur.mext; + cur = cur.next; } cur = headB; while (cur) { @@ -666,7 +749,7 @@ function water(nums) { let curTop = stack.slice(-1); // 当前栈顶元素 let h = Math.min(cur, nums[curTop]) - nums[top]; let w = i - curTop - 1; - res = res + h * w; + res += +h * w; } } stack.push(i); diff --git a/docs/interview/experiences/23leetcodeHotA.md b/docs/interview/experiences/23leetcodeHotA.md index 0a563ad..386363e 100644 --- a/docs/interview/experiences/23leetcodeHotA.md +++ b/docs/interview/experiences/23leetcodeHotA.md @@ -17,6 +17,8 @@ nav: * 1.二叉树的最小深度和最大深度 * 2.最长公共前缀 * 3.删除升序链表中重复出现的所有节点 + * 4.数组转树 + * 5.树转数组 */ ``` diff --git a/docs/interview/experiences/practise/202403/0305.js b/docs/interview/experiences/practise/202403/0305.js index a482be1..7aa864b 100644 --- a/docs/interview/experiences/practise/202403/0305.js +++ b/docs/interview/experiences/practise/202403/0305.js @@ -2,5 +2,284 @@ * 1.零钱兑换 * 2.打家劫舍 * 3.合并区间 - * 4.二叉树中的最大路径和 + * 4.爬楼梯 + * 5.螺旋矩阵 + * 6.字符串相乘 + * 7.最长重复子数组 + * 8.最长公共序列 + * 9.接雨水 + * 10.二叉树的最近公共祖先 + * 11.二叉树中的最大路径和 */ + +// 1/2 5总共有几种可能 +function clim(n) { + let dp = new Array(n + 1).fill(0); + dp[1] = 1; + dp[2] = 2; + for (let i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} +/** +给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 +计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 +你可以认为每种硬币的数量是无限的。 +示例 1: +输入:coins = [1, 2, 5], amount = 11 +输出:3 +解释:11 = 5 + 5 + 1 + */ +var coinChange = function (coins, amount) { + let dp = new Array(amount + 1).fill(Infinity); + for (let i = 1; i <= amount; i++) { + for (let item of coins) { + dp[i] += dp[i - (amount - item)]; + } + } + return dp[amount] == Infinity ? '-1' : dp[amount]; +}; + +/** +输入:root = [1,2,3], targetSum = 5 +输出:false +解释:树中存在两条根节点到叶子节点的路径: +(1 --> 2): 和为 3 +(1 --> 3): 和为 4 +不存在 sum = 5 的根节点到叶子节点的路径。 + */ +var hasPathSum = function (root, targetSum) { + if (!root) return false; + if (root.left == null && root.right == null) { + return targetSum - root.val == 0; + } + return ( + hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val) + ); +}; + +var hasPathSum = function (root, targetSum) { + if (!root) return false; + let queue = [root]; + let valQueue = [root.val]; + while (queue.length || valQueue.length) { + let node = queue.shift(); + let val = valQueue.shift; + if (node.left == null && node.right == null) { + return val - targetSum == 0; + } + if (node.left) { + queue.push(node.left); + valQueue.push(val + node.left.val); + } + if (node.right) { + queue.push(node.right); + valQueue.push(val + node.right.val); + } + } + return false; +}; + +var hasPathSum = function (root, targetSum) { + if (!root) return false; + let dfs = (node, sum) => { + if (!node) return; + if (node.left == null && node.right == null) { + return targetSum == sum; + } + if (node.left) { + dfs(node.left, sum + node.val); + } + if (node.right) { + dfs(node.right, sum + node.val); + } + }; + dfs(root, 0); + return false; +}; + +/** +输入:root = [-10,9,20,null,null,15,7] +输出:42 +解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42 + */ +var maxPathSum = function (root) {}; + +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * + */ +var lowestCommonAncestor = function (root, p, q) { + if (p == root || root == q || root == null) return root; + let left = lowestCommonAncestor(root.left, p, q); + let right = lowestCommonAncestor(root.right, p, q); + if (left !== null && right !== null) return root; + if (left == null && right !== null) return right; + if (left != null && right == null) return left; + return null; +}; + +/** +输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] +输出:6 +解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 + */ + +// 单调栈 +var trap = function (arr) { + if (!arr.length) return 0; + let len = arr.length; + let stack = [0]; + let ans = 0; + for (let i = 1; i < len; i++) { + let cur = arr[i]; + if (cur < arr[stack.slice(-1)]) { + stack.push(i); + } else { + while (cur && cur > arr[stack.slice(-1)]) { + // 左中右 + const top = stack.pop(); + if (stack.length !== 0) { + const lastItem = stack.slice(-1); + // 宽度 + let w = i - lastItem - 1; + // 高度 + let h = Math.min(arr[i], arr[lastItem]) - arr[top]; + ans += h * w; + } + } + stack.push(i); + } + } + return ans; +}; + +function longSame(a, b) { + let [aLen, bLen] = [a.length, b.length]; + let dp = new Array(aLen + 1).fill().map(() => new Array(bLen + 1).fill(0)); + let max = 0; + for (let i = 1; i <= aLen; i++) { + for (let j = 1; j <= bLen; j++) { + if (a[i - 1] == b[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } + max = Math.max(dp[i][j], max); + } + } + return max; +} + +function longestPublicArr(a, b) { + let [aLen, bLen] = [a.length, b.length]; + let dp = new Array(aLen + 1).fill().map(() => new Array(bLen + 1).fill(0)); + let max = 0; + for (let i = 1; i <= aLen; i++) { + for (let j = 1; j <= bLen; j++) { + if (a[i - 1] == b[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[aLen][bLen]; +} + +function mulStr(a, b) { + const [aLen, bLen] = [a.length, b.length]; + let arr = new Array(aLen + bLen).fill(0); + for (let i = aLen - 1; i >= 0; i--) { + for (let j = bLen - 1; j >= 0; j--) { + let p1 = i + j; + let p2 = p1 + 1; + let mul = +a[i] * +b[j]; + let data = mul + arr[p2]; + arr[p2] = data % 10; + arr[p1] = Math.floor(data / 10) + arr[p1]; + } + } + console.log(arr, 'arr'); + while (arr[0] == 0) { + arr.shift(); + } + return arr.length ? arr.join('') : '0'; +} +mulStr('11', '33'); + +function matrix(arr) { + if (!arr.length) return []; + let up = 0; + let down = arr.length - 1; + let left = 0; + let right = arr[0].length - 1; + let ans = []; + while (true) { + // 从左到右 + for (let i = left; i <= right; i++) { + ans.push(arr[up][i]); + } + if (++up > down) break; + // 从上到下 + for (let i = up; i <= down; i++) { + ans.push(arr[i][right]); + } + if (left > --right) break; + // 从右往左 + for (let i = right; i >= left; i--) { + ans.push(arr[down][i]); + } + if (up > --down) break; + // 从下往上 + for (let i = down; i >= up; i--) { + ans.push(arr[i][left]); + } + if (++left > right) break; + } + return ans; +} + +function threeSum(arr) { + if (!arr.length) return []; + if (arr.length < 3) return arr; + arr.sort((a, b) => a - b); + let ans = []; + for (let i = 0; i < arr.length; i++) { + let cur = arr[i]; + if (cur > 0) continue; + if (i > 0 && arr[i] == arr[i - 1]) continue; + let l = i + 1; + let r = arr.length - 1; + while (l < r) { + let sum = arr[l] + arr[r] + cur; + if (sum == 0) { + ans.push([cur, arr[l], arr[r]]); + while (l < r && arr[r] == arr[r - 1]) { + r--; + } + while (l < r && arr[l] == arr[l + 1]) { + l++; + } + l++; + r--; + } else if (sum > 0) { + r--; + } else { + l++; + } + } + } +} + +function rob(arr) { + let len = arr.length; + let dp = new Array(len).fill(0); + dp[0] = arr[0]; + dp[1] = Math.max(arr[0], arr[1]); + for (let i = 2; i < len; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + arr[i]); + } + return dp[len - 1]; +} diff --git a/docs/interview/experiences/practise/202403/0306.js b/docs/interview/experiences/practise/202403/0306.js index 28b83da..ab2a367 100644 --- a/docs/interview/experiences/practise/202403/0306.js +++ b/docs/interview/experiences/practise/202403/0306.js @@ -4,8 +4,136 @@ * 3.超时重传机制 * 4.Promise.finally * 5.reduce + * 6.二叉树中的最大路径和 + * 7.对称二叉树 + * 8.最大深度和最小深度 + * 9.重排链表 + * 10.最小路径和 + * 11.树转数组 + * 12.数组转树 */ +function tryAgain(fn, time) {} + +class Promise { + static finally(cb) { + return this.then( + (val) => Promise.resolve(cb()).then(() => val), + (err) => Promise.resolve(cb()).then(() => err), + ); + } +} +Promise.finally(() => {}); + +function mockNew(fn, ...rest) { + let target = Object.create(fn.prototype); + let res = fn.apply(target, rest); + return res instanceof Object ? res : target; +} + +function mockApply(con, ...rest) { + con = new Object(con) || window; + let sys = Symbol(''); + con[sys] = this; + let res = con[sys](...rest); + delete con[sys]; + return res; +} + +function mockBind(con, ...rest) { + const self = this; + const arg1 = Array.prototype.slice.call(arguments, 1); + let bridgeFn = () => { + const arg2 = Array.prototype.slice.call(arguments); + self.apply(this instanceof self ? this : con, [...arg1, ...arg2]); + }; + let BFn = function () {}; + BFn.prototype = this.prototype; + bridgeFn.prototype = new BFn(); + return bridgeFn; +} + +function Name(name) { + return name; +} +var instance1 = mockNew(Name, name); + +function mocinstanceOf(l, r) { + return r.prototype.isPrototypeOf(l); +} + +// 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] +// 输出:7 +// 解释:因为路径 1→3→1→1→1 的总和最小。 + +function water(arr) { + if (!arr.length) return 0; + let sum = 0; + let stack = [0]; + const len = arr.length; + for (let i = 1; i < len; i++) { + let cur = arr[i]; // 后 + if (cur < arr[stack.slice(-1)]) { + stack.push(i); + } else { + while (cur && cur > arr[stack.slice(-1)]) { + let top = stack.pop(); // 中 + let last = stack.slice(-1); // 前 + let w = i - last - 1; + let h = Math.min(cur, arr[last]) - arr[top]; + sum += w * h; + } + stack.push(i); + } + } + return sum; +} + +function insertLink(A, B) { + let s = new Set(); + let cur = A; + while (cur) { + s.add(cur); + cur = cur.next; + } + cur = B; + while (cur) { + if (s.has(cur)) { + return cur; + } + cur = cur.next; + } + return null; +} + +class LRU { + constructor(limit) { + this.limit = limit; + this.cache = new Map(); + } + get(key) { + if (this.cache.has(key)) { + const val = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, val); + } else { + return -1; + } + } + set(key, val) { + const size = this.cache.size; + if (this.cache.has(key)) { + this.cache.delete(key); + } else { + if (size >= this.limit) { + const oldKey = this.cache.keys().next().value; + this.cache.delete(oldKey); + } + } + this.cache.set(key, val); + } +} + Array.prototype.mockReduce = function (fn, init) {}; function mockInstanceOf(l, r) { diff --git a/docs/interview/experiences/practise/202403/0307.js b/docs/interview/experiences/practise/202403/0307.js index e69de29..d2a4055 100644 --- a/docs/interview/experiences/practise/202403/0307.js +++ b/docs/interview/experiences/practise/202403/0307.js @@ -0,0 +1,8 @@ +/** + * 1.分饼干 + * 2.删除有序数组的重复项 + * 3.二叉树直径 + * 4.背包问题 + * 5.括号生成 + * 6. + */ diff --git a/docs/interview/experiences/practise/202403/0308.js b/docs/interview/experiences/practise/202403/0308.js new file mode 100644 index 0000000..68bdf29 --- /dev/null +++ b/docs/interview/experiences/practise/202403/0308.js @@ -0,0 +1,3 @@ +/** + * 1.每天都在写,到底记住了多少?离03月20日还剩两周,今天要做的就是反思回想 + */ diff --git a/docs/interview/index.md b/docs/interview/index.md index 4b2faa0..99f4d2e 100644 --- a/docs/interview/index.md +++ b/docs/interview/index.md @@ -81,14 +81,14 @@ nav: - 浏览器渲染原理 🌟🌟🌟🌟🌟 - jwt/cookie/session 🌟🌟🌟 - csrf/xss 🌟🌟🌟 -- 跨域 🌟🌟🌟 +- 跨域 🌟🌟🌟🌟 - web 缓存 🌟🌟🌟🌟 - 性能优化 🌟🌟🌟🌟 ### nodejs - ESModule 和 Commonjs 区别 🌟🌟🌟🌟🌟 -- treeShaking 原理 🌟🌟🌟 +- treeShaking 原理 🌟🌟🌟🌟🌟 - v8 相关 - 垃圾回收 🌟🌟🌟 - 如何利用多核 CPU🌟🌟🌟🌟 @@ -96,17 +96,18 @@ nav: ### 网络 -- http1.0/http1.1 -- http2/http3 -- https -- tcp/udp/http -- 缓存 +- http1.0/http1.1/http2🌟🌟🌟🌟 +- http3🌟🌟 +- https🌟🌟🌟 +- tcp/udp/http🌟🌟🌟 +- http 缓存 🌟🌟🌟🌟 +- WebSocket🌟🌟 ### ts -- 各种类型体操 -- 逆变和协变 -- interface 和 type 区别 +- 各种类型体操 🌟🌟🌟🌟🌟 +- 逆变和协变 🌟🌟 +- interface 和 type 区别 🌟🌟🌟🌟🌟 > 在哪里跌倒 就在哪里爬起来 diff --git a/docs/interview/js/15gc.md b/docs/interview/js/15gc.md index d2bb6ad..c0bf23f 100644 --- a/docs/interview/js/15gc.md +++ b/docs/interview/js/15gc.md @@ -80,7 +80,7 @@ foo(); ## 回收策略 -### 标记清楚算法 Mark-sweep +### 标记清除算法 Mark-sweep 此算法分为 **标记** 和 **清除** 两个阶段,标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁 diff --git a/docs/interview/logic/treeMd.md b/docs/interview/logic/treeMd.md index 1b62696..b78bdf0 100644 --- a/docs/interview/logic/treeMd.md +++ b/docs/interview/logic/treeMd.md @@ -90,7 +90,7 @@ var sumNumbers = function (root) { var dfs = (root) => { // 叶子节点 if (root.left == null && root.right == null) { - num = num + Number(root.val); + num += Number(root.val); } if (root.left) { root.left.val = `${root.val}${root.left.val}`; diff --git a/docs/node/index.md b/docs/node/index.md index 2eaffa0..aa8731d 100644 --- a/docs/node/index.md +++ b/docs/node/index.md @@ -21,6 +21,42 @@ nav: - webpack - microFrontend +## webpack/vite/rollup + +### vite 原理 + +基于 esbuild 与 Rollup,依靠浏览器自身 ESM 编译功能, 实现极致开发体验的新一代构建工具! + +相关概念: + +- 依赖: 指开发不会变动的部分(npm 包、UI 组件库),esbuild 进行预构建。 +- 源码: 浏览器不能直接执行的非 js 代码(.jsx、.css、.vue 等),vite 只在浏览器请求相关源码的时候进行转换,以提供 ESM 源码。 + +#### 开发环境 + +- Vite 以 原生 ESM 方式提供源码。这实际上是让**浏览器接管了打包程序的部分工作**:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。 + +- 浏览器执行 ESM 的 import 时,会向 dev server 发起该模块的 ajax 请求,服务器对源码做简单处理后返回给浏览器。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理 + +- Vite 中 HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块失活,使得无论应用大小如何,HMR 始终能保持快速更新。 + +- Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 **304 Not Modified** 进行协商缓存,而依赖模块请求则会通过 **Cache-Control: max-age=31536000**,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。 + +- 使用 esbuild 处理项目依赖,esbuild 使用 go 编写,比一般 node.js 编写的编译器快几个数量级。 + +#### 生产环境 + +集成 Rollup 打包生产环境代码,依赖其成熟稳定的生态与更简洁的**插件机制**。 + +#### 处理流程对比 + +- Webpack 通过先将整个应用打包,再将打包后代码提供给 dev server,开发者才能开始开发 +- Vite 直接将源码交给浏览器,实现 dev server 秒开,浏览器显示页面需要相关模块时,再向 dev server 发起请求,服务器简单处理后,将该模块返回给浏览器,实现真正意义的按需加载。 + +### webpack 中的说说 Loader 和 Plugin 的区别?编写 Loader,Plugin 的思路? + +### 说说 webpack 构建流程 + ## Cjs/Amd/umd/Esmodule ### Cjs @@ -213,8 +249,6 @@ vscode debugger 的使用主要是在 .vscode/launch.json 里面添加调试配 ## ssr -## webpack/vite/rollup - ## BFF(Backends For Frontends)) ## 中间件 diff --git a/docs/node/vite/1.index.md b/docs/node/vite/1.index.md index 20be79c..34a0188 100644 --- a/docs/node/vite/1.index.md +++ b/docs/node/vite/1.index.md @@ -35,7 +35,7 @@ vite 在开发环境和生产环境分别做了不同的处理,在开发环境 - 对于 vite、snowpack 这类工具,提供的都是 noBundle 服务,无需等待构建,直接提供服务。 - 对于项目中的第三方依赖,仅在初次启动和依赖变化时重构建,会执行一个依赖预构建的过程。由于是基于 esBuild 做的构建,所以非常快。 -- 对于项目代码,则会依赖于浏览器的 ESM 的支持,**直接按需访问,不必全量构建**。 +- 对于项目代码,则会依赖于**浏览器的 ESM 的支持**,**直接按需访问,不必全量构建**。 ### 为什么在生产环境中构建使用 rollup?